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"...

    "Will you also put the rocket-meka-Bilou in your test ?" asked my 7-y-o seeing my debug notes for the on-going slopes unit test. He was closer to the reality than what the clockwork-Bilou sketch suggested. The virtual-meka-tester is now zooming at 8 pixels/frame and no longer detects it should start going down. But then the GobController in charge of 'walk-on-the-ground' detects there's no ground anymore and triggers a FAIL event. It should trigger a state change according to the state machine, but MekaBilou has none.

    Now here's the catch: the GobController has not cancelled its move, and over every next frame, it will start flying off (so much for the little wheels I'd drawn) and only the 'killer' tiles I put in the sky can stop it. I'll need some more code to interupt tests when unexpected state transitions occur, apparently.

    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.

    I had to do some more unit-testing on the new level description system. Good thing with that unit-testing environment (running on my dev PC rather than on the NDS or in an emulator) is that it features some memory leak detection. But unfortunately, as I tried to use my scan-mem.pl script -- the one that converts raw addresses stored in log files into filenames and line numbers -- I couldn't get anything relevant.

    The reason for that is that Linux "now" features an address space layout randomization mechanism, that ensure .ELF files are no longer loaded at well-known memory locations, but instead takes advantage of their re*L*ocatable nature to make every instance of the program somewhat different from the others, and therefore make sith-hat hackers' life miserable by denying them simple access to part of the code/data they'd like to use in their exploit. Generally speaking, this is great, but right-here-right-now, this is bad news.

    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 ...)

    Hopefully, this isn't that bad to fix. All I need to do is to extract the 'expected' address of some function within the decoding script, re-compute the offset and substract it to every value found in the logfile and tAke oVeR the wORld (mwah ha haah)... Erhm. The leak. Did I just said 'the world' ?  

    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 ?