Tuesday, February 18, 2020

Don't send namespace do an inline job

Okay, granted, I'm not a namespace master. I know they exist, I know they are helpful, but I'm not using them a lot myself, and I'm never too sure how I should write things like "namespace cp = Clicker::process;". I'm not completely sure whether going for "using namespace ietf::http" within HttpServer class is smart or silly.

Anyway. I had code in a .cpp I wanted to extract into a .h, for it contained assert helpers that should now be used in more than one test program. Unfortunately, when I started using it in my new program, I got the linker complaining that some assert_bool() function was defined in multiple .o files. That was the only one that was no template, unlike assert_equals() and its children. I then foolishly wrapped the whole thing into a "namespace /* anonymous */{", so that things couldn't be reused in multiple areas.

But one instance of gcc got it wrong. You see, I'm not using that assert_bool() in all my .o, and that version of gcc, with its parano flags settings, thought it shouldn't keep going with dead code: assert_bool() wasn't used internally in that translation unit, and it couldn't be used from outside, as it was anonymous for the rest of the world.

What I should actually have done was to define the function "inline". Simply.

Monday, February 17, 2020

Thème Mobile ?

Tout ça parce que j'avais décidé de ne pas prendre de carnet-geek pour la patrouille de St-Valentin, mais que ma fée avait besoin que je l'attende (sans avoir besoin de son smartphone). Du coup j'ai découvert que '?m=1' passait sur le "thème mobile" de blogger. Un thème à l'ergonomie acceptable, mais perfectible (en ce qui concerne les tags, notamment).

Par contre, pour ce qui est du look ... euh ... y'aura un peu de travail, là. 

If you want to customize your Mobile template styles, you need to set the Mobile Template to Custom.

By then any additions to your Theme "Edit HTML CSS" section will be rendered.

Using Blogger built in functionality use the .mobile classname to target the styles for mobile views. Example


.mobile .post-title {
  font-size: 21px;
}


Will change the post title size to 21px on mobile device but does not effect the post title web view style.

