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

View File

@ -4,6 +4,7 @@ import characterConfig from './character-config.json';
import { button, loading } from "./proses";
import { div, dom, icon, text } from "./dom.util";
import { popper } from "./floating.util";
import { clamp } from "./general.util";
const config = characterConfig as CharacterConfig;
@ -182,6 +183,7 @@ export class CharacterBuilder
];
this._stepsContent = [
new PeoplePicker(this),
new LevelPicker(this),
];
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', [
@ -301,7 +303,7 @@ export class CharacterBuilder
}
})
}
private updateLevel(level: Level)
updateLevel(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
return;
@ -355,7 +357,7 @@ export class CharacterBuilder
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)
{
/*
<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._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
@ -496,3 +487,108 @@ class PeoplePicker implements BuilderTab
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;
}
}