Tuesday, September 08, 2026

Terra Ninfa

Prenez le coeur historique de votre ville, tournez-lui le dos pour vous diriger vers le coeur folklorique. Là, au bout du boulevard, à côté des ruines classées du vieil hôpital se tient le nouveau centre multi-culturel combinant bibliothèque, fab lab, salle de spectacle et "classes" de conférences ... et à l'étage, dans "l'espace des possibles" se tient en plus de l'apéro jeu-videos la soirée du "gaming club" où on retrouve des consoles rétros, des tables de jeu de rôle et de jeu de société.

Et une tablette où les développeurs peuvent mettre leur prototype à l'épreuve du playtest. Parmi eux, en juin, il y avait l'ami Tog qui présentait le prototype de jeu Godot qu'il a développé avec sa dame. Le jeu a un petit côté "monument valley" et fait la part belle à la manipulation de l'eau.

Tog n'était pas présent en ce début de Septembre, mais quelques-uns de ses colocataires-de-tablette avaient insisté pour que je revienne avec ma DS même si Dreamland n'est pas encore un jeu. Je suis repassé, donc et récolté quelques "tu me ramènes à mes 8 ans" ou "Franchement, chapeau! On ne dirait pas un jeu fait en solo!", voire même "J'adore les cahiers! Il faut que tu fasses un livre pour nous raconter le dev!"
 

 

Thursday, September 03, 2026

Inspector Widget Improved

Inspector Widget ... le mode pause du moteur de jeu de GEDS qui permet de voir à travers la Matrice. Repérer les zones de collision, mais aussi visualiser le terrain autour des personnages tel que les contrôleur le perçoivent. Sauf que le code qui faisait l'affichage du terrain date de l'époque Apple Assault  et qu'il est plus ou moins inutile avec le nouveau système de map physique.

During AppleAssault and SchoolRush development, the "playfield" feature of InspectorWidget has proven an extremely valuable feature of the GEDS engine. I could get an overview of what the engine sees of the level without having to manually decode contents of the physics map. But with the evolution towards a new, richer encoding, the information it was showing became mostly useless. But hopefully, during the "Blador-vs-Spike" episode, I located where the responsible code was and could start fixing it. Initially, it was looking like the listing below.

Le code qu'il aurait fallu que je retrouve il y a un moment était caché dans InspectorWidget::clearmain(). 

for (int ty=sy; ty<=ey; ty++)
  for (int tx=sx; tx<=ex; tx++) {
    unsigned f=gob[0]->world->getflags(tx,ty);
    unsigned c=BCHECK; // pattern for the "outer ring" of the zoomed tile
    if (f&F_BLOCKING) c=BON; // plain light ring = solid block
    if (f&F_PLAYERTHRU) c=BOFF; // dark ring = air / water / etc.
    if (f&0x10000) c=BCHECK; // checkered ring = special block
    if (f&F_SLOPE) c=97; // striked through = slopes
    char sq[16];
    memset(sq,c,16);
    if ((f&(F_FLOOR|F_SLOPE))==F_FLOOR) memset(sq,BON,4);

    sq[5]=80+((f>>12)&0xf); // 4-digits code in the inner area
    sq[6]=80+((f>>8)&0xf); // encodes the F_* flags for cando() calls.
    sq[9]=80+((f>>4)&0xf);
    sq[10]=80+(f&0xf);
    u16* v = vram+MAP+(ty-sy)*128+(tx-sx)*4;
    for (int i=0;i<16;i++) {
      *v++=sq[i];
      if ((i&3)==3) v+=28;
    }
  }

Pour ceux qui ne parlent pas le C couramment, le listing ci-dessus raconte que chaque pavé de la map va être représenté par un code à 4 caractères entouré d'une bordure dont le pattern est variable. 

  • bordure solide pour ce qui ne laisse pas passer le personnage
  • bordure en damier pour les blocs spéciaux
  • bordure en hachure pour les pentes.

Le code au centre du carré, ce sont les propriétés utilisée par la fameuse fonction cando(). Et il faut bien admettre que maintenant qu'il y a plusieurs types de pentes différents, plus de blocs spéciaux et des blocs aux propriétés particulières, juste les flags pour cando(), c'est devenu à la limite de l'inutile. Parce que la plupart des blocs spéciaux ont les mêmes propriétés et que celles des pentes sont prédéfinies.

Mais heureusemement, j'ai trouvé l'occasion d'améliorer ça.

  • un code à 2 chiffres, c'est un tile à encodage direct. on voit littéralement le byte de la map physique et chacun de ses bits nous renseigne pour une propriété. 44, c'est de l'air.
  • un code à 2 chiffres sous une petite ligne à damier, c'est un bloc spécial. Ici aussi, la valeur et celles du byte de la map. Les valeurs ff, fe et fd servent pour les fameuses "flèches jaunes" de l'éditeur.
  • les pentes sont toujours identifiées par leur contour hachuré. Le code à 4 chiffres donne les hauteurs des pixels au centre du tile. 3456 ou 6543 pour des pentes à 45°. 2233 et 6677 pour des pentes plus faibles en "montée" de droite à gauche, etc.
  • Enfin, pour les autres blocs, on reste sur un code à 4 chiffres qui contient les flags. à l'ancienne. 

The new code varies the shapes a bit more. Some tiles will only have a 2-digit code (the byte straight out of the map), sometimes with a checkered line on top of them for special blocks. Slope tiles still have a 4-digit code, but it now shows how heights ramp up or down along the tile. And the so-called "indirect" tiles, those which can be assigned physical properties such as friction and flow, keep showing the 16-bit cando() flags as before ... at least so far.   

 

 

Tuesday, September 01, 2026

CompoundGob::RefreshPage

I guess you can hardly tell what's going on on the picture below, and I can't blame you for that. It's supposed to be a stomped scorpeye shell, but since I've added crawling animation for the scorpeye, we see that glitchy mess of sprite parts instead.


Ah oui, je m'étais fait un joli scorpion qui marche, mais si vous parvenez à l'assommer, tout à coup, il ne ressemble plus qu'à un tas de pixels complètement glitché. Comme j'envisage de passer refaire un coucou au "gaming club" de vendredi, ça ne serait pas mal de corriger un peu ça.

