Saturday, December 29, 2018

Wformat-truncation

Pretty impressive new feature of gcc/g++ available in the latest devkitarm.
Look at that.


MapWindow.cxx:377:23: warning: %i directive output may be truncated writing 
    between 1 and 2 bytes into a region of size between 0 and 9 [-Wformat-truncation]
snprintf(msg, 32, "@%i,%i : %s [%ix%i]",xpos, ypos, why,
                   ^~~~~~~~~~~~~~~~~~~~~
MapWindow.cxx:689:12:      report("editing meta-layer");
                                  ~~~~~~~~~~~~~~~~~~~~
MapWindow.cxx:377:23: note: directive argument in the range [8, 16] 
MapWindow.cxx:377:23: note: directive argument in the range [8, 16]
 note: 'snprintf' output between 32 and 42 bytes into a destination of size 32

- gcc detected that I'm calling that snprintf function with a value of 'why' that is actually the "editing meta-layer" string, which is 18 bytes long
- it understood that this and some numbers had to fit within 32-bytes output
- the format characters only take 9 bytes
- xpos and ypos are 16-bit values, needing at most 5 characters to be rendered on-screen
- 18 + 9 + 4 = 31. that's the case where all the numbers take only 1 byte. We have barely enough room to display the message (and its terminating zero character)
- 18 + 9 + 5 + 5 + 2 + 2 = 41. That's the case where we have all numbers using their maximum size. Note that GCC/G++ could guess that we'll only use numbers between 8 and 16 by looking at  blockop?16:8 argument!

Having such thing can be super precious when you know what's going on and want to harden your software. Hopefully, there is no overflow expected here. Only truncation. And since the purpose is to put a message on a 32-bytes wide line, I'm fine with truncation here. I'll have to tell that to the compiler the best possible way with some #pragma.

edit: oh, actually the compiler is even smarter than I thought: it can tell whether you checked for the return value and won't bother you if you handled truncation with an if (needed > sizeof(msg) - 1)
If the output was truncated due to this limit, then the return value is the number of characters (excluding the terminating NUL byte)  which would have been written to the final string if enough space had been available.
Oh, and by the way,
*snprintf() write at most size bytes (including the terminating null byte) to str.
So in my code where I want 32 characters on the 32-char-wide screen, I should have a 33-bytes buffer and pass 33 to snprintf.

Friday, December 28, 2018

How to choose the best cat

There is a post on Winter Mute's blog that really feels like it's been written for me. It is about people who build up their own solutions to problems that are researched to death and will stick to them even when being told that better solutions exist, whatever the drawbacks of their current solution. Somehow, it is not that far from the "not invented here" syndrom, explained from the point of view of someone who's been working hard to promote a unified way of doing things.

If I think about it, the very fact that I'm using a Nintendo DS to make games in 2018 feels like using a cat to clean up windows. I've been asked why not doing it with construct 2 quite a number of times, and people who know me would likely tell you that "oh, but he likes doing it for DS so much..." . I did have a look at a few construct 2 tutos, but they never made me feel like I'd love to do that instead of what I'm doing.

I've been asked too why I wouldn't buy a wacom tablet and save my assets as .png files. Granted, I could follow some online tutorials and grow some knowledge about some tools really used in the gaming industry. Yet, I insist on updating my sprite editor and yes, if someone insist that l should really try ASEsprite or something alike, I'm gonna be quite sceptical and likely argue that it's gonna be complicated. (like having to install Windows or Steam first, having to ensure I have a good mouse or be next to my PC to do pixel art, etc.)

I'm afraid it's not just about programming ...
And apparently, yes, I could be using MaxMod -- the cross-platform sound library shipped by devkitpro -- instead of the unpopular libNTXM with all those odd extensions I've been adding over the years. Possibly wintermute could have put something like "oh, but you know, I'm no professional window cleaner. I'm mostly doing this on my spare time" in his rant, and it would look even more like myself. I don't really have any excuse for this one. It's just that I've been investing time to master the code in libNTXM. It feels a bit like it's mine now. I feel home with it and I'm reluctant to leave that home and move to somewhat uncharted territories.

I know there is one thing for which I do not want to make compromises, and that's the quality of the end result. Be it a hobby or not, I do not want the game to feel half-baked. But when it comes to picking either library, using design patterns or learning new tools... well, I don't want anybody to feel offended or sad. But yeah, I'm doing that after my work hours. After my clean-the-mess hours, because aside all the amazing things I do at work, I still wonder if the game ideas I have would work great. Because there was something about C64 programming I loved.

None of this make Wintermute any less right that buckets and mops are better tool to clean windows, of course.

Thursday, December 27, 2018

Todo list update

La "todo map" pour le projet "School Rush" a pas mal progressé depuis sa mise en ligne en 2015. Heureusement, me direz vous, et sans doute aurez-vous raison.

Maintenant que l'objectif "School Rush" est atteint, il va falloir que je reprenne ça, et que j'y ajoute les révisions souhaitées pour le moteur de jeu (gestion des coordonnées relatives pour pouvoir se promener à dos de 'blador correctement).

Back in 2015, I drew a 'todo map' for my School Rush game, trying to capture the 'todo' items scattered over this blog, prioritizing them according to the 'finish the School Rush game' objective and show how they relate to that objective altogether. Now that the game is complete, I've found myself wondering what I should do next ... so let's proceed with a new and fresh list. 

There are a few engine revisions I'd like to do, there are tool updates to be done and tool updates to be validated (esp. regarding the level editor). There is the level map format that needs to evolve in order to allow more flexible multi-palette updates. I also would like to find time to review the scripting language so that we can (at last) edit characters behaviours directly on the NDS.

Il y a aussi des mises à jours d'outil (le fameux "rules.gam" et la supression des ennemis hors-cadre lorsqu'on redimensionne une map) qui n'ont pas encore été suffisamment testés à mon goût sur DS, et un ou deux remaniements du format des niveaux (pour permettre l'utilisation de 16 palettes sur tous les blocs du niveau) qui affectent aussi bien le moteur de jeu que l'éditeur de niveau.

