Add action variants and cursed items.
This commit is contained in:
parent
3081c05b55
commit
8335871883
|
|
@ -72,6 +72,7 @@ export type TreeStructure = {
|
||||||
type CommonState = {
|
type CommonState = {
|
||||||
capacity?: number;
|
capacity?: number;
|
||||||
powercost?: number;
|
powercost?: number;
|
||||||
|
analysed?: boolean;
|
||||||
};
|
};
|
||||||
type ArmorState = { loss: number, health?: number, absorb: { flat?: number, percent?: number } };
|
type ArmorState = { loss: number, health?: number, absorb: { flat?: number, percent?: number } };
|
||||||
type WeaponState = { attack?: number | string, hit?: number };
|
type WeaponState = { attack?: number | string, hit?: number };
|
||||||
|
|
@ -94,17 +95,17 @@ export type CharacterConfig = {
|
||||||
features: Record<FeatureID, Feature>;
|
features: Record<FeatureID, Feature>;
|
||||||
enchantments: Record<string, EnchantementConfig>;
|
enchantments: Record<string, EnchantementConfig>;
|
||||||
items: Record<string, ItemConfig>;
|
items: Record<string, ItemConfig>;
|
||||||
action: Record<string, { id: string, name: string, description: string, cost: number }>;
|
action: Record<string, { id: string, name: string, description: string, cost?: number, variants?: string[], parent?: string }>;
|
||||||
reaction: Record<string, { id: string, name: string, description: string, cost: number }>;
|
reaction: Record<string, { id: string, name: string, description: string, cost?: number, variants?: string[], parent?: string }>;
|
||||||
freeaction: Record<string, { id: string, name: string, description: string }>;
|
freeaction: Record<string, { id: string, name: string, description: string, variants?: string[], parent?: string }>;
|
||||||
passive: Record<string, { id: string, name: string, description: string }>;
|
passive: Record<string, { id: string, name: string, description: string, variants?: string[], parent?: string }>;
|
||||||
texts: Record<i18nID, Localized>;
|
texts: Record<i18nID, Localized>;
|
||||||
trees: Record<string, TreeStructure>;
|
trees: Record<string, TreeStructure>;
|
||||||
|
|
||||||
//Each of these groups extend an existing feature as they all use the same properties
|
//Each of these groups extend an existing feature as they all use the same properties
|
||||||
sickness: Record<FeatureID, { name: string, stage: number }>; //TODO
|
sickness: Record<FeatureID, { name: string, stage: number }>; //TODO
|
||||||
poisons: Record<FeatureID, { name: string, difficulty: number, efficienty: number, solubility: number }>; //TODO
|
poison: Record<FeatureID, { name: string, difficulty: number, efficienty: number, solubility: number }>; //TODO
|
||||||
dedications: Record<FeatureID, { name: string, requirement: Array<{ stat: MainStat, amount: number }> }>; //TODO
|
dedication: Record<FeatureID, { name: string, requirement: Array<{ stat: MainStat, amount: number }> }>; //TODO
|
||||||
};
|
};
|
||||||
export type EnchantementConfig = {
|
export type EnchantementConfig = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -113,6 +114,7 @@ export type EnchantementConfig = {
|
||||||
effect: Array<FeatureEquipment | FeatureValue | FeatureList>;
|
effect: Array<FeatureEquipment | FeatureValue | FeatureList>;
|
||||||
power: number;
|
power: number;
|
||||||
restrictions?: Array<'armor' | 'mundane' | 'wondrous' | 'weapon' | `armor/${ArmorConfig['type']}` | `weapon/${WeaponConfig['type'][number]}`>; // Need to respect *any* of the restriction, not every restrictions.
|
restrictions?: Array<'armor' | 'mundane' | 'wondrous' | 'weapon' | `armor/${ArmorConfig['type']}` | `weapon/${WeaponConfig['type'][number]}`>; // Need to respect *any* of the restriction, not every restrictions.
|
||||||
|
cursed: boolean;
|
||||||
}
|
}
|
||||||
export type ItemConfig = CommonItemConfig & (ArmorConfig | WeaponConfig | WondrousConfig | MundaneConfig);
|
export type ItemConfig = CommonItemConfig & (ArmorConfig | WeaponConfig | WondrousConfig | MundaneConfig);
|
||||||
type CommonItemConfig = {
|
type CommonItemConfig = {
|
||||||
|
|
@ -211,7 +213,7 @@ export type FeatureEquipment = {
|
||||||
export type FeatureList = {
|
export type FeatureList = {
|
||||||
id: FeatureEffectID;
|
id: FeatureEffectID;
|
||||||
category: "list";
|
category: "list";
|
||||||
list: "spells" | "sickness" | "action" | "reaction" | "freeaction" | "passive" | "mastery";
|
list: "spells" | "sickness" | "action" | "reaction" | "freeaction" | "passive" | "mastery" | "poison" | "dedication";
|
||||||
action: "add" | "remove";
|
action: "add" | "remove";
|
||||||
item: string;
|
item: string;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -2001,7 +2001,7 @@ export class CharacterSheet
|
||||||
div('flex flex-col gap-2 h-full', [
|
div('flex flex-col gap-2 h-full', [
|
||||||
div('flex flex-row justify-end items-center', [
|
div('flex flex-row justify-end items-center', [
|
||||||
div('flex flex-row gap-2 items-center', [
|
div('flex flex-row gap-2 items-center', [
|
||||||
dom('span', { class: () => ['italic text-sm', { 'text-light-red dark:text-dark-red': character.variables.spells.length + (character.lists.spells?.length ?? 0) !== character.spellslots }], text: () => `${character.variables.spells.length + (character.lists.spells?.length ?? 0)}/${character.spellslots} sort(s) maitrisé(s)`.replaceAll('(s)', character.variables.spells.length + (character.lists.spells?.length ?? 0) > 1 ? 's' : '') }),
|
dom('span', { class: () => ['italic text-sm', { 'text-light-red dark:text-dark-red': character.variables.spells.length !== character.spellslots }], text: () => `${character.variables.spells.length}/${character.spellslots} sort(s) maitrisé(s)`.replaceAll('(s)', character.variables.spells.length > 1 ? 's' : '') }),
|
||||||
button(text('Modifier'), () => panel.show(), 'py-1 px-4'),
|
button(text('Modifier'), () => panel.show(), 'py-1 px-4'),
|
||||||
tooltip(button(icon('ph:arrows-down-up', { width: 16, height: 16 }), sorter, 'p-1'), 'Trier par', 'right')
|
tooltip(button(icon('ph:arrows-down-up', { width: 16, height: 16 }), sorter, 'p-1'), 'Trier par', 'right')
|
||||||
])
|
])
|
||||||
|
|
@ -2042,7 +2042,7 @@ export class CharacterSheet
|
||||||
const container = div("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-4 text-light-100 dark:text-dark-100 p-8 transition-[width] transition-delay-[150ms]", [
|
const container = div("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-4 text-light-100 dark:text-dark-100 p-8 transition-[width] transition-delay-[150ms]", [
|
||||||
div("flex flex-row justify-between items-center", [
|
div("flex flex-row justify-between items-center", [
|
||||||
dom("h2", { class: "text-xl font-bold", text: "Ajouter un sort" }),
|
dom("h2", { class: "text-xl font-bold", text: "Ajouter un sort" }),
|
||||||
div('flex flex-row gap-4 items-center', [ dom('span', { class: 'italic text-light-70 dark:text-dark-70 text-sm' }, [ text(() => `${spells.length + (character.lists.spells?.length ?? 0)}/${character.spellslots} sort(s) maitrisé(s)`.replaceAll('(s)', spells.length + (character.lists.spells?.length ?? 0) > 1 ? 's' : '')) ]), tooltip(button(icon("radix-icons:cross-1", { width: 20, height: 20 }), () => {
|
div('flex flex-row gap-4 items-center', [ dom('span', { class: 'italic text-light-70 dark:text-dark-70 text-sm' }, [ text(() => `${spells.length}/${character.spellslots} sort(s) maitrisé(s)`.replaceAll('(s)', spells.length > 1 ? 's' : '')) ]), tooltip(button(icon("radix-icons:cross-1", { width: 20, height: 20 }), () => {
|
||||||
setTimeout(blocker.close, 150);
|
setTimeout(blocker.close, 150);
|
||||||
container.setAttribute('data-state', 'inactive');
|
container.setAttribute('data-state', 'inactive');
|
||||||
}, "p-1"), "Fermer", "left") ])
|
}, "p-1"), "Fermer", "left") ])
|
||||||
|
|
@ -2149,7 +2149,7 @@ export class CharacterSheet
|
||||||
const weight = div(() => ['flex flex-row min-w-16 gap-2 justify-between items-center px-2', { 'cursor-help': e.amount > 1 && !!item.weight }], [ icon('mdi:weight', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }), span(() => ({ 'underline decoration-1 decoration-dotted underline-offset-2': e.amount > 1 && !!item.weight }), () => item.weight ? `${item.weight * e.amount}` : '-') ]);
|
const weight = div(() => ['flex flex-row min-w-16 gap-2 justify-between items-center px-2', { 'cursor-help': e.amount > 1 && !!item.weight }], [ icon('mdi:weight', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }), span(() => ({ 'underline decoration-1 decoration-dotted underline-offset-2': e.amount > 1 && !!item.weight }), () => item.weight ? `${item.weight * e.amount}` : '-') ]);
|
||||||
return foldable(() => [
|
return foldable(() => [
|
||||||
markdown(getText(item.description)),
|
markdown(getText(item.description)),
|
||||||
div('flex flex-row gap-1', { list: () => e.enchantments!.map(e => config.enchantments[e]).filter(e => !!e), render: (e, _c) => _c ?? floater(div('flex flex-row gap-2 border border-accent-blue px-2 rounded-full py-px bg-accent-blue bg-opacity-20', [ span('text-sm font-semibold tracking-tight', e.name), div('flex flex-row gap-1 items-center', [icon('game-icons:bolt-drop', { width: 12, height: 12 }), span('text-sm font-light', e.power)]) ]), () => [markdown(getText(e.description), undefined, { tags: { a: preview } })], { class: 'max-w-96 max-h-48 p-2', position: "right" }) }),
|
div('flex flex-row gap-1', { list: () => e.enchantments!.map(e => config.enchantments[e]).filter(e => !!e), render: (e, _c) => _c ?? floater(div(() => ['flex flex-row gap-2 border px-2 rounded-full py-px !bg-opacity-20', { 'border-accent-blue bg-accent-blue': !e.cursed, 'border-light-purple bg-light-purple dark:border-dark-purple dark:bg-dark-purple': e.cursed }], [ span('text-sm font-semibold tracking-tight', e.name), div('flex flex-row gap-1 items-center', [icon('game-icons:bolt-drop', { width: 12, height: 12 }), span('text-sm font-light', e.power)]) ]), () => [markdown(getText(e.description), undefined, { tags: { a: preview } })], { class: 'max-w-96 max-h-48 p-2', position: "right" }) }),
|
||||||
div('flex flex-row justify-center gap-1', [
|
div('flex flex-row justify-center gap-1', [
|
||||||
this.character?.character.campaign ? button(text('Partager'), () => {
|
this.character?.character.campaign ? button(text('Partager'), () => {
|
||||||
|
|
||||||
|
|
@ -2302,9 +2302,11 @@ export class CharacterSheet
|
||||||
}, "p-1"), "Fermer", "left")
|
}, "p-1"), "Fermer", "left")
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
div('grid grid-cols-1 -my-2 overflow-y-auto gap-1', { list: () => Object.values(config.enchantments).filter(e => restrict(e, current.item?.id)), render: (enchant, _c) => _c ?? foldable(() => [ markdown(getText(enchant.description)) ], [div('flex flex-row justify-between', [
|
div('grid grid-cols-1 -my-2 overflow-y-auto gap-1', { list: () => Object.values(config.enchantments).filter(e => restrict(e, current.item?.id)), render: (enchant, _c) => _c ?? foldable(() => [ markdown(getText(enchant.description)) ], [
|
||||||
|
div('flex flex-row justify-between', [
|
||||||
div('flex flex-row items-center gap-4', [ span('text-lg', enchant.name) ]),
|
div('flex flex-row items-center gap-4', [ span('text-lg', enchant.name) ]),
|
||||||
div('flex flex-row items-center divide-x divide-light-50 dark:divide-dark-50 divide-dashed px-2 gap-4', [
|
div('flex flex-row items-center divide-x divide-light-50 dark:divide-dark-50 divide-dashed px-2 gap-4', [
|
||||||
|
enchant.cursed ? span('italic text-sm text-light-purple dark:text-dark-purple', `Malédiction`) : undefined,
|
||||||
span('italic text-sm', `Puissance magique: ${enchant.power}`),
|
span('italic text-sm', `Puissance magique: ${enchant.power}`),
|
||||||
button(icon(() => current.item?.enchantments?.includes(enchant.id) ? 'radix-icons:minus' : 'radix-icons:plus', { width: 16, height: 16 }), () => {
|
button(icon(() => current.item?.enchantments?.includes(enchant.id) ? 'radix-icons:minus' : 'radix-icons:plus', { width: 16, height: 16 }), () => {
|
||||||
const idx = current.item!.enchantments?.findIndex(e => e === enchant.id) ?? -1;
|
const idx = current.item!.enchantments?.findIndex(e => e === enchant.id) ?? -1;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { MarkdownEditor } from "#shared/editor";
|
||||||
import { preview } from "#shared/proses";
|
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, toggle, type Option } from "#shared/components";
|
||||||
import { confirm, contextmenu, fullblocker, tooltip } from "#shared/floating";
|
import { confirm, contextmenu, fullblocker, tooltip } from "#shared/floating";
|
||||||
import { ABILITIES, abilityTexts, ALIGNMENTS, alignmentTexts, categoryText, colorByRarity, 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, 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 characterConfig from "#shared/character-config.json";
|
||||||
import { getID } from "#shared/general";
|
import { getID } from "#shared/general";
|
||||||
import markdown, { markdownReference, renderMDAsText } from "#shared/markdown";
|
import markdown, { markdownReference, renderMDAsText } from "#shared/markdown";
|
||||||
|
|
@ -306,44 +306,109 @@ export class HomebrewBuilder
|
||||||
actions()
|
actions()
|
||||||
{
|
{
|
||||||
let editing: { type: 'action' | 'reaction' | 'freeaction' | 'passive', id: string } | undefined;
|
let editing: { type: 'action' | 'reaction' | 'freeaction' | 'passive', id: string } | undefined;
|
||||||
const render = (type: 'action' | 'reaction' | 'freeaction' | 'passive', feature: { id: string, name: string, description: string, cost?: number }) => {
|
const render = (type: 'action' | 'reaction' | 'freeaction' | 'passive', feature: { id: string, name: string, description: string, cost?: number, variants?: string[], parent?: string }, open?: boolean): { dom: HTMLDivElement; buttons: HTMLDivElement; md: { current: HTMLElement; }; type: "action" | "reaction" | "passive" | "freeaction"; id: string; variants?: any[] } => {
|
||||||
const md = markdownReference(getText(feature.description), undefined, { tags: { a: preview }, class: 'ms-2 px-2 py-1 border-l-4 border-light-30 dark:border-dark-30' });
|
const md = markdownReference(getText(feature.description), undefined, { tags: { a: preview }, class: 'ms-2 px-2 py-1 border-l-4 border-light-30 dark:border-dark-30' });
|
||||||
const buttons = div('flex flex-row items-center gap-2', [ span('text-sm text-light-70 dark:text-dark-70', type), tooltip(button(icon('radix-icons:pencil-1'), () => edit(type, feature.id), 'p-1'), 'Modifier', 'left'), tooltip(button(icon('radix-icons:trash'), () => remove(type, feature.id), 'p-1'), 'Supprimer', 'right') ]);
|
const buttons = div('flex flex-row items-center gap-2', [ span('text-sm text-light-70 dark:text-dark-70', type), feature.parent ? undefined : tooltip(button(icon('radix-icons:symbol'), optionmenu([
|
||||||
|
{ title: "Classe élementaire", click: () => SPELL_ELEMENTS.forEach(e => add(type, { parent: feature.id, variation: elementTexts[e].text })) },
|
||||||
|
{ title: "Rangs de sort", click: () => ["Rang 1", "Rang 2", "Rang 3", "Sorts spéciaux"].forEach(e => add(type, { parent: feature.id, variation: e })) },
|
||||||
|
{ title: "Categories de sort", click: () => SPELL_TYPES.forEach(e => add(type, { parent: feature.id, variation: spellTypeTexts[e] })) },
|
||||||
|
{ title: "Statistique", click: () => MAIN_STATS.forEach(e => add(type, { parent: feature.id, variation: mainStatTexts[e] })) },
|
||||||
|
{ title: "Compétence", click: () => ABILITIES.forEach(e => add(type, { parent: feature.id, variation: abilityTexts[e] })) },
|
||||||
|
{ title: "Variante", click: () => add(type, { parent: feature.id, variation: "" }) },
|
||||||
|
]), 'p-1'), 'Variantes', 'left'), tooltip(button(icon('radix-icons:pencil-1'), () => edit(type, feature.id), 'p-1'), 'Modifier', 'left'), tooltip(button(icon('radix-icons:trash'), () => remove(type, feature.id), 'p-1'), 'Supprimer', 'right') ]);
|
||||||
|
const variants = feature.variants ? feature.variants.map(e => config[type][e] ? render(type, config[type][e]) : undefined).filter(e => !!e) : undefined;
|
||||||
return {
|
return {
|
||||||
dom: div('flex flex-col gap-2', [
|
dom: feature.variants ? foldable(() => variants?.map(e => e.dom), [div('flex flex-col gap-2', [
|
||||||
div('flex flex-row justify-between', [ input('text', { defaultValue: feature.name, input: value => { feature.name = value }, placeholder: 'Nom', class: '!mx-0 w-80' }), div('flex flex-row gap-2 items-center', [ type === 'action' || type === 'reaction' ? div('flex flex-row items-center', [ numberpicker({ defaultValue: feature?.cost ?? 0, input: value => feature.cost = value, class: '!mx-1', max: type === 'action' ? 3 : 2, min: 0 }), text(`point${(feature?.cost ?? 0) > 1 ? 's' : ''}`)]) : undefined, buttons ])]),
|
div('flex flex-row justify-between', [ input('text', { defaultValue: feature.name, input: value => { feature.name = value }, placeholder: 'Nom', class: '!mx-0 w-80' }), div('flex flex-row gap-2 items-center', [ type === 'action' || type === 'reaction' ? div('flex flex-row items-center', [ numberpicker({ defaultValue: feature?.cost ?? 0, input: value => feature.cost = value, class: '!mx-1', max: type === 'action' ? 3 : 2, min: 0 }), text(`point${(feature?.cost ?? 0) > 1 ? 's' : ''}`)]) : undefined, buttons ])]),
|
||||||
md.current,
|
md.current,
|
||||||
|
])], { open: open ?? false, class: { icon: 'self-start p-2', content: 'border-l-2 border-light-35 dark:border-dark-35 flex flex-col gap-4 ms-2 ps-2 py-2' } }) : div('flex flex-col gap-2', [
|
||||||
|
div('flex flex-row justify-between', [ input('text', { defaultValue: feature.name, input: value => { feature.name = value }, placeholder: 'Nom', class: '!mx-0 w-80' }), div('flex flex-row gap-2 items-center', [ (type === 'action' || type === 'reaction') && feature.parent === undefined ? div('flex flex-row items-center', [ numberpicker({ defaultValue: feature?.cost ?? 0, input: value => feature.cost = value, class: '!mx-1', max: type === 'action' ? 3 : 2, min: 0 }), text(`point${(feature?.cost ?? 0) > 1 ? 's' : ''}`)]) : undefined, buttons ])]),
|
||||||
|
md.current,
|
||||||
]),
|
]),
|
||||||
|
variants: variants,
|
||||||
buttons,
|
buttons,
|
||||||
md,
|
md,
|
||||||
type,
|
type,
|
||||||
id: feature.id,
|
id: feature.id,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const add = (type: 'action' | 'reaction' | 'freeaction' | 'passive') => {
|
const add = (type: 'action' | 'reaction' | 'freeaction' | 'passive', variant?: { parent: string, variation: string }) => {
|
||||||
const feature: { id: string, name: string, description: string, cost?: number } = {
|
const feature: { id: string, name: string, description: string, cost?: number, parent?: string } = {
|
||||||
id: getID(),
|
id: getID(),
|
||||||
name: '',
|
name: '',
|
||||||
description: getID(), // i18nID
|
description: getID(), // i18nID
|
||||||
cost: type === 'action' || type === 'reaction' ? 1 : undefined,
|
cost: (type === 'action' || type === 'reaction') && variant === undefined ? 1 : undefined,
|
||||||
|
parent: variant?.parent,
|
||||||
}
|
}
|
||||||
config.texts[feature.description] = { 'fr_FR': '' };
|
setText(feature.description, '');
|
||||||
config[type][feature.id] = feature;
|
config[type][feature.id] = feature;
|
||||||
|
|
||||||
const option = render(type, feature);
|
if(variant === undefined)
|
||||||
options.push(option);
|
{
|
||||||
optionHolder.appendChild(option.dom);
|
const option = render(type, feature);
|
||||||
|
options.push(option);
|
||||||
|
optionHolder.appendChild(option.dom);
|
||||||
|
}
|
||||||
|
else if(config[type][variant.parent] !== undefined)
|
||||||
|
{
|
||||||
|
const parent = config[type][variant.parent]!;
|
||||||
|
parent.variants ??= [];
|
||||||
|
parent.variants.push(feature.id);
|
||||||
|
|
||||||
|
feature.name = variant.variation;
|
||||||
|
setText(feature.description, getText(parent.description));
|
||||||
|
|
||||||
|
const option = render(type, parent, true)
|
||||||
|
const idx = options.findIndex(e => e.id === variant.parent && e.type === type);
|
||||||
|
if(idx !== -1)
|
||||||
|
{
|
||||||
|
options[idx]?.dom?.replaceWith(option.dom);
|
||||||
|
options[idx] = option;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const remove = (type: 'action' | 'reaction' | 'freeaction' | 'passive', id: string) => {
|
const remove = (type: 'action' | 'reaction' | 'freeaction' | 'passive', id: string) => {
|
||||||
const feature = config[type][id]!;
|
const feature = config[type][id]!;
|
||||||
confirm(`Voulez vous vraiment supprimer l'effet "${feature.name}" ?`).then(e => {
|
confirm(`Voulez vous vraiment supprimer l'effet "${feature.name}"${feature.variants !== undefined ? " et ses " + feature.variants.length + " variantes" : ''} ?`).then(e => {
|
||||||
if(e)
|
if(e)
|
||||||
{
|
{
|
||||||
|
if(feature.parent)
|
||||||
|
{
|
||||||
|
const parent = config[type][feature.parent];
|
||||||
|
|
||||||
|
if(parent)
|
||||||
|
{
|
||||||
|
parent.variants = parent.variants!.filter(e => e !== id);
|
||||||
|
if(parent.variants.length === 0)
|
||||||
|
delete parent.variants;
|
||||||
|
|
||||||
|
const option = render(type, parent, true)
|
||||||
|
const idx = options.findIndex(e => e.id === feature.parent && e.type === type);
|
||||||
|
if(idx !== -1)
|
||||||
|
{
|
||||||
|
options[idx]?.dom?.replaceWith(option.dom);
|
||||||
|
options[idx] = option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(feature.variants !== undefined)
|
||||||
|
{
|
||||||
|
feature.variants.forEach(e => {
|
||||||
|
const variant = config[type][e];
|
||||||
|
if(variant)
|
||||||
|
delete config.texts[variant.description];
|
||||||
|
delete config[type][e];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const idx = options.findIndex(e => e.type === type && e.id === id);
|
||||||
|
options.splice(idx, 1)[0]?.dom.remove();
|
||||||
|
}
|
||||||
|
|
||||||
delete config.texts[feature.description];
|
delete config.texts[feature.description];
|
||||||
delete config[type][id];
|
delete config[type][id];
|
||||||
|
|
||||||
const idx = options.findIndex(e => e.type === type && e.id === id);
|
|
||||||
options.splice(idx, 1)[0]?.dom.remove();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -394,8 +459,8 @@ export class HomebrewBuilder
|
||||||
|
|
||||||
option!.md.current.replaceWith(editorDom);
|
option!.md.current.replaceWith(editorDom);
|
||||||
option!.md.current = editorDom;
|
option!.md.current = editorDom;
|
||||||
}
|
};
|
||||||
const options = [...Object.values(config.action).map(e => render('action', e)), ...Object.values(config.reaction).map(e => render('reaction', e)), ...Object.values(config.freeaction).map(e => render('freeaction', e)), ...Object.values(config.passive).map(e => render('passive', e))];
|
const options = [...Object.values(config.action).filter(e => e.parent === undefined).map(e => render('action', e)), ...Object.values(config.reaction).filter(e => e.parent === undefined).map(e => render('reaction', e)), ...Object.values(config.freeaction).filter(e => e.parent === undefined).map(e => render('freeaction', e)), ...Object.values(config.passive).filter(e => e.parent === undefined).map(e => render('passive', e))];
|
||||||
const optionHolder = div('flex flex-col gap-4', options.map(e => e.dom));
|
const optionHolder = div('flex flex-col gap-4', options.map(e => e.dom));
|
||||||
return [ div('flex px-8 py-4 flex-col gap-4', [ div('flex flex-row-reverse', [ button(icon('radix-icons:plus'), optionmenu([{ title: 'Action', click: () => add('action') }, { title: 'Réaction', click: () => add('reaction') }, { title: 'Action libre', click: () => add('freeaction') }, { title: 'Passif', click: () => add('passive') }], { position: 'left-start' }), 'p-1') ]), optionHolder ] ) ];
|
return [ div('flex px-8 py-4 flex-col gap-4', [ div('flex flex-row-reverse', [ button(icon('radix-icons:plus'), optionmenu([{ title: 'Action', click: () => add('action') }, { title: 'Réaction', click: () => add('reaction') }, { title: 'Action libre', click: () => add('freeaction') }, { title: 'Passif', click: () => add('passive') }], { position: 'left-start' }), 'p-1') ]), optionHolder ] ) ];
|
||||||
}
|
}
|
||||||
|
|
@ -482,12 +547,13 @@ export class HomebrewBuilder
|
||||||
const editing = reactive({ tree: undefined as string | undefined });
|
const editing = reactive({ tree: undefined as string | undefined });
|
||||||
return [div('', [
|
return [div('', [
|
||||||
() => editing.tree !== undefined ? undefined : div('flex flex-row gap-1 justify-start overflow-x-auto max-w-full', { list: Object.keys(config.trees), render: (e, _c) => _c ?? div('grid grid-cols-2 gap-2 items-baseline w-64 border border-light-35 dark:border-dark-35 p-2', [ span('text-lg font-semibold tracking-thigh', config.trees[e]!.name), div('flex flex-row justify-end', [ tooltip(button(icon('radix-icons:pencil-1', { width: 16, height: 16 }), () => editing.tree = e, 'p-1'), 'Modifier', 'left') ]), span('italic', `${Object.keys(config.trees[e]!.nodes).length} nodes`) ]) }),
|
() => editing.tree !== undefined ? undefined : div('flex flex-row gap-1 justify-start overflow-x-auto max-w-full', { list: Object.keys(config.trees), render: (e, _c) => _c ?? div('grid grid-cols-2 gap-2 items-baseline w-64 border border-light-35 dark:border-dark-35 p-2', [ span('text-lg font-semibold tracking-thigh', config.trees[e]!.name), div('flex flex-row justify-end', [ tooltip(button(icon('radix-icons:pencil-1', { width: 16, height: 16 }), () => editing.tree = e, 'p-1'), 'Modifier', 'left') ]), span('italic', `${Object.keys(config.trees[e]!.nodes).length} nodes`) ]) }),
|
||||||
() => editing.tree === undefined ? undefined : div('', [
|
() => editing.tree === undefined ? undefined : div('flex flex-col', [
|
||||||
|
,
|
||||||
foldable([ div('flex flex-col gap-2', { list: Object.keys(config.trees[editing.tree]!.nodes), render: (e, _c) => _c ?? dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 dark:hover:border-dark-50"], listeners: { click: () => {
|
foldable([ div('flex flex-col gap-2', { list: Object.keys(config.trees[editing.tree]!.nodes), render: (e, _c) => _c ?? dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 dark:hover:border-dark-50"], listeners: { click: () => {
|
||||||
FeaturePanel.edit(config.features[e]!).then(feature => {
|
FeaturePanel.edit(config.features[e]!).then(feature => {
|
||||||
config.features[e] = feature;
|
config.features[e] = feature;
|
||||||
}).catch(e => {});
|
}).catch(e => {});
|
||||||
}}}, [ markdown(getText(config.features[e]!.description), undefined, { tags: { a: preview } }) ]) }) ], [ span('text-lg font-bold px-2', 'Nodes'), button(text('Nouvelle node'), () => add(), 'py-1 px-2') ], { open: true, class: { title: 'flex flex-row justify-between' } })
|
}}}, [ () => markdown(getText(config.features[e]!.description), undefined, { tags: { a: preview } }) ]) }) ], [ tooltip(button(icon('radix-icons:arrow-left', { width: 16, height: 16 }), () => editing.tree = undefined, 'p-1'), 'Retour', 'right'), span('text-lg font-bold px-2', 'Nodes'), button(text('Nouvelle node'), () => add(), 'py-1 px-2') ], { open: true, class: { title: 'flex flex-row justify-between' } })
|
||||||
]),
|
]),
|
||||||
])];
|
])];
|
||||||
}
|
}
|
||||||
|
|
@ -641,9 +707,14 @@ class FeatureEditor
|
||||||
case "mastery":
|
case "mastery":
|
||||||
list = Object.entries(masteryTexts).map(e => ({ text: e[1].text, value: e[0] }));
|
list = Object.entries(masteryTexts).map(e => ({ text: e[1].text, value: e[0] }));
|
||||||
break;
|
break;
|
||||||
default:
|
case "sickness":
|
||||||
|
case "poison":
|
||||||
|
case "dedication":
|
||||||
list = Object.values(config[buffer.list]).map(e => ({ text: e.name, render: () => div('flex flex-col', [ div('flex flex-row justify-between', [ dom('span', { text: e.name, class: 'font-bold' }) ]), div('text-sm text-light-70 dark:text-dark-70', [ text(renderMDAsText(getText(e.description))) ]) ]), value: e.id }));
|
list = Object.values(config[buffer.list]).map(e => ({ text: e.name, render: () => div('flex flex-col', [ div('flex flex-row justify-between', [ dom('span', { text: e.name, class: 'font-bold' }) ]), div('text-sm text-light-70 dark:text-dark-70', [ text(renderMDAsText(getText(e.description))) ]) ]), value: e.id }));
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
list = Object.values(config[buffer.list]).map(e => ({ text: e.name, render: () => e.parent ? dom('span', { text: e.name, class: 'font-bold' }) : div('flex flex-col', [ div('flex flex-row justify-between', [ dom('span', { text: e.name, class: 'font-bold' }) ]), div('text-sm text-light-70 dark:text-dark-70', [ text(renderMDAsText(getText(e.description))) ]) ]), value: e.variants !== undefined ? `variant_${e.id}` : e.id }));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -651,7 +722,25 @@ class FeatureEditor
|
||||||
buffer.action = value as 'add' | 'remove';
|
buffer.action = value as 'add' | 'remove';
|
||||||
this.edit();
|
this.edit();
|
||||||
}, class: { container: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] w-32' } }) ],
|
}, class: { container: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] w-32' } }) ],
|
||||||
bottom: [ combobox(list, { defaultValue: buffer.item, change: (item) => buffer.item = item, class: { container: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] w-full overflow-hidden truncate', option: 'max-h-[90px] text-sm' }, fill: 'contain' }) ]
|
bottom: [ combobox(list, { defaultValue: buffer.item, change: (item) => {
|
||||||
|
if(item.startsWith('variant_'))
|
||||||
|
{
|
||||||
|
item = item.substring('variant_'.length);
|
||||||
|
const feature = config[buffer.list as "action" | "reaction" | "freeaction" | "passive"][item];
|
||||||
|
if(feature)
|
||||||
|
{
|
||||||
|
this.option = { id: buffer.id, category: "choice", text: "", options: feature.variants?.map(e => ({
|
||||||
|
effects: [{ id: getID(), category: 'list', action: buffer.action, list: buffer.list, item: e }],
|
||||||
|
text: config[buffer.list as "action" | "reaction" | "freeaction" | "passive"][e]?.name,
|
||||||
|
})) ?? [] } as FeatureChoice;
|
||||||
|
this.edit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer.item = item;
|
||||||
|
}
|
||||||
|
}, class: { container: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] w-full overflow-hidden truncate', option: 'max-h-[90px] text-sm' }, fill: 'contain' }) ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private editTree(buffer: Partial<FeatureTree>)
|
private editTree(buffer: Partial<FeatureTree>)
|
||||||
|
|
@ -950,7 +1039,7 @@ const featureChoices: Option<Partial<FeatureOption>>[] = [
|
||||||
{ text: 'Résistance > Psyché', effects: [{ category: 'value', property: 'bonus/defense/psyche', operation: 'add', value: 1 }] }
|
{ text: 'Résistance > Psyché', effects: [{ category: 'value', property: 'bonus/defense/psyche', operation: 'add', value: 1 }] }
|
||||||
]} as Partial<FeatureChoice>}
|
]} as Partial<FeatureChoice>}
|
||||||
] },
|
] },
|
||||||
{ text: 'Bonus à l\'attaque', value: RESISTANCES.map(e => ({ text: `Bonus > ${resistanceTexts[e]}`, value: { category: 'value', property: `bonus/resistance/${e}`, operation: 'add', value: 1 } })) },
|
{ text: 'Difficulté des chocs', value: RESISTANCES.map(e => ({ text: `Difficulté > ${resistanceTexts[e]}`, value: { category: 'value', property: `bonus/resistance/${e}`, operation: 'add', value: 1 } })) },
|
||||||
{ text: 'Magie', value: [
|
{ text: 'Magie', value: [
|
||||||
{ text: 'Rang', value: [
|
{ text: 'Rang', value: [
|
||||||
{ text: 'Rang > Sorts de précision', value: { category: 'value', property: 'spellranks/precision', operation: 'add', value: 1 } },
|
{ text: 'Rang > Sorts de précision', value: { category: 'value', property: 'spellranks/precision', operation: 'add', value: 1 } },
|
||||||
|
|
@ -963,14 +1052,28 @@ const featureChoices: Option<Partial<FeatureOption>>[] = [
|
||||||
{ text: 'Bonus > Savoir', value: { category: 'value', property: 'bonus/spells/type/knowledge', operation: 'add', value: 1 } },
|
{ text: 'Bonus > Savoir', value: { category: 'value', property: 'bonus/spells/type/knowledge', operation: 'add', value: 1 } },
|
||||||
{ text: 'Bonus > Instinct', value: { category: 'value', property: 'bonus/spells/type/instinct', operation: 'add', value: 1 } },
|
{ text: 'Bonus > Instinct', value: { category: 'value', property: 'bonus/spells/type/instinct', operation: 'add', value: 1 } },
|
||||||
{ text: 'Bonus > Œuvres', value: { category: 'value', property: 'bonus/spells/type/arts', operation: 'add', value: 1 } },
|
{ text: 'Bonus > Œuvres', value: { category: 'value', property: 'bonus/spells/type/arts', operation: 'add', value: 1 } },
|
||||||
] },
|
{ text: 'Bonus > Choix par type', value: { category: 'choice', options: [
|
||||||
|
{ text: 'Précision', effects: [{ category: 'value', property: 'bonus/spells/type/precision', operation: 'add', value: 1 }] },
|
||||||
|
{ text: 'Savoir', effects: [{ category: 'value', property: 'bonus/spells/type/knowledge', operation: 'add', value: 1 }] },
|
||||||
|
{ text: 'Instinct', effects: [{ category: 'value', property: 'bonus/spells/type/instinct', operation: 'add', value: 1 }] },
|
||||||
|
{ text: 'Œuvres', effects: [{ category: 'value', property: 'bonus/spells/type/arts', operation: 'add', value: 1 }] },
|
||||||
|
] } as Partial<FeatureChoice> }
|
||||||
|
]},
|
||||||
{ text: 'Bonus par rang', value: [
|
{ text: 'Bonus par rang', value: [
|
||||||
{ text: 'Bonus > Sorts de rang 1', value: { category: 'value', property: 'bonus/spells/rank/1', operation: 'add', value: 1 } },
|
{ text: 'Bonus > Sorts de rang 1', value: { category: 'value', property: 'bonus/spells/rank/1', operation: 'add', value: 1 } },
|
||||||
{ text: 'Bonus > Sorts de rang 2', value: { category: 'value', property: 'bonus/spells/rank/2', operation: 'add', value: 1 } },
|
{ text: 'Bonus > Sorts de rang 2', value: { category: 'value', property: 'bonus/spells/rank/2', operation: 'add', value: 1 } },
|
||||||
{ text: 'Bonus > Sorts de rang 3', value: { category: 'value', property: 'bonus/spells/rank/3', operation: 'add', value: 1 } },
|
{ text: 'Bonus > Sorts de rang 3', value: { category: 'value', property: 'bonus/spells/rank/3', operation: 'add', value: 1 } },
|
||||||
{ text: 'Bonus > Sorts uniques', value: { category: 'value', property: 'bonus/spells/rank/4', operation: 'add', value: 1 } },
|
{ text: 'Bonus > Sorts uniques', value: { category: 'value', property: 'bonus/spells/rank/4', operation: 'add', value: 1 } },
|
||||||
|
{ text: 'Bonus > Choix par rang', value: { category: 'choice', options: [
|
||||||
|
{ text: 'Rang 1', effects: [{ category: 'value', property: 'bonus/spells/rank/1', operation: 'add', value: 1 }] },
|
||||||
|
{ text: 'Rang 2', effects: [{ category: 'value', property: 'bonus/spells/rank/2', operation: 'add', value: 1 }] },
|
||||||
|
{ text: 'Rang 3', effects: [{ category: 'value', property: 'bonus/spells/rank/3', operation: 'add', value: 1 }] },
|
||||||
|
] } }
|
||||||
|
] as Partial<FeatureChoice> },
|
||||||
|
{ text: 'Bonus par élément', value: [
|
||||||
|
...SPELL_ELEMENTS.map(e => ({ text: `Bonus > ${elementTexts[e].text}`, value: { category: 'value', property: `bonus/spells/elements/${e}`, operation: 'add', value: 1 } })) as Option<Partial<FeatureItem>>[],
|
||||||
|
{ text: 'Bonus > Choix par élément', value: { category: 'choice', options: SPELL_ELEMENTS.map(e => ({ text: elementTexts[e].text, effects: [{ category: 'value', property: `bonus/spells/elements/${e}`, operation: 'add', value: 1 }] })) } as Partial<FeatureChoice> }
|
||||||
] },
|
] },
|
||||||
{ text: 'Bonus par element', value: SPELL_ELEMENTS.map(e => ({ text: `Bonus > ${elementTexts[e].text}`, value: { category: 'value', property: `bonus/spells/elements/${e}`, operation: 'add', value: 1 } })) },
|
|
||||||
] },
|
] },
|
||||||
{ text: 'Aspect', value: [
|
{ text: 'Aspect', value: [
|
||||||
{ text: 'Aspect > Durée', value: { category: 'value', property: 'aspect/duration', operation: 'add', value: 15 } },
|
{ text: 'Aspect > Durée', value: { category: 'value', property: 'aspect/duration', operation: 'add', value: 15 } },
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue