From 0c9170af65181bfabf226725e51e838885f65391 Mon Sep 17 00:00:00 2001 From: Tetardtek Date: Fri, 20 Mar 2026 14:34:48 +0100 Subject: [PATCH] feat: suppression boutique legacy + refonte achievements milestones Clickerz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Suppression route /boutique + Boutique.jsx, BoutiqueCard.jsx, shop.json, scss associés (le GeneratorShop sidebar fait déjà le job) - Refonte complète achievements : 27 milestones basés sur le GameState réel (paliers ressources, générateurs, prestige, évolution, easter eggs humour) - Suppression ancien système JSON statique + AchievementsCard legacy - Page achievements : unlocked/locked state-aware, compteur progression --- Frontend/src/components/AchievementsCard.jsx | 28 -- Frontend/src/components/BoutiqueCard.jsx | 65 ---- Frontend/src/data/Achievements.json | 308 ------------------ Frontend/src/data/achievements.ts | 216 ++++++++++++ Frontend/src/data/shop.json | 92 ------ Frontend/src/main.jsx | 7 +- Frontend/src/pages/Achievements.jsx | 82 ++--- Frontend/src/pages/Boutique.jsx | 32 -- Frontend/src/scss/achievements.scss | 129 ++++++-- .../src/scss/components/achievementscard.scss | 33 -- .../src/scss/components/boutiquecard.scss | 76 ----- Frontend/src/scss/shop.scss | 26 -- 12 files changed, 360 insertions(+), 734 deletions(-) delete mode 100755 Frontend/src/components/AchievementsCard.jsx delete mode 100755 Frontend/src/components/BoutiqueCard.jsx delete mode 100755 Frontend/src/data/Achievements.json create mode 100644 Frontend/src/data/achievements.ts delete mode 100755 Frontend/src/data/shop.json delete mode 100755 Frontend/src/pages/Boutique.jsx delete mode 100755 Frontend/src/scss/components/achievementscard.scss delete mode 100755 Frontend/src/scss/components/boutiquecard.scss delete mode 100755 Frontend/src/scss/shop.scss diff --git a/Frontend/src/components/AchievementsCard.jsx b/Frontend/src/components/AchievementsCard.jsx deleted file mode 100755 index c2ee1f3..0000000 --- a/Frontend/src/components/AchievementsCard.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import "../scss/components/achievementscard.scss"; -import PropTypes from "prop-types"; - -function AchievementsCard({ name, description, image, key }) { - AchievementsCard.propTypes = { - name: PropTypes.string.isRequired, - description: PropTypes.string.isRequired, - image: PropTypes.string.isRequired, - key: PropTypes.number.isRequired, - }; - - return ( -
- cartes speciales -
-

{name}

-

{description}

-
-
- ); -} - -export default AchievementsCard; diff --git a/Frontend/src/components/BoutiqueCard.jsx b/Frontend/src/components/BoutiqueCard.jsx deleted file mode 100755 index 2548ad9..0000000 --- a/Frontend/src/components/BoutiqueCard.jsx +++ /dev/null @@ -1,65 +0,0 @@ -// BoutiqueCard.jsx — Legacy shop card (shop.json boosters) -// TODO: Migrate to economy.ts generator system in a future step -import "../scss/components/boutiquecard.scss"; -import "../scss/components/buttons.scss"; -import PropTypes from "prop-types"; -import { useGameStore } from "../store/useGameStore"; - -export default function BoutiqueCard({ - name, - price, - incrementValue, - description, - image, - type, -}) { - const resources = useGameStore((s) => s.state.resources); - - // Legacy shop — disabled for now, generators are in GeneratorShop - const canAfford = resources >= price; - - return ( -
-
-
-
-
-

{name}

-
-

{price}

-
-
-
-
-

- - {type} + {incrementValue} - -

-

{description}

