# BIG EGG 3D — la branche de travail

**Depuis le 27/08/2026, c'est ici qu'on travaille.** Ce dossier contient le jeu
entier — mêmes scripts, mêmes données, mêmes règles — rendu en primitives 3D par
un pont. Toute correction et tout nouveau contenu y vont.

`../big-egg/` est l'ancienne branche 2D, conservée **en lecture seule** comme
référence : on l'ouvre pour comparer ou retrouver une version antérieure, jamais
pour y écrire. Les directives complètes du projet sont dans `../CLAUDE.md`.

## Le principe, en une phrase

**Le jeu 2D tourne tel quel et pilote des avatars 3D.** Ce n'est pas un portage :
c'est le jeu, inchangé, avec un pont qui lui donne un corps.

Concrètement, `autoload/bridge_3d.gd` fait deux choses et rien d'autre :

1. il **éteint le dessin 2D** de ce qui a un corps dans le monde ;
2. il **donne à chaque entité un avatar 3D** qu'il garde collé à elle.

La 2D reste donc la **seule autorité** : positions, collisions, portées, plan de
la salle, cuisine, clients, brigade, sauvegardes. La 3D ne décide de rien, elle
montre. C'est ce qui permet à cette branche de suivre le jeu sans se recoder à
chaque changement.

**Preuve que les fonctions sont intactes :** le smoke test du jeu tourne ici
**sans une modification** et passe.

```bash
GODOT=C:/Users/ELR17/Downloads/Godot_v4.7.1-stable_win64.exe/Godot_v4.7.1-stable_win64_console.exe

"$GODOT" --path big-egg-3d                                                    # jouer
"$GODOT" --headless --path big-egg-3d res://scenes/main/smoke_test.tscn        # les regles du jeu
"$GODOT" --headless --path big-egg-3d res://scenes/main/bridge_3d_check.tscn   # le pont
php big-egg-3d/tools/shapes-3d-manager/tests/mirror_test.php                   # le miroir de l outil
```

## Ce que la 3D ajoute à la donnée : deux champs

**La primitive** et **l'élévation**, dans `data/shapes_3d.json`. Rien d'autre.
Tout le reste — nom, emprise au sol, couleur, biome, comportement — continue de
vivre dans `game_data_master.json`, partagé avec le jeu.

Le champ vertical s'appelle **`tall`**, jamais `height` : côté auteur, `height`
désigne déjà la profondeur de l'emprise au sol d'un meuble.

**Une seule règle d'unités : 1 pixel du jeu = 1 unité 3D.** Le pont de
coordonnées est `Vector2(x, y) → Vector3(x, 0, y)`, et c'est tout.

## L'outil PHP

`tools/shapes-3d-manager/index.php`, à ouvrir sous WAMP. **Ce n'est pas un fork**
du gestionnaire du jeu : il lit le fichier maître en lecture seule pour lister ce
qui existe, et n'écrit que dans `shapes_3d.json`. Dupliquer 450 Ko de PHP et de
JS pour deux champs aurait créé deux vérités qui divergent à la première
modification. Une entité restée sur le défaut de sa collection n'est pas stockée.

## Fichiers propres à la branche

| | |
|---|---|
| `autoload/bridge_3d.gd` | le pont : avatars, sol, caméra, extinction du dessin 2D |
| `scripts/core/shape_3d_factory.gd` | une primitive par entité, **et le pont de coordonnées** |
| `scripts/core/camera_3d_rig.gd` | caméra 3/4 **orthographique**, cadrage et suivi |
| `scripts/core/bridge_3d_check.gd` | le filet du pont |
| `data/shapes_3d.json` | primitive et élévation par entité |
| `tools/shapes-3d-manager/` | l'outil qui écrit ce fichier |

Tout le reste est le jeu, à l'identique, à **neuf lignes près** : une garde
`if Bridge3D.active: return` en tête du `_draw()` de chaque corps du monde
(restaurant, donjon, joueur, monstre, client, employé, assiette, ramassable,
meuble). L'interface, les bulles, les nombres de dégâts et l'arbre d'upgrades
restent en 2D par-dessus la scène.

