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

## Primitives de repli et vrais modèles

`data/shapes_3d.json` garde **la primitive de repli** et **l'élévation**. Un
acteur peut maintenant ajouter un bloc optionnel `model_3d` dans
`game_data_master.json` : chemin GLB, calage visuel et correspondance entre les
états du jeu et les clips du fichier. Chaque état peut avoir sa propre vitesse
d'exécution. **Sur place**, activé par défaut, neutralise la translation
horizontale du root ou du bassin sur une copie locale du clip; le modèle reste
ainsi sur la collision pilotée par la 2D. Un déplacement réel se configure dans
le pattern d'attaque, jamais dans l'animation. L'option **Terminer le clip** retarde une transition visuelle
jusqu'à la fin d'une animation ponctuelle; elle est ignorée pour les clips en
boucle afin de ne jamais figer un acteur. L'option **Boucler** décide de ce
qu'est justement « un clip en boucle » : glTF ne transporte pas cette notion, et
un clip importé arrive donc toujours en `LOOP_NONE`. Sans réglage, les états
`idle`, `walk`, `eat` et `work` bouclent; la case permet de trancher autrement.
Ce n'est pas cosmétique : un avatar piloté par un AnimationTree — dès qu'il
déclare une couche « haut du corps » — n'a aucune relance par image, et sa
marche se fige donc au bout de la durée du clip. Tout ce qui touche au jeu — emprise,
collision, portée, vitesse — reste inchangé et piloté par la 2D.

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.

## Les outils PHP

Le gestionnaire principal `tools/game-data-manager/` porte la section **Modèle
et animations 3D** directement dans les fiches Joueur, Monstres, Clients et
Employés. Il balaie `assets/`, propose les `.glb` disponibles et lit leurs noms
de clips automatiquement. La vitesse et la finition se règlent à côté de chaque
état.

Une animation peut maintenant choisir **Corps entier** ou **Haut du corps**.
Dans le second cas, le gestionnaire propose les os détectés dans le GLB: l'os
racine choisi et tous ses descendants reçoivent un `AnimationNodeOneShot`,
tandis que les hanches et les jambes continuent la locomotion issue du mouvement
2D. Les fondus d'entrée et de sortie sont réglables par état. Le héros livré
utilise `Spine` pour superposer `Triple_Combo_Attack` à `Walking`. Un modèle qui
ne déclare aucune couche haut du corps conserve le chemin `AnimationPlayer`
historique, sans changement de comportement.

