Wednesday, March 10, 2010

B_CRUMBLINGFLOOR

Somehow, I stumbled upon this re-implementation of the Manic Miner / Jet Set Willy engine in Visual Basic. I'm unsure on how I should implement crumbling ground in Bilou (or any block animation in response to character's presence). Let's see how a +20 year old game handled it...

Bon, je l'avoue, je suis intrigué par la quantité d'"éléments spéciaux" présents dans un jeu aussi vieux que "Manic Miner" contient, et la difficulté que j'ai à trouver une manière convaincante de réaliser la même chose dans mon moteur de jeu actuel. Du coup, j'ai mis la main sur une ré-implémentation du jeu en Visual Basic. Voyons donc comment les blocs-qui-disparaissent-quand-on-reste-dessus ont été gérés ...

' block type constants
Private Const B_CRUMBLINGFLOOR      As Byte = 2
Block types are encoded in a separate array, with one byte per 8x8 tile. One such type is "crumbling floor". Other block types include B_AIR, B_FLOOR, B_WALL, B_CONVEYOR and B_NASTY (two of them) and B_SPARE (for switches). Jet Set Willy only has one B_NASTY type, and the other one is converted into B_SLOPE. This is consistent with my own engine.

Carles a construit son moteur de Willy de manière à réutiliser les données du jeu telles quelles -- sans être le code original sous Z80, ça donne malgré tout une bonne idée de ce qui aurait pu être fait. En l'occurence, par exemple, la structure de chaque niveau est donnée par un tableau de la taille de l'écran, indiquant quels graphismes utiliser, mais aussi le type de chaque bloc entre 0 et 7.

B_CRUMBLINGFLOOR est celui qui nous intéresse. En cherchant "crumbling" plus loin dans le code, je tombe donc sur la fonction DoWilly (extrait ci-dessous) qui gère toutes les interactions entre Willy et le monde qui l'entoure. DoConveyors ne fait qu'animer des blocs, de même que DoGuardians se contente de déplacer les monstres. Ce sont les sous-fonctions de DoWilly qui détectent les collisions et altèrent le déplacement du personnage. Exactement le genre d'approche que j'utilisais dans Calimero et Bilou en QuickBasic.

What's interesting (for me :-) in Carles' approach of redoing Willy's world is that he operates directly from the tape file of the spectrum, so not only it has the "readability" of modern BASIC (i'm not that much into Z80 assembly ^^") but it still explains the logic of the original game (how switches, witches and stuff are encoded).
If (.mode = [eWalk]) Then
If Not (WillyCheckFeet()) Then
' code for falling stripped out ...
End If
Call WillyCheckConveyor(keys)
Call WillyCheckCrumblingFloor
End If
The DoWilly() sub shown above proceed step by step, handling interaction of Willy with every item "present" in the game. That is, we have DoConveyors or DoGuardians as well, for sure, but they merely perform animation and movement job. WillyCheckConveyor makes sure conveyor pushes Willy. That's the real job.
Let's thus have a look at WillyCheckCrumblingFloor just below ...


Voyons donc WillyCheckCrumblingFloor dont le code est ci-dessous. Après s'être assurée qu'il y a bien un bloc friable sous les pieds de Willy, elle va utiliser FXCrumblingBlock pour procéder à l'animation (décalage du tile d'un pixel vers le bas) et transforme les propriétés du bloc donné en "B_AIR" si FXCrumblingBlock lui dit de le faire à travers la variable f.
o = .x + (.y \ 8 + 2) * 32
For c = 0 To 1
If (m_RoomBlock(o + c) = B_CRUMBLINGFLOOR) Then
Call FXCrumblingBlock(m_DIBBack, 8 * (.x + c), .y + 16,
                      CAPaper(m_RoomData(o + c)), f)
Call FXCrumblingBlock(m_DIBMask, 8 * (.x + c), .y + 16,
                      I_BLACK, False)
If (f) Then
   m_RoomBlock(o + c) = B_AIR
   Call FXRect(m_DIBBack, 8 * (.x + c), .y + 16, 8, 8,
               CAPaper(m_BlockCA(0)))
End If
End If
Next
FXCrumblingBlock will not be shown here, because it's a pretty dirty function that mixes up game logic, tile animation and rendering engine commands. What I can tell you is that everytime it is invoked, it scroll downs the data of that specific tile, and insert a "blank" line at the top. Once this is done, it scans the whole tile again and sets the "cleared" flag (variable f) to false if it found any non-blank pixel. That's precisely what triggers transformation of B_CRUMBLINGFLOOR into B_AIR.

As the tile "crumbles" exactly one pixel per frame and Willy walks one pixel per frame, it means if you simply walk on crumbling floor, it will be completely crumbled when you're done walking.


Pour moi, c'est là que le bât blesse: FXCrumbblingBlock mélange allègrement rendu vidéo, manipulation des animations et logique du jeu. Je ne la reprend donc pas ici, sachez juste qu'elle décide si oui ou non l'animation est finie en parcourant les pixels qui viennent d'être édités. Si tous les pixels sont "transparents", c'est qu'il n'y a plus du tout de sol et on peut remplacer B_CRUMBLINGBLOCK par B_AIR.

Là-dessus, je vous laisse bras-dessous: il est temps que je prépare ma pitchounette à aller rechercher ma fée à Belle Ile. Ciao.

2 comments:

Anonymous said...

...j'ai toujours appelé mon fidèle mineur "Manic Miner" et non Willy...sur ZX Spectrum 48 k s'entend...

PypeBros said...

ah. Bin moi, j'appelais le dromadaire mutant de "Revenge of the mutant camels" 'super-chameau'. La page wikipédia m'avait donné l'impression qu'il s'appelait génériquement "miner willy".