Aller au contenu principal

Session 110 — Zone tracking + Teleports + Fin de niveau

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 :

  1. Teleports : certaines zones ont {@code telZone}, {@code telX},
    {@code telZ} dans le binaire. Entrer dans une telle zone teleporte
    instantanement le joueur ailleurs.
  2. 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)

  1. LevelBinaryParser : ajoute le champ {@code exitZoneId} a {@code BinData}
    et le lit comme un word a {@code floorLineOffset – 2}.
  2. LevelData : ajoute le champ {@code exitZoneId} (final).
  3. GraphicsBinaryParser : passe {@code bd.exitZoneId} au constructeur de
    {@code LevelData}.
  4. 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)

  1. 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)

  1. PolygonXZ.java (nouveau, package {@code com.ab3d2.world}) :
    utilitaire statique extrait de {@code LiftControl}, expose
    {@code pointInPolygon}, {@code distanceToPolygon}, {@code distanceToSegment}.
  2. LiftControl : refactore pour utiliser {@code PolygonXZ} au lieu de
    ses methodes privees dupliquees.
  3. 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.
  4. 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

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *