Add LevelPicker

This commit is contained in:
Peaceultime 2025-07-22 00:05:06 +02:00
parent ba8c7b05e6
commit 3ef98df5d2
5 changed files with 152 additions and 136 deletions

BIN
db.sqlite

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -186,248 +186,168 @@
"options": { "options": {
"1": [ "1": [
{ {
"training": 35, "description": "+35 points de statistiques.\n+14 PV max."
"health": 14
} }
], ],
"2": [ "2": [
{ {
"training": 1, "description": "+1 point de statistique.\n+3 PV max.\n+2 mana max."
"health": 3,
"mana": 2
}, },
{ {
"health": 6, "description": "+1 point de compétence.\n+6 PV max.\n+3 mana max."
"mana": 3,
"abilities": 1
} }
], ],
"3": [ "3": [
{ {
"training": 2, "description": "+2 points de statistiques.\n+1 point de compétence.\n+3 PV max.\n+1 mana max."
"health": 3,
"mana": 1,
"abilities": 1
} }
], ],
"4": [ "4": [
{ {
"training": 1, "description": "+1 point de statistique.\n+2 points de compétences.\n+4 PV max.\n+2 mana max."
"health": 4,
"mana": 2,
"abilities": 2
} }
], ],
"5": [ "5": [
{ {
"training": 1, "description": "+1 point de statistique.\n+2 points de compétences.\n+4 PV max.\n+2 mana max."
"health": 4,
"mana": 2,
"abilities": 2
}, },
{ {
"training": 1, "description": "+1 point de statistique.\n+1 transformation par jour.\n+8 PV max.\n+4 mana max."
"shaping": 1,
"health": 8,
"mana": 4
}, },
{ {
"training": 2, "description": "+2 points de statistiques.\n+7 PV max.\n+2 mana max."
"health": 7,
"mana": 2
} }
], ],
"6": [ "6": [
{ {
"training": 1, "description": "+1 point de statistique.\n+3 PV max.\n+3 mana max."
"health": 3,
"mana": 3
}, },
{ {
"training": 1, "description": "+1 point de statistique.\n+3 points de compétences.\n+1 sort maitrisé."
"abilities": 3,
"spellslots": 1
} }
], ],
"7": [ "7": [
{ {
"training": 2, "description": "+2 points de statistiques.\n+3 PV max.\n+5 mana max."
"health": 3,
"mana": 5
}, },
{ {
"training": 2, "description": "+2 points de statistiques.\n+5 PV max.\n+2 mana max."
"health": 5,
"mana": 2
} }
], ],
"8": [ "8": [
{ {
"training": 3 "description": "+3 points de statistiques."
}, },
{ {
"training": 1, "description": "+1 point de statistique.\n+6 PV max.\n+6 mana max.\n+1 sort maitrisé."
"health": 6,
"mana": 6,
"spellslots": 1
} }
], ],
"9": [ "9": [
{ {
"training": 1, "description": "+1 point de statistique.\n+3 PV max.\n+5 mana max."
"health": 3,
"mana": 5
}, },
{ {
"training": 1, "description": "+1 point de statistique.\n+2 points de compétences.\n+2 PV max."
"health": 2,
"abilities": 2
} }
], ],
"10": [ "10": [
{ {
"training": 2 "description": "+2 points de statistiques."
}, },
{ {
"training": 1, "description": "+1 point de statistique.\n+1 transformation par jour.\n+2 points de compétences."
"shaping": 1,
"abilities": 2
}, },
{ {
"modifier": 1, "description": "+1 au modifieur de votre choix.\n+1 point de compétence."
"abilities": 1
} }
], ],
"11": [ "11": [
{ {
"training": 1, "description": "+1 point de statistique.\n+7 PV max.\n+1 mana max."
"health": 7,
"mana": 1
}, },
{ {
"training": 1, "description": "+1 point de statistique.\n+2 PV max.\n+5 mana max."
"health": 2,
"mana": 5
}, },
{ {
"training": 1, "description": "+1 point de statistique.\n+2 points de compétences."
"abilities": 2
} }
], ],
"12": [ "12": [
{ {
"training": 2, "description": "+2 points de statistiques.\n+1 sort maitrisé."
"spellslots": 1
}, },
{ {
"training": 2, "description": "+2 points de statistiques.\n+8 PV max."
"health": 8
}, },
{ {
"training": 2, "description": "+2 points de statistiques.\n+7 mana max."
"mana": 7
} }
], ],
"13": [ "13": [
{ {
"training": 1, "description": "+1 point de statistique.\n+1 point de compétence.\n+2 PV max.\n+2 mana max."
"health": 2,
"mana": 2,
"abilities": 1
}, },
{ {
"training": 1, "description": "+1 point de statistique.\n+1 transformation par jour.\n+4 PV max.\n+4 mana max."
"shaping": 1,
"health": 4,
"mana": 4
} }
], ],
"14": [ "14": [
{ {
"training": 3, "description": "+3 points de statistiques.\n+3 PV max.\n+5 mana max."
"health": 3,
"mana": 5
}, },
{ {
"training": 3, "description": "+3 points de statistiques.\n+6 PV max.\n+1 mana max."
"health": 6,
"mana": 1
} }
], ],
"15": [ "15": [
{ {
"training": 1 "description": "+1 point de statistique."
}, },
{ {
"health": 5, "description": "+1 point de compétence.\n+5 PV max.\n+5 mana max."
"mana": 5,
"abilities": 1
} }
], ],
"16": [ "16": [
{ {
"training": 1, "description": "+1 point de statistique.\n+3 PV max.\n+5 mana max."
"health": 3,
"mana": 5
}, },
{ {
"training": 1, "description": "+1 point de statistique.\n+5 PV max.\n+2 mana max."
"health": 5,
"mana": 2
} }
], ],
"17": [ "17": [
{ {
"training": 2, "description": "+2 points de statistiques.\n+1 point de compétence.\n+1 sort maitrisé."
"abilities": 1,
"spellslots": 1
}, },
{ {
"training": 1, "description": "+1 point de statistique.\n+1 transformation par jour.\n+2 points de compétences.\n+1 sort maitrisé."
"shaping": 1,
"abilities": 2,
"spellslots": 1
}, },
{ {
"training": 1, "description": "+1 point de statistique.\n+1 point de compétence.\n+7 PV max.\n+5 mana max."
"health": 7,
"mana": 5,
"abilities": 1
} }
], ],
"18": [ "18": [
{ {
"training": 1, "description": "+1 point de statistique.\n+6 PV max.\n+1 mana max."
"health": 6,
"mana": 1
}, },
{ {
"training": 1, "description": "+1 point de statistique.\n+2 PV max.\n+5 mana max."
"health": 2,
"mana": 5
} }
], ],
"19": [ "19": [
{ {
"training": 2, "description": "+2 points de statistiques.\n+2 points de compétences.\n+6 PV max.\n+3 mana max."
"health": 6,
"mana": 3,
"abilities": 2
}, },
{ {
"training": 2, "description": "+2 points de statistiques.\n+2 PV max.\n+5 mana max.\n+1 sort maitrisé."
"health": 2,
"mana": 5,
"spellslots": 1
} }
], ],
"20": [ "20": [
{ {
"training": 2 "description": "+2 points de statistiques."
}, },
{ {
"modifier": 1, "description": "+1 au modifieur de votre choix.\n+1 point de compétence."
"abilities": 1
} }
] ]
} }

View File

@ -4,6 +4,7 @@ import characterConfig from './character-config.json';
import { button, loading } from "./proses"; import { button, loading } from "./proses";
import { div, dom, icon, text } from "./dom.util"; import { div, dom, icon, text } from "./dom.util";
import { popper } from "./floating.util"; import { popper } from "./floating.util";
import { clamp } from "./general.util";
const config = characterConfig as CharacterConfig; const config = characterConfig as CharacterConfig;
@ -182,6 +183,7 @@ export class CharacterBuilder
]; ];
this._stepsContent = [ this._stepsContent = [
new PeoplePicker(this), new PeoplePicker(this),
new LevelPicker(this),
]; ];
this._content = div('flex-1 outline-none max-w-full w-full overflow-y-auto'); this._content = div('flex-1 outline-none max-w-full w-full overflow-y-auto');
this._container.appendChild(div('flex flex-1 flex-col justify-start items-center px-8 w-full h-full overflow-y-hidden', [ this._container.appendChild(div('flex flex-1 flex-col justify-start items-center px-8 w-full h-full overflow-y-hidden', [
@ -301,7 +303,7 @@ export class CharacterBuilder
} }
}) })
} }
private updateLevel(level: Level) updateLevel(level: Level)
{ {
this._character.level = level; this._character.level = level;
@ -320,7 +322,7 @@ export class CharacterBuilder
} }
} }
} }
private toggleLevelOption(level: Level, choice: number) toggleLevelOption(level: Level, choice: number)
{ {
if(level > this._character.level) //Cannot add more level options than the current level if(level > this._character.level) //Cannot add more level options than the current level
return; return;
@ -355,7 +357,7 @@ export class CharacterBuilder
this.add(config.peoples[this._character.people!]!.options[level][choice]!); this.add(config.peoples[this._character.people!]!.options[level][choice]!);
} }
} }
private toggleTrainingOption(stat: MainStat, level: TrainingLevel, option: number) toggleTrainingOption(stat: MainStat, level: TrainingLevel, option: number)
{ {
} }
@ -422,17 +424,6 @@ class PeoplePicker implements BuilderTab
constructor(builder: CharacterBuilder) constructor(builder: CharacterBuilder)
{ {
/*
<div class="flex flex-1 gap-4 p-2 overflow-x-auto justify-center">
<div v-for="(people, i) of config.peoples" @click="model.character.people = i" class="flex flex-col flex-nowrap gap-2 p-2 border border-light-35 dark:border-dark-35
cursor-pointer hover:border-light-70 dark:hover:border-dark-70 w-[320px]" :class="{ '!border-accent-blue outline-2 outline outline-accent-blue': model.character.people === i }">
<Avatar :src="people.name" :text="`Image placeholder`" class="h-[320px]" />
<span class="text-xl font-bold text-center">{{ people.name }}</span>
<span class="w-full border-b border-light-50 dark:border-dark-50"></span>
<span class="text-wrap word-break">{{ people.description }}</span>
</div>
</div>
*/
this._builder = builder; this._builder = builder;
this._nameInput = dom("input", { class: `mx-4 caret-light-50 dark:caret-dark-50 text-light-100 dark:text-dark-100 placeholder:text-light-50 dark:placeholder:text-dark-50 this._nameInput = dom("input", { class: `mx-4 caret-light-50 dark:caret-dark-50 text-light-100 dark:text-dark-100 placeholder:text-light-50 dark:placeholder:text-dark-50
@ -495,4 +486,109 @@ class PeoplePicker implements BuilderTab
{ {
return this._content; return this._content;
} }
}
class LevelPicker implements BuilderTab
{
private _builder: CharacterBuilder;
private _content: Array<Node | string>;
private _levelInput: HTMLInputElement;
private _pointsInput: HTMLInputElement;
private _healthText: Text;
private _manaText: Text;
private _options: HTMLDivElement[][];
constructor(builder: CharacterBuilder)
{
this._builder = builder;
this._levelInput = dom("input", { class: `w-14 mx-4 caret-light-50 dark:caret-dark-50 text-light-100 dark:text-dark-100 placeholder:text-light-50 dark:placeholder:text-dark-50 bg-light-20 dark:bg-dark-20 appearance-none outline-none px-3 py-1 focus:shadow-raw transition-[box-shadow] focus:shadow-light-40 dark:focus:shadow-dark-40 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50 data-[disabled]:bg-light-20 dark:data-[disabled]:bg-dark-20 data-[disabled]:border-light-20 dark:data-[disabled]:border-dark-20`, listeners: {
input: (e: Event) => {
this._builder.character.level = parseInt(this._levelInput.value) ?? 1;
this.updateLevel();
},
keydown: (e: KeyboardEvent) => {
let value = this._levelInput.value;
switch(e.key)
{
case "ArrowUp":
value = clamp(parseInt(value) + 1, 1, 20).toString();
break;
case "ArrowDown":
value = clamp(parseInt(value) - 1, 1, 20).toString();
break;
default:
break;
}
if(this._levelInput.value !== value)
{
this._levelInput.value = value;
this._builder.character.level = parseInt(value);
this.updateLevel();
}
}
}});
this._pointsInput = dom("input", { class: `w-14 mx-4 text-light-70 dark:text-dark-70 tabular-nums bg-light-10 dark:bg-dark-10 appearance-none outline-none px-3 py-1 focus:shadow-raw transition-[box-shadow] border bg-light-20 bg-dark-20 border-light-20 dark:border-dark-20`, attributes: { type: "number", disabled: true }});
this._healthText = text("0"), this._manaText = text("0");
this._options = Object.entries(config.peoples[this._builder.character.people!]!.options).map(
(level) => [ div("w-full h-px", [div("border-t border-dashed border-light-50 dark:border-dark-50 w-full"), div("sticky top-0", [ text(level[0]) ])]),
div("flex flex-row gap-4 justify-center", level[1].map((option, j) => dom("div", { class: ["flex border border-light-50 dark:border-dark-50 px-4 py-2 w-[400px]", { 'hover:border-light-70 dark:hover:border-dark-70 cursor-pointer': (level[0] as any as Level) <= this._builder.character.level, '!border-accent-blue bg-accent-blue bg-opacity-20': this._builder.character.leveling?.some(e => e[0] == (level[0] as any as Level) && e[1] === j) ?? false }], listeners: { click: e => {
this._builder.toggleLevelOption(level[0] as any as Level, j);
}}}, [ dom('span', { class: "text-wrap whitespace-pre", text: option.description }) ])))
]);
this._content = [ div("flex flex-1 gap-12 px-2 py-4 justify-center items-center", [
dom("label", { class: "flex justify-center items-center my-2" }, [
dom("span", { class: "pb-1 md:p-0", text: "Niveau" }),
this._levelInput,
]),
dom("label", { class: "flex justify-center items-center my-2" }, [
dom("span", { class: "md:text-base text-sm", text: "Points restantes" }),
this._pointsInput,
]),
div("flex justify-center items-center gap-2 my-2 md:text-base text-sm", [
dom("span", { text: "Vie" }),
this._healthText,
]),
div("flex justify-center items-center gap-2 my-2 md:text-base text-sm", [
dom("span", { text: "Mana" }),
this._manaText,
]),
button(text('Suivant'), () => this._builder.display(1), 'h-[35px] px-[15px]'),
]), div('flex flex-col flex-1 gap-4 mx-8 my-4', this._options.flatMap(e => [...e]))];
}
update()
{
const values = this._builder.values;
this._levelInput.value = this._builder.character.level.toString();
this._pointsInput.value = (this._builder.character.level - this._builder.character.leveling.length).toString();
this._healthText.textContent = values.health?.toString() ?? '0';
this._manaText.textContent = values.mana?.toString() ?? '0';
this.updateLevel();
}
private updateLevel()
{
this._builder.updateLevel(this._builder.character.level as Level);
this._options.forEach((e, i) => {
e[0]?.classList.toggle("opacity-30", ((i + 1) as Level) > this._builder.character.level);
e[1]?.classList.toggle("opacity-30", ((i + 1) as Level) > this._builder.character.level);
e[1]?.childNodes.forEach((option, j) => {
'hover:border-light-70 dark:hover:border-dark-70 cursor-pointer'.split(" ").forEach(_e => (option as HTMLDivElement).classList.toggle(_e, ((i + 1) as Level) <= this._builder.character.level));
'!border-accent-blue bg-accent-blue bg-opacity-20'.split(" ").forEach(_e => (option as HTMLDivElement).classList.toggle(_e, this._builder.character.leveling?.some(e => e[0] == ((i + 1) as Level) && e[1] === j) ?? false));
})
});
}
validate(): boolean
{
return this._builder.character.people !== undefined;
}
get dom()
{
return this._content;
}
} }