Aller au contenu principal

Session 134 — Triggers / items-cles / switches / floor damage / textes de zone

Resume

Etape majeure : portage complet des mecaniques de progression du niveau. Le joueur peut
desormais (1) etre bloque par les portes verrouillees, (2) deverrouiller en ramassant
les items-cles ou en tuant les aliens-gardiens, (3) actionner les switches avec la touche
USE, (4) prendre des degats sur les sols toxiques/lave, (5) declencher les textes de
narration du niveau a la mort d’un alien ou au ramassage d’un item.

Step 0 — Plomberie d’extraction

  • LnkParser : ajout de OFS_FLOOR_DATA = 0x14BC0 et de la lecture de GLFT_FloorData_l
    (16 entrees de 4 bytes : MSW = degats, LSW = sound effect). Cette table etait absente,
    ce qui empechait toute application de damage sur les sols toxiques.
  • LevelBinaryParser.ObjData : ajout de currentCP (offset +28 dans EntT, le control
    point initial). Avant on skippait ce word.
  • LevelJsonExporter : sortie de currentCP sur chaque objet ; sur chaque door/lift,
    sortie de xCoord (zl_Word9), zCoord (zl_Word10), gfxOfsLong (zl_GraphicsOffset).
    Ajout du bloc floorData[] (16 entrees) dans definitions.json.

Step 1 — Door unlock par items (le vrai mecanisme ASM des cles)

Decouverte architecturale : il n’y a pas de « systeme de keys » dans AB3D2. Les cles
sont simplement des items avec EntT_DoorsAndLiftsHeld_l != 0. A chaque frame le
ObjectHandler ASM (newaliencontrol.s:127-128, :210-211) OR ces bits dans le masque
global Anim_DoorAndLiftLocks_l. La porte d’index N teste btst N, Anim_DoorAndLiftLocks_l.
Quand l’item est ramasse, son sprite disparait, ses bits ne sont plus OR-es, la porte
s’ouvre. C’est le meme mecanisme que pour les aliens-gardiens.

  • NEW : world/DoorLockState.java — masque global reconstruit chaque frame en
    parcourant levelScene/items/ (aliens vivants OU items collectables presents).
  • MODIFIED : world/DoorControl.java — nouveau champ doorIndex lu depuis UserData,
    nouvelle methode isLocked() qui consulte DoorLockState.isDoorLocked(doorIndex).
    Fallback retro-compat sur l’ancien isLockedByAlien() si doorIndex < 0.
  • MODIFIED : tools/convert/LevelSceneBuilder.java — nouvelle methode
    parseDoorIndexByZone(json) qui calcule l’index ASM (= position dans le tableau
    doors[] du JSON). Chaque door_* Node recoit son doorIndex UserData.
  • MODIFIED : app/GameAppState.java — appel DoorLockState.update(itemsNode) chaque
    frame apres pickupSystem.update(), reset en cleanup.

Step 2 — Switches (fidele ASM + extension custom)

Decouverte architecturale : dans l’ASM original, les switches togglent un bit dans
le word Conditions mais ce bit n’est jamais relu (newanims.s:1418, 1424 :
; and.w Conditions,d2 est commente). Les switches sont donc purement decoratifs en
vanilla. Choix utilisateur (decision session 134) : implementer les deux modes —
fidele ASM par defaut, extension custom activable par fichier de mapping.

  • NEW : world/SwitchState.java — equivalent du word Conditions ASM. Methodes
    statiques toggleBit, isSwitchOn, setSwitch. Init a 0 (= mode single-player
    ASM, cf. hires.s:597).
  • NEW : world/SwitchControl.java — control JME attache a chaque switch Node.
    Detecte la touche USE + proximite (60 unites Amiga = ~1 m JME) + cooldown 0.5s.
    Sur trigger : pressed = !pressed, SwitchState.toggleBit(conditionBit),
    log. TODO futur : changer la texture pressed/unpressed, jouer le sample #10.
  • NEW : world/SwitchDoorMap.java — extension custom optionnelle. Charge
    assets/levels/switch_door_map_<levelId>.json s’il existe, annote chaque
    door_* Node avec requiredSwitches UserData (CSV d’indices). Si fichier
    absent : retour fidele ASM (switches decoratifs).
  • MODIFIED : tools/convert/LevelSceneBuilder.java — Node switches ajoute au
    scene-graph, methode addSwitches(json, switchesNode, fb) qui parcourt
    switches[] du JSON et cree un switch_<idx> Node par entree (midpoint des
    2 points consecutifs = formule ASM newanims.s::p1_SpaceIsPressed).
  • MODIFIED : world/DoorControl.java — nouveau champ requiredSwitches (int[]).
    La methode isLocked() ajoute une couche 2 : si requiredSwitches != null,
    au moins un switch doit etre ON pour deverrouiller (OR logique).
  • MODIFIED : app/GameAppState.java — attache SwitchControl a chaque switch_* Node,
    applique le SwitchDoorMap si present, branche SwitchControl.PLAYER_USE sur la
    touche A_USE (meme touche que pour les portes RAISE_PLAYER_USE).

Step 3 — Floor damage (sols toxiques / lave / acide)

