fix: refactor store to singleton class pattern (s.subscribe fix)
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 21s

Exported $state proxies were confused with Svelte stores by SvelteKit
runtime, causing "s.subscribe is not a function" on /jeu.

Fix: encapsulate all $state fields in a Game class, export singleton.
Components import { game } and access game.state, game.click(), etc.
Class fields are proper $state — no raw proxy exported.
This commit is contained in:
2026-03-28 20:39:21 +01:00
parent cce7fa3190
commit 67931eeadb
16 changed files with 277 additions and 325 deletions

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { state, buyNode, doResetTree, doUpgradeConvergence } from '$lib/stores/game.svelte';
import { game } from '$lib/stores/game.svelte';
import {
canBuyEvolutionNode,
getSpentDna,
@@ -44,14 +44,14 @@
let activeBranch = $state<Branch>('ponte');
let branchConfig = $derived(BRANCH_CONFIG[activeBranch]);
let branchNodes = $derived(state.evolutionTree.filter((n) => n.branch === activeBranch));
let spentDna = $derived(getSpentDna(state.evolutionTree));
let branchNodes = $derived(game.state.evolutionTree.filter((n) => n.branch === activeBranch));
let spentDna = $derived(getSpentDna(game.state.evolutionTree));
let hasUnlocked = $derived(spentDna > 0);
let resetCost = $derived(getTreeResetCost(state));
let canReset = $derived(canResetTree(state));
let conv = $derived(state.evolutionTree.find((n) => n.id === 'convergence'));
let canBuyConv = $derived(canBuyEvolutionNode(state, 'convergence'));
let canUpgradeConv = $derived(canUpgradeConvergence(state));
let resetCost = $derived(getTreeResetCost(game.state));
let canReset = $derived(canResetTree(game.state));
let conv = $derived(game.state.evolutionTree.find((n) => n.id === 'convergence'));
let canBuyConv = $derived(canBuyEvolutionNode(game.state, 'convergence'));
let canUpgradeConv = $derived(canUpgradeConvergence(game.state));
function handleReset() {
if (!canReset) return;
@@ -59,7 +59,7 @@
const confirmed = window.confirm(
`Reinitialiser l'Arbre d'Evolution ?\n\nTu recuperes ${spentDna} ADN Ancestral.${costLabel}\nTous les noeuds seront verrouilles.\n\nConfirmer ?`
);
if (confirmed) doResetTree();
if (confirmed) game.resetTree();
}
function getNodeRowClass(node: EvolutionNode, isExcluded: boolean, canBuy: boolean): string {
@@ -70,13 +70,13 @@
}
</script>
{#if state.prestigeCount >= 1}
{#if game.state.prestigeCount >= 1}
<div class="flex flex-col gap-2">
<!-- Header -->
<div class="flex justify-between items-center px-1">
<span class="gp-title">Evolution</span>
<div class="flex items-center gap-2">
<span class="gp-value gp-accent-amber">{formatNumber(state.ancestralDna)} ADN</span>
<span class="gp-value gp-accent-amber">{formatNumber(game.state.ancestralDna)} ADN</span>
{#if hasUnlocked}
<button
onclick={handleReset}
@@ -108,8 +108,8 @@
<div class="gp flex-1 min-w-0 border-t-2 {branchConfig.color}">
<span class="gp-title text-center {branchConfig.accent}">{branchConfig.label}</span>
{#each branchNodes as node}
{@const isExcluded = node.exclusive_with ? (state.evolutionTree.find((n) => n.id === node.exclusive_with)?.unlocked ?? false) : false}
{@const canBuy = canBuyEvolutionNode(state, node.id)}
{@const isExcluded = node.exclusive_with ? (game.state.evolutionTree.find((n) => n.id === node.exclusive_with)?.unlocked ?? false) : false}
{@const canBuy = canBuyEvolutionNode(game.state, node.id)}
{@const cost = node.repeatable && node.unlocked ? getRepeatableCost(node) : node.cost}
<div class={getNodeRowClass(node, isExcluded, canBuy)}>
<div class="flex flex-col min-w-0">
@@ -132,7 +132,7 @@
{:else}
<button
disabled={!canBuy}
onclick={() => buyNode(node.id)}
onclick={() => game.buyNode(node.id)}
class="gp-btn {canBuy ? 'gp-btn--buy' : 'gp-btn--disabled'}"
>
{formatNumber(cost)}
@@ -164,7 +164,7 @@
{#if tier < maxTier}
<button
disabled={!canUpgradeConv}
onclick={() => doUpgradeConvergence()}
onclick={() => game.upgradeConvergence()}
class="gp-btn {canUpgradeConv ? 'gp-btn--buy' : 'gp-btn--disabled'} w-full"
>
{canUpgradeConv ? `Evoluer Omega (${conv.tierUpgradeCost} ADN)` : `Requis : 2 capstones (${conv.tierUpgradeCost} ADN)`}
@@ -180,7 +180,7 @@
</div>
<button
disabled={!canBuyConv}
onclick={() => buyNode('convergence')}
onclick={() => game.buyNode('convergence')}
class="gp-btn {canBuyConv ? 'gp-btn--buy' : 'gp-btn--disabled'}"
>
{conv.cost}