Probleme
Le moteur ASM original utilise constamment {@code Plr1_ZonePtr_l} (la zone
courante du joueur) pour decider de plein de choses : teleports, fin de
niveau, brightness, sons d’ambiance, sons de pas… Cote Java, on n’avait
aucun tracking de zone : impossible de savoir dans quelle zone se trouve
le joueur, donc impossible d’implementer ces features.
Deux features specifiques manquaient au gameplay :
- Teleports : certaines zones ont {@code telZone}, {@code telX},
{@code telZ} dans le binaire. Entrer dans une telle zone teleporte
instantanement le joueur ailleurs. - Fin de niveau : chaque niveau a un {@code Lvl_ExitZoneID_w}
(zone de sortie). Y entrer doit declencher la fin de niveau.
Diagnostic ASM
Teleport (cf. {@code hires.s:2902-2945} dans {@code Plr1_Control}) :
move.l Plr1_ZonePtr_l, a0
move.w ZoneT_TelZone_w(a0), d0 ; offset 38 dans struct Zone
blt .noteleport ; <0 = pas de teleport
move.w ZoneT_TelX_w(a0), newx ; offset 40
move.w ZoneT_TelZ_w(a0), newz ; offset 42
; ... test collision ...
.teleport:
Plr1_XOff_l = TelX
Plr1_ZOff_l = TelZ
Plr1_YOff_l = (Y - oldFloor) + newFloor ; preserve hauteur relative
Plr1_ZonePtr_l = nouvelle zone
jouer son #26 (sample teleport)
Fin de niveau (cf. {@code hires.s:2086-2107}) :
move.l Plr1_ZonePtr_l, a0
move.w (a0), d0 ; ZoneID actuelle
cmp.w Lvl_ExitZoneID_w, d0
bne.s noexit
jmp endlevel ; on est dans la zone exit
L'{@code Lvl_ExitZoneID_w} est lue depuis le binaire a {@code floorLineOffset – 2}
(cf. {@code hires.s:374} : {@code move.w -2(a2),Lvl_ExitZoneID_w} ou
{@code a2 = base + floorLineOffset}).
Architecture Java (8 modifications + 2 nouveaux fichiers)
Cote donnees (parsing)
- LevelBinaryParser : ajoute le champ {@code exitZoneId} a {@code BinData}
et le lit comme un word a {@code floorLineOffset – 2}. - LevelData : ajoute le champ {@code exitZoneId} (final).
- GraphicsBinaryParser : passe {@code bd.exitZoneId} au constructeur de
{@code LevelData}. - LevelJsonExporter : ajoute {@code « exitZoneId »: N} au top-level du JSON.
Note : les champs {@code telZone}/{@code telX}/{@code telZ} par zone etaient
deja exportes (session 99).
Cote scene 3D (build-time)
- LevelSceneBuilder :
– Record {@code ZD} etendu pour inclure {@code telZone}, {@code telX}, {@code telZ}.
– {@code parseZones} les lit depuis le JSON.
– Nouvelle methode {@code parseExitZoneId(json)} pour le top-level.
– {@code root.setUserData(« exitZoneId », …)} sur le levelScene.
– Pour chaque zone, ajout de userData {@code telZone}/{@code telX}/{@code telZ}- {@code floorXZ} (polygone reconstruit depuis les edges, en CSV).
Cote runtime (gameplay)
- PolygonXZ.java (nouveau, package {@code com.ab3d2.world}) :
utilitaire statique extrait de {@code LiftControl}, expose
{@code pointInPolygon}, {@code distanceToPolygon}, {@code distanceToSegment}. - LiftControl : refactore pour utiliser {@code PolygonXZ} au lieu de
ses methodes privees dupliquees. - ZoneTracker.java (nouveau, package {@code com.ab3d2.world}) :
– Charge la liste des zones depuis le {@code Node « zones »} du levelScene.
– Methode {@code update(Vector3f playerPos)} qui :- Tente d’abord la zone precedente (cache)
- Sinon balaye toutes les zones jusqu’a trouver le polygone contenant
- Expose {@code currentZoneId}, {@code getCurrentZone()}, {@code getZone(id)},
{@code isExitZone(id)}, {@code inExitZone()}. - Record interne {@code ZoneInfo} pour les snapshots des userData.
- GameAppState :
– Champ {@code private ZoneTracker zoneTracker}
– Garde-fous {@code endLevelTriggered}, {@code teleportCooldown}
– Initialisation dans {@code initialize()} apres {@code attachChild(levelScene)}
– {@code zoneTracker.setCurrentZoneId(p1Zone)} dans {@code placePlayer()}
– Nouvelle methode {@code applyZoneLogic(tpf)} appelee dans {@code update()}
apres {@code applyLiftPushY()}
– Nouvelle methode {@code applyTeleport(ZoneInfo)} :
conversion coords Amiga -> JME, {@code setPhysicsLocation}, cooldown 0.5s
– Nouvelle methode {@code endLevel()} : detach + attach LevelSelectAppState
– Overlay debug zone (BitmapText haut-gauche, toggle F3) :
affiche zone courante, position monde Amiga (= editeur), floorH/roofH,
marquage TEL/EXIT. Visible par defaut.
Garde-fous
- Boucles teleport : un cooldown de 0.5s empeche le joueur de re-teleporter
immediatement apres avoir atterri (si jamais la destination etait elle-meme
une zone-telep, ce qui serait surprenant mais possible). - Rebonds endlevel : un boolean {@code endLevelTriggered} evite
d’attacher plusieurs fois le LevelSelectAppState pendant la frame de
transition. - Polygones invalides : zones avec moins de 3 points -> {@code floorXZ}
null -> {@code ZoneTracker} les ignore, point-in-polygon retourne false. - JSON anciens : si {@code exitZoneId} ou {@code telZone} sont absents,
on retombe sur -1 (= rien ne se passe).
Pour tester
./gradlew convertLevels # regenere les level_X.json (ajoute exitZoneId)
./gradlew buildScenes # regenere les scene_X.j3o (ajoute userData zones)
./gradlew run
Test fin de niveau : trouver la zone exit du niveau (= sortie du level
A, etc.) et y aller. Le joueur doit etre renvoye au menu LevelSelect des
qu’il y entre. Le log console affichera :
=== Joueur dans zone EXIT N -> fin de niveau ===
Test teleport : trouver une zone avec {@code telZone >= 0} dans le
JSON ({@code grep ‘ »telZone »:’ assets/levels/level_A.json | grep -v ‘ »telZone »:-1’}).
Marcher dedans, le joueur doit etre instantanement deplace ailleurs. Log :
=== Teleport zone N -> zone M (telX/telZ) -> (jx/jy/jz) ===
Limitations / TODO
- Son de teleport : l’ASM joue le sample 26 ({@code Aud_SampleNum_w=26}).
Pas implemente cote Java (TODO en commentaire dans {@code applyTeleport}). - Effet visuel teleport : l’ASM fait un shimmer/flash blanc
({@code Game_TeleportFrame_w}). Pas implemente (TODO). - Hauteur Y au teleport : l’ASM preserve la hauteur RELATIVE au sol
({@code Plr_YOff = (oldY – oldFloor) + newFloor}). On utilise simplement
le sol de la zone destination + offset, suffisant pour les cas de base. - Niveau suivant : pour l’instant {@code endLevel()} retourne au menu
LevelSelect. Plus tard, on enchainera automatiquement vers level+1 avec
un ecran d’inter-niveau. - Performance : le balayage toutes-zones brut force se fait au plus 1
fois par frame (cas frequent : la zone n’a pas change, hit cache).
~134 zones max, ~10 ops par zone -> negligeable (< 0.1ms par frame).
Fichiers modifies / nouveaux
Nouveaux :
– {@code world/PolygonXZ.java} : pointInPolygon + distance utilities
– {@code world/ZoneTracker.java} : tracker zone courante + accesseurs
Modifies :
– {@code core/level/LevelBinaryParser.java} : lecture {@code exitZoneId}
– {@code core/level/LevelData.java} : champ {@code exitZoneId}
– {@code core/level/GraphicsBinaryParser.java} : transmet exitZoneId
– {@code tools/LevelJsonExporter.java} : ecrit {@code exitZoneId} top-level JSON
– {@code tools/LevelSceneBuilder.java} : record ZD etendu, userData zones, exitZoneId
– {@code world/LiftControl.java} : refactore pour utiliser PolygonXZ
– {@code app/GameAppState.java} : zoneTracker + applyZoneLogic + applyTeleport + endLevel