La cause du problème, je la connais: l'animation de la marche est construite avec 5 sprites hardware: un "large" pour la carapace du scorpeye et un carré pour chaque "patte". En revanche, les animations "carapace seule" et "carapace qui tourne parce qu'on l'a lancée" sont toujours inchangées et n'utilisent que 3 sprites hardware. Et le couac, c'est qu'en plus, le sprite large n'est pas sur le même d'une animation à l'autre. Il était donc temps que je me gratte un peu la tête et que je retrouve les fonctions-clé pour gérer ça, en particulier loadAnim() dans CompoundGob et setupOAM() qui peut redéfinir les tailles et aspects des sprites.

What happens is that you have dedicated bits in the hardware sprite entries to indicate whether you want a square, tall or wide sprite and of what size. So far, in my engine, those properties are defined once when you allocate hardware sprites for an object and then preserved as we just update coordinates, VRAM location and optionally palette slot of each sprite as we animate them. But by mixing the new crawl and the old spinning animations, I'm breaking an old habit of sharing the same structure for all animations of a given game object. So I need to extend the game engine with the following function:

  void refreshPages(const GobAnim *ani) {
    unsigned nlimbs = ani->getnlimbs();
    unsigned i;
    pages = ani->getpages();
    for (i = 0; i < nlimbs; i++) {
      if (oam[i]==NO_OAM) continue;
      pages[i]->setupOAM(sprites + oam[i], 0 /*?*/);
    }
    for (; i < nboam; i++) {
      if (oam[i]==NO_OAM) continue;
      sprites[oam[i]].attribute[0] = ATTR0_DISABLED;
    }
    nboam = nlimbs;
  } 

Je dois dire qu'au départ, je m'attendais à plus compliqué, mais la petite fonction ci-dessus et une brave ligne de plus, les animations se sont réparées presque d'elles-même. à utiliser avec prudence tout de même: le système ne se déclenche que si les deux animations ont un nombre différent de sprites.

To be honest, I was expecting it to be harder to code. It was a bit tedious to locate where to act in the code (state transition? animation loading ?) and I spent a significant part of a holiday afternoon in ddd setting conditional breakpoints to figure it out. Then I got puzzled by the update/setup/allocate functions manipulating hardware sprites through the "SpritePage" class: the one that is used at every frame to update what we see on screen keeps aspect ratio and size of the sprite unchanged... but after all, it was just a matter of a small function called when we detect that the current number of OAMs (aka hardware sprites) is different from the number of limbs the animation uses. Pretty and straightforward.

Monday, August 24, 2026

Crawling Scorpeye

Sur bsky, MagicalScope nous poste d'impressionnants designs d'objets magiques qui sont ensuite vendus à des internautes. Parmi eux, je suis tombé sur une potion-qui-marche particulièrement inspirante. J'ai d'abord eu le réflexe de voir comment je pourrais l'intégrer comme nouveau perso dans la pyramide jusqu' ce que je réalise que mon brave scorpeye pourrait simplement profiter du type de déplacement que suggère cette potion tout en restant lui-même. 

Après des mois où on voyait un scorpeye tout immobile dans un coin de la salle pyramidique, voici enfin une petite animation (encore un brin brouillonne) du scorpeye patrouillant autour de son trésor. J'aurai probablement un peu de travail à faire sur le moteur de jeu: l'animation de la carapace lancée supposait 3 sprites hardware alors que la marche en crabe en utilise 5 ... le résultat est assez bizarre à voir quand on assomme et ramasse notre scorpeye.

At last! After months (years ?!) of design blockage on how-the-heck-am-I-going-to-make-scorpeye-walk, I can propose you a prototype animation for the crawling scorpeye ! Maybe not as "crawling" as I had imagined after seeing MagicalScope's mimmic potion, but that shall be a start.

Key idea for the redesign is to embrace the "limbless" nature of Bilou's world and grant the scorpeyes 4 versatile spike sattelites. It can use them to crawl, pinch or sting depending on the situation's need. It will make it clear whether it's currently safe to stomp on its shell to stun it.