**Le geste appartient à l'arme** (billet 92). La fiche arme choisit un clip du
modèle du héros (**Geste du héros**) et sa vitesse (0 = celle de l'attaque) ;
le pont le joue pendant le coup de l'arme qui frappe, sur la même couche haut du
corps. Vide, l'arme garde l'attaque générale. Les douze gestes du héros
viennent de Mesh2Motion (CC0), transférés sur son rig Meshy par
`tools/blender/hero_gestures.py` (Blender 5.2, sans interface) : rotations os
par os en espace monde, membres alignés sur la pose de repos source, torse et
clavicules gardant la leur, doigts ignorés, aucune translation. Deux candidats
par famille : estoc (`Sword_Attack`, `Punch_Jab`), taille (`Sword_Regular_A`,
`_B`), frappe de haut (`Chop_Tree`, `Sword_Attack_Air_Vertical`), lancer
(`OverhandThrow`, `Throw_Object`), tir (`Pistol_Shoot`, `Spell_Simple_Shoot`),
jet continu (`Two_hand_Blast`, `Farm_Watering`). Le premier est posé sur les
armes par `migrate_hero_gestures_20261015.py`. `bridge_3d_check` vérifie le
clip et la vitesse sur une vraie attaque, et que chaque geste nommé existe ;
`--gesture-captures=<dossier>` (en fenêtre) photographie chaque geste.

**La ligne d'animation du geste** (billet 119). Le geste porte la même ligne
qu'un état de modèle : Sur place, Boucler, Terminer, Couche, Entrée, Sortie
(défauts : ceux de l'attaque du héros). Chaque arme joue sa propre copie du clip
(`Model3DFactory.gesture_clip`, bibliothèque `gesture_<arme>`) : deux armes qui
partagent un clip peuvent le vouloir l'une en boucle, l'autre non, sans toucher
aux états du héros. « Corps entier » passe par le même OneShot, filtre coupé :
les jambes figent le temps du geste. **Terminer l'emporte sur l'arrêt net** pour
le héros : un coup qui s'arrête sur un corps s'arrête en 2D, mais son geste va au
bout (le rouleau à pâte, budget d'une touche, se coupait au contact). Un geste en
boucle qui doit finir achève son tour (`break_loop_at_end`). Les monstres gardent
leur règle : une lame bloquée abandonne son clip.

`tools/shapes-3d-manager/index.php` reste volontairement petit : il ne règle que
la primitive et la hauteur de repli dans `shapes_3d.json`. Il est utile quand un
acteur n'a pas encore de modèle ou si un GLB ne peut pas être chargé. Il ne
connaît en revanche pas `room_elements` : ces entrées tombent sur le repli
`box`/32, et une entrée écrite à la main dans `shapes_3d.json` serait effacée
au premier enregistrement de l'outil, qui reconstruit sa table à partir des
seules collections qu'il affiche.

### Les décors portent aussi un modèle

`restaurant_elements` et `room_elements` acceptent le même bloc `model_3d` que
les acteurs. Deux choses les en distinguent. D'abord ils n'ont aucune animation :
la fabrique s'en accommode, un GLB statique passe sans `AnimationPlayer`.
Ensuite **la teinte d'auteur ne s'applique pas** à un avatar qui porte un
modèle — `tint` ne va qu'à la primitive de repli. Un modèle sans texture rend
donc un volume gris, là où la boîte procédurale était colorée : un GLB de décor
doit arriver texturé, sans quoi il est moins lisible que la boîte qu'il remplace.

Aucune des deux collections n'auteure de hauteur : un meuble vaut 70 unités, un
objet de salle 32. Comme la fabrique ajuste le modèle sur cette hauteur **avant**
d'appliquer `scale`, c'est `scale` qui porte les proportions réelles. Le repère
est le héros : 132 unités pour environ 1,75 m, soit ~75 unités au mètre.

## 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/model_3d_factory.gd` | chargement, calage automatique et animation des GLB |
| `scripts/core/camera_3d_rig.gd` | caméra 3/4 en perspective légère, **même échelle partout**, suivi sur les deux axes |
| `scripts/core/bridge_3d_check.gd` | le filet du pont |
| `scripts/core/room_terrain.gd` | le relief d'une salle, **côté logique** (voir plus bas) |
| `scripts/core/biome_dressing_3d.gd` | l'habillage des biomes et le caractère de chaque salle |
| `assets/shaders/biome_grass.gdshader` | l'herbe qui plie au vent et sous les pas |
| `scripts/core/relief_check.gd` | le filet du relief et du Cratère |
| `scripts/combat/combat_impact.gd` | le résumé d'un coup qui porte, **côté logique** (voir plus bas) |
| `scripts/core/impact_fx_3d.gd` | ce qu'on voit quand un coup porte : geste, gerbes, taches au sol, flash, éclair, secousse, arrêt sur image |
| `assets/textures/impact/` | ses textures : gestes du Kenney Particle Pack (CC0, licence jointe), 21 planches de particules `fx_*` et cinq formes de tache dessinées |
| `scripts/core/entrance_contract.gd` | les effets d'entrée des créatures et leur durée, **côté logique** (voir plus bas) |
| `scripts/core/entrance_fx_3d.gd` | ce qu'on voit quand un renfort arrive : fondu, nuage, foudre, boule de feu, faille, colonne, roulade, chute, sortie de terre |
| `assets/shaders/entrance_rift.gdshader`, `entrance_beam.gdshader` | la fissure lumineuse de la faille, la colonne de la téléportation |
| `data/shapes_3d.json` | primitive et élévation par entité |
| `tools/shapes-3d-manager/` | l'outil des primitives de repli |
| `tools/game-data-manager/` | le gestionnaire principal, y compris modèles et clips 3D |

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.

## Le relief des salles

Depuis le 01/10/2026, les salles de la Vallée du Cratère (biome 1) ne sont plus
plates. Le relief n'est **pas** un décor : il est défini une seule fois, côté
logique, dans `scripts/core/room_terrain.gd`, que le donjon bâtit à chaque
entrée de salle (`RoomTerrain.current`) et que le pont lit.

- **Une seule règle** : la marche garde une vitesse constante le long du sol, donc
  avance moins vite au plan sur une vraie pente (au-delà de 10°, au plus −26 %).
  Les volants, l'esquive, les reculs et les attaques n'en tiennent pas compte.
- **Le pont suit** : avatars, butin, tirs (à hauteur de taille au-dessus du sol
  qu'ils survolent), textes flottants, visée (le rayon touche la surface, pas un
  plan), caméra (qui suit la hauteur sous le joueur) et zones d'attaque, qui
  épousent le sol. Ce qu'une lèvre cache d'une zone se redessine en contour pâle.
- **Les passages se raccordent** : le relief s'éteint sur les côtés ouverts.
- **Le Cratère** creuse la salle d'El Tuna et les duels de Bob qui la remplacent.
  Son feu est un objet de salle solide, ancré au centre par la donnée
  (`elements.<id>.anchor = "center"`, réglable au gestionnaire).

Chaque salle du biome tient aussi un **caractère** tiré de son nom
(`ROOM_THEMES` de `biome_dressing_3d.gd`) : pièces maîtresses à place fixe,
semis, sol. Les modèles sortent de `tools/blender/crater_meadow_props.py`, avec
une occlusion ambiante cuite dans les couleurs de sommet.

## Les effets d'impact

Depuis le 02/10/2026 (ligne 64 du suivi), un coup qui porte se voit. Le
`take_damage` d'un monstre ou du héros, qui a déjà tout décidé, résume le coup
dans `CombatImpact` (point, direction, types, part de vie, coup fatal) ; le pont
vide cette file à chaque image et la passe à `ImpactFx3D`. Trois couches,
toutes réglées au gestionnaire :

- **le geste**, celui du pattern d'attaque, qu'une arme ou une ligne d'attaque
  de monstre peut surcharger : griffure, tranchant, estoc, choc, morsure,
  souffle au sol. Le contact du corps n'a pas de pattern : son geste vit sur la
  fiche (`direct_hit_impact_style`, choc contondant si absent) ;
- **l'élément**, les particules que nomme chaque type de dégâts (flammèches pour
  le feu, éclats de glace pour la glace…), à sa couleur ;
- **la matière** de la cible (onglet Patterns › Matières d'impact), nommée par
  chaque monstre et par le profil joueur : une à trois gerbes de gouttes,
  étincelles, échardes, touffes de poil, poussière…

Le ressenti — flash du modèle, éclair, secousse, arrêt sur image — se règle dans
`combat_rules`, et chaque effet se coupe dans les options du joueur. Rien de
cela ne touche une règle ; l'arrêt sur image ralentit seulement le temps pour
tout le monde, et ne joue jamais en headless sauf si un contrôle l'allume. La
secousse passe par `h_offset`/`v_offset` de la caméra, que le support de caméra
n'utilise pas.

### Le sens du coup et les taches au sol

Depuis le 03/10/2026 (ligne 66 du suivi), la matière gicle **dans le sens du
coup**, et le geste lui donne sa forme (`SPLASHES` d'`impact_fx_3d.gd`) : jet
serré qui traverse pour l'estoc, éventail pour le tranchant, gerbe courte qui
s'écrase pour le choc, trois filets pour la griffure. Chaque coup dévie un peu
de son axe et varie en quantité et en vitesse ; un liquide jaillit parfois d'un
jet plus long. Les gerbes des éléments ne changent pas.

Une gerbe cochée **« trace au sol »** dans sa matière laisse des taches à sa
couleur, là où les gouttes retombent : une tache principale et des gouttelettes
allongées dans leur sens de vol, qui disent après coup d'où venait le coup. Un
coup fatal ajoute une flaque qui s'étale sous le corps. Les taches sont des
`Decal` : elles épousent le relief, mais ne peignent que les maillages marqués
par `ImpactFx3D.receive_stains()` (couche de rendu 11) — la dalle du donjon et
le sol peint des biomes — jamais une créature, le héros, l'herbe ou un
télégraphe. Elles vivent le temps de la salle, puisque le pont rebâtit la scène
à la suivante ; au-delà de 120, la plus ancienne s'efface. Le restaurant n'en
reçoit pas, et l'option « Traces au sol » les coupe.

### La bibliothèque de particules

Depuis le 03/10/2026 (ligne 67 du suivi), chaque débris a sa famille : 21 au
total (`KINDS` d'`impact_fx_3d.gd`, miroirs `CombatImpact.PARTICLES`,
`IMPACT_PARTICLES` en PHP et en JS). Liquides (gouttes, gelée), débris durs qui
tombent vite en tournoyant (éclats génériques, échardes, gravier, tessons,
fragments de coquille, écailles), débris légers qui flottent (plumes, touffes de
poil, feuilles, bouts de carton), nuages (poussière, fumée), éléments
(flammèches, piment, glace, éclairs, bulles, grésillement) et étincelles.

- Chaque famille a sa planche `assets/textures/impact/fx_<famille>.png` de
  **quatre formes** (2 × 2), et chaque particule en tire une au hasard
  (`anim_offset`). Les planches sortent de `tools/dev/draw_impact_particles.py`
  (formes blanches ombrées, teintées en jeu ; les taches, de
  `draw_impact_stains.py`) et sont importées avec mipmaps.
- Réglages propres : `aligned` (l'image suit le sens du coup), `mix` (une fumée
  reste sombre quelle que soit la couleur de l'explosion), `glint` (une écaille,
  un tesson, la glace luisent par éclats), `companion` (la vapeur de l'acide, la
  chaleur du piment), `randomness` (la durée de vie varie de 25 % ; pas pour les
  éclairs, que cette variation rendait invisibles sur une vie aussi brève).
- La couleur reçue est lue en **sRGB** (`vertex_color_is_srgb`) : lue comme
  linéaire, elle sortait délavée — le sang rose, le gravier blanc.
- `bridge_3d_check` vérifie que la liste et les réglages ne divergent pas, que
  chaque famille a sa planche et tire ses formes au hasard.

## Les entrées des créatures

Depuis le 14/10/2026 (ligne 81 du suivi), un renfort ou une invocation ne
surgit plus d'un coup. Chaque fiche de monstre, **niveau par niveau**, choisit
son effet d'entrée (gestionnaire, section VARIANTE, `entrance_effect`) :
apparition instantanée, fondu, pouf cartoon (éclat et nuage), éclair qui frappe
le sol, boule de feu qui s'écrase, faille sci-fi d'où elle remonte, téléportation
sci-fi (colonne de lumière), roulade depuis hors champ, chute depuis le haut,
sortie de terre.

- **Qui la joue.** Le donjon dit comment chaque créature arrive
  (`Enemy.ARRIVAL_*`). La vague d'ouverture attend déjà dans la salle ; une
  vague qu'un dialogue ou un fait réveille arrive comme elle, et celles qui
  tombent dans le même élan aussi. Seuls les **vrais renforts** et les
  **invocations** jouent leur entrée. Le bac à sable la joue, pour la voir.
- **Pendant l'entrée, hors combat.** C'est la fuite à l'envers : `active` est
  faux, la collision coupée, et tout ce qui lit `active` l'ignore déjà — coups,
  tirs, souffles, visée, jauges. Elle compte pourtant dans la salle, et ne bouge
  pas d'elle-même (sauf la roulade, que la 2D fait vraiment rouler depuis le
  bord de l'arène). La mort, le renvoi et une scène coupent l'entrée. Un groupe
  ne joue pas d'un seul bloc : chacune attend jusqu'à 0,3 s, invisible.
- **La 2D décide, le pont montre.** `entrance_contract.gd` donne la durée de
  chaque effet ; `Enemy` publie `entering`, l'effet et l'avancement (0 à 1).
  `entrance_fx_3d.gd` en tire l'image à chaque image : l'avatar caché, à moitié
  transparent, sous le sol, dans le ciel, qui roule ou s'écrase, puis rendu
  tel qu'il était. Les gerbes, la lumière, la secousse, les brûlures au sol et
  la teinte du modèle viennent d'`ImpactFx3D` et obéissent aux mêmes options du
  joueur. Les formes propres (foudre, boule de feu, faille, colonne, ombre,
  monticule, nuages cartoon cernés de sombre) sont construites par le code ou
  par les deux shaders, sans image.
- **Headless.** Les entrées y sont coupées, comme l'arrêt sur image, sauf si un
  contrôle les allume (`Enemy.entrances_in_headless`) : le choix d'un effet par
  l'auteur ne doit pas faire tomber des contrôles sans rapport.
- `dev.py check --area entrance` éprouve le trajet du maître au catalogue, les
  quatre arrivées dans le vrai donjon, la règle « hors combat », et chaque effet
  à mi-course côté pont. Les captures se font en fenêtre, `--fixed-fps 60`.

## Six pièges déjà payés

**Une lumière s'éteint à deux pas, en unités du monde.** Godot divise l'intensité
d'une `OmniLight3D` par la distance puissance `omni_attenuation`, distance comptée
en unités — et il y en a 75 au mètre. À 1,0 (la valeur par défaut), un feu
n'éclairait plus rien au-delà de quelques pas. Les feux du biome prennent 0,5 et
une énergie en conséquence. Et la portée d'une lumière ignore l'échelle de son
nœud : elle se donne en unités du monde, jamais en mètres de modèle.

**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.

**Une garde `_draw()` éteint l'information en même temps que le corps.** Ce que
la 2D dessinait ne se limitait pas à l'apparence: le secteur d'une attaque, la
vie d'une créature, l'avancement d'une cuisson **disent l'état du jeu**. Éteint
d'un bloc, le combat devient illisible — un monstre chargeait son coup sans que
rien ne l'annonce. Tout ce qui informe doit **remonter dans la scène 3D**:
`InfoBadge3D` au-dessus de la tête, `AttackField3D` au sol. Devant chaque garde
ajoutée, se demander ce que ce `_draw()` disait en plus de sa forme.

**Une salle change de forme sans changer de scène.** `advance_to_next_room()`
vide la salle et recalcule `arena` **sur place**: le chemin du fichier ne bouge
pas. Le pont, qui ne surveillait que lui, gardait les murs et le cadrage de la
salle précédente — dans une salle qui défile, le joueur passait au travers d'un
mur resté petit et la caméra ne le suivait pas, son axe ayant été déduit d'une
arène disparue. La clé de reconstruction porte donc la scène **et**
`world_bounds()`.

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

Le héros livre maintenant un GLB riggé et animé. Les autres acteurs restent sur
leurs primitives ou leurs visuels 2D tant qu'aucun modèle ne leur est choisi ;
le même formulaire est déjà disponible sur leur fiche. Un export sans clip Idle
emploie la première pose de marche comme posture neutre.

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.
