From 3ef98df5d2354c04a419f13fa92bf54a5fd47edf Mon Sep 17 00:00:00 2001 From: Peaceultime Date: Tue, 22 Jul 2025 00:05:06 +0200 Subject: [PATCH] Add LevelPicker --- db.sqlite | Bin 761856 -> 761856 bytes db.sqlite-shm | Bin 32768 -> 32768 bytes db.sqlite-wal | Bin 0 -> 8272 bytes shared/character-config.json | 164 +++++++++-------------------------- shared/character.util.ts | 124 +++++++++++++++++++++++--- 5 files changed, 152 insertions(+), 136 deletions(-) diff --git a/db.sqlite b/db.sqlite index c15aae5406c21c3fa90eb291c42190feb72638b0..ba9eab26adff3dff70964e56662a4ef6ea5397a2 100644 GIT binary patch delta 190 zcmZoTpx1CfZv#sJ4-3Bs1OF2KOZ;X09-9RMEci`ySXn|jSyD`lj7%&ol62G3j16>6 zOiV0vEzQkMbdyp{(vnh5EiDYq%rojXPtaijsh%8{uQGW-d@NU8lL{CAbp{rG{mtwN z8vL4?Kv_m&%-S50zf6E>^Ms~`wg$$w2Bx+K=C%fwwg%R=2DY{a_O=F&wg%3&2Ciid G+zJ2ztu?R! delta 158 zcmZoTpx1CfZv#sJ4>Nxm1OF2KOZ;Vg`!)*%Sn!)LFfi=kWJ$9yFiJHxGS*ErH&4_x zv9L7OO-eIN)HO>qPf9XQHApr}Gt6LR-lxOD63WRkIWAvi@`CtSuDqw$x%jU$F!M)l zW>3)IX9XH|k#Vv@yvpW?{AB`6(WOldZ4HcV4NPqf%xw)UZ4In#4Qy=<>}?GkZ4I1l K4P46_xD@~qI4?2) diff --git a/db.sqlite-shm b/db.sqlite-shm index fe9ac2845eca6fe6da8a63cd096d9cf9e24ece10..da6eab59a0257a33a28f9ac70471735adb7594f9 100644 GIT binary patch delta 159 zcmZo@U}|V!s+V}A%K!pQK+MR%AONCw0r4FxkGY4gXgGiHUi?k#9oL30^Gkj?Yj3$n ps(PSNU;r}rKN5fnPps!;WdpJ~Ha4aQGBR##{LRY9w6T$20{~4)G-3b% delta 62 zcmZo@U}|V!VwQNMJNb2>Dz^o@1e@vP+ni9o2#n7F1^_1B_e2_vL3$fb2ISWhed`e2ek6;yT z)lw`J?8GjlOrxl!N(yWDp6EBQ5N4KzWtd%de&y%~vx5pgovW&|)fhka4u4+!cIM@W zN7vTH{hgzdnw9R_Gx?6-fI{0H@{a&Q;+}&kN^pg011!) z36KB@kN^pgz&{h{HtHKq9hHto83ZE}0`lC0N6o=W$5ZsudlbXAT6WD?9jp<(%Nly) z6QHvhTnYjh<0`wLw1|1w{CvHtCOb{7M4uB12RW(?m|X-bbAW)_CC&wpCJoc??@Nnc zn>v{s`d}3VEh8Y7LL&kKlfCA>;9i+HOlLQ{>FVFeIa=c(XTeG}8n9X;_=wpjX-%*6 zuzdBbtH)}WQua(5@Q#%(nU~;uWm1i&X+DX%KkeSc(S;3a%K1U z-8dJRw5L^j+I}oY3KAdz5+DH*AOR8}0TLhq5+DH*Ac6l;U~6q!)pfJKxU)UXZtu+} Po0GaE_OD)^j}wYtL2row literal 0 HcmV?d00001 diff --git a/shared/character-config.json b/shared/character-config.json index d87ff51..2fde7a5 100644 --- a/shared/character-config.json +++ b/shared/character-config.json @@ -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." } ] } diff --git a/shared/character.util.ts b/shared/character.util.ts index dd90652..adffc22 100644 --- a/shared/character.util.ts +++ b/shared/character.util.ts @@ -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) { - /* -
-
- - {{ people.name }} - - {{ people.description }} -
-
- */ 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 @@ -495,4 +486,109 @@ class PeoplePicker implements BuilderTab { return this._content; } +} +class LevelPicker implements BuilderTab +{ + private _builder: CharacterBuilder; + private _content: Array; + + 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; + } } \ No newline at end of file