"Mais où sont passées ses pinces ?" me demanderez-vous ? Eh bien c'est justement là tout le sel du redesign: les même membre lui serviront soit de pattes, soit de pinces, soit de morceau de queue de scorpion. Je décide que ce sera plus fun et plus lisible qu'un vrai arachnide avec tous ses chéli-chose (J.L.N ? c'est quoi le bon nom, encore ?) 

As a bonus, an intermediate step where I was studying the design of the magic potion and considering "well, why not. It would be an extended version of Inkjet that can move along... quite fits the universe. You lose some part of the crab-like suggestion by not having limbs between the pinching part and the body, but that's how I came up with the idea of keeping scorpeye and having it walk on its pincher, so I'm okay with it.


 

The Lost Tiled Tutorials

Many of the tutorials I had encountered between SEDS and LEDS are now out of the web. Too bad, they were key material I sent readers towards when I did not feel like translating some lengthy explanation about tiled games in general. Since I'm editing some part of the blog as if it was going to be the Chapter 1: Tiled Games of a book, I found myself digging through archive.org to find the original material, print it and review it as much as possible. 

The most influential of them all was certainly the MC Kids big post. Where we have Greggman discussing *really* how they made the game back on NES days. It details several clever tricks that help making a quite-sized game on limited resources, some of which make mostly sense if you're on #6502 CPU (like having a set of byte tables rather than structures), but also the key gamedev concept of a "hotspot", a single point that will be used to model the position of the character on slopes.

It is also the most official one, as the post was originally written in 1992 for the Journal of Computer Game Design while the game was from 1992 as well. And since there was an official body publishing it, I can't quite just bring in many pictures of it ... but I guess it's fair to study it, take notes and then post my own notes about what was being said.  

Entre la création de SEDS et celle de LEDS, il y a eu pas mal de posts qui faisaient référence à l'un ou l'autre tutoriel sur la conception de jeux à base de tiles. Evidemment, là-maintenant, un grand nombre de ces tutos ont disparu d'Internet ... juste au moment où je me dis que ce sujet serait bien comme premier chapitre.

Le plus gros d'entre eux, c'était sans doute la présentation du code de gestion des collisions perso/monde dans le jeu NES M.C. Kids, auquel je faisais référence pour les lecteurs Anglophones en 2007 ... Pas trop question d'en faire des captures d'écran dans tous les sens par contre: le truc avait quand-même été publié dans un journal officiel à l'époque. Mais un scan de mes notes, ça, ça me semble honnête. 

The paper goes into significant details of how the levels are encoded (1 byte per 16x16 block, that is used to lookup the 4 tiles composing the block and its type among 100 possible types), how collision with terrain are handled (with direction-conditional testpoints and 5 collision functions per tile type). The author relates the fight to get that extra memory and how you could map the whole level into memory and thus make it easily explorable and transformable thanks to this, but how 8x8 granularity would have explosed the RAM space budget.

It is also completed with retrospective thought of the author about how they'd have inserted "beginning of hill" and "end of hill" types (helpful to save lookups in the slope management) and how they'd have made the "how do I move left[tiletype, xpos]" array providing absolute position rather than relative (+1, 0, -1) positions in a revised version of the engine.

Bon, par contre, près de 20 ans plus tard, il faut bien avouer que pas grand-chose de tout ce qui est présenté là ne se sera retrouvé dans le moteur de jeu de SchoolRush. Entre les ruses propres au 6502 et les choix d'implémentation que l'auteur lui-même indique qu'il n'aurait pas refait pareil rien qu'un an plus tard, il reste juste le concept de "hot-spot" -- réduire le personnage à 1 point quand il faut décider où il se trouve dans une pente -- et le conseil général que "mettre des pentes dans un jeu, ça complique significativement le code". Mais c'était intéressant à relire malgré tout.  

Retrospectively, most of it did not end up in the GEDS engine. With a 66MHz CPU, if you realize that you need one extra memory lookup to get proper slope implementation, you go for that extra lookup. And if you may need more than one, you just write a loop. It's not about being lazy or not, it's about getting the most of what you have. And nowadays, you could "easily" add some "start-of-slope", "end-of-slope" meta tiles by means of auto-tiling rules.

The second note-worthy series are Tony PA tutorials about tiled games development. These were for flash games, with full-running examples at each step and detailed ActionScript code. Smartly enough, Tony starts his tutorials with top-down playfield in which the character can move freely, and then step by step introduces gravity, moving platforms, ladders and finally slopes. 

I was enjoyed to start reading it as it featured a picture of Charlie the Duck (which may explain why I was researching about it in 2007 and certainly why I just posted about it, btw :P) and saying "Sure if our hero is a jumper-type of hreo, he could still [proceed forward in a stair-like, slopeless ground], but normal heroes are very happy if they can avoid jumping. It could have been a good resource, but I knew from the time I've spotted a drawing with "impossible slopes" that I couldn't derive from what was presented there.

Un autre tuto qui sortait du lot, c'était la page sur les pentes de TonyPA. On lui doit la phrase sur les héros qui sont content de faire des bonds (hmmm ... Jill of the Jungle ?) et ceux qui trouvent que les pentes, c'est bien. J'avais laissé tombé assez vite à l'époque constatant que plusieurs interdits allaient sans doute me pourrir la vie ... Il faut noter que Tony faisait ses tuto pour de l'ActionScript, ce qui lui permettait d'avoir une petite démo interactive dès sa page web, mais ce qui donnait aussi du code plutôt étrangeoïdal pour qui a fait un minimum de C/Pascal/Basic dans sa vie.

A côté de ça, il faut reconnaître à Tony que sa petite démo s'inscrivait dans la continuation d'autres tutos qui démarrent dès le niveau "sokoban" pour ajouter petit à petit la gravité, les échelles, etc. Par contre, les "combinaisons interdites" qui proviennent du fait qu'on désactive tout test de collision horizontal dès qu'on est sur une pente ? Le personnage qui logiquement grimpe des escaliers tandis que son image se déplace le long d'une droite continue ? Non. Je me félicite de ne pas avoir insisté. Mais il aura le mérite de m'avoir aidé à déterminer pourquoi je veux des pentes dans Bilou. Je n'ai pas la physique de Sonic, mais depuis Keen 4, les niveaux à pentes semblent plus intéressants que leurs équivalents tout à angle droit. Et une pente, ça permet aussi de faire prendre de la hauteur à un projectile du type carapace de tortue, en plus de rendre le niveau plus organique.

During this 2026 retro-review, I noted that most of the slope logic seems to imply that only the display of the character is aligned with the slope. The logic entity made of testpoints and hitboxes simply moves along a stair. The two locations are only re-aligned if we jump from a slope. It also bypass the problem of solid-ground-tiles-and-slopes by disabling all horizontal checks while you're on a slope. That explains some of the "forbidden tile combinations", and implies that if you make a slope in a path that is high enough for certain characters in your game but not all, the engine will completely ignore the fact that the monster you're fleeing through that narrow passage should get hit in the face and not be able to follow you. And that has been a critical thing to address for my games even before I started working with the Nintendo DS.

So why would you put slopes in a Mario game ? so that your koopa shell keeps flowing forward and not bounce back in your face. Here's why. (Or simply to make your organic level feel organic and not just look organic). 

A last one ? Hopefully, the video from Vblank Entertainment about the conversion of Retro City Rampage into ROM City Rampage is still online. It goes into details about what tiles are and how it allows a large world to run on a sub-MegaByte cartridge, what palettes are, why you need to care about not putting too many sprites per line and so on. If you ever need a primer to the "Retro Game Mechanics Explained" series and feel like sitting idle for 10 minutes, this is the best I can think of at the moment.

(The author ended up writing his own NES emulator full with debugging features and his own high-level assembly -- NESHLA hosted on sourceforge -- in the process)



Charlie the Duck

Imagine: you're in 1998 or so, using the University Internet room over lunch time with the hope of filling your two precious floppies with some more tutorials on how to make interactive software with the DJGPP compiler (and find the DJGPP make tool, which the previous library you brought back apparently needs for building). And you suddenly stumble upon some Super Mario World clone for MS-DOS ! The same web page also mentions another platformer: Charlie the Duck.

Un jeu de plate-forme où on rebondit sur des monstres pour les vaincre et où on ramasse des bonus qui font "blip". Le ton est enfantin, les graphismes sont colorés ... On s'offre même un peu de scrolling parallaxe.

Si ça ne vous impressionne pas, c'est parce que je n'ai pas encore précisé qu'il s'agit d'un shareware réalisé pour MS-DOS. Et par une seule personne s'il vous plaît: Charlie the Duck

Il me semble bien que j'étais tombé dessus la première fois aux alentours de '97 ou '98, pendant que je cherchais des infos sur les DOS Extenders, les bibliothèques graphiques pour DJGPP du style de Allegro et ce genre de choses. Et il est fort probable que je sois d'abord tombé sur le clone de Mario World du même auteur avant de tomber sur Charlie... mais je n'ai pu trouver aucune infos corroborant cette impression. Mais j'avais été impressionné. Puis un peu déçu: Mike non plus n'avait pas encore trouvé de solution convaincante au problème de la musique de fond pendant que le jeu tourne.

I did check Charlie back then, and was impressed by its reactiveness and the amount of mechanics implemented. Think about it: Charlie can even warp to other places by diving into some pools of waters. How many Mario clone do you know that provide a true replacement for the warp pipes ? But there was one thing the MS-DOS version of Charlie was lacking: music. And after 3 years of RSD-GameMaker, that was a no-go. I can't remember whether some part of Charlie the Duck was open source but the minute I noticed it was mute, it stopped matter. Even though there was his "tile studio" application using DJGPP+Allegro on sourceforge

But meanwhile, Mike Wiering -- the sole Charlie author -- was busy in another University.   

Je sais que je suis retombé sur Charlie et Mike au début de ce blog, ou plus précisément sur le TFE de Mike que j'ai pris à l'époque pour une thèse de doctorat. L'équipe de recherche sur la programmation fonctionnelle du coin voyait d'un bon oeil la création d'une bibliothèque de création de jeux (de plate-forme) pour étoffer son palmarès d'applications et comme le langage s'appelait "Clean" (il est assez proche d'Haskell, au passage), on a eu droit à la Clean Game Library. Le document est sympa et utilise abondamment Charlie pour les illustrations. La bibliothèque est en théorie multi-plate-forme mais a été distribuée pour DirectX. Elle a aussi permis à Mike de faire une suite à son jeu, qui reçoit un accueil mitigé suite à des ajouts/suppressions de mécaniques de jeu.

