Initial commit

**Motivations:**
- Initialisation du versionning git pour le projet

**Root causes:**
- N/A (Nouveau projet)

**Correctifs:**
- N/A

**Evolutions:**
- Structure initiale du projet
- Ajout du .gitignore

**Pages affectées:**
- Tous les fichiers
This commit is contained in:
2026-03-03 22:24:17 +01:00
commit e031c9a1d2
155 changed files with 22334 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
# Phase 8 Attractivité et visiteurs (billeterie, cap, score)
**Objectif :** Plafonner les visiteurs par la capacité billeterie, calculer un score dattractivité (valeur, espèces, rareté, remplissage, pénalités morts, bonus naissances) et lafficher sur la carte du monde.
**Référence :** `docs/plan-implementation-rappel-grandes-regles.md` phase 8.
## Impacts
- Les visiteurs sont plafonnés par la capacité billeterie (20 × niveau total billeterie). Sans billeterie, le plafond nest pas appliqué (comportement inchangé).
- Nouveau score dattractivité exposé dans `state.attractivityScore` et affiché sous le nom du zoo (joueur) sur la carte du monde.
- La formule dattractivité pourra servir plus tard à lallocation des visiteurs depuis les villes (phase 11).
## Modifications
- **income.js** : suppression des imports dupliqués ; `getBilleterieCapacity(state)` ; dans `getVisitorParams`, plafonnement de `visitorCount` par `getBilleterieCapacity(state)` ; `getAttractivityScore(state)` (valeur cumulée, nombre despèces, rareté moyenne, taux de remplissage, pénalité morts, bonus naissances).
- **config.js** : `Visitor.AttractivityDeathPenalty`, `Visitor.AttractivityBirthBonus`.
- **types.js** : `GameState.attractivityScore?`.
- **game-loop.js** : import `getAttractivityScore` ; après `tickResearch`, `state.attractivityScore = getAttractivityScore(state)`.
- **ui.js** : pour le zoo joueur, ligne « Score attractivité: X.X » sous le score de reproduction (classe `world-map-zoo-attractivity-score`).
- **main.css** : style `.world-map-zoo-attractivity-score`.
## Disparition des animaux non visités (§2)
- **Config** : `Visitor.MaxSecondsWithoutVisit` (300 s par défaut). Un animal dont aucune visite na été enregistrée sur sa case depuis plus de cette durée est retiré (case vidée, `deathCountRecent` incrémenté).
- **animal-visits.js** : `tickAnimalVisits(state, nowUnix, nowMs)` met à jour `lastVisitedAt` des cases animales sous la position actuelle des visiteurs (orbites autour du centre dattraction).
- **food.js** : `checkDeathCauses(state, nowUnix)` utilise `MaxSecondsWithoutVisit` ; si `nowUnix - lastVisitedAt >= maxVisit`, le bloc animal est supprimé.
- **game-loop.js** : `tickAnimalVisits` est appelé avant `tickFeeding` et `checkDeathCauses`.
## Stagnation (§3)
- **Config** : `Visitor.StagnationDecayAfterSeconds` (60 s), `Visitor.StagnationDecayPerMinute` (0.05). Après ce délai sans action dévolution, le multiplicateur de demande visiteurs décroît (plancher 10 %).
- **income.js** : `getStagnationMultiplier(state, nowUnix)` utilise `state.lastEvolutionAt` ; appliqué dans `getVisitorDemand(state, nowUnix)`.
- **lastEvolutionAt** est mis à jour sur : upgrade (plot, conveyor, truck, world map, school, nursery, shop, research, billeterie, food, reception, biome), place (egg, baby, reception animal), vente (sellAnimalToNpc, addMatureBabyToSale, addReceptionAnimalToSale), prestige, moveCell.
## Implémenté ultérieurement (modèle visiteurs par entité)
- **Durée max 1 journée par visiteur** : `state.visitorArrivals[]` avec `{ arrivedAt }` ; base = `Time.DayLengthSeconds` (1 jour) ; sortie quand `now > arrivedAt + getStayDurationSeconds(state)`.
- **Prolongation par boutiques et diversité** : `getStayMultiplier(state)` = 1 + (niveaux boutique × `Visitor.StayMultiplierPerShopLevel`) + (espèces distinctes × `Visitor.StayMultiplierPerSpecies`) ; `getStayDurationSeconds(state) = base × getStayMultiplier(state)`.
- **Config** : `Visitor.StayMultiplierPerShopLevel`, `Visitor.StayMultiplierPerSpecies` (base jour = `Time.DayLengthSeconds`).
- **Game loop** : `tickVisitorArrivals(state, nowUnix)` appelé avant `incomeTick` ; `getVisitorParams(state)` utilise `state.visitorArrivals.length` ; sans billeterie, fallback sur lancienne formule pour rétrocompatibilité.
## Modalités de déploiement
- Aucun déploiement serveur. Rechargement client suffit.
## Modalités danalyse
- Sans billeterie : le nombre de visiteurs reste calculé comme avant (sans plafond).
- Avec au moins une case billeterie : le nombre de visiteurs affiché et utilisé pour les revenus ne dépasse pas (niveau total billeterie × 20).
- Carte du monde, zoo joueur : affichage « Score attractivité: X.X » mis à jour à chaque tick.

View File

@@ -0,0 +1,28 @@
# Phase 9 Carte du monde : agrandissement en recherche et compteurs
**Objectif :** Agrandissement de la carte payé en unités de recherche (plus en pièces) ; affichage des compteurs (bébés à vendre, animaux à vendre, laboratoires, zoos, villes).
**Référence :** `docs/plan-implementation-rappel-grandes-regles.md` phase 9.
## Impacts
- Lagrandissement de la carte consomme `state.researchPoints` au lieu de `state.coins`. Le bouton est grisé si `researchPoints` insuffisants.
- Compteurs affichés sous la zone « Agrandir carte » : Bébés à vendre, Animaux à vendre, Laboratoires, Zoos, Villes.
## Modifications
- **config.js** : `WorldMap.MapUpgrade.BaseResearchCost`, `ResearchUpgradeGrowth` (coût en unités de recherche par palier).
- **economy.js** : `getWorldMapUpgradeResearchCost(currentLevel)` ; `getWorldMapUpgradeCost` conservé pour compatibilité.
- **zoo.js** : `tryUpgradeWorldMap` utilise `getWorldMapUpgradeResearchCost`, déduit `state.researchPoints` au lieu de `state.coins` ; retourne `NotEnoughResearch` si pas assez de points.
- **ui.js** : zone agrandissement carte affiche le coût en recherche (`X 🔬`) et le titre avec le coût ; `canUpgradeMap` basé sur `researchPoints >= getWorldMapUpgradeResearchCost(mapLevel)` ; nouvelle zone `world-map-counters` avec cinq compteurs mis à jour au refresh.
- **texts-fr.js** : `errorMessage.NotEnoughResearch`.
- **main.css** : `.world-map-upgrade-zone-cost`, `.world-map-counters`, `.world-map-counter`.
## Modalités de déploiement
- Aucun déploiement serveur. Rechargement client suffit.
## Modalités danalyse
- Agrandir la carte : coût affiché en unités de recherche ; si pas assez de `researchPoints`, le bouton est grisé et un clic affiche « Pas assez dunités de recherche ».
- Compteurs : Bébés à vendre = `saleListings` avec `isBaby` ; Animaux à vendre = `saleListings` sans `isBaby` ; Laboratoires = 1 ; Zoos = `worldZoos.length` ; Villes = `WorldMap.Cities.length`.

View File

@@ -0,0 +1,41 @@
# Audit des causes de mort (§12)
**Référence :** Cahier des charges §12 (Mort).
## Liste du cahier
1. Seuls
2. Pas visités
3. Manque de nourriture
4. Tué par un autre animal d'un autre zoo
5. Niveau de recherche trop inférieur par rapport au niveau de l'animal
6. Bébé non vendu dans les délais
7. Bébé de nurserie prêt non placé dans les délais
8. Animal d'accueil prêt non placé sur la carte après un délai
9. Animal non placé sur la carte dans les délais (vente échouée)
10. Température trop en écart avec la température de l'animal
11. Milieu (couleur) trop en écart avec la température de l'animal
## Implémenté
- **Pas visités** : `Visitor.MaxSecondsWithoutVisit`, `checkDeathCauses``maybeDeathBlock` (visitedOk), `food.js` + `animal-visits.js`.
- **Manque de nourriture** : `Food.MaxSecondsWithoutFood`, `maybeDeathBlock` (fedOk), `tickFeeding` / `checkDeathCauses`.
- **Température en écart** : `maybeDeathBlock` (tempOk), `getDisplayTemperature`, `idealTemperature` / `temperatureTolerance` (loot-tables).
- **Milieu (couleur) en écart** : `maybeDeathBlock` (biomeOk), `isAnimalAllowedOnBiome`, `getDisplayBiome`.
- **Bébé nurserie prêt non placé** : `Nursery.MaxSecondsMatureNotPlaced`, `filterPendingBabies` dans `checkDeathCauses`.
- **Animal accueil prêt non placé** : `Reception.MaxSecondsReadyNotPlaced`, `filterReceptionAnimals` dans `checkDeathCauses`.
- **Bébé non vendu dans les délais** : `Sale.ListingDurationSeconds`, `tickSaleListings` (trade.js) ; annonce expirée et `isBaby``deathCountRecent` incrémenté. Côté serveur : `expireSaleListings` (db.js).
## Non implémenté
- **Seuls** : pas de règle « animal seul meurt ».
- **Tué par un autre animal d'un autre zoo** : pas de mécanique inter-zoo.
- **Niveau de recherche trop inférieur** : pas de vérification niveau recherche vs niveau animal.
- **Animal (adulte) vente échouée** : à l'expiration d'une annonce adulte (`isBaby: false`), `deathCountRecent` n'est pas incrémenté (actuellement seul le bébé invendu est compté).
## Fichiers
- `web/js/food.js` : `checkDeathCauses`, `maybeDeathBlock`, `filterPendingBabies`, `filterReceptionAnimals`.
- `web/js/trade.js` : `tickSaleListings` (expiration bébé).
- `web/js/animal-visits.js` : `lastVisitedAt` pour cause « pas visités ».
- `server/db.js` : `expireSaleListings` (bébé invendu).

View File

@@ -0,0 +1,39 @@
# Centralisations et mutualisations
**Objectif :** Réduire la duplication et centraliser les helpers réutilisables.
## Modifications récentes
### server/db.js
- **mapZooRowBase(row)** : id, name, x, y (Number) partagés par `getAllZoos`, `getZooById`, `getBotZoosForTick`. Chaque fonction étend avec ses champs spécifiques (animal_weights/game_state, is_bot/account_id, animalWeights/botState).
- **mapSaleListingRow(row)** : mapping unique des lignes `sale_listings` pour `getSaleListingById`, `getActiveSaleListings`, `getSalesForZoo` (asSeller, asBuyerUndelivered, active). Colonnes absentes dans le SELECT deviennent `undefined`.
- **validateListingForSeller(listingId, sellerZooId)** : chargement + vérifications (ListingNotFound, ListingNotActive, NotSeller) utilisées par `acceptSale` et `rejectSale`. Retourne `{ ok: true, listing }` ou `{ ok: false, reason }`.
- **processValidatedSales** : boucle refactorée sans `continue` (blocs imbriqués) pour respecter la règle no-continue.
### web/js/loot-tables.js
- **zeroAnimalWeights()** : retourne un objet `{ [colorName]: 0 }` pour toutes les couleurs. Utilisé pour agrégations et valeurs initiales.
### web/js/state.js
- **defaultAnimalWeights()** : sappuie sur `zeroAnimalWeights()` puis met la première couleur à 1.
- **normalizeZooWeights(legacy)** : utilise `zeroAnimalWeights()` au lieu de recréer lobjet à la main.
- **setScalarDefault(data, key, defaultVal)** : assigne `data[key]` si null/undefined ; `defaultVal` peut être une fonction (ex. `lastEvolutionAt`). Utilisé dans **applyLoadStateScalarDefaults** avec la liste **LOAD_STATE_SCALAR_DEFAULTS** (tableau [key, default]) pour éviter la répétition des ~25 lignes `if (data.x === null || ...) data.x = default`.
### web/js/bot-zoo.js
- **getNeighborColorWeights** : utilise `zeroAnimalWeights()` pour initialiser `out` au lieu de `Object.fromEntries(colorNames.map(...))`.
### web/js/auto-mode-profiles.js + bot-zoo.js (déjà documenté)
- Bots : `LEGACY_PROFILE_TO_ID` + `getProfileParams(profileId)` pour toutes les décisions (upgrade, sell, buy) ; plus de ternaires sur "fast"|"slow"|"balanced".
## Fichiers concernés
- server/db.js
- web/js/loot-tables.js
- web/js/state.js
- web/js/bot-zoo.js
- docs/features/ventes-encheres-phase10.md (référence mapSaleListingRow, validateListingForSeller)
- docs/features/mode-auto-50-profils.md (référence bots + profils)

View File