Après, j'aimerais bien pouvoir m'attaquer à une adaptation du langage de script pour qu'on puisse les éditer directement depuis la DS. nom de code: beds / geds3.

Wednesday, December 26, 2018

virtual tilesets

How should I handle tilesets larger than the 1024 tiles the NDS hardware is designed to use? Quick maths show that with 16K unique tiles, I'm already consuming 1MB of RAM. Full 16-bit tile identifiers would address the whole 4 MB of RAM the NDS has.

Unlike in Eric's games, I cannot assume here that the RAM is just a cache for the whole game data (ROM) because the game data may only be accessed through the SD card interface, abstracted through DLDI which -- afaik - - does not support asynchronous reads. And the score-table l implemented lately suffered noticeable lag while accessing the filesystem.


So there will be only main memory and video memory. Echoing what happens on x86 CPUs Virtual memory management, I'll use "logical" for the identifiers related to the full tileset and "physical" for the identifiers related to (temporary) video memory locations.The two functions that deal with translation between the two  sets of tiles are linked to the scrolling updates: since the level map is made of logical identifiers, we need logical-to-physical conversion there. The other(new) function kicks out trees that no longer need to be in VRAM.

I haven't really found a use for a physical-to- logical map. And all my plans for a smart combine-bitmaps system to decide which tile to evict fell flat when I realised it would be smaller to just keep an integer counter for every physical tile. So I will avoid over-complicating things and just go for the array of counters.

I know the logical-to-physical map could use a hash table implementation. It would have at most 1k items present, i'd say that means the main table should be at least 2k entries large. One important thing to consider is that we will have to remove items quite often, which means resolving collisions with chained list would work better than by using the next available entry in the table. Especially given that we will likely find consecutive tiles present, for instance when we have a large structure on screen. Good thing is that since there is an upper limit to the amount of items present, we know an extra 1 k entries for the chained list with always be enough.

