You've already forked obsidian-visualiser
Item Improvements added to the homebrew manager.
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import type { Ability, ArmorConfig, AspectConfig, CharacterConfig, CommonItemConfig, DamageType, Feature, FeatureChoice, FeatureEquipment, FeatureItem, FeatureList, FeatureState, FeatureTree, FeatureValue, ItemConfig, Level, MainStat, MundaneConfig, RaceConfig, Resistance, SpellConfig, TrainingLevel, WeaponConfig, WeaponType, WondrousConfig } from "~/types/character";
|
||||
import type { Ability, ArmorConfig, AspectConfig, CharacterConfig, CommonItemConfig, CraftingType, DamageType, Feature, FeatureChoice, FeatureEquipment, FeatureItem, FeatureList, FeatureState, FeatureTree, FeatureValue, ImprovementConfig, ItemConfig, Level, MainStat, MundaneConfig, RaceConfig, Resistance, SpellConfig, TrainingLevel, WeaponConfig, WeaponType, WondrousConfig } from "~/types/character";
|
||||
import { div, dom, icon, span, text, type NodeChildren } from "#shared/dom";
|
||||
import { MarkdownEditor } from "#shared/editor";
|
||||
import { preview } from "#shared/proses";
|
||||
import { button, checkbox, combobox, foldable, input, multiselect, numberpicker, optionmenu, select, tabgroup, table, toggle, type Option } from "#shared/components";
|
||||
import { button, checkbox, combobox, foldable, input, multiselect, numberpicker, optionmenu, select, tabgroup, table, tagpicker, toggle, type Option } from "#shared/components";
|
||||
import { confirm, contextmenu, fullblocker, tooltip } from "#shared/floating";
|
||||
import { ABILITIES, abilityTexts, ALIGNMENTS, alignmentTexts, colorByRarity, DAMAGE_TYPES, damageTypeTexts, elementTexts, LEVELS, MAIN_STATS, mainStatShortTexts, mainStatTexts, masteryTexts, rarityText, RESISTANCES, resistanceTexts, SPELL_ELEMENTS, SPELL_TYPES, spellTypeTexts, subnameFactory, weaponTypeTexts } from "#shared/character";
|
||||
import { ABILITIES, abilityTexts, ALIGNMENTS, alignmentTexts, colorByRarity, craftingText, DAMAGE_TYPES, damageTypeTexts, elementTexts, LEVELS, MAIN_STATS, mainStatShortTexts, mainStatTexts, masteryTexts, rarityText, RESISTANCES, resistanceTexts, SPELL_ELEMENTS, SPELL_TYPES, spellTypeTexts, subnameFactory, weaponTypeTexts } from "#shared/character";
|
||||
import characterConfig from "#shared/character-config.json";
|
||||
import { getID } from "#shared/general";
|
||||
import markdown, { markdownReference, renderMDAsText } from "#shared/markdown";
|
||||
@@ -32,6 +32,7 @@ export class HomebrewBuilder
|
||||
{ id: 'aspects', title: [ text("Aspects") ], content: () => this.aspects() },
|
||||
{ id: 'actions', title: [ text("Actions") ], content: () => this.actions() },
|
||||
{ id: 'items', title: [ text("Objets") ], content: () => this.items() },
|
||||
{ id: 'improvements', title: [ text("Améliorations") ], content: () => this.improvements() },
|
||||
{ id: 'trees', title: [ text("Arbres") ], content: () => this.trees() },
|
||||
], { focused: 'training', class: { container: 'flex-1 outline-none max-w-full w-full overflow-y-auto', tabbar: 'flex w-full flex-row gap-4 items-center justify-center relative' } });
|
||||
|
||||
@@ -512,6 +513,7 @@ export class HomebrewBuilder
|
||||
rarity: 'common',
|
||||
equippable: false,
|
||||
consummable: false,
|
||||
craft: { natural: 0, mineral: 0, processed: 0, magical: 0 },
|
||||
};
|
||||
switch(category)
|
||||
{
|
||||
@@ -530,7 +532,7 @@ export class HomebrewBuilder
|
||||
config.items[item.id!] = item;
|
||||
};
|
||||
const remove = (item: ItemConfig) => {
|
||||
confirm(`Voulez vous vraiment supprimer l'effet "${item.name}" ?`).then(e => {
|
||||
confirm(`Voulez vous vraiment supprimer l'objet "${item.name}" ?`).then(e => {
|
||||
if(e)
|
||||
{
|
||||
delete config.texts[item.description];
|
||||
@@ -568,6 +570,59 @@ export class HomebrewBuilder
|
||||
}
|
||||
}) ] ) ];
|
||||
}
|
||||
improvements()
|
||||
{
|
||||
const defaultImprovement = (): ImprovementConfig => {
|
||||
return {
|
||||
id: getID(),
|
||||
name: '',
|
||||
description: getID(), // i18nID
|
||||
craft: {},
|
||||
rarity: 'common',
|
||||
cursed: false,
|
||||
power: 0,
|
||||
effect: [],
|
||||
};
|
||||
};
|
||||
const add = () => {
|
||||
const improvement = defaultImprovement();
|
||||
setText(improvement.description, '');
|
||||
config.improvements[improvement.id!] = improvement;
|
||||
};
|
||||
const remove = (improvement: ImprovementConfig) => {
|
||||
confirm(`Voulez vous vraiment supprimer l'amélioration "${improvement.name}" ?`).then(e => {
|
||||
if(e)
|
||||
{
|
||||
delete config.texts[improvement.description];
|
||||
delete config.improvements[improvement.id];
|
||||
}
|
||||
});
|
||||
};
|
||||
const edit = (improvement: ImprovementConfig) => {
|
||||
ImprovementPanel.edit(improvement).then(f => {
|
||||
Object.assign(config.improvements[f.id]!, f);
|
||||
}).catch((e) => {});
|
||||
}
|
||||
return [ div('flex px-8 py-4 flex-col gap-4', [ div('flex flex-row-reverse', [ button(icon('radix-icons:plus'), add, 'p-1') ]), div('grid grid-cols-2 gap-1', {
|
||||
list: () => Object.keys(config.improvements),
|
||||
render: (e, _c) => {
|
||||
const improvement = config.improvements[e];
|
||||
|
||||
if(!improvement) return;
|
||||
|
||||
return _c ?? div('border border-light-35 dark:border-dark-35 p-1 gap-2', [ div('flex flex-row justify-between', [
|
||||
div('flex flex-row items-center gap-4 ps-2', [ span(() => [colorByRarity[improvement.rarity], 'text-lg font-semibold'], () => improvement.name) ]),
|
||||
div('flex flex-row gap-1', [
|
||||
div('flex flex-row items-center divide-x divide-light-50 dark:divide-dark-50 divide-dashed px-2', [
|
||||
div('flex flex-row w-12 gap-2 justify-between items-center px-2', [ icon('game-icons:bolt-drop', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }), span('', () => improvement.power) ]),
|
||||
]),
|
||||
button(icon('radix-icons:pencil-2'), () => edit(config.improvements[e]!), 'p-1'),
|
||||
button(icon('radix-icons:trash'), () => remove(config.improvements[e]!), 'p-1'),
|
||||
])
|
||||
]), div('px-2 pb-1', [ () => markdown(getText(improvement.description)) ]) ]);
|
||||
}
|
||||
}) ] ) ];
|
||||
}
|
||||
trees()
|
||||
{
|
||||
const add = () => {
|
||||
@@ -976,8 +1031,13 @@ export class ItemPanel
|
||||
}, 'p-1'), 'Annuler', 'left'),
|
||||
]),
|
||||
foldable([
|
||||
div('flex flex-row col-span-2 gap-2 items-center justify-between', [ span('flex flex-row gap-2 items-center', 'Fabrication'), div('flex flex-row items-center gap-2 w-1/2', [ select<CraftingType>(Object.entries(craftingText).map(e => ({ text: e[1], value: e[0] as CraftingType })), { defaultValue: _item.craft.ability ?? 'crafter', change: (v) => _item.craft.ability = v, class: { container: '!w-1/2' } }), numberpicker({ defaultValue: _item.craft.difficulty ?? 0, input: (v) => _item.craft.difficulty = v, class: 'w-12' }) ]), div('flex flex-row items-center gap-1', [
|
||||
numberpicker({ defaultValue: _item.craft.natural, disabled: _item.craft.natural === undefined, input: (v) => _item.craft.natural = v, class: 'w-10' }),
|
||||
numberpicker({ defaultValue: _item.craft.mineral, disabled: _item.craft.mineral === undefined, input: (v) => _item.craft.mineral = v, class: 'w-10' }),
|
||||
numberpicker({ defaultValue: _item.craft.processed, disabled: _item.craft.processed === undefined, input: (v) => _item.craft.processed = v, class: 'w-10' }),
|
||||
numberpicker({ defaultValue: _item.craft.magical, disabled: _item.craft.magical === undefined, input: (v) => _item.craft.magical = v, class: 'w-10' }),
|
||||
]) ]),
|
||||
div('flex flex-row gap-2 items-center justify-between', [ div('flex flex-row gap-2 items-center', [ checkbox({ defaultValue: _item.weight !== undefined, change: function(value) { _item.weight = value ? 0 : undefined; if(this.parentElement?.parentElement?.children[1]) { (this.parentElement.parentElement.children[1] as Element & { disabled: boolean }).disabled = !value; (this.parentElement.parentElement.children[1] as HTMLInputElement).value = value ? '0' : ''; } this.parentElement?.toggleAttribute('data-disabled', !value) }, class: { container: '!w-4 !h-4' } }), span('', 'Poids'), ]), numberpicker({ defaultValue: _item.weight, disabled: _item.weight === undefined, input: (v) => _item.weight = v, class: '!w-1/3' }), ]),
|
||||
div('flex flex-row gap-2 items-center justify-between', [ div('flex flex-row gap-2 items-center', [ checkbox({ defaultValue: _item.price !== undefined, change: function(value) { _item.price = value ? 0 : undefined; if(this.parentElement?.parentElement?.children[1]) { (this.parentElement.parentElement.children[1] as Element & { disabled: boolean }).disabled = !value; (this.parentElement.parentElement.children[1] as HTMLInputElement).value = value ? '0' : ''; } this.parentElement?.toggleAttribute('data-disabled', !value) }, class: { container: '!w-4 !h-4' } }), span('', 'Prix'), ]), numberpicker({ defaultValue: _item.price, disabled: _item.price === undefined, input: (v) => _item.price = v, class: '!w-1/3' }), ]),
|
||||
div('flex flex-row gap-2 items-center justify-between', [ div('flex flex-row gap-2 items-center', [ checkbox({ defaultValue: _item.capacity !== undefined, change: function(value) { _item.capacity = value ? 0 : undefined; if(this.parentElement?.parentElement?.children[1]) { (this.parentElement.parentElement.children[1] as Element & { disabled: boolean }).disabled = !value; (this.parentElement.parentElement.children[1] as HTMLInputElement).value = value ? '0' : ''; } this.parentElement?.toggleAttribute('data-disabled', !value) }, class: { container: '!w-4 !h-4' } }), span('', 'Capacité magique'), ]), numberpicker({ defaultValue: _item.capacity, disabled: _item.capacity === undefined, input: (v) => _item.capacity = v, class: '!w-1/3' }), ]),
|
||||
div('flex flex-row gap-2 items-center justify-between', [ div('flex flex-row gap-2 items-center', [ checkbox({ defaultValue: _item.powercost !== undefined, change: function(value) { _item.powercost = value ? 0 : undefined; if(this.parentElement?.parentElement?.children[1]) { (this.parentElement.parentElement.children[1] as Element & { disabled: boolean }).disabled = !value; (this.parentElement.parentElement.children[1] as HTMLInputElement).value = value ? '0' : ''; } this.parentElement?.toggleAttribute('data-disabled', !value) }, class: { container: '!w-4 !h-4' } }), span('', 'Puissance magique'), ]), numberpicker({ defaultValue: _item.powercost, disabled: _item.powercost === undefined, input: (v) => _item.powercost = v, class: '!w-1/3' }), ]),
|
||||
div('flex flex-row gap-2 items-center justify-between', [ div('flex flex-row gap-2 items-center', [ checkbox({ defaultValue: _item.equippable, change: function(value) { _item.equippable = value; this.parentElement?.toggleAttribute('data-disabled', !value) }, class: { container: '!w-4 !h-4' } }), span('', 'Equipable'), ]), div('flex flex-row gap-2 items-center mx-4', [ checkbox({ defaultValue: _item.consummable, change: function(value) { _item.consummable = value; this.parentElement?.toggleAttribute('data-disabled', !value) }, class: { container: '!w-4 !h-4' } }), span('', 'Consommable'), ]) ]),
|
||||
@@ -1020,6 +1080,61 @@ export class ItemPanel
|
||||
});
|
||||
}
|
||||
}
|
||||
export class ImprovementPanel
|
||||
{
|
||||
static descriptionEditor: MarkdownEditor = new MarkdownEditor();
|
||||
static render(improvement: ImprovementConfig, success: (improvement: ImprovementConfig) => void, failure: (improvement: ImprovementConfig) => void)
|
||||
{
|
||||
const _improvement = JSON.parse(JSON.stringify(improvement)) as ImprovementConfig;
|
||||
ImprovementPanel.descriptionEditor.content = getText(_improvement.description);
|
||||
ImprovementPanel.descriptionEditor.onChange = (value) => setText(_improvement.description, value);
|
||||
const effectContainer = div('grid grid-cols-2 gap-4 px-2 flex-1', _improvement.effect?.map(e => new FeatureEditor(_improvement.effect!, e.id, false).container));
|
||||
return dom('div', { attributes: { 'data-state': 'inactive' }, class: 'border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10 border-l absolute top-0 bottom-0 right-0 w-[10%] data-[state=active]:w-1/2 flex flex-col gap-2 text-light-100 dark:text-dark-100 p-8 transition-[width] transition-delay-[150ms]' }, [
|
||||
div('flex flex-row justify-between items-center', [
|
||||
tooltip(button(icon('radix-icons:check', { width: 20, height: 20 }), () => {
|
||||
success!(_improvement);
|
||||
ImprovementPanel.descriptionEditor.onChange = undefined;
|
||||
}, 'p-1'), 'Valider', 'left'),
|
||||
dom('label', { class: 'flex justify-center items-center my-2' }, [
|
||||
dom('span', { class: 'pb-1 md:p-0', text: "Nom" }),
|
||||
input('text', { defaultValue: _improvement.name, input: (v) => { _improvement.name = v }, class: 'w-96' })
|
||||
]),
|
||||
tooltip(button(icon('radix-icons:cross-1', { width: 20, height: 20 }), () => {
|
||||
failure!(improvement);
|
||||
ImprovementPanel.descriptionEditor.onChange = undefined;
|
||||
}, 'p-1'), 'Annuler', 'left'),
|
||||
]),
|
||||
foldable([
|
||||
div('flex flex-row gap-2 items-center justify-between', [ div('flex flex-row gap-2 items-center', [ span('', 'Puissance magique'), ]), numberpicker({ defaultValue: _improvement.power, disabled: _improvement.power === undefined, input: (v) => _improvement.power = v, class: '!w-1/3' }), ]),
|
||||
div('flex flex-row gap-2 items-center justify-between', [ span('flex flex-row gap-2 items-center', 'Fabrication'), div('flex flex-row items-center gap-2 !w-2/3', [ select<CraftingType>(Object.entries(craftingText).map(e => ({ text: e[1], value: e[0] as CraftingType })), { defaultValue: _improvement.craft.ability ?? 'crafter', change: (v) => _improvement.craft.ability = v, class: { container: '!w-1/2' } }), numberpicker({ defaultValue: _improvement.craft.difficulty ?? 0, input: (v) => _improvement.craft.difficulty = v, class: 'w-12' }) ]) ]),
|
||||
], [ span('text-lg font-bold', "Propriétés"), div('flex flex-row gap-2 items-center justify-between', [ div('flex flex-row gap-2 items-center', [ span('', 'Rareté'), ]), select(Object.keys(rarityText).map(e => ({ text: rarityText[e as Rarity], value: e as Rarity })), { defaultValue: _improvement.rarity, change: (v) => _improvement.rarity = v, class: { container: '!w-1/2' } }), ]) ], { class: { content: 'group-data-[active]:grid grid-cols-2 my-2 gap-4', title: 'grid grid-cols-2 gap-4 mx-2', container: 'pb-2 border-b border-light-35 dark:border-dark-35' }, open: true } ),
|
||||
foldable([ div('p-1 border border-light-40 dark:border-dark-40 w-full bg-light-25 dark:bg-dark-25 min-h-48 max-h-[32rem]', [ ImprovementPanel.descriptionEditor.dom ])], [ span('text-lg font-bold px-2', "Description des effets") ], { class: { container: 'gap-4 pb-2 border-b border-light-35 dark:border-dark-35' }, open: true, }),
|
||||
div('flex flex-row gap-4 items-center pb-2 border-b border-light-35 dark:border-dark-35', [ dom('h3', { class: 'text-lg font-bold', text: 'Restrictions' }), tagpicker([], { defaultValue: Object.keys(improvement.restrictions ?? {}), class: { container: 'data-[focused]:shadow-raw transition-[box-shadow] data-[focused]:shadow-light-40 dark:data-[focused]:shadow-dark-40' } }) ]),
|
||||
foldable([ effectContainer ], [ dom('h3', { class: 'text-lg font-bold', text: 'Effets' }),
|
||||
tooltip(button(icon('radix-icons:plus', { width: 20, height: 20 }), () => {
|
||||
const f = { id: getID(), };
|
||||
_improvement.effect ??= [];
|
||||
_improvement.effect.push(f as any as FeatureValue | FeatureEquipment | FeatureList);
|
||||
effectContainer.appendChild(new FeatureEditor(_improvement.effect, f.id, true).container);
|
||||
}, 'p-1 hidden group-data-[active]:block'), 'Ajouter', 'left'),
|
||||
], { class: { container: 'flex flex-col gap-2 w-full', title: 'flex flex-row justify-between px-2' } })
|
||||
]);
|
||||
}
|
||||
static edit(improvement: ImprovementConfig): Promise<ImprovementConfig>
|
||||
{
|
||||
let container: HTMLElement, close: Function;
|
||||
return new Promise<ImprovementConfig>((success, failure) => {
|
||||
container = ImprovementPanel.render(improvement, success, failure);
|
||||
close = fullblocker([container], {
|
||||
priority: true, closeWhenOutside: false,
|
||||
}).close;
|
||||
setTimeout(() => container.setAttribute('data-state', 'active'), 1);
|
||||
}).finally(() => {
|
||||
setTimeout(close, 150);
|
||||
container.setAttribute('data-state', 'inactive');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const featureChoices: Option<Partial<FeatureOption>>[] = [
|
||||
{ text: 'PV max', value: { category: 'value', property: 'health', operation: 'add', value: 1 }, },
|
||||
|
||||
Reference in New Issue
Block a user