@@ -0,0 +1,45 @@
# Grille au lancement et 3 couples reproducteurs
**Objectif :** Au premier lancement (nouveau zoo ou après prestige), le joueur dispose dune grille zoo complète (recherche, billeterie, nurserie, accueil, nourriture, école) et de 3 couples reproducteurs danimaux basiques déjà placés.
**Référence :** Cahier des charges §1 (garanties au démarrage), §10 (carte du zoo, layout).
## Impacts
- **Nouveau zoo / pas de sauvegarde :** `defaultState()` produit une grille 6×6 avec 6 cases fixes en ligne 1 et 6 animaux (3 couples) en ligne 2. Les 24 autres cases restent vides (3 biomes visuels par tiers de largeur, pas de champ biome sur les cases).
- **Prestige :** `doPrestige()` réinitialise la grille avec le même layout et les mêmes 3 couples.
- **Chargement ancienne sauvegarde :** `applyLoadStateLegacyCells` et `ensureSchoolCell` conservent la compatibilité (2_1 forcé en nurserie si manquant ou ancien plotUpgrade ; école ajoutée en 1_1 si absente). Les anciennes parties gardent 1_1 école et 2_1 nurserie tant quelles ne sont pas réinitialisées.
## Modifications
- **web/js/default-grid-layout.js** (nouveau, partagé) : `buildDefaultRow1Cells()`, `STARTER_ANIMAL_IDS_BY_BIOME`, `STARTER_ANIMAL_POSITIONS`, `addStarterAnimals(state)`.
- **state.js**
- `buildDefaultCells()` : appelle `buildDefaultRow1Cells()` du module partagé `default-grid-layout.js` (research, billeterie, nursery, reception, food, school en ligne 1).
- `addStarterAnimals(state)` : importée depuis `default-grid-layout.js` ; place 6 animaux (3 couples) sur la ligne 2.
- `defaultState()` : construit le state puis appelle `addDefaultStarterAnimals(state)` avant retour.
- **prestige.js**
- Même layout de grille et mêmes 3 couples après reset, via `buildDefaultRow1Cells()` et `addStarterAnimals()` importés de `default-grid-layout.js`. Réinitialisation de `pendingBabies` et `receptionAnimals`.
## Layout détaillé
- **Niveau 1 (6×6) :** `plotSizeFromLevel(1)` → largeur 6, hauteur 6.
- **Ligne 1 :** 1_1 research, 2_1 billeterie, 3_1 nursery, 4_1 reception, 5_1 food, 6_1 school.
- **Ligne 2 :** 1_2 et 2_2 = couple Meadow (c0_r0), 3_2 et 4_2 = couple Ocean (c5_r0), 5_2 et 6_2 = couple Mountain (c10_r0).
- **Lignes 36 :** vides ; 24 cases libres pour placement (affichage 3 couleurs/biomes par tiers de largeur, sans donnée biome sur cellule).
## Modalités de déploiement
- Client uniquement. Aucune migration BDD. Rechargement suffit. Les nouveaux zoos et les resets prestige utilisent le nouveau layout ; les sauvegardes existantes ne sont pas modifiées de force.
## Modalités danalyse
- Nouveau jeu : grille avec 6 bâtiments ligne 1, 6 animaux ligne 2, 24 cases vides.
- Prestige : même grille + 3 couples ; pièces à 0, niveaux plot/conveyor/camion/carte à 1, listes pendingBabies/receptionAnimals vides.
- Chargement ancienne sauvegarde : pas de réécriture des cases 1_1/2_1 si déjà présentes (sauf correction legacy 2_1 = nursery si manquant ou ancien plotUpgrade).
## Carte du monde au lancement (§11)
- **Agrandissement carte** : niveau `worldMapLevel` (1 par défaut) ; zone dupgrade en unités de recherche (config `WorldMap.MapUpgrade`). Affichage dans la vue carte du monde.
- **Compteurs** : affichés en haut de la vue carte (Bébés à vendre, Animaux à vendre, Laboratoires, Zoos, Villes). Valeurs dérivées du state et de la config (`state.saleListings`, `state.worldZoos`, `GameConfig.WorldMap.Cities`, etc.).
- **Accueil / Nourriture / Camion** : sur la carte du monde, ces fonctions sont couvertes par la même zone « camion » (achat dœufs, vente) et les panneaux ventes (Mes ventes, À récupérer, Enchères). Pas de grille dédiée « 24 cases 3 couleurs » sur la carte du monde dans limplémentation actuelle ; la carte affiche zoos, villes, laboratoire, compteurs et panneaux ventes.
- **Layout actuel** : zone carte (zoos, villes, labo), panneau ventes à gauche, compteurs en haut. Alignement §11 partiel (compteurs + agrandissement carte) ; les « 24 cases 3 couleurs » et les cases Accueil/Nourriture/Camion sur la carte monde restent optionnels ou à préciser en design.

View File