Now, one entry in such a table is at least 16-bit logical id + 16-bit physical id + 16-bit next pointer. that takes us close to 18kB for the table, while a plain array of physical identifiers indexed by logical id would take at most 32 kB. Granted, the diffeence is critical for SNEs deving, and likely significant for GBA deving. But here? is it really worth the extra memory lookups ? Is it worth doing divisions to get a good hash function? We'll look into that if I ever need more than 8192 unique tiles in a level.

Saturday, December 22, 2018

November playtesting

You might think a 16-years-old wouldn’t have interest in School Rush anymore, this is not the case for A. She will ask me whether I have my DS with Bilou’s game almost every time we spend more than half an hour in the same room. So whenever I know I’ll be visiting her family, I try to speed up coding so I can have fresh play-testing feedback.

As you have guessed, it happened on the 1st of November, with the “game over” release candidate. She had to leave early, so I couldn’t gather the level completion times I hoped to have. But as she was playing, most of the kids in the house gathered around the sofa, and asked whether they could play the game too.

For most of them, it was their first time with the game, although I presume most of them had prior experience with a gamepad. They seemed to enjoy the game, did not show excessive frustration, and globally favoured the “float” power-up over the punch.

Interestingly, they intuitively named the inkers “the lifts”, meaning that they considered them as useful feature to beat the level rather than as some opponents (possibly because they’ve seen their elder using them in first place).

One of them even made it until the pencils-in-a-cave challenge of level 3 before asking for help… and then failing with the three-sponges-over-the-ink challenge.

Sunday, December 16, 2018

One last fix.

I'm pretty pleased to hear my 5-year old asking me to play School Rush. But for some reason, j.l.n has a fascination about power. And in School Rush, he wants to get the power-ups. He knows the pendats hold them and so he wants the pendats down.

"Papa, je peux jouer à Bilou?" .. Celle-là, je l'ai attendue ! Et voici donc J.l.n qui, du haut de ses presque-6-ans s'attaque au niveau qui avait été dessiné sous la direction de sa grande soeur. Mais pour une raison que je n'ai pas encore identifiée, mon p'tit bonhomme est plus ou moins fasciné par la puissance. Et dans school rush, la puissance, c'est le power-up du "gros poing". Il veut l'avoir et donc il veut éliminer les crayons qui le détiennent ...

