Thursday, July 18, 2019

documenting

My colleagues brought me from the habit of explaining lots of things through comments into the habit of writing almost no comments in my code. Explaining a lot came from my 386 assembly years, where I'd have to write code that does the desired processing on the one side, and carefully detailed what I was trying to achieve in the comments section.

The new fashion is to go from
sprtrsp(spr1, spr2); // make the sprite more transparent
into
makeSpriteTransparent(newSprite, oldSprite);

As I'm applying this over the QuickGo class to better see whether I can structure that code better, I still stumble upon code chunks that remain pretty obscure. Why the hell am I doing those computation on the map size ? and why the test with those * 4 multiplications ?

Hopefully, seeing the widget in action with up to 4 slices of the 'School Rush' level made me understand myself again.


When I want to fit a large stripe of level or a tileset into something that's closer to a square, I tend to cut it in two and put the halves above one another. If that isn't enough yet, I repeat the process. In each step, I exactly halve the largest dimension but double the other. It only makes sense to do so if I'm going closer to a square by doing so, that is if width/2 >= height * 2 when the image was currently wider than high. That exactly converts to width >= height * 4.

When that condition is false, I could still keep processing it further, but by doing so, I'd make the picture less readable. It doesn't make sense to consider it once we have reached the perfect square of course (the sprite rendering the mini-map is always square), but if we end up with a 3x2 rectangle and consider going into 2x3 instead, then sticking to 3x2 remains more readable.

Saturday, July 13, 2019

Monster selector

Okay, it's not much and it wouldn't even be posted alone despite I haven't been posting about tools development for weeks. I have now the 'selected monster' arrow showing you where on the map is the selected monster if you happen to get it off-screen.

But it is not alone. Changing that required more refactoring that I kept track of, and I'll have to break that giant "stash: okay, it works" commit into proper commits. It's not just that the code was old or buggy: my coding standards have quite evolved by working in a team where most of the code you have to deal with has been written long ago (and likely not by yourself).

It's not that much about the lack of comments. Having something like

// use sprite tile 0 for the cursor
sprites[CURSOR].attribute[2] = 0;

only works if this is the only place in your code where it can happen, and without coding habits that guide you towards

const oamno_t CURSOR_TILE=0;
sprite[CURSOR].attribute[2] = CURSOR_TILE;

you're very unlikely to get it done at a single place.