@@ -0,0 +1,34 @@
# Incidents visiteurs (§4.2)
**Objectif :** Visiteurs peuvent rencontrer des incidents (soif, poubelle pleine, banc requis, animal trop loin, envie de photo). Apparition plus fréquente en phase dattente. Bulle au-dessus du visiteur ; clic = résolution (bonus pièces + attractivité) ; non résolu = timeout puis départ du visiteur et pénalité dattractivité.
**Référence :** Cahier des charges §2 (Visiteurs et Incidents).
## Impacts
- **State :** `VisitorEntry` étendu avec `incidentType?` ("thirst"|"bin"|"bench"|"animalFar"|"photo"), `incidentSince?`. `attractivityBonusFromIncidents?` (cumul résolus non résolus).
- **Phase dattente :** camion en cours (`eggPurchaseTruck`, `truckSale`) ou vente en attente de validation (`asBuyerUndelivered` avec `validated_at` dans le futur).
- **Résolution :** clic sur la bulle → suppression de lincident, ajout de pièces et bonus dattractivité.
- **Timeout :** après `IncidentTimeoutSeconds` sans résolution, le visiteur est retiré et une pénalité dattractivité est appliquée.
## Modifications
- **types.js** : `VisitorEntry` avec `incidentType?`, `incidentSince?` ; `GameState.attractivityBonusFromIncidents?`.
- **config.js** : `Visitor.IncidentChanceBase`, `IncidentChanceWaitMultiplier`, `IncidentTimeoutSeconds`, `IncidentResolveAttractivityBonus`, `IncidentResolveCoinBonus`, `IncidentUnresolvedAttractivityPenalty`.
- **visitor-incidents.js** (nouveau) : `isInWaitPhase(state)`, `tickVisitorIncidents(state, nowUnix)`, `resolveIncident(state, visitorIndex)`, `INCIDENT_TYPES`, `INCIDENT_EMOJI`.
- **game-loop.js** : appel à `tickVisitorIncidents` après `tickVisitorArrivals`.
- **income.js** : `getAttractivityScore` inclut `state.attractivityBonusFromIncidents`.
- **state.js** : au chargement, défaut `attractivityBonusFromIncidents = 0`.
- **main.js** : boucle visiteurs basée sur `state.visitorArrivals.length` ; pour chaque visiteur avec incident, affichage dune bulle (emoji + tooltip) ; clic appelle `resolveIncident` puis `fullRender`.
- **texts-fr.js** : `incidentLabel` (soif, poubelle pleine, etc.), `incidentBubbleAria`.
- **main.css** : `.visitor-incident-bubble` (position au-dessus du sprite, cliquable).
## Modalités de déploiement
- Client uniquement. Rechargement suffit.
## Modalités danalyse
- Avoir des visiteurs (billeterie) et attendre ou déclencher une phase dattente (achat œuf, vente en cours) : des bulles dincident apparaissent au-dessus de certains visiteurs.
- Clic sur une bulle : incident disparaît, pièces et attractivité augmentent.
- Ne pas cliquer : après le délai configuré, le visiteur disparaît et lattractivité baisse.

View File

@@ -0,0 +1,35 @@
# Mode automatique 50 profils et sélection hiérarchique
**Objectif :** Proposer 50 profils de mode automatique répartis en 5 familles, avec une interface de sélection en deux étapes (Famille → Spécialisation) et utilisation des paramètres du profil choisi pour les décisions dupgrade du joueur.
**Référence :** Cahier des charges §5 ; plan daction BLOC 3.
## Impacts
- **State :** `autoModeProfileId` (150) stocke le profil choisi ; `autoModeProfile` ("fast"|"slow"|"balanced") reste pour rétrocompatibilité et est mappé à un profil par défaut (balanced→25, fast→33, slow→7).
- **Mode auto joueur :** Les décisions dupgrade (parcelle, compétences, camion) utilisent les paramètres du profil (seuil de dépense, probabilité dupgrade) au lieu des 3 profils legacy uniquement.
- **UI :** Clic sur le bouton mode auto quand il est inactif ouvre un panneau (Famille → Spécialisation) ; choix dune spécialisation active le mode auto avec ce profil. Clic quand le mode auto est actif le désactive.
- **Bots :** Les décisions upgrade/sell/buy utilisent `getProfileParams(LEGACY_PROFILE_TO_ID[profile])` ; paramètres centralisés dans auto-mode-profiles (plus de ternaires fast/slow/balanced).
## Modifications
- **web/js/auto-mode-profiles.js** (nouveau) : 50 profils avec id, familyId (15), spendThreshold, upgradeChance, sellChance, clés i18n (label, priorities, risks). Familles : Conservateurs (110), Éleveurs (1120), Commerçants (2130), Expansionnistes (3140), Scientifiques (4150). `getEffectiveProfileId(state)`, `getProfileParams(profileId)`, `getProfilesByFamily(familyId)`, `getAllProfiles()`.
- **web/js/types.js** : `GameState.autoModeProfileId?`, `autoProfilePickerOpen?`, `autoProfilePickerFamily?`.
- **web/js/state.js** : `saveState` omet les champs `autoProfilePickerOpen` et `autoProfilePickerFamily` avant persistance ; `applyLoadStateScalarDefaults` remet ces champs à false/undefined au chargement.
- **web/js/bot-zoo.js** : `tickPlayerAutoMode` utilise `getEffectiveProfileId(state)` et `getProfileParams(profileId)` ; `playerAutoDoOneUpgrade(state, params, rng)` reçoit des paramètres numériques. Pour les bots, `tickBotDecisions` utilise `LEGACY_PROFILE_TO_ID[b.profile]` et `getProfileParams(profileId)` ; `botDecideUpgrade`, `botDecideSell`, `botDecideBuy` reçoivent un objet `params` (spendThreshold, upgradeChance, sellChance) au lieu du libellé.
- **web/js/ui.js** : Clic sur le bouton mode auto ouvre le picker si inactif ; panneau avec 5 familles puis 10 spécialisations, bouton Annuler. Au choix dune spécialisation : `autoModeProfileId`, `autoMode: true`, fermeture du picker.
- **web/js/texts-fr.js** : `autoProfileFamilyLabel` (5 familles), `autoProfileSpecialisationLabel` (150), `autoProfilePrioritiesLabel`, `autoProfileRisksLabel` (placeholders), libellés du picker.
- **web/css/main.css** : `.auto-profile-picker-wrap`, `.auto-profile-picker-title`, `.auto-profile-picker-step`, `.auto-profile-picker-families`, `.auto-profile-picker-specialisations`, boutons famille/spécialisation, bouton Annuler.
## Modalités de déploiement
- Client uniquement. Rechargement suffit.
## Modalités danalyse
- Désactiver le mode auto, cliquer sur le bouton mode auto : le panneau « Choisir le profil » saffiche avec 5 familles.
- Choisir une famille : affichage des 10 spécialisations.
- Choisir une spécialisation : le mode auto sactive avec le profil correspondant (🤖), le panneau se ferme.
- Annuler : le panneau se ferme sans activer le mode auto.
- Avec le mode auto actif, les upgrades automatiques utilisent les paramètres du profil sélectionné (seuil de dépense et probabilité dupgrade).
- Ancienne sauvegarde sans `autoModeProfileId` : comportement identique à « balanced » (profil 25).