## Trois pièges déjà payés

**Le recul d'une caméra inclinée se DIVISE.** Pour descendre de `HEIGHT`, le
rayon parcourt `HEIGHT / tan(inclinaison)` en profondeur. Avec une
multiplication, la caméra visait 700 unités au-delà du point voulu et on ne
voyait que le quart bas de la salle.

**`size` d'une caméra orthographique est l'étendue de l'IMAGE, pas du sol.** Un
sol vu en biais est comprimé de `sin(inclinaison)` : une profondeur de 870 unités
n'occupe que 686 de hauteur d'image.

**Un `await` dans une fonction de contrôle en fait une coroutine.** L'appelant
reprend la main au premier `await`, annonce « OK » et quitte avant la moitié des
contrôles. Un test qui ne s'exécute pas est pire qu'un test faux. Corollaire :
un repli qui rend la valeur attendue (`return focus` quand le calcul est
impossible) fait comparer une valeur à elle-même — `ground_aim()` rend donc
`Vector2.INF`, jamais une valeur complaisante.

## La rue

Son côté est **déclaré**, pas supposé: `Restaurant.STREET_SIDE` vaut `north`,
`west` ou `east`, et tout en découle — trottoir, porte percée dans la bonne
façade, files, sens de marche des passants. La branche est à `east`.

Le **comptoir takeout garde sa façade nord** quelle que soit la rue. Le faire
suivre entraînerait son ancrage déclaré dans la donnée (`anchor: north_wall`),
ses deux zones de service — les deux moitiés de sa face accessible, qui pivote
avec lui — et la règle de pose. C'est un chantier à part. En attendant, le mur
nord porte une **vitrine**: son tronçon devant le comptoir est abaissé, on voit
la file par-dessus.

Côté passants, `Customer._crossed_x()` projette la position sur l'axe déclaré au
lieu de ne lire que l'abscisse: sans ça, un passant ne franchissait jamais sa
porte dès que la rue cessait d'être horizontale.

## Trois interrupteurs

| touche | effet |
|---|---|
| **F2** | bascule l'affichage: **primitives** ↔ **visuels 2D** (les 313 feuilles déjà produites, dressées face à la caméra) |
| **F3** | l'afficheur de diagnostic (cadence, focus fenêtre, touche physique, vecteur de déplacement) |

Et deux drapeaux dans `bridge_3d.gd`, prévus pour être coupés sans rien casser :

- `varied` — la variété de forme, d'élévation et de teinte des créatures et des
  clients. Elle est **dérivée de leur identifiant**, pas tirée au sort: le même
  monstre a toujours la même allure d'une partie à l'autre, et sa teinte part de
  la palette de son biome — une région forme une famille. Une entrée explicite
  dans `shapes_3d.json` l'emporte toujours.
- `show_names` — le nom au-dessus des têtes, en retrait et plus terne que
  l'information de jeu. Un aide-mémoire de développement, à couper le jour où la
  salle se lit sans lui.

Le mode **visuels 2D** ne remplace pas les primitives: il permet de voir la
salle habillée de l'art existant pendant que la 3D se construit. Il ne prend que
la **première case** de chaque feuille — la vue sud au repos: rejouer l'animation
directionnelle en 3D demanderait de refaire la logique de `VisualHolder`. Une
entité sans art retombe sur son volume plutôt que de disparaître.

## Limites assumées

Les avatars sont des **primitives** : boîtes, cylindres, sphères, capsules. Le
but n'est pas la ressemblance mais l'architecture — si le montage tient avec des
cubes, y glisser des modèles générés ne touchera que `Shape3DFactory.build()`.

Les entités n'ont pas d'animation : elles se déplacent et s'orientent, elles ne
marchent pas. C'est ce qu'un rig apporterait.

Le tri en profondeur est natif : le paramètre `front_depth` envisagé côté 2D (le
devant solide contre l'arrière franchissable) est **sans objet** ici.