Je n'ai jamais oublié Charlie, même si avec le temps, je l'ai confondu avec le jeu-référence d'un bouquin qui parlait du mode X (et de la bibliothèque FastGraph ?) Et en re-creusant un peu le sujet pour ce post, je tombe sur un morceau croustillant de Wikipedia: 

Game-Maker seems also to have made an impression in the Benelux, with references in various academic papers,[26] coverage in the largest game magazine in the region,[27] and dissection by the local demoscene.[28]

Parce que si [26] est la présentation de Mike à la conférence IFL aux Pays-Bas en '99, si je n'ai jamais entendu parler du magazine [27], la démoscène locale, c'est bibi, et la dissection, ce sont mes posts ^_^Eh oui, les gars: vous êtes en train de lire un site web référencé sur Wikipédia!

Mike's master thesis  turned into a paper for the Implementation of Functional Language workshop in 1999 "Using Clean for Platform Games", where Clean is a functional language designed at his University. Yet, that was building for Windows and DirectX 5 while I remember playing the game on MS-DOS.

I did discover that back in the early blogging days. "Clean Game", that was something I was eager to read. When it turned it was using a haskell cousin over DirectX, it cooled down any urge-to-post-about-it I initially had. But fun fact, Mike did mention RSD Game-Maker into his thesis, and somebody on the Internet found it peculiar that it happened only a few hundredths of km away from that small other country where someone had been writing scripts to extract and repair old RSD games ... And so, thanks Mike, for your duck and for contributing to Wikipedia linking to this very blog ^_^

Sunday, August 23, 2026

Blador vs. Spike

While I was checking collisions/interactions were still working fine after introducing the G_FIGHT group, I noted something not so right about the dumblador. Something I don't think I've ever blogged although it was available since the anniversary level: throw a dumblador into pencil spikes and it will snap to the spike it lands on, turning itself into an additional safe platform.

But with the current engine, you may end up with the blador "waking up" and start walking within spikes. You may also see it stopping completely misaligned and almost everytime, it would be too low compared to where it used to stop.

I first tried to tune collision areas, but that did not seem to help. I checked some values in the DDD debugger, but it's a bit of a lottery: you can't really tell in advance whether you'll find yourself in an interesting case. Note to future self: there are save states in desmume! (In such a case, I should have saved the emulator state before throwing the blador, save again when the breakpoint triggers, fast-forward to see whether the condition will happen and only then go back in time to either review the step-by-step behaviour or change a little bit the initial conditions and try another throw).

But I had not came back from the future to leave myself a note yet, so instead, I started adding coordinates of colliding entities in the "gobscript expression debugger" so I could have something that is stepping faster than DDD, and present precisely the information I needed (although I could have used it in base 10 :P).

And there I realised that when the blador ended up misaligned, it was colliding with a spike whose position was a mere multiple of 8 rather than a multiple of 16 ...

Back in SchoolRush times, it was mandatory to be a 16x16 pixels block if you wanted to have special behaviour such as hurting the player. But it changed with the tiled engine revision where each individual 8x8 tile can be of any 64 special type (or any of the other 192 "normal" types).

Larger blocks are still possible thanks to "refer too" special tiles that point towards the top-left corner tile of the block. When loading an older map, the conversion is automatic, but when I created the "school teleporter" room for the 3-rooms-demo, I just added 1-tile spikes instead. With a small change to the Level Editor, you can now press A to reveal the redirecting arrows (default behaviour is to mirror the pink tile instead). That should make such errors easier to spot in the future, if I ever make them again.

So the script for the dumblador now looks like