View File

@@ -0,0 +1,28 @@
# Phase 6 Reproduction
**Objectif :** Deux animaux de même type, dont au moins un provient dun autre zoo, en proximité produisent un bébé après un délai. Le délai est réduit par le score de reproduction du zoo et ladéquation température/milieu.
**Référence :** `docs/plan-implementation-rappel-grandes-regles.md` phase 6.
## Impacts
- Nouveau module `web/js/reproduction.js` (détection de paires, timers, naissances).
- État : `reproductionTimers`, `fromOtherZoo` sur animaux et bébés.
- Bébés créés par reproduction vont en nurserie si place libre, sinon en `saleListings` (vente phase 10).
## Modifications
- **types.js** : `AnimalCell.fromOtherZoo?`, `PendingBaby.fromOtherZoo?`, `GameState.reproductionTimers?`.
- **config.js** : `Reproduction.BaseSeconds`, `Reproduction.MaxDistance`.
- **state.js** : `defaultState.reproductionTimers`, migration `loadState` pour `reproductionTimers`.
- **zoo.js** : `addPendingBaby(state, animalId, fromOtherZoo?)`, `placeMatureBabyOnCell` / `placeReceptionAnimalOnCell` renseignent `fromOtherZoo` sur lanimal placé ; `tryBuyBaby` appelle `addPendingBaby(..., true)`.
- **reproduction.js** : `getReproductionScore`, `getBiomeReproductionFactor`, `getTemperatureFactor`, `findReproductionPairs`, `tickReproduction` ; appel depuis la game loop après `checkDeathCauses`.
## Modalités de déploiement
- Aucun déploiement serveur. Rechargement client suffit.
## Modalités danalyse
- Vérifier en jeu : placer deux animaux de même type (dont un acheté en accueil/conveyor) sur deux cases adjacentes ; après le délai (réduit par score/biome/temp), un bébé apparaît en nurserie ou en vente.
- `state.reproductionTimers` doit contenir les paires en attente avec `dueAt` ; après échéance, entrée retirée et `birthCount` incrémenté.

View File