-
-
- -
-
- ); -} - -BoutiqueCard.propTypes = { - name: PropTypes.string.isRequired, - price: PropTypes.number.isRequired, - incrementValue: PropTypes.number.isRequired, - description: PropTypes.string.isRequired, - image: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, -}; diff --git a/Frontend/src/data/Achievements.json b/Frontend/src/data/Achievements.json deleted file mode 100755 index be17fa6..0000000 --- a/Frontend/src/data/Achievements.json +++ /dev/null @@ -1,308 +0,0 @@ -[ - { - "id": 1, - "name": "Véritable cactus en peluche", - "founded": false, - "image": "https://i.goopics.net/n0tuti.jpg" - }, - { - "id": 2, - "name": "Brosse à dents sans poil", - "founded": false, - "image": "https://i.goopics.net/hd42tk.jpg" - }, - { - "id": 3, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Un jour, Dark Vador s’est attaqué à Chuck Norris. Depuis, il fait de l’asthme." - }, - { - "id": 4, - "name": "Photo d'Ayoub", - "founded": false, - "image": "https://i.goopics.net/fpanvh.jpg" - }, - { - "id": 5, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Pourquoi on met une selle sur un cheval ? Parce qu'en dessous, elle tomberait." - }, - { - "id": 6, - "name": "Parapluie invisible", - "founded": false, - "image": "https://i.goopics.net/b8ms4o.jpg" - }, - { - "id": 7, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Qu'est-ce qui est plus merveilleux que de faire tourner un enfant sur un tourniquet ? L'arrêter avec une pelle." - }, - { - "id": 8, - "name": "Savon qui gratte", - "founded": false, - "image": "https://i.goopics.net/oeefev.jpg" - }, - { - "id": 9, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Pourquoi les moutons aiment le chewing-gum ? Car c'est bon pour la laine." - }, - { - "id": 10, - "name": "Moule à glaçons géant", - "founded": false, - "image": "https://i.goopics.net/2sdm53.png" - }, - { - "id": 11, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Quand Chuck Norris fait un programme, il installe les modules, code et vend le programme... ensuite il demande à quoi il doit servir." - }, - { - "id": 12, - "name": "Casquette avec ventilateur intégré", - "founded": false, - "image": "https://i.goopics.net/wjhe5i.jpg" - }, - { - "id": 13, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Chuck Norris a invité Albert Einstein...à son dîner de cons." - }, - { - "id": 14, - "name": "Game boy color de tonton", - "founded": false, - "image": "https://i.goopics.net/w82iwu.jpg" - }, - { - "id": 15, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Vous savez pourquoi les pets puent ? Pour que les sourds en profitent !" - }, - { - "id": 16, - "name": "Mug moustache", - "founded": false, - "image": "https://i.goopics.net/csu9z1.jpg" - }, - { - "id": 17, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Qu'est ce qui est jaune et qui n'attend pas ? Un citron pressé" - }, - { - "id": 18, - "name": "Peau de grenouille rare", - "founded": false, - "image": "https://i.goopics.net/uwjwn1.jpg" - }, - { - "id": 19, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Que met un développeur sur sa voiture en hiver ? Une bash." - }, - { - "id": 20, - "name": "Stickers Gnia gnia gnia 5 minutes la présentation", - "founded": false, - "image": "https://i.goopics.net/8jids3.png" - }, - { - "id": 21, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Comment une blonde fait-elle pour faire un double de ses clefs ? Elle les photocopie." - }, - { - "id": 22, - "name": "Photo de ta mamie...", - "founded": false, - "image": "https://i.goopics.net/htqzwa.jpg" - }, - { - "id": 23, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Qu'est-ce qu'un cochon volant ? Un aéroport." - }, - { - "id": 24, - "name": "Livre de names de Tonton", - "founded": false, - "image": "https://i.goopics.net/avr4b4.png" - }, - { - "id": 25, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Mon grand-père avait prédit que le Titanic coulerait, il l'avait répété maintes fois...Mais on a préféré le virer de la salle de cinéma" - }, - { - "id": 26, - "name": "Oreiller qui ronfle", - "founded": false, - "image": "https://i.goopics.net/4ydby9.png" - }, - { - "id": 27, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Un jour, les Power Rangers ont combattu Chuck Norris. Depuis, on les appelle les Teletubbies." - }, - { - "id": 28, - "name": "Haut-parleur de douche non étanche", - "founded": false, - "image": "https://i.goopics.net/ugqwdj.jpg" - }, - { - "id": 29, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Qu'est-ce qui a deux pattes et qui saigne ? Un demi-chien..." - }, - { - "id": 30, - "name": "Kit de survie du marais au wasabi et moutarde forte", - "founded": false, - "image": "https://i.goopics.net/dakyj9.png" - }, - { - "id": 31, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Sur quel site internet peut-on trouver un lave-vaisselle pas cher ? Meetic." - }, - { - "id": 32, - "name": "Lunettes de soleil pour joueur de valorant", - "founded": false, - "image": "https://i.goopics.net/dxjicl.png" - }, - { - "id": 33, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Chuck Norris mine de la crypto-monnaie...avec la calculette de sa montre Casio" - }, - { - "id": 34, - "name": "Parfum au PHP: aucune odeur", - "founded": false, - "image": "https://i.goopics.net/o2435t.png" - }, - { - "id": 35, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Quand Google ne trouve pas quelque chose, Il demande à Chuck Norris." - }, - { - "id": 36, - "name": "Couronne de nénuphars lumineuse", - "founded": false, - "image": "https://i.goopics.net/d4su7e.png" - }, - { - "id": 37, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Quelles sont les choses les plus lourdes de l'univers ? Soleil, Étoiles, Trou noir...et node_modules..." - }, - { - "id": 38, - "name": "Kit de survie pour la fin du monde", - "founded": false, - "image": "https://i.goopics.net/ltcik6.png" - }, - { - "id": 39, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Quelle est la fée la plus paresseuse ? La fée Néante" - }, - { - "id": 40, - "name": "Bougie parfumée à l'essence de pizza", - "founded": false, - "image": "https://i.goopics.net/a3hv8n.jpg" - }, - { - "id": 41, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Que mettre dans un kit de survie pour la fin du monde ? Du chocolat, des cookies et un DVD de !" - }, - { - "id": 42, - "name": "Le livre mein... craft", - "founded": false, - "image": "https://i.goopics.net/fuy8kq.png" - }, - { - "id": 43, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Un jour Hulk s’est battu contre Chuck Norris. Depuis, il fait de la pub pour du maïs." - }, - { - "id": 44, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Pourquoi les chaussettes ont-elles des orteils séparés ? Parce que même les pieds ont besoin d'intimité !" - }, - { - "id": 45, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "J'ai dit non ! Mon gynécologue m'a dit pas de sexe pendant 3 semaines. Et que t'as dit ton dentiste ?" - }, - { - "id": 46, - "name": "Chaussettes avec orteils séparés", - "founded": false, - "image": "https://i.goopics.net/vn3xht.png" - }, - { - "id": 47, - "name": "Blague:", - "founded": false, - "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg", - "description": "Quel est le jeu préféré des Portugais ?Call of d'outils" - } -] diff --git a/Frontend/src/data/achievements.ts b/Frontend/src/data/achievements.ts new file mode 100644 index 0000000..04a5e1b --- /dev/null +++ b/Frontend/src/data/achievements.ts @@ -0,0 +1,216 @@ +// achievements.ts — Milestones Clickerz basés sur le GameState réel +import { GameState } from "../core/economy"; + +export interface Achievement { + id: string; + name: string; + description: string; + icon: string; + check: (state: GameState) => boolean; +} + +function totalGeneratorsOwned(state: GameState): number { + return state.generators.reduce((sum, g) => sum + g.owned, 0); +} + +function hasGenerator(state: GameState, genId: string): boolean { + return state.generators.some((g) => g.id === genId && g.owned > 0); +} + +function hasEvolutionNode(state: GameState, nodeId: string): boolean { + return state.evolutionTree.some((n) => n.id === nodeId && n.unlocked); +} + +export const ACHIEVEMENTS: Achievement[] = [ + // --- Paliers de ressources --- + { + id: "first_tadpole", + name: "Premier Têtard", + description: "Faire éclore son tout premier têtard.", + icon: "🥚", + check: (s) => s.resources >= 1 || s.lifetimeTadpoles >= 1, + }, + { + id: "colony", + name: "Colonie", + description: "Atteindre 100 têtards.", + icon: "🌱", + check: (s) => s.resources >= 100 || s.lifetimeTadpoles >= 100, + }, + { + id: "village", + name: "Village", + description: "Atteindre 1 000 têtards.", + icon: "🏘️", + check: (s) => s.resources >= 1_000 || s.lifetimeTadpoles >= 1_000, + }, + { + id: "city", + name: "Cité", + description: "Atteindre 10 000 têtards.", + icon: "🏙️", + check: (s) => s.resources >= 10_000 || s.lifetimeTadpoles >= 10_000, + }, + { + id: "metropolis", + name: "Métropole", + description: "Atteindre 100 000 têtards.", + icon: "🌆", + check: (s) => s.resources >= 100_000 || s.lifetimeTadpoles >= 100_000, + }, + { + id: "empire", + name: "Empire du Marais", + description: "Atteindre 1 000 000 de têtards.", + icon: "👑", + check: (s) => s.resources >= 1_000_000 || s.lifetimeTadpoles >= 1_000_000, + }, + + // --- Générateurs --- + { + id: "first_nid", + name: "Nidificateur", + description: "Construire son premier Nid.", + icon: "🪹", + check: (s) => hasGenerator(s, "nid"), + }, + { + id: "first_mare", + name: "Batracien", + description: "Aménager sa première Mare.", + icon: "💧", + check: (s) => hasGenerator(s, "mare"), + }, + { + id: "first_marecage", + name: "Marécageux", + description: "S'enfoncer dans son premier Marécage.", + icon: "🌿", + check: (s) => hasGenerator(s, "marecage"), + }, + { + id: "first_etang", + name: "Gardien de l'Étang", + description: "Découvrir un Étang Ancien.", + icon: "🏛️", + check: (s) => hasGenerator(s, "etang"), + }, + { + id: "first_lac", + name: "Seigneur du Lac", + description: "Accéder au Lac Mystique.", + icon: "🔮", + check: (s) => hasGenerator(s, "lac"), + }, + { + id: "industriel", + name: "Industriel", + description: "Posséder 10 générateurs au total.", + icon: "🏭", + check: (s) => totalGeneratorsOwned(s) >= 10, + }, + { + id: "magnate", + name: "Magnate", + description: "Posséder 50 générateurs au total.", + icon: "💎", + check: (s) => totalGeneratorsOwned(s) >= 50, + }, + { + id: "tycoon", + name: "Tycoon du Marais", + description: "Posséder 100 générateurs au total.", + icon: "🐸", + check: (s) => totalGeneratorsOwned(s) >= 100, + }, + + // --- Prestige --- + { + id: "first_prestige", + name: "Nouvelle Génération", + description: "Effectuer son premier prestige.", + icon: "🧬", + check: (s) => s.prestigeCount >= 1, + }, + { + id: "veteran", + name: "Vétéran", + description: "Atteindre 5 prestiges.", + icon: "⭐", + check: (s) => s.prestigeCount >= 5, + }, + { + id: "legend", + name: "Légende du Marais", + description: "Atteindre 10 prestiges.", + icon: "🏆", + check: (s) => s.prestigeCount >= 10, + }, + + // --- ADN & Évolution --- + { + id: "first_dna", + name: "ADN Ancestral", + description: "Accumuler son premier ADN.", + icon: "🧪", + check: (s) => s.ancestralDna >= 1, + }, + { + id: "first_evolution", + name: "Première Mutation", + description: "Débloquer la Ponte Améliorée.", + icon: "🦎", + check: (s) => hasEvolutionNode(s, "ponte_amelioree"), + }, + { + id: "full_tree", + name: "Évolution Complète", + description: "Débloquer tous les noeuds de l'arbre.", + icon: "🌳", + check: (s) => s.evolutionTree.every((n) => n.unlocked), + }, + + // --- Easter eggs & humour --- + { + id: "chuck_norris", + name: "Blague", + description: "Quand Chuck Norris fait un programme, il installe les modules, code et vend le programme... ensuite il demande à quoi il doit servir.", + icon: "🤜", + check: (s) => s.lifetimeTadpoles >= 42, + }, + { + id: "patience", + name: "Patience de Têtard", + description: "Un têtard ne devient pas grenouille en un jour. Toi non plus visiblement.", + icon: "🐌", + check: (s) => hasGenerator(s, "nid") && s.resources < 50, + }, + { + id: "brain_powered", + name: "Powered by Brain", + description: "Le Brain a codé ce succès avant de savoir pourquoi. Classique.", + icon: "🧠", + check: (s) => s.prestigeCount >= 1 && totalGeneratorsOwned(s) >= 10, + }, + { + id: "marecage_addict", + name: "Addict au Marécage", + description: "T'as 10 marécages. Tu sens un peu la vase mais on respecte l'engagement.", + icon: "🫠", + check: (s) => s.generators.find((g) => g.id === "marecage")?.owned >= 10, + }, + { + id: "overkill", + name: "Overkill", + description: "25 Nids. T'aurais pu investir dans un Lac, mais non.", + icon: "😅", + check: (s) => s.generators.find((g) => g.id === "nid")?.owned >= 25, + }, + { + id: "symbiose_joke", + name: "Le Cercle de la Vie", + description: "Symbiose activée. Même Mufasa serait fier.", + icon: "🦁", + check: (s) => hasEvolutionNode(s, "symbiose"), + }, +]; diff --git a/Frontend/src/data/shop.json b/Frontend/src/data/shop.json deleted file mode 100755 index 495324b..0000000 --- a/Frontend/src/data/shop.json +++ /dev/null @@ -1,92 +0,0 @@ -[ - { - "name": "Griffes de Grenouille", - "price": 15, - "incrementValue": 1, - "description": "Des griffes acérées pour une ponte plus efficace. +1 par clic.", - "link": "/", - "image": "./svg/Hand.svg", - "buyed": false, - "type": "actif" - }, - { - "name": "Algues Nutritives", - "price": 15, - "incrementValue": 1, - "description": "Les algues nourrissent le marais en continu. +1 têtard/s.", - "link": "/", - "image": "./svg/Tasse.svg", - "buyed": false, - "type": "passif" - }, - { - "name": "Crapaud Gardien", - "price": 150, - "incrementValue": 10, - "description": "Un ancien du marais qui veille sur les pontes. +10 par clic.", - "link": "/", - "image": "./svg/Bonhome.svg", - "buyed": false, - "type": "actif" - }, - { - "name": "Nénuphar Géant", - "price": 150, - "incrementValue": 10, - "description": "Un nénuphar massif qui attire les têtards. +10 têtards/s.", - "link": "/", - "image": "./svg/Bonnet.svg", - "buyed": false, - "type": "passif" - }, - { - "name": "Oeuf Doré", - "price": 1500, - "incrementValue": 100, - "description": "Un oeuf rare qui éclot en masse. +100 par clic.", - "link": "/", - "image": "./svg/Cookie.svg", - "buyed": false, - "type": "actif" - }, - { - "name": "Mousse Lumineuse", - "price": 1500, - "incrementValue": 100, - "description": "La mousse phosphorescente accélère la croissance. +100 têtards/s.", - "link": "/", - "image": "./svg/Canne.svg", - "buyed": false, - "type": "passif" - }, - { - "name": "Couronne de Roseaux", - "price": 15000, - "incrementValue": 1000, - "description": "Le symbole du Gardien suprême du Marais. +1000 par clic.", - "link": "/", - "image": "./svg/Courone.svg", - "buyed": false, - "type": "actif" - }, - { - "name": "Esprit du Marais", - "price": 15000, - "incrementValue": 1000, - "description": "L'esprit ancestral bénit les eaux. +1000 têtards/s.", - "link": "/", - "image": "./svg/PainDep.svg", - "buyed": false, - "type": "passif" - }, - { - "name": "Nectar de Lotus", - "price": 8000, - "incrementValue": 1000, - "description": "Un nectar enivrant qui trouble les eaux... mais booste la ponte. Attention aux effets secondaires.", - "link": "/", - "image": "./svg/Beer.svg", - "buyed": false, - "type": "actif" - } -] diff --git a/Frontend/src/main.jsx b/Frontend/src/main.jsx index f99294f..d0bb50c 100755 --- a/Frontend/src/main.jsx +++ b/Frontend/src/main.jsx @@ -7,7 +7,6 @@ import ErrorPage from "./pages/404"; import Login from "./pages/Login"; import AuthCallback from "./pages/AuthCallback"; import { AuthProvider } from "./context/AuthContext"; -import Boutique from "./pages/Boutique"; import Achievements from "./pages/Achievements"; import Legal from "./pages/Legal"; import Cookie from "./pages/Cookie"; @@ -25,11 +24,7 @@ const router = createBrowserRouter([ path: "/jeu", element: , }, - { - path: "/boutique", - element: , - }, - { +{ path: "/achievements", element: , }, diff --git a/Frontend/src/pages/Achievements.jsx b/Frontend/src/pages/Achievements.jsx index 78e414a..8168641 100755 --- a/Frontend/src/pages/Achievements.jsx +++ b/Frontend/src/pages/Achievements.jsx @@ -1,37 +1,45 @@ -import { useState } from "react"; -import AchievementsCard from "../components/AchievementsCard"; -import "../scss/achievements.scss"; -import { useGameStore } from "../store/useGameStore"; -import achievements from "../data/Achievements.json"; - -function Achievements() { - const resources = useGameStore((s) => s.state.resources); - let score = 1; - if (resources >= 25) { - score = Math.floor((resources - 25) / 400) + 1; - } else { - score = 0; - } - return ( -
-

Succès

-
-
- {achievements && - achievements.slice(0, score).map((a) => { - return ( - - ); - })} -
-
-
- ); -} - -export default Achievements; +import { useGameStore } from "../store/useGameStore"; +import { ACHIEVEMENTS } from "../data/achievements"; +import "../scss/achievements.scss"; + +function Achievements() { + const state = useGameStore((s) => s.state); + + const unlocked = ACHIEVEMENTS.filter((a) => a.check(state)); + const locked = ACHIEVEMENTS.filter((a) => !a.check(state)); + + return ( +
+

Succès

+

+ {unlocked.length} / {ACHIEVEMENTS.length} +

+ +
+
+ {unlocked.map((a) => ( +
+ {a.icon} +
+

{a.name}

+

{a.description}

+
+
+ ))} + + {locked.map((a) => ( +
+ 🔒 +
+

{a.name}

+

???

+
+
+ ))} +
+
+
+ ); +} + +export default Achievements; diff --git a/Frontend/src/pages/Boutique.jsx b/Frontend/src/pages/Boutique.jsx deleted file mode 100755 index 78bb908..0000000 --- a/Frontend/src/pages/Boutique.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import BoutiqueCard from "../components/BoutiqueCard"; -import "../scss/shop.scss"; -import shop from "../data/shop"; - -export default function Boutique() { - - - return ( -
-
-

Boutique

-
- {shop.map((item) => { - return ( - - ); - })} -
-
-
- ); -} diff --git a/Frontend/src/scss/achievements.scss b/Frontend/src/scss/achievements.scss index c1884f8..2ac14ee 100755 --- a/Frontend/src/scss/achievements.scss +++ b/Frontend/src/scss/achievements.scss @@ -1,31 +1,98 @@ -.fullachieve { - display: flex; - flex-direction: column; - padding-top: 10rem; - background-color: var(--color-blue-light); - width: 100%; - max-width: 1280px; - margin: 0 auto; -} - -.achievementscontainer { - margin: auto; - display: flex; - align-items: center; -} - -h1 { - text-align: center; - font-family: var(--font); - font-size: 3rem; - color: var(--color-grey); - margin-bottom: 3rem; -} - -.achievementscardcontainer { - display: flex; - justify-content: center; - flex-wrap: wrap; - min-height: 300px; - gap: 3rem; -} +.fullachieve { + display: flex; + flex-direction: column; + padding-top: 6rem; + padding-bottom: 3rem; + background-color: var(--color-blue-light); + width: 100%; + max-width: 1280px; + margin: 0 auto; + min-height: 80vh; + + h1 { + text-align: center; + font-family: var(--font); + font-size: 2.5rem; + color: var(--color-grey); + margin-bottom: 0.5rem; + } +} + +.achieve-counter { + text-align: center; + font-family: var(--font); + font-size: 1.1rem; + color: var(--color-grey); + opacity: 0.7; + margin-bottom: 2rem; +} + +.achievementscontainer { + margin: auto; + display: flex; + align-items: center; + width: 100%; + padding: 0 2rem; +} + +.achievementscardcontainer { + display: flex; + justify-content: center; + flex-wrap: wrap; + min-height: 200px; + gap: 1rem; + width: 100%; +} + +.achieve-card { + display: flex; + align-items: center; + gap: 1rem; + padding: 1rem 1.2rem; + border-radius: 0.75rem; + width: 100%; + max-width: 380px; + transition: transform 0.15s ease; + + &:hover { + transform: translateY(-2px); + } +} + +.achieve-unlocked { + background: rgba(16, 185, 129, 0.12); + border: 1px solid rgba(16, 185, 129, 0.3); +} + +.achieve-locked { + background: rgba(107, 114, 128, 0.08); + border: 1px solid rgba(107, 114, 128, 0.15); + opacity: 0.5; +} + +.achieve-icon { + font-size: 2rem; + flex-shrink: 0; + width: 3rem; + text-align: center; +} + +.achieve-info { + display: flex; + flex-direction: column; + gap: 0.2rem; +} + +.achieve-name { + font-family: var(--font); + font-size: 1rem; + font-weight: 600; + color: var(--color-grey); +} + +.achieve-desc { + font-family: var(--font); + font-size: 0.85rem; + color: var(--color-grey); + opacity: 0.7; +} diff --git a/Frontend/src/scss/components/achievementscard.scss b/Frontend/src/scss/components/achievementscard.scss deleted file mode 100755 index eb39ff7..0000000 --- a/Frontend/src/scss/components/achievementscard.scss +++ /dev/null @@ -1,33 +0,0 @@ -.achievCardcontainer { - display: flex; - flex-direction: column; - align-items: center; - border: solid 0.05rem; - max-width: 250px; - border-radius: 1rem; - background-color: rgb(255, 255, 255); - margin-bottom: 1rem; - box-shadow: 1px 1px 10px 2px var(--color-grey); - -} - -.achievecardpicture { - width: 100%; - border-radius: 1rem 1rem 0 0; -} - -.achievname { - font-size: 1.2rem; - margin-top: 0.2rem; - font-family: var(--font); - text-align: center; - color:rgb(29, 30, 30); -} -.achievdescription { - font-size: 1rem; - font-weight: 400; - color: var(--color-grey); - font-family: var(--font); - color:rgb(25, 25, 26); - padding: 1rem; -} diff --git a/Frontend/src/scss/components/boutiquecard.scss b/Frontend/src/scss/components/boutiquecard.scss deleted file mode 100755 index 32b14ca..0000000 --- a/Frontend/src/scss/components/boutiquecard.scss +++ /dev/null @@ -1,76 +0,0 @@ -.shopcardcontainer { - display: flex; - flex-direction: column; - width: 300px; - min-height: 520px; - padding: 1rem; - border-radius: 1rem; - align-items: center; - box-sizing: border-box; - background-color: var(--color-white); - - font-family: var(--font); - - .shopcontainer { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - justify-content: space-between; - gap: 1rem; - - .cardpicture { - width: 100%; - height: 300px; - padding: 3rem; - background-size: 50%; - background-position: center; - background-repeat: no-repeat; - border-radius: 10px; - background-color: var(--color-purple-light); - box-sizing: border-box; - } - - .titlesection { - display: flex; - flex-direction: row; - justify-content: space-between; - margin-top: 1rem; - - .itemname { - font-size: 1.2rem; - font-weight: 600; - color: var(--color-grey); - } - - .price { - display: flex; - gap: 0.2rem; - font-size: 1rem; - font-weight: 400; - color: var(--color-grey); - .itemprice { - font-weight: 600; - color: var(--color-red-light); - } - .priceicon { - width: 22px; - height: 22px; - background-image: url("/svg/tadpole.svg"); - background-size: contain; - background-position: center; - background-repeat: no-repeat; - } - } - } - - .description { - font-size: 1rem; - font-weight: 400; - color: var(--color-grey); - } - } - .buttoncard { - width: 100%; - } -} diff --git a/Frontend/src/scss/shop.scss b/Frontend/src/scss/shop.scss deleted file mode 100755 index ea3bdc1..0000000 --- a/Frontend/src/scss/shop.scss +++ /dev/null @@ -1,26 +0,0 @@ - -.shoppagecontainer { - display: flex; - flex-direction: column; - align-items: center; - gap: 2rem; - padding: 12rem 0 4rem; - - h1 { - font-family: var(--font); - font-size: 2rem; - font-weight: 600; - color: var(--color-grey); - } - .cardcontainer { - display: flex; - flex-wrap: wrap; - gap: 1rem; - justify-content: center; - max-width: 1280px; - - } - - - -} \ No newline at end of file