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 deOFS_FLOOR_DATA = 0x14BC0et de la lecture deGLFT_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 decurrentCP(offset +28 dans EntT, le control
point initial). Avant on skippait ce word.LevelJsonExporter: sortie decurrentCPsur chaque objet ; sur chaque door/lift,
sortie dexCoord(zl_Word9),zCoord(zl_Word10),gfxOfsLong(zl_GraphicsOffset).
Ajout du blocfloorData[](16 entrees) dansdefinitions.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
parcourantlevelScene/items/(aliens vivants OU items collectables presents). - MODIFIED :
world/DoorControl.java— nouveau champdoorIndexlu depuis UserData,
nouvelle methodeisLocked()qui consulteDoorLockState.isDoorLocked(doorIndex).
Fallback retro-compat sur l’ancienisLockedByAlien()sidoorIndex < 0. - MODIFIED :
tools/convert/LevelSceneBuilder.java— nouvelle methode
parseDoorIndexByZone(json)qui calcule l’index ASM (= position dans le tableau
doors[]du JSON). Chaquedoor_*Node recoit sondoorIndexUserData. - MODIFIED :
app/GameAppState.java— appelDoorLockState.update(itemsNode)chaque
frame aprespickupSystem.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 wordConditionsASM. Methodes
statiquestoggleBit,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>.jsons’il existe, annote chaque
door_*Node avecrequiredSwitchesUserData (CSV d’indices). Si fichier
absent : retour fidele ASM (switches decoratifs). - MODIFIED :
tools/convert/LevelSceneBuilder.java— Nodeswitchesajoute au
scene-graph, methodeaddSwitches(json, switchesNode, fb)qui parcourt
switches[]du JSON et cree unswitch_<idx>Node par entree (midpoint des
2 points consecutifs = formule ASMnewanims.s::p1_SpaceIsPressed). - MODIFIED :
world/DoorControl.java— nouveau champrequiredSwitches(int[]).
La methodeisLocked()ajoute une couche 2 : sirequiredSwitches != null,
au moins un switch doit etre ON pour deverrouiller (OR logique). - MODIFIED :
app/GameAppState.java— attacheSwitchControla chaqueswitch_*Node,
applique leSwitchDoorMapsi present, brancheSwitchControl.PLAYER_USEsur 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 blocfloorData[]de
definitions.json(16 entrees(damagePerTick, soundEffect)). - NEW :
world/FloorDamageSystem.java— AppState qui consulte
zoneTracker.getCurrentZone().floorNoise()toutes les 2 sec, lookup dans
floorData[], appliquedamagePerTickaPlayerHealthState.takeDamage(). - MODIFIED :
tools/convert/LevelSceneBuilder.java— recordZDetendu avec
floorNoise, upperFloorNoise, water, upperFloorH. Chaquezone_NNNode recoit
ces UserData. Lecture defensive du JSON (defaults a 0 si champs absents). - MODIFIED :
world/ZoneTracker.ZoneInfoetendu avec les memes champs +
hasUpper()helper. - MODIFIED :
app/GameAppState.java— instanciationFloorDamageSystemapres
zoneTrackerethealthStatecrees, 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 flagjustDiedFlag(set
parAlienAI.justDied(), consomme parAlienControlSystem.update()une seule
fois). - MODIFIED :
core/ai/AlienAI.java—justDied()set le flag a true. - NEW :
world/LevelMessageLoader.java— parse le blocmessages[]de
level_<X>.jsonen 10 entrees indexees, support echappement JSON basique. - MODIFIED :
tools/convert/LevelSceneBuilder.java::addItems— propage
displayTextdu JSON vers lespriteNode.setUserData("displayText", ...). - MODIFIED :
world/AlienControlSystem.java— nouveaux champshudState+
levelMessagesavec setters. Lecture dedisplayTextUserData dans
initialize(). Boucleupdate()detectejustDiedFlag, push
levelMessages[textId]viahudState.pushMessage(text, TAG_NARRATIVE). - MODIFIED :
world/PickupSystem.java— meme mecanisme danscollect(): si
l’item a undisplayTextUserData et quelevelMessages[id]existe, push
le texte custom en plus du « Got X » generique. - MODIFIED :
app/GameAppState.java— appelLevelMessageLoader.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 > 0dans 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].