I had to fix things like that, as over time, I ended up with two widgets deciding to use sprite tile 0 for their own special purpose. I had to migrate the 'sprites granularity' into the recently-introduced "UsingSprites" class as I realised that more and more parts of the code now depended on that granularity (like whenever you're allocating sprite tiles) and top-screen and bottom-screen might not be using the same granularity...

Among the ugly things in LEDS code, I shall name a global "UI-like" object -- QuickGo -- that renders the level into a small bitmap, so that we can quickly navigate the level. That object is created in the 'MapEditWindow', but it is never inserted as a widget. It seems to be a global pointer, which is accessed through LayersWindow -- the part of the application where it actually shows up -- and even there, it doesn't directly reacts to user inputs. Instead, there is a 'QuickWidget' object that doesn't show anything on screen, but that does produce events on stylus touches that MapEditWindow will further catch and process.

Such a "code-that-works" approach that breaks almost every coding convention in the "GEDS" library is tricky to reason upon. You see QuickGo in a window, so you should be allowed to assume it is part of the UI, and used in that window. 

Tuesday, July 09, 2019

Salut!

Salut. Je m'appelle Bilou et je suis explorateur de l'espace. j'ai eu le malheur de m'approcher un peu trop d'une planète non-répertoriée dans le quadrant 79.03.24 et j'ai été mystérieusement happé sur la planète. Mon astronef est hors d'usage, la planète pour le moins curieuse et mon sort serait lié à 7 pierres mystiques auquelles les autochtones prêtent des pouvoir magiques.

Je n'irais pas jusqu'à dire que je peux compter sur mon coéquipier et ami Bouli pour nous tirer de là : cette planète a l'air de faire ressurgir en lui tout ce qu'il semblait avoir réglé comme problèmes d'adolescent... quand il ne se fait pas tout simplement kidnapper *soupir*

centres d'intérêts: astroconduite, planetologie, haute-technologie, biologie des blobs, physique des plate-formes scrollantes, téléportation par ondes Wi-Fi, spirographe hyper-spatial, tir de précision à l'astro-flasheur à perforation en vrille

(c'était la présentation du personnage fictif "Bilou" sur Facebook, mais l'Ordinateur a décidé de remplacer ça par une "description de page" qui est malheureusement limitée à 255 caractères >_<

Toolset and MonsterPropWindow

I've got less free-reading time during my vacation this time, and likely, I should be not blogging right now, but cleaning up all the mess that going to vulcan(eifel) and back brought to the house. When I did get some time, I spent it clarifying some obscure parts of my Level Editor which puzzled me when I initially tried to add some additional monster edition features, like being able to decide within LEDs whether a given game object belongs to the 'hero' cast, the 'evil' cast or none of them.


I refactored a few things to make that possible already and I hope I'll get some time to continue working on it within the upcoming days.

I also took some time documenting one of the more elaborate (but arcane) widget that I might have to use to do that: the 'ToolSet' meta-widget, which is truly a bunch of buttons hacked to behave like the typical radiobuttons of point-and-click GUIs.

Tuesday, June 25, 2019

couldn't list the directory.

For some reason, recent desmume (0.9.11) shipped with my Ubuntu distro cannot list files in directories passed as --cflash-path.
and this time, my 'old version' of desmume binaries are 32-bit while the system is running a 64-bit OS. I had multiarch-support package installed, but not libc6:i386, nor libstdc++6:i386, and as a result, and I just had a weird message about the 'file not being found', while it was obviously there. Once those core packages had been installed, I got more coder-friendly error messages mentioning which library was missing, so I could just find the corresponding package with apt-file and install e.g. libsdl1.2debian:i386, libglib2.0-0:i386 and the like.

And eventually, I could run a copy of desmume-cli so old that --version isn't even supported :P 0.9.6 svnr3873 dev+, it says. And that one granted me with proper file loading, so I'll be able to investigate whether my edits on the extended monsters editions are OK or not ... next time.

(and no, this time adding --load-type=1 did not help)
(bug confirmed with filesystem/libfat/libfatdir example from devkitpro)

Sunday, June 02, 2019

Iconoclasts


I could hardly point you to the old "wish-to-play-that-game" blog post about Konjak's "Iconoclast" game, because I've been post-poning writing that post since 2011, hence the "#veryOldDraft" tag. 

From the very first notice on the tigsource blog, I've been in love with the pixel art used. I loved the gameplay as well, which I managed to get running through Wine on my old laptop ... but It wasn't comfortable to go much further than the first true boss.

But yesterday, I just noticed it was available on the Nintendo Switch, so this is my second indie download, and it's a marvel to experience the game at full frame rate with responsive and friendly controls. Plus, the switch has a "snapshot" button, and a separate galery mode, meaning that I'll be able to do pixel studies when I'm not playing it.


Bon, il y a 8 ans que j'aurais dû commencer cet article, parce qu'il y a 8 ans que Iconoclast de Konjak m'avait tapé dans l'oeil. Il y a même 8 ans presque que j'y ai joué pour la première fois, dans le salon de mes beaux-parents sur un portable Linux tournant laborieusement Wine. J'avais apprécié, mais l'expérience n'avais rien de comparable à avoir la chose au milieu de son D-PAD et calibré pour tourner à pleine puissance sur la Nintendo Switch. Je ne regrette pas l'attente et je ne regrette pas la dépense (vu que la version PC avait été gratuite :P)

La bande son est de toute bonne qualité aussi (sans pousser jusqu'au niveau d'un Shantae ou d'un Fez quand même), et je suis assez surpris de constater qu'elle est signée également Konjak! "Pixel" avait fait de même dans "Cave Story", mais ça reste suffisament rare pour le mentionner.

Bon, je vais quand-même faire une petite intro du jeu pour ceux qui le découvriraient ici: Robine est une jeu passionnée de mécanique. Elle n'hésite pas à donner un coup de clé à gauche ou à droite pour dépanner les gens de sa colonie. Elle se débrouille aussi assez bien avec un flingue, et ça va lui venir bien à point, parce que dans son monde, le carburant est considéré comme une manne divine et seuls les prêtres assermentés peuvent intervenir sur les équippements alimentés en carburant sans être pourchassés par les agents du Projet et "jugés" pour hérésie.

Je pourrais être tenté de dire qu'on est face à un métroïdvania, vu qu'on se déplace librement dans des niveaux un poil labyrinthique, qu'on débloque des mises à jour de ses armes pour continuer à progresser dans le jeu et qu'on dégomme tout ce qui bouge sauf si ça nous adresse la parole avant de nous charger. Mais ce serait une insulte de ne pas préciser la présence d'une forte composante RPG, étant donné la quantité de PNJ qui nous distillent leur connaissance du monde, de ses intrigues et des relations curieuses entre les différentes factions. De nouveau, c'est de Cave Story qu'on se rapproche le plus ici.

Encore que Konjak a veillé à choisir des mécaniques de combat qui permettent aussi des phases de puzzle entre les séquence de combat pur -- chose qui colle tout à fait au caractère du personnage, ce qui ne gâche rien.

Saturday, June 01, 2019

touchInit()

J'espère que ce sera la dernière surprise dans la branche "mise à jour de mon kit de développement DS"... Après avoir enfin réussi à avoir des programmes qui tournent à nouveau sur la DS (et dans l'émulateur, au passage), je finis par me rendre compte que plus aucun de mes widgets ne fonctionne. Au point que j'en viens à soupçonnner quelque-chose dans le code qui relaie les mesures depuis le composant SPI en charge de l'écran tactile à travers le processeur ARM7 pour être exploité sur le processeur ARM9. D'autant que tous mes programmes utilisent un code ARM7 "custom", chose qui ne sera forcément pas testée lors des mises à jour de libnds, devkitARM et leurs sbires.

Mais mon code ARM7 voit toujours bien son gestionnaire d'interruption appelé, recompiler un des programmes d'exemple avec mon code ARM7 au lieu de la version officielle marche toujours ... Ou en tout cas, c'est l'impression que ça donne au premier regard. En débugguant de plus près, je constate que mon code sur l'ARM9 reçoit bien des 'touchPosition' en bout de chaine, mais chose curieuse, deux paramètres (px et py) sont toujours nuls. Il s'agit des valeurs traduites en pixels, à l'aide des paramètres de calibrage. Et le programme d'exemple fait pareil: il ne nous donne que les valeurs "brutes".

En réalité, il faut maintenant appeler une fonction "touchInit" pour appliquer les données de calibrage, et la version du devkitpro que j'avais installée avait fait la mise à jour correspondante dans le "code ARM7 par défaut", mais pas encore dans les programmes d'exemple utilisant du code ARM7 custom ... On va finir par y arriver.