@@ -0,0 +1,28 @@
# Phase 7 Score de reproduction du zoo
**Objectif :** Exposer un score de reproduction agrégé (birthCount, feedingRate) pour affichage et pour attacher à lentité vendue (reproductionScoreAtSale). Réutiliser `getReproductionScore` pour le délai de reproduction (phase 6) et pour laffichage.
**Référence :** `docs/plan-implementation-rappel-grandes-regles.md` phase 7.
## Impacts
- Un seul « zoo score » : `getReproductionScore(state)` utilisé dans la game loop pour le délai de reproduction et pour remplir `state.reproductionScore` (affichage).
- Carte du monde : case « Score repro » sous le nom du zoo (joueur).
- Vente : les entrées `saleListings` créées (nursery full en reproduction) portent `reproductionScoreAtSale` pour usage côté acheteur (phase 10).
## Modifications
- **types.js** : `GameState.reproductionScore?`.
- **game-loop.js** : après `tickReproduction`, `state.reproductionScore = getReproductionScore(state)` ; import de `getReproductionScore` depuis `reproduction.js`.
- **reproduction.js** : lors du push en `saleListings` (NoFreeNursery), ajout de `reproductionScoreAtSale: getReproductionScore(state)`.
- **ui.js** : pour le zoo joueur sur la carte du monde, ajout dune ligne « Score repro: X.X » sous le nom (classe `world-map-zoo-reproduction-score`).
- **main.css** : style `.world-map-zoo-reproduction-score`.
## Modalités de déploiement
- Aucun déploiement serveur. Rechargement client suffit.
## Modalités danalyse
- En jeu : onglet carte du monde, zoo joueur → sous le nom, affichage « Score repro: X.X » (mis à jour à chaque tick).
- Après une naissance (reproduction) sans place en nurserie, une entrée dans `saleListings` doit contenir `reproductionScoreAtSale` égal au score courant du zoo.

View File

@@ -0,0 +1,51 @@
# Phase 10 Ventes et enchères (bébés et animaux adultes)
**Objectif :** Mise en vente de bébés et danimaux depuis le camion ; affichage des ventes sur la carte du monde ; expiration des annonces (bébé invendu meurt) ; enchères (offres joueurs/bots), validation par le vendeur, transfert vers lacheteur (nurserie/accueil), persistance serveur (API/BDD).
**Référence :** `docs/plan-implementation-rappel-grandes-regles.md` phase 10.
## Vérification des phases précédentes
Voir `docs/plan-verification-phases-0-a-9.md`. Les prérequis (4, 6, 7, 9) sont en place.
## Impacts
- Glisser un **bébé mature** (nurserie) ou un **animal prêt** (accueil) sur la zone camion (vente) → lentité est retirée de la nurserie/accueil et ajoutée à `state.saleListings` ; création côté serveur via `POST /api/sales` ; lannonce apparaît sur la carte du monde dans la case du zoo joueur.
- Les annonces expirent après un délai configurable (côté client via `tickSaleListings` et côté serveur via `expireSaleListings`) ; si lannonce concernait un **bébé** et na pas été vendue, le bébé est considéré mort (`deathCountRecent` incrémenté côté client et serveur).
- **Enchères :** les joueurs et les bots peuvent enchérir sur les ventes actives (`POST /api/sales/:id/bid`). Le vendeur peut accepter ou refuser la meilleure enchère (`POST /api/sales/:id/accept`, `POST /api/sales/:id/reject`). À lacceptation, la vente passe en statut `sold` avec un délai de **validation différée de 10 minutes** : les pièces ne sont pas transférées immédiatement ; `validated_at = now() + 10 min`. Un traitement à la lecture (`processValidatedSales`) exécute le transfert (débit acheteur, crédit vendeur) et passe le statut à `validated` lorsque `validated_at <= now()`. Lacheteur ne peut « Récupérer » quaprès validation ; un **sablier** (compte à rebours) saffiche pour les ventes en attente.
## Modifications
- **config.js** : `Sale.ListingDurationSeconds` (3600), `Sale.DefaultPrice` (50).
- **trade.js** : `addMatureBabyToSale`, `addReceptionAnimalToSale`, `tickSaleListings`.
- **game-loop.js** : appel à `tickSaleListings` après `tickReproduction`.
- **ui.js** : zone camion (drop) crée la vente localement et appelle `createSale` si API configurée ; carte du monde : affichage des ventes (sync avec `getSales()` à louverture) ; panneau « Mes ventes » (Accepter / Refuser), « À récupérer » (Récupérer), « Enchères » (saisie montant + Enchérir).
- **types.js** : `SaleListing` étendu avec `serverId?`, `bestBidAmount?`, `bestBidderZooId?`, `status?` ; `GameState.salesFromApi?` pour les données renvoyées par `GET /api/sales`.
- **api-client.js** : `getSales()`, `createSale(payload)`, `placeBid(listingId, amount)`, `acceptSale(listingId)`, `rejectSale(listingId)`, `deliverSale(listingId)`.
- **Serveur**
- **server/migrations/001_sale_listings.sql** : tables `sale_listings` (…), **002_sale_listings_validated_at.sql** : colonne `validated_at TIMESTAMPTZ`, statut `validated` ajouté au CHECK.
- **server/db.js** : `mapSaleListingRow(row)` centralise le mapping des lignes `sale_listings` (utilisé par `getSaleListingById`, `getActiveSaleListings`, `getSalesForZoo`). `mapZooRowBase(row)` pour zoos (getAllZoos, getZooById, getBotZoosForTick). `validateListingForSeller(listingId, sellerZooId)` pour les vérifications vendeur (acceptSale, rejectSale). … `acceptSale` (ne transfère plus les pièces ; pose `sold_at`, `validated_at = now() + 10 min`), `processValidatedSales()` (transfert et passage à `validated`), `markSaleDelivered` (exige `status = 'validated'`).
- **server/routes/sales.js** : GET `/api/sales` appelle `processValidatedSales()` avant `getSalesForZoo` si auth. Réponses incluent `validated_at`.
- **server/bot-tick.js** : après `expireSaleListings`, récupération des ventes actives et enchère aléatoire dun bot (si assez de pièces, pas vendeur).
- **web** : panneau « À récupérer » : sablier et compte à rebours (ex. « Validation dans X min ») pour les ventes `status === 'sold'` avec `validated_at` dans le futur ; bouton « Récupérer » désactivé jusquà validation. **texts-fr.js** : `salesPendingValidation`, `salesValidationInMinutes`.
- **texts-fr.js** : `errorMessage.BabyNotMature`, `NoBabyInNursery`, `AnimalNotReady`, `NoAnimalInReception`.
- **web/css/main.css** : styles `.world-map-sales-panel`, `.sales-panel-title`, `.sales-panel-row`, boutons Accepter/Refuser/Récupérer/Enchérir.
## Modalités de déploiement
- **Base de données :** exécuter la migration `server/migrations/001_sale_listings.sql`, puis `server/migrations/002_sale_listings_validated_at.sql` (validation différée).
- Redémarrage serveur et rechargement client.
## Modalités danalyse
- Glisser un bébé mature / animal prêt sur la zone camion : annonce créée localement et sur le serveur (si API configurée) ; affichage sur la carte du monde.
- Ouvrir la carte du monde avec API + auth : `getSales()` est appelé ; panneau « Mes ventes », « À récupérer », « Enchères » affiché selon les données.
- Enchérir sur une vente active : saisir un montant > meilleure enchère puis « Enchérir ».
- En tant que vendeur : « Accepter » ou « Refuser » sur la meilleure enchère ; à lacceptation, vente en statut `sold`, `validated_at = now() + 10 min` ; pas de transfert de pièces immédiat.
- En tant quacheteur ayant gagné une vente : affichage sablier « Validation dans X min » tant que `validated_at > now()` ; après 10 min, un prochain GET exécute `processValidatedSales`, la vente passe en `validated`, le bouton « Récupérer » sactive ; clic ajoute le bébé/animal à la nurserie/accueil puis appelle `deliverSale`.
- Expiration : après `end_at`, la vente passe en `expired` ; si bébé, `deathCountRecent` du vendeur incrémenté (GET /api/sales ou bot tick).
- Bots : à chaque tick, un bot peut placer une enchère sur une vente active (autre zoo, assez de pièces).
## Phase suivante
Phase 11 Villes : `docs/features/villes-phase11.md`.

View File

@@ -0,0 +1,25 @@
# Phase 11 Villes
**Objectif :** Cases des villes sur la carte du monde avec nom et nombre maximum de visiteurs vers les zoos ; plafond ou répartition des visiteurs depuis les villes.
**Référence :** `docs/plan-implementation-rappel-grandes-regles.md` phase 11.
## Dépendances
- Phase 8 (Attractivité et visiteurs).
## Livrables (implémentés)
- **Cases des villes :** Sur la carte du monde, chaque ville affiche le nom et le « nombre maximum de visiteurs vers les zoos » (`maxVisitorsTowardZoos`). Rendu : icône 🏙️ + libellé nom + ligne « max N ».
- **Règle d'attraction :** Dans `getCityAttraction` (income.js), la contribution de chaque ville est plafonnée par `city.maxVisitorsTowardZoos` : `contrib = min(maxVisitorsTowardZoos, raw * 100)` avec `raw = 1/(1+distance)`, puis somme × `CityAttractionScale`. Les villes proches contribuent plus, sans dépasser leur plafond.
## Fichiers modifiés
- **config.js** : `WorldMap.Cities[].maxVisitorsTowardZoos` (ex. 80, 100).
- **income.js** : `getCityAttraction()` utilise le plafond par ville.
- **ui.js** : Rendu des villes avec nom et « max N » ; tooltip et aria-label avec « max N visiteurs vers zoos ».
- **main.css** : `.world-map-city-label`, `.world-map-city-max-visitors`.
## Phase précédente
Phase 10 Ventes et enchères : `docs/features/ventes-encheres-phase10.md`.