remember your first pendat ?
But the thing is, the first encounter was designed to let the player easily avoid the pen, not to easily dispatch it. So he tries, again and again, asking for help when he's out of bladors (amno) because he's so scared to get down in the pencil' lair, grabbing them back. He's wishing so hard that you could throw a sponge at the pencil instead, wich would turn its harmful spike into a friendly moving platform (yeah, he's been watching me playing some Rayman, too).

Oui, mais l'endroit où on rencontre son premier pendat n'a pas vraiment été pensé pour qu'il soit facile de vaincre le pendat. Bien que ce soit tout à fait possible, on nous invite plutôt à le contourner en passant par les crayons. Mais ça, J.l.n ne veut pas en entendre parler. Il me demande de l'aider en lançant le taille-crayon pour lui (jusqu'ici j'ai refusé) ou d'aller récupérer le taille-crayon tombé à côté du crayon (le "trou" du crayon lui fait peur. Il m'invente des solutions où on peut lancer une éponge -- moins effrayante -- sur la pointe du crayon pour le transformer en une inoffensive plate-forme mobile (je garde ça pour l'encourager à essayer de faire du gobscript lui-même l'an prochain), etc.

I'm not going to grant that wish, partly because that could affect the gameplay balance too deeply, and partly because doing that himself could be a powerful motivation to learn letters, words and numbers...
But there is one more thing, something I can -- and did change : fixing a collision bug with the pendat. See, every time J.l.n makes a successful throw, the game never triggers the collision. He always manages to throw the blador at the exact time where the pendat bounces back and before it turns back. And during those few frames, the pendat is unfortunately unvulnerable in "aftermaths" version.
This is clearly a bug, an easy to fix one, and I'm quite surprised that it never annoyed anyone else so far. Anyway, that makes one more release to make one little kid's life easier.


Mais bon, il y a quand même un truc que je peux -- ai que j'ai -- corrigé: un bug de collision jusque là passé inaperçu et qui permet au taille-crayon de passer à travers le crayon sans le toucher quand celui-ci est dans son animation "rebondit en arrière après s'être cogné à un mur en courant".

Thursday, December 06, 2018

16 couleurs.

Bon, c'est un peu inévitable: à présenter Bilou sur un forum de dévelopeurs NES arrive la question des "demakes" sur GBA, SNES et autres MegaDrive. J'avais déjà un peu regardé ce que ça pourrait donner suite à un commentaire de MonsieurL sur UltimateConsole, mais ni la SNES ni la MegaDrive n'est vraiment convaincante. Pourtant, il y a déjà des bibliothèques pour les aspects bas-niveau qui auraient pu être intéressantes.

Pour commencer, la MegaDrive n'a que 4 palettes de 15 couleurs, décors et  sprites inclus. Ici, dans Bilou, j'ai travaillé avec 8 palettes de 256 couleurs. Bon, on est bien d'accord, je n'utilise pas l'entièreté des possiblités, mais j'ai quand même au moins 6 couleurs de pieds (2 pour Bilou, 2 pour les Pendats et 2 pour les Dumbladors), 4 couleurs de mains, plus des livres et des fardes qui font pas mal dans le color swap. Bref, il faudrait presque considérer une réduction à 60 couleurs fixes. Il y a bien quelques palettes sympa dans ces eaux-là, ce n'est quand même pas top-sexy, comme résultat.

La SuperNES, de son côté a quand-même droit à 8 palettes de 15 couleurs pour les sprites et 8 autres pour le décor. On est déjà nettement plus à l'aise. Jusqu'à 1024 tiles par plan de décor et 512 pour les sprites (moitié moins que sur DS. On ne s'en sortira pas sans une technique façon Zmiro ou Perry) pour un total de 64KB de mémoire vidéo ... presque 10 fois moins que sur la DS. Il faudra aussi compter avec un maximum de 34 sprites 8x8 par scanline (or je fais pas mal de recouvrement) mais bon, c'est pas un bullet hell non plus. Lors des tests automatiques, j'ai au plus 70 objets actifs (pas forcément tous visibles) en même temps.

Un autre élément à prendre en compte, c'est que sur la planète PAL, la gravité est 224/192 fois plus forte que sur la planète DS. tout y est donc un peu plus tassé. Ce n'est pas vraiment un problème pour les indigènes, mais Bilou a tendance à se tasser, ce qui nuit à son charisme. J'utiliserais probablement les lignes de pixels transparents (le corps de Bilou fait plutôt 16x13  pixels au sol et 16x14 en chute libre)

Il y a donc 32 lignes inutilisées (en noir sur l'image) que je pourrais exploiter pour insérer un HUD, vu qu'on perd l'écran du bas.

Bref, Piet, si ça t'inspire, il faudrait que la musique tienne en 64K (disons 48K pour les samples, 16 pour les patterns et le player) et n'utilise que 8 pistes au maximum.

Et pour rire, avec une seule palette de 16 couleurs, on arrive à  ... quelque chose de pas complètement moche, mais quand même fort loin de l'original (bon, c'est de la conversion automatique, évidemment).

Par contre, le homebrew sur GBA serait légal dans certains pays où le homebrew NDS est frappé d'interdiction ... ça mérite qu'on y réfléchisse ...

edit : bon, j'ai pas pu m'empêcher de faire un gros montage de plein de screenshots de SchoolRush, de retirer tous les sprites et de faire "conversion en mode indexé" pour voir où on en est (avec les différentes variantes de teintes pour les livres, le sol, etc). Bin ça fait 190 couleurs en tout. Alors que la SuperNES n'en a que 120 à me proposer ... sur GBA, par contre, ça passerait sans soucis.

 edit again: oui, mais une SuperNES, ça sort un signal analogique. Et sur un signal analogique, le dithering passe beaucoup mieux que sur écran LCD. Je peux donc avoir une variante du contenu qui passera pas trop mal (moyennant quelques retouches sur les crayons qui peuvent se passer des petits pixels isolés et prendre une teinte légèrement différente de celle utilisée sur DS ... ce genre de choses).

A suivre, donc, finalement. Mais attention: la SuperNES n'a au mieux que 3 plans et ne pourra pas faire les vagues avec des sprites parce qu'elle n'autorise au mieux que 34 sprites de 8x8 sur une ligne horizontale, l'image en faisant 32 de large.

strtrololol


All of sudden, we realised that we had plenty of 'strtoul' that weren't checking they were actually receiving a number as argument. So we went for fixes and have a few more conditions that could trigger an exception.

If instead you suggest to go for stringstream, remember: it will expect either '.' or ',' depending on the value of LC_NUMERIC ... Maybe it isn't that bad I'm using mostly sscanf() in GEDS code :P Well, as long as I remember not to use %i when I mean %d, that is.

Saturday, December 01, 2018

School Zone: aftermaths

Five year ago, I released a single-level "anniversary" game featuring Bilou in a school zone settings. A bit later, in early November, I had the opportunity to have it play-tested by my nephews, who were 7 to 13 years old by then. From there difficulties to approach the game, I added one "preliminary" level that is now level 1 in school Rush.

Voici 5 ans, je vous faisais un jeu-anniversaire avec Bilou dans la zone de l'école. Quelques semaines plus tard, en septembre, mes p'tits n'veux me faisaient me rendre compte qu'il me fallait quelque-chose d'autre comme niveau pour introduire les mécaniques du jeu. Ce niveau non-tutoriel est depuis devenu le premier niveau de School Rush.

So yeah, the 5 levels I'm releasing today for Bilou's 25th  anniversary took me long time to make. And it doesn't even feature the level that started it all. Because meanwhile, I grew interest in speedruns, and I wanted the game to focus on going as fast as we could and save the books. Things with a focus on wandering, discovering, solving or exploring will happen in another small game.

I'm proud I could get feedback from several professionals with the 2016 release. I hope the game is better now. May there be many of you finding it, may you have a fun time playing it. This is my gift to whoever makes free software, free music, or free videos. Thank you all for making coding possible/enjoyable


The engine code is LGPL, the tools 'used to make it (animations and level editors) are GPL, the art and level design remain my copyrights, but they are free to play, and free to share unmodified as part as this School Rush release.

Aujourd'hui, le développement de School Rush prend fin. Ce fut long, parfois fastidieux, mais je voulais l'amener jusqu'au bout. Comme dans tout jeu, il y a des choses qui ont été abandonnées en cours de route. Soit qu'elles ne convenaient pas, soit qu'elles m'écartaient du but premier du jeu... soit qu'elles promettaient de devenir un gouffre de développement rendant irréaliste toute sortie tant que je serais en solo sur le développement. Que ce jeu soit mon cadeau à tous ceux qui ont écrit du code free software que j'utilise tous les jours, les musiques que j'écoute en codant, les vidéos sympa qui m'ont donné envie de continuer. Merci à tous pour votre travail.

I'll just update November post with the link to December contents, if you don't mind. How-to-play etc. is there. If you're looking for a changes list, it's been posted already ;)

History tracked on playeradvance, tigsource, homebrewlegends