Et moi, il faut que je m'adapte à un truc comme
<div class="mobile-date-outer date-outer"> <div class="date-header"> <span>Monday, February 17, 2020</span> </div> <div class="mobile-post-outer"> <a href="http://sylvainhb.blogspot.com/2020/02/theme-mobile.html?m=1"> <h3 class="mobile-index-title entry-title" itemprop="name"> Thème Mobile ? </h3> <div class="mobile-index-arrow">›</div> <div class="mobile-index-contents"> <div class="mobile-index-thumbnail"> <div class="Image"> <img src="https://1.bp.blogspot.com/-7w5i6XE-rQg/Xkp_FEWuuCI/AAAAAAAAI_E/b4RuPqzNa2U8w-glm-OsOIgAwT3b7XaowCLcBGAsYHQ/s72-c/Screenshot_2020-02-17%2BBlogger%2BBilou%2BHomeBrew%2527s%2BBlog%2B-%2BTheme.png"> </div> </div> <div class="post-body"> Tout ça parce que j'avais décidé de ne pas prendre de carnet-geek pour la patrouille de St-Valentin, mais que ma fée avait besoin que je... </div> </div> <div style="clear: both;"></div> </a> <div class="mobile-index-comment"> </div> </div> </div>
  • la partie "bannière" est bien trop haute. Il faudra changer la valeur height dans #header-wrapper (80px) et ajuster le padding du #header (top: 10px)
  • le titre du blog doit arrêter de se faire compresser sauvagement (padding droit de #header).
  • la petite "flèche" à droite de chaque entrée n'est plus à droite. Il faut lui rendre son thème.
  • la ligne d'aide à la navigation doit se faire une place plus normale. (disons avec position: absolute, top: 100px, right:20px)
  • il faut remettre une ligne entre les items
  • il faut que les thumbs soient flottants (float et padding sur #mobile-index-thumbnail)
  • il ajuster les pixels de bordure sur la gauche.
  • il faut réparer la largeur de la page (widthde #content-wrapper, #header-wrapper)
Bon, ce qui tient lieu d'éditeur de thème pour blogger est assez peu ergonomique, et la technique du  .mobile ne marche pas pour tout. Donc, pardon mais il est possible que le look du site soit tout cassé pendant quelques jours parce que je ne vais quand-même pas prendre congé pour ça :-P

    Sunday, February 16, 2020

    Plate-formes volantes ?

    Si je veux faire quelque-chose de plus "aérien" pour la dernière zone, il va me falloir des plate-formes volantes. Mais voilà, je n'ai pas envie de leur coller des petites hélices (qui aurait fabriqué ça) ou d'invoquer du gravitium (pas partout, en tout cas). Pas envie de resservir le cliché du "bloc de pierre collé à un autre bloc de pierre qui vole grâce à des petites ailes de Parakoopa" ...

    Pas convaincu non plus par des piafs qui auraient justement une forme de plate-forme. Il y a quelque-chose d'un peu ce genre-là dans DKC:TF, mais il s'agit de plus de bumpers volants -- rôle que les parakoopas avaient employé déjà bien avant. Ici, je veux bien parler de quelque-chose de suffisamment large pour qu'on puisse marcher dessus et de suffisamment coopératif pour qu'on puisse traverser un écran avant qu'il ne s'en aille.

    You know, I'm trying to make Bilou's Adventure a somewhat consistent world. I won't say "believable" or anything like that, because we play as a ball with hands/feet floating around him anyway, but I'd like to avoid introducing too many alien elements like gravity-defying blocks or cartoon pockets or even human characters. If something exists in the game's world, at least Bilou and Bouli must find it believable. And that makes designing a mountain level with floating platforms ... a bit more challenging. I'd like to avoid the generic, Zool-like "floating rectangle -- just don't ask", and its Mario-like "floating rectangle with not-so-little wings".

    Je m'oriente vers un croisement entre les zeppelins et les piafs. Les saucisses/zeppelins, c'est sympa. J'ai bien aimé l'idée dans Endymion, dans Titan et dans "L'incident Jésus". Dans Kirby, aussi ^_^. Avec un bec et des zyeux, on peut leur donner plus d'expressivité et les écarter d'une trop grande ressemblance avec Kirby, justement.

    ils sont suffisamment dépendant des caprices du vent pour ne pas vraiment pouvoir éviter que Bilou ne les emprunte, pas vraiment mis en danger par ses atterrissages non plus.

    I've postponed this post for a while as I was considering and then discarding ideas. I finally converged towards zeppelin-birdos. I loved zeppelin-like sentient creatures in Endymion, Titan and Frank Herbert science fiction books. They're close enough from a regular platform shape so that we can land and walk on them, not just bump on them. But in Bilou's world, it would be best if they had eyes and stuff that can suggest they feel something. And giving them little wings will make them more interesting to look at when you're not on them. But they're wings are too small to be able to do anything but follow the winds. And their body is soft enough to avoid taking damage from Bilou's lands.

    Sure, I'm not the first one to try using second-class creatures as platforms. One of the most prominent example I can think of are the owls in Tropical Freeze. You'll note that these are more floating bumpers than floating platform, though: you cannot move freely just like on a parakoopa. And it make sense: if you had intruders landing on your head, chances are you wouldn't just wait for them to leave. Even the Parabeetles don't just keep moving unchanged if you land on them. Maybe SML2 owls ?

    J'ai écarté juste avant ça des tapis volants (pourtant, des écharpes rouges volantes, ça aurait été un clin d'oeil sympa) et des plate-formes pendues à des poulies. Des cordes et des ponts suspendus, il y en aura sans aucun doute, mais je n'ai aucune raison pour justifier que Bilou ne puisse pas s'accrocher au "câble porteur", et donc c'est très différent d'une plate-forme volante.

    A final word about dismissed designed. No flying carpet (flying scarf could have been fun). No walkable cloud. Since I plan to have ropes between peaks here and there, I must admit I've been tempted to go for platforms-on-pulleys. But no matter how I approached it, I couldn't explain why Bilou wouldn't just grab the carrying rope in case of emergency. Knowing my kids, they'd feel it unfair if they went for the rope and just fell through what is just decoration element.

    Saturday, February 15, 2020

    Soldier Blade: can you be Mr. Perfect ?

    Quelque part entre 2000 et 2001, mon frère me ramène un émulateur PC-Engine et une quantité impressionnante de ROM pour aller avec. N'étant pas un fan de Bonks, je lève un sourcil circonspect. Une machine de 1989 ? T'es sérieux, là ? Mais deux semaines plus tard, j'arrive au bout de Tyrian, donc je jette quand-même un coup d'oeil à ce que son CD a dans le ventre ...

    Et je tombe sur Soldier Blade. J'ai toujours eu un faible pour les shoot-em-up (un reste de ma phase 'quand je serai grand, je serai astronaute' d'avoir lu les Yoko Tsuno, j'imagine), mais à part Warhawk sur C64, je dois bien reconnaître que j'ai finalement assez peu joué aux titres de shoot, largement dominés à l'époque par le style 'R-Type' où le vaisseau explose au moindre pet de moustique.

    Tyrian était plus dans la veine de Warhawk, avec une barre de bouclier qui compense la majeure partie des tirs encaissés. Mourir, dans l'un comme dans l'autre, c'était recommencer le niveau du début, parfois en aillant perdu une partie de ses power-ups dont je dépends tant. D'où mon attachement à Tyrian où au moins on fait l'acquisition de certaines armes de manière définitive.

    Soldier Blade avait un comportement différent et assez unique pour moi: on peut stocker des power-ups, les consommer comme super-bombes (généralement contre les boss), les combiner pour monter en puissance (si je ramasse 3 power-ups "laser" à la suite, sans ramasser de 'plasma' ou de tirs rouges, je passe au laser-niveau-3 en trident. Cerise sur le gateau, si j'ai le laser-3 et que je ramasse un 'plasma', je passe directement à plasma-3 (note bien ça, Space Invaders Extreme).

    Si je me fais latter, mon vaisseau explose, éparpillant mes power-up autour de moi. Un autre vaisseau arrive alors assez rapidement à la rescousse pour reprendre le combat. Suffisamment rapidement pour que je puisse récupérer au moins 2/3 des power-ups perdus. La pénalité est beaucoup moins rude que dans un R-Type où on redémarrerait avec un vaisseau "tout nu".

    Avec une bande-son tout en synthé qui donne la super-pèche, des graphismes certes aux coloris limités mais réalisés avec un brio, le jeu a beau tourner sur un processeur 8-bit, je suis scotché. Mais voilà: le jeu ne propose aucune sauvegardes et cet émulateur-ci, contrairement à z-snes et nesticle (que j'ai beaucoup utilisés sur mon AMD K6-II à l'époque) n'offre pas de save-states. Du coup, le seul moyen d'aller plus loin dans le jeu en cas de game over, c'est de recommencer et de s'améliorer. Recommencer de la mission 1.

    Bref, après quelques semaines d'entrainement intensif (l'avantage d'avoir réussi en 1ere session à l'unif, c'est qu'on a deux mois de vacances ;-) arriver à la mission 6 était devenu une sorte de formalité. Un peu comme faire ses gammes. Y arriver avec suffisamment de vies pour pouvoir atteindre ce que je croyais être le boss final, c'était une autre histoire: on parle quand-même d'un niveau 2 fois plus long que les autres, avec des espèces de boss rush. Battre le boss final, je n'y suis jamais parvenu. Il aurait presque fallu faire un sans-faute jusqu'au boss pour pouvoir tenter le coup.

    Si le jeu n'est pas à proprement parler un 'bullet hell', il y a quand-même énormément de projectiles à éviter, chacun susceptible de détruire notre vaisseau. Si bien que j'ai fini par prendre conscience que je jouais mieux quand je n'essayais plus de regarder en détail les différents objets du jeu. Je fixais mes yeux au centre de l'écran (non plus sur mon vaisseau) et je concentrais mon attention sur ma vision périphérique. Je pouvais alors bien mieux détecter les différent mouvements et guider mon vaisseau vers les zones dégagées.

    Je ne suis pas devenu "Mr. Perfect". Je n'ai pas fini le jeu. Peut-être si j'avais pu me rendre compte que le vaisseau-compagnon peut faire office de bouclier contre à peu près tous les projectiles ?

    Wednesday, February 12, 2020

    Thèmes Elementaux

    Faute d'un meilleur terme. Mais je suis retombé sur une cogitation de fin 2019 quand est apparu l'idée de faire un 'Bilou's Dream Land', mais avant que la décision ne tombe de prendre la montagne (Peaks Zone) comme 4eme et dernier niveau.

    You know those many games where the levels are designed around "elemental" themes. Like fire - ice - forest - water - air - metal. It's almost everywhere. And it's not the most novel idea you can think of. But it brings in something interesting, as all the challenges / monsters can be designed around a unifying concept. And generic ideas can be declined according to levels, like fire-bats in the fire zone, freezing-bats in the ice zone, and so on.

    I'd like to have diversity in 'Bilou Dream Lands', but not that kind of diversity. What I'd like to have is gameplay diversity. I've ruled out having keen-like keys and doors in the School Zone because it would make little sense, but I'd like to have them in a Bilou game anyway. At least in the pyramids. For the lack of a better term, I'll think of those as "elefundamental gameplay themes".


    pour l'inspiration. Hoshi wo ou kodomo via Tohad
    Quel environnement utiliser pour la 4eme zone de ce nouveau jeu? La logique voudrait que je réponde "le chateau" pour m'intégrer au mieux dans le design existant (monstre, tilesets, etc.) Mais ça nous donnerait avec la School Zone et la Pyramide un 3eme environnement "artificiel" qui risquerait d'être redondant.

    Je suis très tenté de répondre "le temple englouti", mais je préfère le garder pour un jeu plus complet du point de vue scénario. Il faudrait que je trouve 4 "thèmes éléfondamentaux" au niveau du gameplay. La pyramide apporte le thème "labyrinthique". l'école quelque-chose de plus "militaire" avec des ennemis organisés et sous les ordres de Square Root.

    La logique voudrait aussi que le 4eme monde soit le plus exigeant au niveau du "platforming". Potentiellement plus vertical et/ou avec plus de trous/lave/... (partant du principe que je ne recyclerai pas le niveau vertical de fin de School Rush pour ce jeu).

    So, what are the 'gameplay themes' for the already-designed zones ? and what should the 4th zone offer to complete the experience in an interesting way ? Pyramid level at least is clear to me: it brings in the 'maze-like' theme. The School zone brings in the 'invasion army' theme with pendats following SquareRoot orders.

    There wasn't much 'theme' for the green zone, but it could turn to cooperation with NPCs like the rabit and the frog. Now, what should be in the 4th zone. What is missing ? Possibly some tricky-platforming elements, with moving platforms and so. Think about mushroom levels in NSMB, if you want to picture what I'm talking about. The extra bonus with 'platforming' as the fundamental theme for level 4 is that it stems for open-space areas rather than enclosed areas and will better balance the game than, say, a "castle zone" or "lost temple" would do.

    Je n'ai pas de meilleur nom pour ça, mais si vous voyez les niveaux-champignons de SMB et NSMB, vous avez une idée de ce que la 'peaks zone' pourrait apporter comme thème élémental. Bon, bin j'ai plus qu'à trouver quelque-chose qui convienne pour les plate-formes volantes, du coup...

    Pourquoi ce trip "thème élémental" ? Disons qu'il est classique dans les jeux de baser les différents mondes autour d'un thème basé sur un élément (eau, feu, roche, air ...). Chacun de ces thèmes inspire à son tour des idées d'obstacles, de monstres, de boss... Mais si on prenait le contre-pied. Si on cherchait à avoir des thèmes de gameplay pour définir chaque "monde".

    Nouveau Gimp

    Je n'ai malheureusement pas réussi à ce que Gimp conserve ses anciens paramètres lors de l'installation de mon nouveau PC de bureau. J'ai même été franchement contrarié de les voir passer à une interface "tout intégrée en une seule fenêtre" comme le "nouveau" lecteur PDF d'adobe (eh non, plus moyen de continuer à utiliser Acrobat XI un peu plus longtemps). Mais au moins, chez gimp, on a une cochette [x] Single Window Mode pour si on veut utiliser une image de référence sur un écran pendant qu'on bidouille ses pixels-à-soi sur l'écran d'à côté.

    Restons confiant, donc. Après tout, j'ai aussi pu remettre mon 'F12 = snapshot" en cherchant 'screen' dans Edit > Keyboard Shortcuts. (enfin, il fallait quand-même d'abord activer [x] Use dynamic keyboard shortcuts dans Edit > Preferences).

    Je n'étais pas franchement convaincu par l'interface revisitée pour changer la taille du pinceau/crayon. 'faut dire que je suis plus souvent entre 1 et 4 pixels qu'entre 20 et 800, personnellement. La 'nouvelle' interface sépare chaque barre en deux zones, une qui permet un règlage absolu (sur le dessus de la barre. Le curseur souris devient une flèche verticale) ou relatif (sur le dessous de la barre. Le curseur ressemble à "changer la taille des colonne"). Mais ni l'un ni l'autre n'offre la vitesse de réaction que j'attends de mon outil.

    Je sais que j'ai déjà changé ça: j'avais remappé CTRL+SHIFT+roulette, qui par défaut change la 'forme' du pinceau (ovale vertical -> cercle -> ovale horizontal). J'ai juste oublié comment j'avais fait.

    5-10 minutes d'exploration des préférences. Toujours rien. Je cale. Je finis par refaire une recherche et tomber sur la vidéo-tuto où j'avais probablement trouvé le truc la première fois. Je n'étais pas loin: il faut bien aller dans 'input controllers' (logique), mais ce n'est pas un clic droit qui me proposera de configurer les choses. Un bon vieux double-clic-qu'on-avait-oublié-depuis-l'ipad.

    J'ai pris tool-increase-size-relative, mais je ne suis pas 100% convaincu. J'aurais aimé pouvoir passer de 1x1-2x2-3x3-4x4-5x5 à 6x6, au lieu de quoi j'ai le choix entre 1x1-2x2-4x4-8x8-... et 1x1-3x3-3x3-3x3-5x5-5x5-5x5-7x7-...
    Bah. ça fera l'affaire au bureau, j'imagine.

    Tuesday, February 11, 2020

    Au tour de meka-blador ...

    Même si, j'avoue, mon analyse de "et qu'est-ce qu'on fait maintenant" m'avait conduit à définir MetaEdit dans l'éditeur de niveau comme la priorité suivante ... Mais la tentation a été trop grande: comment se comporterait un vrai personnage dans les tests automatiques de pentes que je viens de faire fonctionner avec un 'tic-tac-Bilou' complètement abstrait.

    Je n'en suis pas au stade du Test.Read("blador.cmd"), mais j'ai au moins pu charger un vrai .spr et en extraire les animations (Merci, MedsAnim) puis constuire un nouveau cas de test basé sur un bout de texte extrait directement de blador.cmd.

    Pour les bonnes nouvelles on s'arrête bientôt. Le meka-blador obtenu comme ça ne passe pas les tests. C'est qu'il est basé sur la vieille technique des testpoints qui sondent l'environnement autour de lui, et l'arrêtent avant qu'il n'approche trop du bord d'une plate-forme (là où le comportement de Bilou le laisserait avancer au-delà du bord). Mais blador ne saute pas.

    Bref, mes collègues m'ont lancé dans la lecture de "Working Effectively with Legacy Code" où il est notamment question de "comment va-t-on s'y prendre pour que notre code écrit avant l'introduction des tests unitaires puisse subir des tests unitaires sans endommager sa fonctionalité". Je fais donc des p'tits exercices pratiques avec un iStateInspector que l'on peut définir au niveau de la classe GameObject et qui sera passé aux appels à GobState(::do_checks(), notamment) à la place de l'ancien flag "verbose_prints". Les vieux printf avec des coordonnées et des CON_GOTO peuvent donc quitter le code de GameObject.o: ils seront remplacé par des appels au StateInspector (s'il existe) qui peut être défini au niveau de MapTests.

    Monday, February 10, 2020

    GameEngine design: script-to-code

    Somewhere in 2009, I added support for actions triggered by the script-part of my game engine but implemented in C++. This is the cornerstone of "GEDS" game engine. This is how run-time monsters spawning and sound effects work. This is also how level load request are processed.

    The design survived the years, so let's see how responsibilities are split.

    • Anything that will start invoking code upon game event (collision, new input, animation completion, etc.) is captured in a *Gun instance. The instance carries all the parameter needed for firing a specific kind of code. E.g. An instance of the LevelGun capture the specific level script to load. An instance of GobGun captures which type of monster to spawn, etc.
    • Configuring the *Gun instance happens at script parsing time. In other terms, most of the *Guns could actually be constant objects.
    • The c++ code instanciating an *Gun must be located from the name of the action on the script (with a using [action-name] ([arguments]) as [holster-slot-number] statement). That requires every type of "gun" to have its associated "factory" class, so that an instance of each "factory" can be registered into an std::map to be the link between actionname and the *Gun constructor.
    • Amount of support code for a new *Gun or a new *GunFactory is minimal. Each class have only one (virtual) method, either create() or shoot().
    • *Gun instance typically forget about what they 'shot' as soon as shooting is done. Often, the created object is registered as an animated item at the game engine, which will take care of running its code periodically. This may lead to the need for an additional "progress" object when there isn't any yet, like with the "TrackSequence" following the instructions (e.g. shooting more guns) as a sound track unrolls in the music player.
    • when processing script expressions, a palette of "guns" (the multi-slot holster, if you want) is received in addition to the set of variables accessible to the script. However, the transitions between states are the one capturing those palettes.
    • The GameScript is responsible for recording every *Gun instance created, so that they can all be reclaimed when the level is destroyed and we switch to something new.
    • There is one guns palette per "state machine file", with the constraint that all the states used by a single "character" in the game have to be described within the same file, this means that a "character" can only use up to 16 different sounds+special effects.
    • The GameScript has the ownership of all these palettes. Reusing guns from parent's palette is explicitly requested. The GameScript knows when to stop using a given palette for new transition and switch to a new/old one because it has seen input/end statements. There is room for improvement here.
    Lots of guns. But anyone in GEDS can at most carry 16 of them at a time.
    Clearly, the major limit comes from fixed-size palettes of gun, which itself comes from the byte-code nature of the (parsed) script expression where only 4 bits are dedicated to the storage of which-gun-to-use. This is perfectly ok for a Mario-like game, where the distinct number of actions per character remains low. I would definitely need to extend this (and other parts of the game engine like sprite memory management) if I was to create a run-and-gun game where you can pick up lots of different weapons, each requiring a different kind of amno sprite and sound effect.

    Sunday, February 09, 2020

    Thank you, Fabien

    It's a true pleasure to keep reading the reports of Fabien Sanglard about the "polygons of Another World", one system after another. He went for people who did the ports, like a great investigator from Pix'n'Love would and mix those live coding moments with technical details about the various 16-bit architectures that make it quite exciting and pleasant to discover.

    Of course, there's already a significant amount of things I have learnt about the bowels of Another World: because Eric Chahi himself documented them on his website. Like the language used for game logic, the development of the polygon modelisation tool and so on. That makes it (imho) perfectly for Fabien to focus on getting high-performance drawing primitives. But if you really want to, he covered the virtual machine source code back in 2011. 

    For instance the episode about the homebrew GBA port finally allowed me to understand why the heck there were thumb-vs-ARM modes and IWRAM-vs-EWRAM and things alike in gba coding. With a simple diagram of how chips and busses were connected. That could definitely help if I was to port some Bilou games to the GBA later on (likely I'll take closer attention to make that possible with "Bilou Dream Land"). But I enjoyed the description of how the Amiga used its blitter to fill polygons quickly, too. and how the SNES version could have been running at 60 fps if the manager had said yes to a SuperFX chip on the cartridge.


    Thursday, February 06, 2020

    Attempting to reference a deleted function

    Now, all of sudden, the C++ compiler decides to make copies of my exception class before throwing it. Well, apparently, it is allowed to do so. But for some other reason, that very same C++ compiler (wasn't gcc this time) decided that my classes shouldn't receive a default copy constructor, and complains that "my" code is "attemtpy to reference a deleted function" when throwing the exception.

    Oh well.

    Most of the code isn't new, and it has compiled with this very compiler with these very flags just an hour ago. But while working on it, I suddenly changed at least one thing: I made it so that all the arguments of the constructor for the base class (the one complaining) are now constants. I suspect that Mr-Smart-C++ decided that my code would be happy to have one CustomException in .rodata with the corresponding fields compile-time-ready and that it would then copy that into the area allocated for ExtendedException (child class) instead of "constructing" a new one from scratch.

    Now, why wouldn't it create a default copy constructor ? Well, I introduced something new in the class, that is a std::stringstream. And it turns out this one has a 'deleted' copy constructor... this apparently propagates and makes the containing class considering it should flag the copy constructor as deleted as well.

    Just when you thought you had mastered the language. Welcome to Wonderland ...

    Sunday, February 02, 2020

    Bilou Dream Land (codename)

    Bon, j'ai plus ou moins déjà présenté l'idée, mais comme c'est un peu perdu au milieu d'autre chose je remets le couvert. J'ai décidé de mon prochain projet de jeu. Il s'agit toujours d'un jeu de plate-forme, et toujours aussi dans l'univers de Bilou. Mais ce ne sera pas "infinite Pyramid". Pas encore.

    My 'todo map' for 2019 was trying to review all the things that could possibly use a bit of work, but had no clear direction of where to go.  For 2020, I finally have a direction to follow: a new game code-named "Bilou Dream Land" so far.
    • Ce sera une version "courte" de Bilou's Adventure avec un début et une fin. Plus de gameplay en boucle cette fois-ci.
    • On y parcourra la Green Zone, la School Zone, la Desert Zone et probablement la Peaks Zone.
    • Les zones seront plus courtes que prévues pour Bilou's Adventure: plutôt la taille d'un niveau de Kirby's DreamLand (d'où le nom de code) que celle d'un monde de SMB. (ça fait quand même l'équivalent 2 à 3 niveaux de la version BASIC de Bilou's Adventure).
    • Dans la mesure du possible, je reprendrai les level design d'origine, avec des adaptations pour corriger les 'injustices', mais je m'autorise à avoir des portes-magiques sans devoir les justifier.
    • J'essaierai d'y introduire des boss.
    • Pas de pouvoirs 'définitifs' à la Rayman, mais des power-ups comme ceux qu'on a pu voir dans School Rush, probablement avec un nouveau-venu qui fait grappin
    • Les PNJs de la BD pourraient bien servir de "pouvoirs spéciaux" liés à une zone. Napin qui creuse, Froggy qui nage... vous voyez l'idée.
    The idea is to pick four environments from Bilou's Adventure and use them to create a platformer that has the length, the rhythm and the depth of the seminal Kirby's Dreamland. I'd reuse existing level design as much as possible, like the 'anniversary School Zone' map and the sketched maps for Green Zone.

    If we think of Bilou's Adventure as a Cave Story-like adventure with multiple endings based on player's in-game choices and actions, then Bilou Dream Land would be the set of levels you'd play if you'd go for an any-%, going for the quickest-to-reach ending where Bilou and Bouli barely managed to escape the planet, not trying to investigate anything about why they crashed and whether that world needs to be saved.

    There'd be some fixing on the old maps, possibly some keys or switches by NPCs, but that shouldn't be as ambitious as The Big Green Zone Relooking I've been talking about 3 years ago. The aim would rather to make the game more fun to play, and clearly not to get rid of any magic door.

    Saturday, February 01, 2020

    DoSlopes

    Ok. Ajouter des pentes dans un jeu de plate-forme, ça devrait ne pas être compliqué, mais dès qu'on modélise son niveau à l'aide de dalles pré-définies (toujours les bons vieux tiles), ça cesse d'être simple. J'en ai déjà beaucoup parlé (cf. le tag) mais sans jamais vraiment (reality check: si) présenter la solution que j'utilise depuis Apple Assault: la fonction doslopes.

    L'algorithme derrière doslopes travaille uniquement avec le 'hotspot' des sprites, et veille à garder ce point en contact avec le sol tout en appliquant un déplacement horizontal donné (qui peut s'étendre sur plusieurs tiles). Il utilise aussi une fonction annexe -- groundheight(x,y) -- qui donne la hauteur du sol pour un tile du niveau à partir de la base de ce tile. On y reviendra.

    L'algorithme est constitué d'une boucle optionnelle (en jaune) pour atteindre le tile de destination puis une phase d'affinage (en vert) qui calcule la position du sol pour la position horizontale voulue.
    En sortie, on obtiendra un  déplacement vertical correspondant au déplacement horizontal donné. A chaque déplacement intermédiaire, doslopes s'assure que l'on est pas en train de s'envoyer dans un mur à l'aide de la fonction cando -- la base de la gestion des interactions sprites/niveau.


    Le principe de base, c'est de regarder la hauteur du dernier pixel d'un tile pentu juste avant de le quitter. Il peut avoir une 'hauteur = -8' ce qui fait positionne le joueur juste au-dessus du tile (comme on le fait normalement pour du sol) et permet du coup de regarder le bon tile pour la suite de la pente au tour suivant.

    Friday, January 31, 2020

    looser throw specifier

    J'utilisais assez volontiers les descriptions de "quelles exceptions peuvent être produites par cette fonction" en Java. Nettement moins en C++. J'ai en tête que le compilateur n'était pas si fiable que ça sur ce point et que le jeu n'en vaut donc pas la chandelle. Au point qu'avec les derniers GCC, j'ai peut-être bien dû supprimer des throw(iScriptException) pour que le code de libgeds continue de fonctionner.

    Mais il y a au moins un cas pour lequel ça semble fonctionner et qui mérite qu'on y réfléchisse: les destructeurs (et plus particulièrement les destructeurs d'exception).

    
    class InvalidArgs : public LibraryException {
    public:
        InvalidArgs(const std::string &detail) : 
            LibraryException(INVALID_ARG_SUBCODE, INVALID_ARG_NONSENSE, detail)
        {}
        virtual ~InvalidArgs() throw() {}
       // LibraryException has defined its own destructor unable to throw any exception.
       // we can only make this stricter when sub-classing it.
    }; 
    
    
    Update: Il y a une FAQ sur la norme ISO C++, qui traite justement de la question des exceptions dans les constructeurs et les destructeurs. On y explique que tout les mécanismes de la bibliothèque standard partent du principe qu'un destructeur ne peut pas lancer d'exception. Entre autres parce qu'en réaction à une exception, votre libc++ va identifier les destructeurs de tous les objets alloués sur la pile jusqu'au code du catch et les invoquer, mais libc++ ne sait généralement pas faire remonter deux exceptions à la fois.

    Donc il ne faut pas laisser une exception sortir d'un destructeur, et ajouter un throw() à la déclaration de son destructeur est un bon moyen que le compilateur nous y force. En particulier si on définit ça au sommet d'une hiérarchie de classes, auquel cas le compilo va aussi nous avertir si on essaye d'assouplir le contrat pour faire passer plus d'exception hors d'une méthode-fille que ne le permettait la méthode-parent.

    (merci à hg grep pour avoir retrouvé les anciennes lignes où j'avais des throw(xxx). C'est un bon complément mercurial à hg annotate quand on cherche quelque-chose qui a disparu plutôt que le moment où quelque-chose a été introduit.)

    Thursday, January 30, 2020

    Yodlei

    eh, on pourrait facilement faire en sorte que les rocher ressemblent à un empilement de becs et de serres ^_^ (bon pas forcément tous les rochers)
    C'est le deuxième jeu présenté pour cette édition de l'Ultime Décathlon: Mickey Magical Quest premier du nom. Un jeu pour lequel j'ai un coup de coeur depuis qu'il est sorti, même si j'avoue avoir gardé un souvenir mitigé de ma partie sur Game Boy Advance il y a quelques années.

    Mais oui, si je n'envisage pas un Bilou's Adventure sans un grappin, c'est entre-autres à cause de ce jeu et de son niveau montagnard. Et comme justement je suis occupé à cogiter à un environnement de la montagne pour le projet "Bilou's Dreamland", je me refais deux ou trois fois le niveau dans Youtube, histoire de regarder un peu si on peu trouver des idées ré-exploitables. (Sinon, on ira piocher dans Celeste et dans le Temple Suspendu Dans les Airs des Voyages d'Endymion ;)

    Mickey Magical Quest comes back: it will be featured at the Ultimate Decathlon, a French speedrunning event I'm following with my kids. MMQ is one of my significant games, although I have fantasied playing it much more than I've actually played it, and its mountain level is one of the core reasons I'd like to have a hook-thing in Bilou's adventure too (other reasons being Fury of the Furries and WORMS's ninja rope).

    Since I'm also considering using 'mountain peaks' as the final level for "Bilou's Dreamland", I've checking the speedruns and longplays for re-usable design elements, but there was actually little to pick apart for "mountains are birds' realm. We'll need some bird-like creatures, not just palette-swapped berrybats".


    Joli aussi, les pics qui dépassent de la mer de nuages.
    • "Pour la montagne, penser à avoir des ponts (genre Sonic Bridge Zone sur SMS), des cavernes avec des structures métalliques (morceaux du vaisseau échoué) dedans. Si les structures métalliques sont mauves, c'est très bien aussi."
    • "Les ponts sont là à cause d'autochtones qui essaient d'utiliser le vaisseau échoué comme source de matières premières, évidemment."
    • "On pourrait imaginer une évolution des berrybats qui se sont adaptées à la montagne. Et si les 'chavrous' peuvent utiliser leur cornes pour se déplacer rapidement le long des cordes / ponts, c'est tout bon.
    • 'faudra trouver quelque-chose qui fasse piaf. Les gros piafs, c'est parfaitement à sa place dans la montagne, et ça remplit bien l'espace de jeu dégagé." Si on ne veut pas que les piafs fassent trop "bah, c'est des oiseaux", on peut leur donner un look de ptérodactyle en costume de Batman.
    • Et ce serait marrant que les 'tentacules' de la pyramide soient en fait les marmotes de la montagne qui ont perdu leur fourrure en s'adaptant au désert.
    Mouais. Pas grand-chose en fait. Entre les 'petits-oeufs-qui-vous-attaquent' et les guèpes, l'ennemi le plus original au final, c'est un courant d'air :-/

      There's no places.sqlite home...

      Evidemment, qui dit nouvelle machine dit "ach bin non, on va fous faire une noufelle profil pour fotre firefox!" (prononcez 'fawerfogz'). J'avais repompé le répertoire 'profiles' pour le mettre sur un linux bidon histoire d'aller à la pêche aux mots de passe sauvegardés, mais on va pas pousser si loin pour retrouver les fichiers dans l'historique des téléchargements histoire de donner des infos pertinentes pour 388156*.mpeg, si ?

      Eh bien, ça se passe dans places.sqlite. Avec deux ou trois autres choses, d'ailleurs.

      Tuesday, January 28, 2020

      Meka-Bilou au rapport ...

      J.L.N s'est beaucoup intéressé à ce que je faisais, ce.tte.s dernière.s semaine.s sur mon ordinateur. Il y avait régulièrement une espèce de mini-niveau (en ASCII art) dans un coin de mon écran qui accompagne les rapports d'erreur du système de test des terrains pentus. Je suppose que ça attirait son attention.

      Difficile d'expliquer le principe du unit-testing à un louloup de 7 ans, mais il y avait aussi mon carnet avec le même "niveau" et un bilou-mécanique prêt à le traverser. "Oh oui, c'est le tic-tac-Bilou de parrain! Tu mettras aussi celui avec un réacteur?"

      Il ne croyait pas si bien dire. Après avoir reporté des paquets de coordonnées sur un schéma du 'niveau' et analysé dans ddd à quel étapes de la fonction "doslopes()" elles correspondaient, j'ai déjà pu mettre en évidence (et corriger) quelques couacs. Le fait par exemple que doslopes n'indiquaient "on est sur une pente" que si le dernier tile visité est pentu. Malheureusement, ça signifiait un glitch vertical au moment de passer sur le 'sol plat' à la fin d'une pente à plus grande vitesse parce que le code du GobController qui fait appel à doslopes pensait devoir "éviter un sursaut brusque"...

      Mais le plus curieux m'attendait encore. Lancé à presque 8 pixels / frame voilà Meka-Bilou arrivé en haut de la pente, et par un malencontreux concours de circonstance (plus un p'tit bug), il ne détecte pas qu'il devrait redescendre. Le code du GobController, lui, constate bien qu'il n'y a plus moyen de se déplacer normalement sur le sol et génère un FAIL (qui doit normalement provoquer un changement d'état dans le comportement du personnage). Sauf que, désolé, les amis: meka-Bilou n'a pas de machine d'état, et le contrôleur n'a pas annulé le mouvement (il était possible: on ne rentrait pas dans un mur). Du coup, rien n'empêche le même mouvement d'avoir lieu au(x) cycle(s) suivant(s). Faisant fi de ses "roulettes", Meka-Bilou s'est mis à traverser le ciel à toute allure jusqu'à ce qu'il soit stoppé net par un 't'as rien à faire là' qui met fin au test.

      Un peu de code en plus, et je peux à présent capturer les transitions 'on fail' dans mes cas de tests et tout arrêter quand ça se produit. Et ça me fait penser que, quand j'aurai réussi à importer le comportement des applemen et autre dumbladors dans mon environnement de tests, ça pourrait être intéressant de marquer certaines portions de tests plus sophistiqués comme "interdiction de tomber ici", "interdiction d'atterrir là" ...

      Monday, January 20, 2020

      test de pente en cours

      Voilà, j'ai mis en route quelques modifications pour appliquer les idées du nouveau système de description du niveau avec comme point de départ un p'tit morceau de code qui crèe un 'niveau de référence' et place un personnage simple dessus qui devra avancer d'un bout à l'autre sans ce faire stopper.

      Tout ça se passe dans l'environnement 'unit-testing' plutôt que sur la console elle-même et donc est testé contre les fuites de mémoire. Mais au moment d'essayer de comprendre pourquoi il y a une fuite, je me rends compte que scan-mem.pl (qui traduit les adresses stockées dans un fichier trace%d.log en nom de fichier/fonction/numéro de ligne) ne marche plus.

      La faute au "nouveau" système de brouillage d'espace mémoire qui ne charge plus les fichiers .elf des programmes Linux à des adresses fixes (définies dans le programme) mais qui va utiliser la faculté des fichiers ELF à se reloger en mémoire pour que chaque exécution du programme utilise des adresses légèrement différentes. L'idée est de faire en sorte qu'un hacker qui parviendrait à provoquer un crash dans votre machine avec des données à lui (au hasard, une police .ttf véreuse ;) ne puisse pas pour autant appeler la fonction popen() ou system() avec des données à lui ... parce qu'il faudrait d'abord qu'il devine où elle se trouve dans ce processus-ci.

      Ça ne fait pas mes affaires, mais c'est assez simple à régler: une 'simple' ligne supplémentaire dans le fichier log pour indiquer l'emplacement actuel d'une fonction précise et un appel à nm fichier_elf | grep nom_de_fonction dans le script de décodage, et je peux recalculer le décalage utilisé, le soustraire aux valeurs trouvées dans le fichier log et conquérir le monde (mouah hah hah ...)

      Euh. Enfin. On va déjà essayer de conquérir le "leakage: 0%", ce sera pas si mal.

      Où était le memory leak, alors?

      Le bloc incriminé, c'est une palette d'actions spéciales qui est allouée dans le parseur de scripts. Je n'utilise encore rien de ce genre pour mes petits tests, mais ils sont alloués automatiquement quand on crée le parseur. Normalement, ils devraient ensuite être délégués à GameScript quand le parseur est détruit (et qu'on est prêt à jouer au niveau) .. sauf que dans ce cas-ci c'est le destructeur de GameScript qui détruit le ScriptParser, mais il le fait après avoir regardé aux palettes d'actions ... et donc cette délégation tombe trop tard. Facile à corriger.

      Sunday, January 12, 2020

      un p'tit pas de plus pour LEDS

       Bien. J'ai donc un éditeur de niveau capable de faire l'affichage des niveaux même quand j'utilise le "nouveau" format. Y compris pour les blocs spéciaux. J'ai encore un peu de travail à faire dessus (notamment faire en sorte que les boutons à droite de l'éditeur ne s'incrustent pas dans le niveau chaque fois qu'on les invoque), mais c'est plutôt bien parti.

      Avec tout ça, et l'introduction de nouveaux types de pente, je me dis qu'il deviendra bien utile de pouvoir faire des cas de tests sur des micros-niveaux, avec un système capable de détecter qu'on arrive bien à passer les "obstacles" quelques soient les variations de conditions initiales (légèrement plus à l'ouest, vitesse qui n'est pas un multiple entier de pixels, etc.) sans pour autant entrer en contact avec les blocs supposés en dehors du trajet.

      If I want to be effective with introducing more complexity in the sloped-ground engine code, I think I'll have to add some more unit-testing. I've learnt the hard way that sub-pixel initial position, interaction with step-motion and animation quirks can lead to broken slopes code, and that debugging that takes lots of time because it needs parts in-engine breakpoints and parts in-debugger breakpoints.

      A micro-level with a few simple slope scenarios, easy control on the initial state that can be automated should help a lot. And help is welcome. Plus, it makes support for those slopes independent from Level editor updates, which is welcome as well.


      Si ça peut sembler une perte de temps, vu que je n'ai encore aucun moyen d'ajouter des nouveaux types de pentes dans LEDS, ça ne serait peut-être pas si mal. En plus, dans un niveau comme celui de *deline, il est assez inconfortable de vouloir vérifier si la logique du code de gestion des pentes fonctionne bien comme prévu, parce qu'on est en permanence interrompu par ce que les autres "personnages" du jeu font.

      Ça pourrait aussi être l'occasion d'introduire un truc qui me titille depuis un moment: une série alternatives de constructeurs pour les objets principaux du moteur de jeux (déclarateurs de blocs spéciaux, états du comportement des personnages, zones de collisions, etc) qui ne passent pas forcément par une analyse de bloc de texte ... et seraient donc un premier pas vers la possibilité de "compiler" du gobscript en code C++ (ou assembleur 68000 ?)

      tempting to use that as an excuse to introduce an alternate set of constructors that could build objects without necessarily having to rely on text parsing. Like state.Using<GobWalkerController>("") rather than relying on "using walker;" processed by GobState::parse(). That would be handy for GBA or 16-bits ports of the engine, but it isn't easy to achieve with the current codebase, unfortunately. The core reason being that classes for the controllers are encap~insulated into a separate .o that was meant for 'dynamic linking' with the engine 'happily ignoring they even exist', except through a std::map of factories that can be invoked by controller names.

      Thursday, January 09, 2020

      Pentes et JumpThru

      Je me rends compte qu'il y a un truc à propos des pentes que je n'ai apparemment pas convenablement expliqué sur ce blog. Voyez donc cette "capture d'écran" de l'éditeur de niveau avec les types de blocs qui sont révélés en bleu transparent. Il est assez facile d'y voir les zones considérées comme solides, et même la petite pente au niveau de la souche d'arbre (allez, je vous la marque en vert flashy). Puis il y a un troisième élément qui revient assez souvent, ce sont ces lignes horizontales, notamment dans la branche (je vous les marque en rose ou en jaune, petits veinards ;)

      There's something I seem to have missed to explain about my implementation of slopes. Let's check this screenshot of the level editor, with the tile types revealed. Some areas are obviously solid (covered with fully-painted overlay squares). Some are slope (okay, I'll paint them in flashy green), and then there's a third recurrent item, identifiable with horizontal stripes (like the one painted in pink, on the tree branch).

      Those striped tiles are 'jump-through' platformes, helping the player to climb up structures in the level. They are simply implemented by changing the "world flags" that we're looking for depending on whether we're moving up or down, by acting like ground if we fall down, and like air if we jump through them.


      Elles correspondent au type "jump-thru", qui a la particularité de laisser passer ce qui monte mais pas ce qui descend. Aucune magie là-dedans: c'est simplement le "GravityController" qui ajuste le test effectué selon qu'on monte ou qu'on descend. Ce type agit aussi comme du sol sans agir comme un mur: si on arrive dans la branche par le bas avec une vitesse horizontale, on la conserve.

      Là où ça devient intéressant (ou louche comme une passoire sans trous), c'est pour les blocs "jump thru" qui sont marqués en jaune. Non, il n'y a pas de passage secret, et on est jamais sensé sortir de la souche.

      Now, there is another location with yellow jump-through blocks. This is no sweet secret passage. This is directly linked with the slope nearby. You see, depending on the controller used for a given state, we can decide to check for CAN_MOVE_THROUGH or CAN_FALL_THROUGH. And while we're walking along a slope, we don't try to fall.

      Imaginons un instant qu'on ait un personnage occupé à monter cette pente. Il a un point de contact (en vert flashy) qui doit rester sur la pente, et une zone de collision (en bleu). Pour qu'un déplacement soit valide, l'ensemble des blocs sur lequel la zone de collision arrive doit posséder les bonnes propriétés -- par exemple "on peut passer à travers" ou "on peut nager dedans". C'est le principe de la fonction cando() dans mon moteur de jeu.

      Mais dans le cas d'une pente, on voit clairement que la zone bleue peut "déborder" sur les blocs juste à-côté des blocs-pente. Si on les rends solides comme des murs, le personnage restera bloqué en bas de la pente... d'où la réutilisation des blocs au-travers desquels on sait tout faire sauf tomber.

      (C) The UnDisbeliever
      why we need more than slopes & blocks.
      If we had instead a solid block at the end of the slope, the character would get stuck. It was perfectly detailed in the Undisbeliever's recent blog post on his SNES engine tile collision description. I tried to show the same with the zoomed screenshot, with a 'hot spot' in green, the 'cando(MOVE_THROUGH)' area in blue and the intersection of the jump-through tiles and that area with some pink-ish pixels.

      I guess we could also have just used empty tiles for most of the rest of the slope (but the top, that is). I suppose I grew the habit of using jump-through tiles under slopes so that monsters work properly, especially those with check-points. If not, when they'll check a few pixels away from their hot spot whether there is still ground to walk on, they would wrongly consider they should turn back.

      Ils permettront aussi qu'un personnage dirigé par une "IA" qui sonde la map autour de lui ne pense qu'il arrive au bord d'une plate-forme en trouvant un tile "creux" quelques pixels sous son point de contact.

      Voilà, j'espère que c'était intéressant ;)


      Wednesday, January 08, 2020

      L'encodage de la physique du sol

      Bon, j'ai pris un peu le temps de cogiter à ce dont j'ai besoin pour les nouveaux types de sols dans mon moteur de jeu. Je sais que je veux plus te types de pentes et plus de types de physiques, mais en relisant mes notes sur les pentes pour comparer ça à celles de Ishisoft, je suis tombé sur un os:

      Si je veux des tapis roulants qu'on ne sait pas traverser, il me faut des tiles "physiques" avec la propriété "IS_WALL". Ou plutôt sans F_PLAYER_THRU. Cette propriété va être testée sur l'ensemble de la zone que le joueur (ou un ennemi si on regarde F_MONSTER_THRU) voudrait occuper après son mouvement.

      I took some time to make a list of those 'special grounds' I'd like to have in my game engine. More slopes, that was an easy one. But while I was comparing my plan to Ishinsoft's tigsource post, I noticed a potential issue.

      If I want to have conveyer belts that act as solid structures, I'll need physics-changing tiles that also have some IS_WALL property. Well, actually, that doesn't have F_PLAYER_THRU, since the 'cando' system allow move to some area only if a set of properties is true for all the tiles covered by this area.


      Mais si je veux des plate-formes glissantes en jump-thru, il me faut des tiles "physique du sol modifiée" sans la propriété "IS_WALL" (donc avec F_PLAYER_THRU, cette fois-ci). Jusqu'ici, comme la physique "glace glissante" et "tapis roulants" sont dissociés, je pourrais m'en sortir en définissant aussi les propriétés de collision pour chaque type de physique.

      I also want slippery jump-through ground, which requires physics-modifier tiles that act as plain air if you jump through them (so with F_PLAYER_THRU set). Hopefully, slippery ice and conveyer belts are distinct things, so I could just have per-physics-tile properties set and we'd be done.

      Les ennuis commencent si je veux un mur solide avec des pentes et une physique particulière: à l'intérieur de la structure, il faut que des tiles avec 'F_PLAYER_THRU' pour qu'il n'y ait pas de murs cachés dans la pente. (Un vieux truc mais toujours indispensable avec 'cando' et ses flags) et sur les bords de la structures, il faut que je repousse les assauts des sprites trop entreprenant pour les obliger à attendre le sommet avant de s'installer.

      ça me consommera 2 types de tiles physique. Ce n'est pas forcément un cas fréquent et on aura pas nécessairement besoin de dédoubler tous les types de physique.

      Things really get nasty when you consider structures like Aladdin's sandfalls (we should have some in some Sonic games too). There are physics tiles (pushing the player to the left) involved and slope tiles involved. Some physics tiles will have to let the player thru, else they will get stuck into the slopes (that mostly happens in the middle of the structure), and other physics tiles will have to act as walls at the edge of the structure, so that aggressive speedrunners don't get through. I don't see shortcuts to workaround the situation: we'll need two separate tiles type here, despite they'll have the same effect on physics (push player to the left).


      Du coup 1) il faudra regarder sous une pente pour avoir ses paramètres de physique et 2) ce sont les contrôleurs qui conserveront les informations dont ils ont besoins. Le coefficient de friction dans le contrôleur 'momentum' qui traduit les impulsions en vitesse et 'stopper', le fait d'être poussé dans un sens ou dans l'autre dans 'walker' et 'needground' etc. (on y reviendra).

      Et pour afficher tous ces types (pas loin de 250 en tout), il me faudra plus que mes 16+n petits tiles utilisés actuellement. Je n'ai pas la place pour mettre tout ça dans la "zone libre" de caractères prévues pour les widgets (128 caractères, juste après les codes ASCII): je tombe dans l'espace prévu pour les écrans. Mais j'ai assez de place après les écrans. (oui, ça n'a rien avoir, mais j'avais besoin de le garder sous la main).

      Thursday, January 02, 2020

      Kirby's Dreamland

      Permettez-moi de revenir en 1993. Jeune ado qui vient de commencer à envisager de laisser tomber son projet "Calimero" pour quelque chose de plus ambitieux avec pour personnage une boule bleue: Bilou. Puis mon frangin revient de chez notre jeune voisin avec un ovni pour Gameboy: Kirby's dreamland.

      Fan de jeu de plate-forme, je fais d'abord la grimace devant le fait que le personnage peut voler à volonté: ça va tuer complètement l'intérêt du jeu. Puis je vois l'écran-titre, et je suis scotché par l'animation du personnage.

      Pourtant le nombre d'images en lui-même n'est pas si énorme. Si on fait abstraction des sprites gonflés, c'est même assez standard pour l'époque. Mais avec un dosage parfait des versions "applaties" sur les murs et les petites étoiles qui soulignent les chocs au sol, cette petite variante ou Kirby (je dois vraiment me concentrer pour ne pas écrire "Bilou") plonge vers le bas quand il tombe depuis trop longtemps ... la magie prend. Et les petites "cutscenes" ajoutent juste ce qu'il faut pour que les temps de chargement deviennent une partie amusante de l'expérience.

      Le jeu est court. 4 mondes, 4 boss. Pas de nette séparation de chaque monde en 'niveaux', mais plutôt une séquence de 'salles' entre-coupées par des portes ou des petites animations (faisant généralement intervenir une 'warp star' et un peu d'humour). Vient ensuite une sorte de "boss rush" précédé de salles "remixées" du niveau de chacun de ces boss avant d'affronter enfin sur son ring King Dedede -- qui pour une fois semble responsable des maux dont on l'accuse.

      C'est le premier jeu Nintendo qu'on termine (heureusement, vu nos 15 ans?) et nous découvrons alors le mode "extra" que je n'ai toujours pas fini 25 ans plus tard. Si j'ai joué au maximum de 'vrais Kirbys' que j'ai pu rencontrer depuis (dédaignant les épisodes basés sur des pinceaux et des bouts de ficelle -- peut-être à tort), Kirby's dreamland a sans aucun doute gardé une place spéciale dans mon coeur et il est souvent arrivé que je remette la cartouche dans ce qui me tenait lieu de game boy pour me refaire une petite partie.

      Si ça n'a pas été facile de faire apprécier le jeu à J.l.n (qui préfère quand Kirby peut attraper des pouvoirs) ou à *deline, les choses ont changé quand il s'est vu mis à l'honneur lors de la compétition de speedrunning francophone l'an dernier: l'ultime décathlon 7 que les enfants suivent maintenant avec avidité.

      Voilà. Ca faisait bientôt 10 ans que je voulais prendre le temps de parler de ce petit jeu. C'est chose faite. Et si je m'y mets enfin, c'est qu'en voyant J.l.n aller jusqu'à Lololo/Lalala hier, je me suis dit qu'il y avait quelque-chose d'intéressant dans la formule "Dream Land", et que je devrais peut-être bien m'en inspirer pour faire un Bilou Dream Land, techniquement moins ambitieux que "Infinite Pyramid" et probablement faisable en 2 ans si je m'y prends bien.

      J'ai donc repris les maps du jeu entre deux motiliums pour voir un peu dans quelle catégorie on joue.

      Green Greens : 26 écrans, avec des zones relativement grandes et des environnements simples et ouverts

      Lololo Castle : environ 30 écrans. Nombreuses petites zones inter-connectées. Principalement à l'intérieur avec une tendance à désorienter le joueur.

      Floating Islands : 36 écrans plus quelques salles bonus et des cutscenes. Mélange intérieur/extérieur tout en restant dans le thème 'île aux pirates'

      Bubbly Clouds : probablement aux alentours de 42 écrans. avec une progression de "pays des nuages" vers "monde des étoiles" en passant par le panthéon grec perché sur les nuages.

      Si mes derniers chiffres sur le nombre d'écrans est douteux, c'est que pour les petites salles, les gars des studios HAL ne se sont pas fixés sur des nombres d'écrans entiers. On se retrouve ainsi dans le Lololo castle avec des salles de taille 256x192, 320x192 et 384x128 pas franchement alignée sur l'écran 160x128 du jeu quand il tourne sur game boy.

      L'idée pour la version Bilou serait de recycler donner vie à un maximum de niveaux du jeu "Bilou's Adventure" prévu à l'origine sans entrer dans les niveaux "souterrains". Pas encore de temple perdu, de zone des insectes-de-chantier ou d'océan musical donc. Je garde la zone du désert vu les récents travaux sur les monstres en guise de "floating islands", et il me reste à trouver un bon environnement (probablement montagnard ... l'influence de Céleste) pour jouer le rôle de zone finale.

      Il faut aussi que je décide ce que je fais pour les Boss. J.l.n voudra des boss à affronter, mais ça va clairement augmenter le temps de développement. Le jeu perdra tout intérêt si les enfants ont passé l'âge limite pour jouer à Fortnite d'ici qu'il soit fini.

      Wednesday, January 01, 2020

      TileMap

      I had a remark in my notebook, wrote a few months ago, where I felt it weird that some parts of the game engine would require a SpriteSet where they actually needed information about the level map, and that the SpriteSet class was actually the only object capturing the map pointer, as well as its dimensions.

      I then realised there was several places in the code where you'd load a map from a file, and it was tricky to use only one of them because one is within SpriteSet and the other (in the level editor) has no use of such a SpriteSet. It was time to introduce a new class with informations about a loaded map. Refactoring things went pretty fine and I could even repair a few things thanks to the 'unit-testing' tools (and thanks to the 'watchpoint' feature of ddd).

      Well, I realise that doesn't mean a lot. That's all I could craft since I'm on holiday, I'm afraid. Happy new year.

      edit: now, with only one class doing .map - to - memory conversions, I can make a new unit-testing tool that operates on level maps. I stuck to the idea of having some 'generic' tool invoked in scripts to define test cases based on reference data, but I pushed it on step further than 'cmdck' I wrote in early 2019: 'mapdo' handles a stack of level maps in the way a RPN arithmetic evaluator handles a stack of numbers. And you can quite easily define new 'operators' that act on that stack. It will help for things like cropping, splitting, patching etc. and can also help for tests such as "load level1.map save test1.map load test1.map eq" to check the level hasn't changed after being saved-and-reloaded (e.g. on-the-fly newmeta conversion produces the same contents as PHYS chunk loading).
      • by just passing "show" as the constructor argument to CommandHandler, this handler will be invoked whenever we see "show" on the command line
      • CommandContext.maps is the stack of level maps,
      • CommandContext.next() provides one token from the command line, for both commands-to-handlers lookup or for command arguments.
      • it could be further simplified into a void run(cc) since we no longer need to communicate the amount of command line tokens we consumed.

      I love how both the core loop and the handlers are elegant, although I'd have loved a more in-line syntax like case "show": or show=>sub { ... } as well, but they wouldn't have worked in C++, would they ?