$THROWN :anim3 {
  using gravity(24,1536)
  testpoint off (8,18)
  test 0 (0,0)-(8,12) ${F_BAD|G_FIGHT|F_WEAPON} //# weapon against other monsters.
  test 1 (0,15)-(16,16) ${F_BAD|F_PLATFORM};
  area 0 (0,0)-(16,12) ${F_ISINK} //# deleted-by-ink
  area 1 (0,0)-(16,8) ${G_FIGHT|F_HURT} // # stand-on-spike
}

$THROWN->$STUNSTAND on hit1 (0 :1 0 :0 Lc $vPlatform(2));
$STUNFALL->$STUNSTAND on hit1 (0 :1 0 :0 Lc $vPlatform(2));

and the script for the pencil spike looks like

 block 21 {
   is spike "0101030307070f0f"
   area (7,0)-(8,16) 10010010 # hurts
   on hit [0] (t)
 }

 Together, they allow to get a functional blador-align-on-spike (aligning is performed by the Lc opcode), but there's still one thing to fix if I don't want to see dumblador waking up and start walking on pencils: duplicate the $STUNSTAND state into something like $SPIKED, while removing the "get feet" area. But well, a remaining glitch makes some more fun for ScreenshotSaturday ;-)



 

Monday, August 17, 2026

Testing landing-on-slope

 

While reviewing the "tiled" chapter produced by my blogpress tools, I remembered that landing on a slope still produces odd effects from time to time. And then came an idea to reproduce these conditions in the unit-test environment.

Just as I was observing that the "dkp54" branch has been around from a long time and so has been the "cflags" branch and that "right, I promised myself I wouldn't merge things unless I've first checked they still pass automated tests."

And unfortunately, right then, the tests were not passing, they were crashing. First with an exception and then with a segmentation fault. The "offending" commit was something from 2025, a few lines that will report (rather than ignore) syntax errors within the GobState parsing.

It took some times to identify where the error was introduced, much less to fix it (hopefully) and so the 'dkp' branch (toolkit update) is now finally merged and the cflags can be just that: a branch propagating the new collision mechanism (and not a adventurous combination of features).

Saturday, August 15, 2026

Le fight club, version cflags

Last summer, I started reworking collisions in my game engine. The idea was to be able to define groups, so that e.g. collision-based communications between a stunned blador and its feet would never interfere with a bouncy branch and things it bounces away. Sure, there was way to do that already, by assigning them separate collision flags, but I've long ran out of these, forcing me to make weird groups and combinations.

Oui, parce que bon, ça fait maintenant un moment que j'ai ouvert la branche "new-cflags" dans laquelle on a la possibilité de définir des groupes de collision, et je m'en suis même servi pour gérer les portes, mais si je veux permettre à un appleman de rebondir contre les autres applemen tout en passant "à travers" les petits vers, je me retrouve devant un imbroglio avec la question épineuse "je le mets où, mon nouveau flag ? et est-ce que ça coince ?"

With recent work on the appleman, I wanted to make a distinction between "weapon-sensitive area, from a light object" and "weapon-sensitive area, from a heavy objects", but I wouldn't know how to encode it anymore. So far, the groups had only been used to implement the doors, but felt like it was time to define a new group, to deal with every collision that deals damage to entities in the game... which I finally decided to call the "fight" group.

Alors c'était l'occasion de se demander "et si en fait on faisait un groupe pour toutes ces actions qui retirent des points de vie à un des objets ?". Petit à petit, hein, en vérifiant qu'on ne casse rien (et donc, forcément avec une vidéo Screenshot Saturday où tout d'un coup, on passe à travers les branches au lieu de rebondir et à travers les pommes sans qu'elles ne se doutent de rien...)

Un nouveau G_FIGHT donc (j'ai un peu chipoté pour trouver le nom du groupe puis le Fight Club s'est rappelé à mon bon souvenir, tel une évidence inévitable). Peu à peu, les différents scripts-personnages sont convertis en nettoyant les vieilleries comme les scripts avec des valeurs numériques plutôt que des combinaisons de symboles. Puis arrive le moment crucial de réactiver la branche-qui-rebondit, et là, je me rends compte que garder dans G_FIGHT l'action principale F_STOMP, celle que Bilou utilise jouer à Super Mario assommant un goomba, ça ne va pas marcher. ça va imposer aux zones de collision destinées à être de simples plate-formes de faire partie du Fight Club alors qu'elles n'ont même pas de "points de vie". D'autant plus problématiques qu'on a aussi des flags du type F_PLATFORM pour, par exemple, empiler des taille-crayons sans qu'ils n'infligent de dommages.

It wasn't that straightforward ... I mostly broke everything first and then repaired things one after another. Last week, for instance, only the woodworm would still interact with Bilou. but now the code is cleaner and I think everything is repaired ... I may to a bit more time travelling to check the blador / tiled pencils interaction ... it looks like it isn't working as good as it did previously.

Then a few things just did not resume working, like jumping higher when you press the JUMP button while bouncing on a branch ... mostly because some hit areas needed to be duplicated and transition depending on them needed to be reassigned to the new area. Current GobScript doesn't make that easy to refactor. 

Ah, and yeah, below is a snippet of what the area collisions look like now.   

Heureusement, j'avais prévu de garder une portion des flags "neutres" (CFLAGS_LONE_FLAGS), valables qu'il y ait accord sur les groupes ou non. On pourra donc mettre la branche dans le groupe G_GROUND (qui n'existe pas encore) dont F_PLATFORM ferait partie et lui ajouter un "ah, oui, on prend F_STOMP aussi, même si ça ne fait pas vraiment partie du groupe". Certaines des zones de collisions ont dû être dédoublée (une avec G_FIGHT, l'autre sans), un petit défaut dans la réécriture quand ça arrive et on se retrouve avec des branches qui ne rebondissent plus aussi bien qu'avant ... un petit schéma, un peu de ddd et ça se remet en place. Le code pour tester deux zones de collisions ressemble donc maintenant à


 cflags GobArea::test(const GobArea *o, GameObject *g, cflags mask, GobCollision* gc) const {
     if (o==this) return 0;
     cflags group = mask & CFLAGS_GROUP_MASK;
-    if (group && (flags & CFLAGS_GROUP_MASK) != group) return 0;
+    if ((flags & CFLAGS_GROUP_MASK) != group) mask &= ~CFLAGS_IN_GROUP_FLAGS;
     if ((mask & flags & CFLAGS_EXPR_MASK)==0) return 0;
 
     // congratulations: you may compare coordinates, now.
     

edit: there was one last thing I had spotted that wouldn't work as expected: sticking dumbladors on pencil spikes. Especially, the dumblador might still wake up. So I dug the good old SchoolRush and checked what happened. Verdict: the dumblador *might* wake up there too, but it looks like it's much more unlikely to happen due to terrain geometry.

Tuesday, August 04, 2026

A bit of CI/CD ?

I'm not considering to add some for these projects at this point, despite I now have a few things on codeberg and codeberg sure comes with some CI/CD option, maybe they'll look like the one of gitlab which I've had to deal with today.

Most of gitlab's CI options are tweaked through variables within the .gitlab-ci.yml file of your repository. Most of those variables are free to configure yourself but some directly affect the “Getting source from Git repository” step, especially GIT_SUBMODULE_STRATEGY, GIT_STRATEGY (where you can tell whether you want a full clone or a mere fetch of the branch you're using in your job).

And unfortunately, it's one of those case where git turns out to be complicated, with many trivial things (do you have a "main" branch here?) requiring long commands (git show-ref --quiet main ... you thought that would have been one of the 8+ modes of "git branch" command ? too bad :P) that you may have to post-process. For instance, you can pick only a few commits at the top of some branch, you need not to crawl back to that initial commit.


  mkdir mere_window
  cd mere_window
  git init                   # a new place to toy with commits
  git fetch ~/myFavoriteRepo --depth 10
  git checkout FETCH_HEAD    # so we're somewhere on the history
  whereami 80

See ? 1) not all commits are there, although I asked 'whereami' (alias wh) to show me at least 20 of them, and 2) at the bottom of it, there's one tagged with (grafted). If you had branches merged lately, you might have more. Such grafts still have the same ID as the original commit, but they contain much more things if you show them: the whole content you're missing since repository started is condensed in such grafts. They'll delegate that extra weight to a deeper commit if you decide to fetch again with a higher depth, and turn back to their original size.

But back to gitlab. Its default setting seems to be to fetch with a depth of 20, performing what appears to be dubbed "a shallow fetch". You can spot that with "Fetching changes with git depth set to 20...". Mostly sufficient but if the CI/CD is introduced late in the life of the repository, and if you've been working on long branches for a while, it might not know about the main branch at all. That turned to be a problem for some packaging steps, but hopefully, we have GIT_DEPTH that can tweak things, and setting it to "0" is the way to tell gitlab to drop the --depth argument altogether. There at least, you should have some value of "main", even if it isn't the latest (just the latest known to your branch, I presume).

Gitlab may also play trick on you by keeping the things you already fetched when you start some old test again, just to find that the branch is now listed. You know it does when the job step will mention it "Reinitialized existing Git repository in your_path".

One more trap to avoid: git branch does not list *all* branches. Only those you created locally by checking something out. You'll need an extra -a to also show the remote branches (or -r to see only them).

Sunday, August 02, 2026

Un dernier ver ?

Si vous avez un peu essayé n'importe laquelle de mes démos "green zone" ces 20 dernières années, ça n'a pas pu vous échapper: le petit ver jaune - ce croisement entre un combattant dans Worms Armageddon et un poison slug de Commander Keen - est pénible.


Jusque là, je ne lui avais jamais fait d'animation "éliminé, le petit ver". On l'assomme, il attend, il repart. Il n'avait qu'un rôle minime dans Apple Assault... et le fait qu'on puisse le réassommer à volonté y permettait de reprendre des points. On le supportait.

I've had to apologize about that point to about any beta-tester who tried the Green Zone: the woodworm feel unfair and annoying. You could stomp it, but after a mere second, it would wake up and resume worming. Unlike the Appleman or the the Dumblador, there's no visual clue that the worm is about to wake up, so you're likely to take a hit from something that seemed defeated just a couple of seconds earlier.

Enfin, on le supportait mais de loin, parce que sa manie de repartir à l'attaque sans avertissement, c'est certainement ce qui vous a pompé le plus. Alors le week-end dernier, après avoir déposé les gamins en camp, j'ai allumé la DS et refait 3 petits dessins de ce ver tournoyant dans les airs pour qu'au moins, quand on lui roule dessus avec une pomme, on en soit quitte. ça ajoute de l'interaction et en théorie, ça devrait être rigolo à voir.

The core reason for this are a) the lack of a proper "defeated for real good" animation and b) the fact that worm.cmd was intended to be a tutorial for "the most simple ennemy you could think of". I could justify it for Apple Assault, where the fact it was never really defeated implied you'd always have a way to build up your punch meter. But for Dreamland, it no longer makes sense. Especially when you can throw an apple rolling over them! So I picked up my NDS last week-end and drew a few frames that make it look like it's spinning mad in the air. Yes, exactly as if you had used the bat of Worms Armageddon (or PWorms demo ;).

Il m'a fallu un peu de chipotage pour que le sens de rotation soit cohérent avec celui de la pomme, et j'en ai profité pour lui rajouté un état "détection" qui le remballe directement dans l'état "assommé" s'il sent Bilou à proximité. J'aimerais aussi rajouter une autre fioriture, un état "se fait rouler dessus" qui le maintiendrait au sol jusqu'à ce que la pomme soit passée, et ça, ça va sans doute demander un petit contrôleur supplémentaire...

I'm not quite done with it. Not yet. I feel like the worm starts spinning a bit too early, and I don't really want to fix that with a hard-coded delay but rather by saying "when the apple is done rolling, the worm get caught by the "leftover motion" and *then* starts spinning and falling. Maybe it could be settled with additional hitboxes, and maybe not. If not, I sketched a state machine fix that would help, with a "rolled_over" state, a way to attach the worm to the apple when entering that state and a controller that triggers an event when we're the attached item is far enough... or maybe the regular "track another gob" controller would do the trick if I make the "dead zone" (where it doesn't pull your controlled gob to the left or right) configurable in size (spoiler: it isn'tnow it is). That would give


$STUNNED->$ROLLEDOVER on hit0 [w0 0 >] (128 :0 A);
$STUNNED->$ROLLEDOVER on hit1 [w0 0 <] (128 ~ :0 A);
$ROLLEDOVER->$LKICKED on event0 [v0 0 <] (400 ~:1);
$ROLLEDOVER->$RKICKED on event0 [v0 0 >] (400 ~:1);
$ROLLEDOVER->$RECOVER on done (0 :1)
with
$ROLLEDOVER :anim7 {
    using tracking(@ over 12);
}

And then I'll have something else to fix regarding whether a monster that get hit by a flying-apple-weapon is strong enough to turn back the apple (funky funghi, appleman, caterpillar) or let it fly through (worm, berrybat).  

Un dernier détail qu'il faudra régler: de base, si on touchait le ver avec la pomme avant que la pomme ne se soit mise à rouler, la pomme rebondissait dans la direction opposée. Un reste de quand elle était un taille-crayon, j'imagine. Mais maintenant que j'ai modifié le script de la pomme pour qu'elle passe à travers le ver, elle peut aussi passer à travers les autres pommes ... pas terrible. Il faudrait bien que je me donne une variante de F_WEAPON qui indique "ennemi léger" et une autre "ennemi lourd", ou encoder ça dans encore-une-autre-variable-propre des gobs, ce qui serait sûrement plus facile, mais moins cohérent avec le type de programmation proposée ... un peu comme ajouter un "if" bash dans un Makefile, quoi :P

L'occasion de passer au nouveau système de flags de collisions ?

LEDS ... The forgotten branch

I completely forgot to tell you about that patch from early June that allowed to see clearly the space occupied by larger monsters in LEDS. And I forgot to pull it from "the cube" and to push it on sourceforge, too.

It did not work exactly how I hoped though, so there was still some manual tweaking to get those bouncy branches properly aligned with the tiled tree when I assembled the tree, and I even figured out why. But well, June was fairly crowded IRL.

I even missed to mention that when testing the new tree at my fairy's birthday, my brother almost immediately soft-locked Bilou by just trying to walk from a tiled branch to the sprite branch. It took me some time and a good deal of DDD to realise what was happening: the bouncy-block hidden within the branch would only progress in its state machine if something was falling on it but in this very case, Bilou isn't falling. So Bilou did the transition to the "soft-landing" state while the thing he's landing on ignored him altogether.

So I'm taking a moment this morning to try a patch I think was still required: seeing some random collision box will not help you craft your level. What you need to see is the bounding box of the object, the one that materialize its presence in the world.

...

edit: Ah. Yeah, of course. I'll have to push that new patch, else I'll start hunting for it again when I'll want it running on NDS for more map patching ^^"

Wednesday, July 22, 2026

Apple Assault Advance ?

 Ah, yeah. Early this month, while the heatwave had not burnt our energy and house wasn't yet a post-camp-departure-mess, I had that Silly Idea (tm) that I could enter the

I would have read https://gbadev.net/resources.html#articles, installed the GBA devkit in addition to the NDS devkitpro (reality: done, but where) and I'd have quickly (ahem) converted the Green Zone graphics into 16-colors-per-tile so that they could fit in the smaller video memory of the GBA. Some sprites (funky funghi, Bilou) would have been turned into 16-colors too while some others (appleman, mostly) would have stuck to 256 colors because they're taking more than 16 :P

Only then, I'd have tried to rebuild the Dreams engine for GBA and imported the Dreams animation over the Apple Assault levels, and that would have been the basis for Apple Assault Advance. Luckily enough, the sound engine promoted by devkitpro would have been happy about my brother's good old 6-track variant of String Tracking.

Likely, I'd have needed to find a way to compile the GobScript state machines into plain old data that could stay in ROM so that the smaller-sized (256K?) GBA RAM could have been saved for really-read/write features. Same for the maps, most likely ... They're not that big, so they could have been fit to RAM. Maybe they would have to so that collectible tiles would have worked. (not that there are many such things in Apple Assault).

But that was before blogpress started pressing me to develop it again. I think I'll focus on making first steps with bosses in NDS Dreams engine, instead :P 

Saturday, July 18, 2026

The July Demo

Ooops Ooops oops. It's about time I tell you about the releasable nds I uploaded last week ... I wanted something featuring the latest Funky Funghi and applemen improvements that I could share over the not-too-new itch.io page and tried the development log feature to write a blurp about it. But of course, July is always super-crowded to the point I feel like an impostor when just taking time for me. And carbon-induced heatwave didn't help.

I also managed to put it on sourceforge download before my own laptop's battery told me to stop and go to bed...

Swimming got improved, too, and we finally see all the branches of that important tree, although it could use more leaves at the top ... that shall happen later on.
  

Wednesday, July 15, 2026

It's blogpress time again

French journalists have a word for that seasonal topic that comes up again in newspapers when every high-end interviewers are on holiday and things are up to interns: "Le marronnier de l'été" ... and it may feel that doing some blog-to-printable/epub conversions pops up every summer here, but hopefully not for the same reason.

The motion started with the late-night idea that "tiled" engine discussions could be a chapter 1 if the blog ever had to be converted into a book, but also that editions could use their own tag, like "ch1" for that chapter 1. On the next day, I was dusting off repositories and working directories for the next "blogpress" round, motivated by the fact that I'll have up to early August before the next automated snapshot/takeout.

I wrote a small introduction to that chapter (french only, atm) and then grew surprised by how little preparation there was on that "tiled" tag. Reader would just be thrown stuff at their eyes, and they'd better understand things. Sure, I introduced it rather late, not as I was writing those posts... I discovered a bit sadly that most of the tutorial I had used by the time are now offline (and even their archive.org copy are firewalled T_T by "my" most powerful computer).

I edited some of the early posts in "tiled" following the idea that "Many posts contain parts that would be useful for a chapter (say, one about tiled game engines) but also parts that don't quite fit. So how about having <span> or <div> tags assigning those items a "no-ch1" (or no-ch5 when I'll be at chapter 5) and let the simple.css make them display: none ?"

Finally, 4 days ago, I managed to get all the issues fixed in my scripts and re-generate a good-looking page that I can use in calibre to export an epub file. And because each issue involved a good deal of html snippets and that I'd rather have a clipboard to compare them when that happens, I decided to use codeberg's issue tracking system for that. 