Port fidele de hires.s:6217-6240. L’ASM applique le damage toutes les 100 frames
@ 50 Hz = 2 secondes ; on reproduit avec un timer en secondes (= independant du
framerate JME).

  • NEW : world/FloorDataLoader.java — parser regex pour le bloc floorData[] de
    definitions.json (16 entrees (damagePerTick, soundEffect)).
  • NEW : world/FloorDamageSystem.java — AppState qui consulte
    zoneTracker.getCurrentZone().floorNoise() toutes les 2 sec, lookup dans
    floorData[], applique damagePerTick a PlayerHealthState.takeDamage().
  • MODIFIED : tools/convert/LevelSceneBuilder.java — record ZD etendu avec
    floorNoise, upperFloorNoise, water, upperFloorH. Chaque zone_NN Node recoit
    ces UserData. Lecture defensive du JSON (defaults a 0 si champs absents).
  • MODIFIED : world/ZoneTracker.ZoneInfo etendu avec les memes champs +
    hasUpper() helper.
  • MODIFIED : app/GameAppState.java — instanciation FloorDamageSystem apres
    zoneTracker et healthState crees, attache + detach en cleanup.

Limite V1 : pas de detection Plr1_StoodInTop_b (etage haut), on utilise toujours
floorNoise ; pas de son joue (TODO quand AudioManager).

Step 4 — Texte de zone sur kill alien + ramassage item

Port fidele de newaliencontrol.s:388-396 (alien Destructable) et :596-604
(item Plr1_CollectItem). Le binaire AB3D2 expose 10 messages de 160 chars dans
le header texte du niveau ; chaque alien/item peut avoir EntT_DisplayText_w >= 0
qui indexe ce tableau.

  • MODIFIED : core/ai/AlienRuntimeState.java — nouveau flag justDiedFlag (set
    par AlienAI.justDied(), consomme par AlienControlSystem.update() une seule
    fois).
  • MODIFIED : core/ai/AlienAI.javajustDied() set le flag a true.
  • NEW : world/LevelMessageLoader.java — parse le bloc messages[] de
    level_<X>.json en 10 entrees indexees, support echappement JSON basique.
  • MODIFIED : tools/convert/LevelSceneBuilder.java::addItems — propage
    displayText du JSON vers le spriteNode.setUserData("displayText", ...).
  • MODIFIED : world/AlienControlSystem.java — nouveaux champs hudState +
    levelMessages avec setters. Lecture de displayText UserData dans
    initialize(). Boucle update() detecte justDiedFlag, push
    levelMessages[textId] via hudState.pushMessage(text, TAG_NARRATIVE).
  • MODIFIED : world/PickupSystem.java — meme mecanisme dans collect() : si
    l’item a un displayText UserData et que levelMessages[id] existe, push
    le texte custom en plus du « Got X » generique.
  • MODIFIED : app/GameAppState.java — appel LevelMessageLoader.loadForLevel(...)
    et passe le tableau aux deux systemes (AlienControlSystem + PickupSystem).

Note : le HudAppState n’est pas attache (cf. session 75), donc les messages sont
pour l’instant juste dans la queue du HudState (visible via log debug). Quand le
HUD sera reactive, les textes s’afficheront automatiquement.

Step 5 — Dynamic lights (verification)

AnimBrightnessSystem existant scanne le levelScene et anime les vertex colors
selon les patterns BrightPulse1-5 et BrightFlicker1-2 toutes les ~40ms
(session 130). Pas de mecanique « switch declenche lumiere » dans l’ASM original :
les patterns sont baked par-point dans pointBrights du binaire. Rien a porter.

Fichiers touches

NEW   src/main/java/com/ab3d2/world/DoorLockState.java
NEW   src/main/java/com/ab3d2/world/SwitchState.java
NEW   src/main/java/com/ab3d2/world/SwitchControl.java
NEW   src/main/java/com/ab3d2/world/SwitchDoorMap.java
NEW   src/main/java/com/ab3d2/world/FloorDataLoader.java
NEW   src/main/java/com/ab3d2/world/FloorDamageSystem.java
NEW   src/main/java/com/ab3d2/world/LevelMessageLoader.java
MOD   src/main/java/com/ab3d2/tools/convert/LnkParser.java
MOD   src/main/java/com/ab3d2/tools/convert/LevelBinaryParser.java
MOD   src/main/java/com/ab3d2/tools/convert/LevelJsonExporter.java
MOD   src/main/java/com/ab3d2/tools/convert/LevelSceneBuilder.java
MOD   src/main/java/com/ab3d2/world/DoorControl.java
MOD   src/main/java/com/ab3d2/world/ZoneTracker.java
MOD   src/main/java/com/ab3d2/world/AlienControlSystem.java
MOD   src/main/java/com/ab3d2/world/PickupSystem.java
MOD   src/main/java/com/ab3d2/core/ai/AlienRuntimeState.java
MOD   src/main/java/com/ab3d2/core/ai/AlienAI.java
MOD   src/main/java/com/ab3d2/app/GameAppState.java

Procedure de test

./gradlew compileJava                    # valide la compilation
./gradlew convertLevels buildScenes      # regenere les .j3o avec les nouveaux UserData
./gradlew run                            # lance le jeu
  • Door lock par cle : LEVEL_A, ramasser le passkey, la porte associee s’ouvre.
  • Floor damage : chercher une zone avec floorNoise > 0 dans le JSON, marcher
    dessus, HP devrait baisser toutes les 2 sec.
  • Switch toggle : LEVEL_A a un switch en zone 6 ; s’en approcher + touche E,
    le log montre [Switch X] toggled -> ON/OFF.
  • Texte sur kill : tuer un alien avec displayText >= 0 ; log
    Alien #X killed -> push message[Y].

Laisser un commentaire

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