# 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()** : s’appuie sur `zeroAnimalWeights()` puis met la première couleur à 1.
- **normalizeZooWeights(legacy)** : utilise `zeroAnimalWeights()` au lieu de recréer l’objet à 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(...))`.
- **getUpgradeMaxLevels()** : retourne `{ plotMax, skillMax, truckMax }` depuis la config. Utilisé par `botDecideUpgrade` et `playerAutoDoOneUpgrade` pour éviter la répétition des trois lectures `GameConfig.Plot?.MaxLevel ?? 8`, etc.

### web/js/trade.js

- **getSaleListingDefaults()** : retourne `{ duration, price }` depuis `GameConfig.Sale` (ListingDurationSeconds, DefaultPrice). Utilisé par `addMatureBabyToSale` et `addReceptionAnimalToSale` pour éviter la duplication des deux lignes de config.

### 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)