script output, as uploaded on codeberg

what it looks like on the blog

what it looks like copy-pasted in office

what I had from older (2025) script output
We're not fully done, but it's getting in good shape. I couldn't get it printable, though, as the browser seems to ignore the CSS instructions that make it neat and stylish on screen. 
Next points would be
  • recognized purple-colored-text as English
  • if a picture is large enough to cover most of the column width, don't try to make it floating.

A sad discovery while working at all this was that the 72x72 thumbs are no longer automatically provided by the takeout. They might have annoyed me when doing the last conversion back in 2025, but no more than I month later I had figured out that they could be a "magical portal" to posts and uploaded them to neocities.

And now I have another project for those thumbs. I had included the "milestone" tag together with "tiled" to get a bit of reminder of what had already happened and what not between two "tiled" posts, but many of the milestones are too large and distracting compared to what I want. The first paragraph (?), the date, title and the thumbnail would likely be sufficient. Hopefully, I can recontruct some thumbs URIs from regular picture URIs, but I'll need to resort on my drive-archived blogthumbs.zip for the missing ones...  

There's one line of all that that I'd like to bring here. It's the incarnation of "make the possible easy" part of Perl:

$post=~s/src=\"([^"]+)"/src="$larger{$pix{$1}}$escaped{$1}" alt="$1 ; $pix{$1}"/sg; 

In that single line, we iterate through (thanks, g modifier) all the image URLs of a single post and patch them using the hash tables that we constructed by parsing "temporary" files that other scripts produced. No need for split-patch-join sort of loop. 

edit: Some commands used to generate the latest version:

THUMBS=../thumbs/thumbz.html perl ../git/list-atoms.pl ../bilou/feed.atom <(cat ../pictures3.lst ../morepix.lst ../pictures2.lst) tiled '(milestone)' > tm3t.html
where "thumbz.html" is the page featuring a link & thumbnail for every post, which list-atom will look for contents. It needs so many .lst files because I worked on list-
grep '<!-- all images' -A9999 tm3t.html | grep ^i | fe - "cp % /d/local/pages/geds/attach_files/ -v"
I added a flat list of filenames with the pictures at the tail of the HTML file so you can "more easily" cherry-pick the files you need (save-as-html in web browser is doing too weird things)
BLOG=https://sylvainhb.blogspot.com perl ../git/thumbify.pl <(perl ../git/summarize-atoms.pl ../bilou/feed.atom) > thumbz.html
Above: how the thumbs page is generated, below, how I got some -0- thumbs from files that had more normal identifiers. (applied in the folder where I unziped blogthumbs.zip I got from that-time-where-thumbs-appeared-in-exported-data.
ls i* | sed -e 's/\(i[0-9][0-9]*\)-\([^0]\)-\(.*\)/mv \1-\2-\3 \1-0-\3/' | grep -v '^i' > renamethumbs.sh

edit+: fine-tuning keeps going on mastodon

Friday, June 26, 2026

4 nuances et 4 canaux ?

Bon, oui, le GB studio, c'est rigolo comme concept. Et ça fait pas mal de titres tout à fait sympa comme ce dinosaure de luxe. Mais y convertir du Bilou, je vois mal le truc ... même Badman aurait du mal. .. mais, petit délire de Mars ... j'ai déjà un jeu qui avait été pensé pour le monochrome dès le début: Caliméro!

On ne va pas se mentir, par contre, ça n'avait pas vraiment été pensé pour les 160 pixels de large du DMG. Le personnage ne ferait pas loin de la moitié de l'écran!

I've spent a good amount of hours on GameBoy, back in the 90's, playing Super Mario Lands, Kirby and of course, Link's Awakening. So when I hear fellow developers enjoying to work with environment like GameBoy Studio and reaching a significant audience, I can't help thinking "shouldn't I try gameboy dev as well ?"

But what game would I make there that wouldn't be over-ambitious for the platform ? That can't be Bilou and while there has been a pseudo-gameboy level in Badman III, it's unclear it would be working with the rest of the game. But ... how about "my" monochrome hero in his monochrome adventures because it was originally designed on a monochrome 8086 back in 1990 ?

Une fois remis à une une échelle un peu plus raisonnable, avec un perso en 24x24, ou quelque-chose comme ça, on commencerait à pouvoir jouer. Pas que le jeu deviendrait automatiquement un hit, hein, mais voilà.

So I looked up for a GB screen template, dug my old Calimero screenshots and started to tweak colors to see how it would look like... and spoiler alert: it looked huge. Calimero was designed for 720x348 screen resolution and then ported to VGA 640x480 keeping its original sprite data, of 54x50 pixels or so. that's nearly 1/3rd of the GB screen in each direction. I know Calimero "gameplay" has always been about die-and-retry and you-should-have-known, but it would be pushing the thing too far. Scaling the sprite to some 24x24 would allow something much more playable.

Oh, bin puisqu'on en parle, je pourrais aussi vous mettre le lien vers une série de scans de l'été dernier. La Pipe Zone, bien sûr, inspirée de SMB3 et qui sera un thème récurrent dans les niveaux de mon frère, la pyramide dont le design ne m'a jamais convaincu et la "water zone" qui a un petit côté Alex Kidd.

Même quelques boss ;)

And what better post could I find to put a link towards the set of scanned level designs I did last summer ? Scans, I mean. Level designs are from 1989-1991 and from my brother. Let me tease you with this "pipe world" (obviously influence by the pipe world of SMB3). I'll let you explore the link and find the pyramid levels as well as Alex-Kidd-inspired bosses.

Been reading this far ? well, that deserves a link to that other mastodon thread where I tried to convert some of Bilou screenshots into 4-shades-of-grey mockups, after somebody posted a music disk of Mickey Magical Quest for Game Boy in January '25...

Et on pourrait essayer de finir en beauté avec une petite image de Bilou en 4 niveaux de gris, avant et après retouche par votre serviteur de janvier 2025... qui ne tient absolument pas compte des contraintes du genre "3 couleurs + transparence par pavé de 8x8", ni du nombre max de sprites par lignes, ni du nombre max de tiles de la GB ni rien. Un pur délire de Gimp, quoi.