Thursday, May 25, 2023

Battle Plan : Big Caterpillar

Maybe these should stay secret. Maybe I should make sure my blog doesn't spoil the game by having too much "strategy guide" items in it. Who knows ... Anyway, here's a few ideas on how being able to throw things or having the Big Punch power-up would affect the fight.

It keeps the 'crawl' and the 'tail attack' of the BASIC version. It keeps the 'changing target' where there's one segment to hit, and that's the one where the magic stone currently is. It simplifies the fight by assuming we're good hitting *the segment with the stone* rather than the one next to it "so that it pushes the stone in the right direction".

J'avoue, ça fait un moment que j'hésite à les poster, celles-là. Ce serait moins drôle que mon blog se transforme en soluce du jeu avant même qu'on ne puisse l'essayer, non ? Mais bon, en même temps, j'ai besoin de pouvoir me confronter à mes idées, les trier, les rassembler... et les cahiers à points ne sont pas forcément l'idéal pour ça.

Voilà donc mes notes pour le face-à-face Bilou/Big Caterpillar. Le seul boss qui ait jamais été codé dans la version BASIC. Je conserve la manière dont il avance en rampant sur le sol et son attaque "coup de queue". Comme je l'avais déjà dit, le fait que Bilou puisse depuis SchoolRush lancer des objets change la donne. Plus question de demander de frapper toujours la tête ou la queue. Mais si on doit viser un segment en particulier, il faut que ce soit évident pour le joueur. J'avais déjà envisagé d'utiliser la gemme magique comme illustration pour ça, mais avec un système tordu où il aurait fallu frapper le segment *suivant* la gemme pour la forcer à avancer, frapper le segment précédent l'ayant au contraire fait reculer. Plus rien de ce genre: frapper la gemme la fera avancer, frapper ailleurs sera sans effet.

Then I started trying to bring all that into one single state machine. The goal was to ensure actions taken by the boss would make sense and not lead to tricky corner cases. For instance, the 'tail attack' should only be performed if there's enough room in front of the boss so that it does not attack through the edge of the arena. (as a bonus, that also means that it won't do that attack in a case where that would 'trap' Bilou in a situation where taking damage is inevitable). But then there's a new 'coil' attack that is completely horizontal.

Mais à côté des "idées de gameplay", j'ai voulu regarder si je parvenais à rassembler tout ça en un comportement cohérent. A quel moment faire quelle attaque pour éviter que la moitié du boss ne se retrouve dans un arbre ou qu'une des attaques devienne complètement imparable parce que le joueur n'a pas la marge de manoeuvre nécessaire. C'est comme ça qu'on se retrouve avec une attaque supplémentaire, horizontale et dans laquelle le boss s'assomme lui-même (ou écrase Bilou) quand il arrive trop près des murs.

Saturday, May 20, 2023

Zelda Breath of the Kingdom

Parce que non, j'avais pas pré-commande de Tears of the Kingdom. Il faut dire que je n'avais pas été complètement convaincu par Breath of the Wild, même si je l'ai finalement acheté pour mon compte après avoir fini celui que mon frère m'avait filé. Au bout d'un mois et demi. Bon, je dis "fini", en vrai, ça voulait juste dire "vaincu Ganon". En mode de base. S'il est vrai que ce sont surtout les enfants qui ont en suite joué à "Charly Top Chef", j'ai quand-même repris la manette (malgré son joycon drift) pour trouver tous les souvenirs de Zelda et débloquer une fin un poil moins crispée.

Mais non, je n'ai toujours vaincu aucun Lynel, peut-être même aucun Hynox ni aucune de ces grosses bêtes. J'ai un peu tâté du dragon (excellente idée, ces dragons: une des meilleures du jeu) et des équipements inhabituels, mais sans chercher non plus à compléter la tenue archéonique ni rien de ce genre. Bref, j'aurais facilement pu continuer à faire des choses intéressantes dans le jeu pendant des heures et des heures, mais je ne l'ai pas fait.

Le fait que le jeu demande de passer de longs moment juste à "tenir la barre" en allant d'un point à un autre y a joué pour beaucoup, mais par-dessus tout, il y a le sentiment que ce serait immoral d'aller faire tout ce genre de tourisme alors que Zelda est toujours dans le château à essayer de confiner Ganon. J'aurais tellement aimé qu'avec un DLC ils nous rajoutent la princesse en PNJ que l'on escorte d'un village à l'autre comme suggéré dans la séquence de fin. On serait allé repousser un Lynel pendant qu'elle discute avec les Zoras. Ce genre de choses.

ça, ça aurait eu du sens. Mais à ma connaissance, rien de ce genre n'a jamais été proposé.

Bref, il aura finalement fallu que je me retrouve complètement HS, incapable de dessiner ou même de lire, trop assommé pour décider d'un truc à regarder à la télé. Alors pourquoi pas regarder Link se balader sur le plateau du prélude ?

Friday, May 12, 2023

using between

 

Bon, imaginer un gameplay sympa avec des ponts, c'est une chose. Mais comment programmer le comportement de ce genre de ponts au fait ? hein ? La bonne nouvelle, c'est qu'on a re-fait un passage dans une autre pleine de jeu avec encore plus de ponts, de cordes, et j'en passerelles et des meilleures. Une image comme ci-dessus, par exemple, suggère que Bilou est nettement plus massif que le reste des rondins, sans quoi il ne perturberait pas autant la répartition des charges sur la corde.

So, I now have fun ideas of how to use bridges in Bilou: Dreamland. Sounds like it could be a good idea to also figure out how I'm going to implement such things with my game engine... One thing is almost obvious: the bridge will be made of a set of independent-but-interacting game objects. I'm not going to try and make it one big animated object. If I did, I'd have to manually adjust collision boxes for segments for every frame. That's not what it is for. Same observation applied to the Green Zone boss: Big Caterpillar. And much like the bridge, Big Caterpillar is made of segments that must be able to interact to give the illusion of a single object.

Mais revenons un cran en arrière, voulez-vous ? Alors que J.L.N était face à son premier mini-boss dans Minish Cap, je me suis mis à prendre quelques notes sur le type de contrôleurs qu'il faudrait ajouter à mon moteur de jeu pour pouvoir faire quelque-chose de ce genre. Puis, sur la lancée, à ce que je pouvais en tirer comme conclusions pour la réalisation de Big Caterpillar, le boss de la Green Zone.

Il y a déjà une chose que le moteur de jeu de School Rush peut faire: c'est attacher un Gob (objet mobile fait de sprite(s)) à un autre, et utiliser sa position dans certains contrôleurs. C'est ainsi p.ex. que la chauve-souris-baie se dirige vers Bilou. ou que SpongeBop sait se balancer au bout d'un clou. C'est encore comme ça qu'on peut transporter un dumblador et que ses pieds le suivent à la trace. Mais dans tous ces exemples, on a un seul objet de référence et un objet référent. Quand il y a plusieurs référents (3 BerryBats) pour un même objet de référence (Bilou), ils s'ignorent les uns les autres. Or, pour permettre de positionner les segments internes de Big Caterpillar entre sa tête et sa queue, il faut les coordonnées des deux autres. C'est forcé :P

So since I've just been studying a bit how BC could be implemented, let's see what it can teach us. The game engine can attach one Gob to another one. That's how SpongeBop swings and how BerryBat aim at their target. It is quite clear that those "attach" links will play a role in animating BC, but the question is "who should be attach with whom, and why". When BC will be crawling, for instance, it is easy to make his head move forward during the "expanse" phase and then make the tail move forward (while the head is stopped) in the "contract" phase. As the distance between head and tail shrinks or grows, the location of other segments should adjust.

It is quite easy to find math expression for those locations based on head and tail coordinates. Unfortunately, that'd require *two* attach links per segment, and the game engine only handle one (including in expression opcodes to attach and detach things when collisions occur). I either have to revise the whole engine and break the assumption that One Attach Link Shall Be Enough For Everybody, or I have to come up with some clever way out. That way out could be how a between(...) controller could use not only the attached gob as a reference, but also *the one to which that is attached*. I.e. segments are all attached to the tail, tail is attached to the head and when segment is processed, it reads segment.attach == tail and tail.attach == head to get the data it needs.

Mais gérer deux pointeurs (un "avant" et un "arrière"), ça compliquerait méchamment les choses. Entre autres, ça rendrait difficile le choix de 'qui doit bouger le premier", et ça demanderait une série de bytecodes supplémentaires pour les opérations "attache, détache" des expressions de la machine d'état (que j'aurais cru plus nombreuses. [ ] à vérifier).

Let's see whether that can be enough. I also long for the opportunity to have swinging vines in some Bilou game. They could also be made of chained Gob through attach links. At least when they're hanging free. But as soon as Bilou is using them, I'd like the rules to change. Now Bilou's position dictates how one part of the vine behaves because of tension. If we can use collision to re-write attachments when that happens, that turns into a job for between(...) again.

Retournons vers le pont en faisant une halte à une autre mécanique que je tiens à faire apparaître dans Dreamlands: pouvoir s'accrocher à des trucs qui pendent. J'aimerais depuis longtemps que ce genre d'objet dans Bilou soit au moins aussi bien animé que dans le remake de Pharao's Curse par Lazycow. ce qui suppose

  • un mouvement ondulant lorsque la liane est livrée à elle-même (pas de balancé excessif à la DKC)
  • une certaine raideur quand Bilou s'y accroche (pas question de passer en mode 'corde ninja')
  • Si Bilou n'est pas au bout de la corde, l'extrémité libre continue d'agir librement alors que la moitié tendue est entièrement contrainte par la position de Bilou et celle du point d'attache.

Si j'essaie de dessiner des petites flèches rouges sur ce schéma pour représenter "qui a besoin de connaître la position de qui", on voit que la corde devra réorganiser ces liens quand elle passe d'un "état" à l'autre. Normalement, c'est jouable avec des collision. Et ça confirme mon intuition construite avec Big Caterpillar: le jeu, c'est d'avoir les deux points de référence (début et fin) qui sont directement liés l'un à l'autre (fin->attach == debut) et un contrôleur between() qui suit segment->attach pour trouver fin puis fin->attach pour avoir début et détermine la position-cible à partir des positions de fin et début.

Et pour le pont ? Va-t-il forcément falloir deux liens par bûche ? Je crois que non, mais ça supposera de faire apparaître à la volée deux objets "coincés" par collision sous la bûche qui a reçu un poids, chacun étant la fin d'une chaîne qui remonte jusqu'à un des points d'attache du pont. Et donc que la largeur maximale pour les zones de collision déterminera la largeur maximale du pont.

In Theory, it should be possible to do something similar with the bridge, at least to get a nice alignment of the logs when the force applied is so strong that logs' weights becomes neglectible in comparison. Yet, it's obvious there will be two chains here, both using the same "origin" location (the log on which the force is applied). I thought it could be fixed by creating temporarily two GOBs (red dashes) that are pushed by a collision box of the weighted log (red hatching), each being attached to either side of the bridge (red circles). "slave" logs that must just align would then attach to the dashed-GOB that's on their side (green arrows). But maybe we could even do it without the dashed-GOBs if sides attached to the weighted log and not the other way round.

Saturday, May 06, 2023

Funky 2.1 / A bridge to fun


Il y a bien longtemps (wow. 11 ans!), j'envisageais de faire un niveau vertical plein de Funky Funghis mais je voulais aussi quelque-chose qui soit plus original que la caverne aux champis de Commander Keen... Peut-être en utilisant un levier comme dans la BD.

Sauf que ... j'essaie que la forêt donne la sensation d'un environnement pas trop artificiel. Quelques statues, ok. Un puit, passe encore. Mais des planches-levier-d'acrobates ? J'aimerais autant pas. Bilou qui transporte des cailloux et des planches pour fabriquer lui-même le levier ? Je vais perdre les joueurs en route.

Back in '93, my brother had drawn a jumping mushroom, directly inspired from the Shadowlands of Commander Keen, and placed a few in the Green Zone. Back in October 2000, it became "Toadstool", the first ambiguous character, that is not 100% a baddy, but still not 100% friendly either. In February 2009, it became "Funky Funghi", and in 2012, as I tried to make it an area with plenty of them, again drawing inspiration from the Cave of Descendent of CK4, where we'd sometimes have one appearing from the top of the screen, sometimes have one catching our feet from below, and so on. There was also some places with something acting like a teeterboard, where Funky Funghi's siblings would (unvoluntarily?) help Bilou by propelling him upwards as they bop around.

J'ai tenté d'autres approches, inspirés par l'une ou l'autre vidéo let's play (mais j'ai oublié lesquelles ^^" de l'excellent Tiny Thor à venir), comme cette gelée/résine dans les branches d'arbres ... d'un point de vue purement mécanique, ça pourrait faire l'affaire. D'un point de vue intégration dans l'univers ... euh ... bof bof.

There was such teeterboard in the webcomic, and it is unclear where it came from. Bilou uses it for revenge. That could have been something Bilou made up on purpose.. who knows. But it would feel odd to see some in a green zone level, says my 23-years-older brain. I tried to come up with alternative recently, like this sort of jelly which could possibly work in another environment by which I can't convince myself truly works within a giant hollow tree.

Puis mes gamins sont passés à nouveau sur une plaine de jeu, et dans les plaines de jeux, il y a ces ponts suspendus sur lesquels les uns passent en courant pour faire tout trembler tandis que d'autres râlent, cramponnés aux rambardes, mais tentent de passer quand-même. Puis il y a ceux qui sautent pour tout secouer encore plus. Et dans ma green zone aussi, il y a des ponts (même si je n'ai encore presqu'aucun post qui en montre ^^"), bien qu'ils étaient assez peu marquant dans la version BASIC (en gros, certains morceaux pouvaient tomber. Voilà).

Mais pour "dreamland", ce serait assurément assez sympa que ce côté "un pont, ça tangue" soit exploité, et en particulier, que Funky Funghi puisse faire "voler" Bilou vers le haut si on a le bon timing.

So I'm glad my kids were on a playground last week-end, one with those bridges you run across to shake them more than the lad before did. There *are* bridges in the green zone already (I just haven't shown them to you yet). No sane level designer making a game a few years after Sonic the Hedgehog would craft a platformer without bridges. And I had thought of using them as trampolines, where you slowly get higher and higher by jumping again and again. Why not have Funky Funghi "use" them to propel you ?

Evidemment, laisson à Funky Funghi son côté "utile, oui, mais attention". Par exemple, si Bilou se contente de passer sur le pont, on peut très bien imaginer que chaque fois que Funghi atterrit, il secoue assez le pont pour que Bilou perde pied. S'il retombe d'un grand saut, il pourrait faire décoller Bilou -- plus ou moins l'équivalent d'un saut normal, mais pas forcément vertical.

Now, it's still Funghi. Even when it is useful, it remains dangerous. we could imagine that if Bilou doesn't "prepare" the bridge for being propelled, he would just be bounced away towards Funghi, but not much higher than a regular jump would have. We can also imagine that when Funghi makes just a small bop, it just makes the bridge shake and be somewhat stunned by that.

Bon, un truc pareil, par contre, il faudra l'introduire pour que le joueur puisse l'exploiter. Par exemple quand c'est Bilou qui joue le rôle de poids lourd et un autre personnage (au hasard, Appleman) se fasse propulser. Ça ne sert pas à grand chose, mais c'est rigolo. Ne pas oublier de mettre des trucs rigolos dans un jeu vidéo: on est là avant tout pour s'amuser.

It will take some training if we want the player to figure out things like that. And training requires some sandbox to experiment. So why not make sure that the player will have encountered easy-to-handle baddies (e.g. applemen) on bridges before and had the opportunity to realize that stomping on a bridge has side-effects. It shouldn't be too hard to make it fun and it's always good to have a few things that are just fun in a game.

Tuesday, May 02, 2023

A bit of shadow

Once upon a tweet, was a blog post about making dsgametools widget more readable and nice to use by adding a shadow to the default font. It was the youngest of the Todo family and its sibling would often make fun of it because it had the lowest possible priority for the project.

But then, on an idle day were the coder so tired he couldn't read a screen, though he had the Todo category open on his boox. "Well, why not" he thought, and he let our little Thumby Todo post jump over a blank page of his notebook.

It started sketching one letter, with its right and bottom empty lines of pixels, and split it in 3: Darkness, where light normally never comes; Highlights, where light might strike harder. And it turns out that if Highlights were to fall and the darkness picked up its power, then the letter would look all flat. And then the color that never changes, regardless of whether Highlights or Darkness is winning.

Bon, quelque part entre 2 scorpions, j'ai eu envie de creuser un peu cette histoire d'ombre pour les caractères dans les outils DS. Le mockup donne tellement mieux que mes captures d'écran ... en plus, ça se prête à merveille à un peu de programmation sur papier. Un petit schéma d'abord... puis quelques routines comme à l'époque du cours d'algo. Et puis j'ai fini par me rendre compte que chaque fois que je passais devant cette page-là dans mon livre-ordinateur cahier-agenda, ça réchauffait un peu mon coeur de codeur de penser qu'un jour, peut-être, j'aurais le temps d'essayer ça.

Very well, thought the coder, but I do not feel like hand-painting every 128 characters of the default font. Can we find a simple set of rules that, given a regular 1-bit-per-pixel font would deduce the 2-bit-per pixel font with highlights ? Yes, of course. And that's a perfect fit for bringing back paper coding.

Well, as usual, it then took a good deal of checks and debugging to find where that code should actually be invoked, what prevents it from working and so on. 

Et puis zut. On est dans un projet perso pour le plaisir, pas dans un projet-pro pour le client, donc au diables le Canban, les priorités, les milestones et tout ça. hg branch et voyons ce que ça donne... eh bien, ça donne bien. J'adore. Juste un hic: ça verrouille presque 40 couleurs de la palette utilisée par SEDS. Ou plutôt, dès qu'on charge un .spr qui utilise plus de 216 couleurs, l'effet est ruiné par un texte qui devient illisible. Parce que oui, j'utilise les palettes étendues de la NDS pour le moteur de jeu et pour presque toute l'interface graphique sauf pour le texte et ... pour le widget "palette" :-P

But there it is. Working. And even on emulator, the feel of it is just wonderful. And all it takes is some NES-glory palette swapping, where the text is moved from the 'interactive' palette with black shadow to the 'active' palette where 'shadow' is actually white and highlight has background color, as if the text had been pushed down by one pixel. Unfortunately, there's a price to pay: instead of 4 system colors, I need 40 of them (2 x 16-color palette + 4 on the 3rd palette). And so far, in SEDS, that means we can only use 216 colors instead of 250.

edit: but there is hope: SEDS is currently *not* using the multi-palettes mode (unlike LEDS) It likely wouldn't suffer from some extra system colors if it did. Converting AnimEDS sounds less appealing, because that has all its interactive windows on the 'main' screen rather than on sub-screen like older tools ^^"

Thursday, April 20, 2023

Retour chez les Minish

 

J'ai *enfin* remis la main sur ma cartouche de Minish Cap! C'était bien à la S-Team que je l'avais confiée, mais à Mlle A, et non pas à M. N (oui, ils ont grandi, maintenant). Du coup, pas besoin de prendre tout de suite un abonnement Nintendo+ avec ma switch et J.L.N a donc commencé un 4eme Zelda en parallèle. Il a pas mal avancé tout seul: tout le premier donjon. Là, il coince dans le village et insiste sur le fait qu'il *faut* qu'il puisse y devenir tout petit pour aller dans le Mont Gongle poursuivre sa quête.

J'ai donc relancé une partie moi aussi, parce qu'il y a une chose dont je me souviens bien dans ce jeu: les 'clés/portes' sont parfois loin d'être intuitives. Prenez ce tonneau, par exemple, qui renferme un item indispensable à notre progression. Lors de votre premier passage, il y a de fortes chances que des minish montent la garde devant chacune de ses entrées (et pas de chance, vous ne savez pas leur parler). Trouvez le minish qui connait la langue des humains, il vous dira d'aller dans le tonneau prendre ce qu'il faut pour vous faire comprendre. 

Oui, mais bon, c'est pas cohérent, là. "Comment j'explique au minish qui ne me comprend pas qu'il doit me laisser entrer dans la réserve pour prendre un truc pour qu'il me comprenne, hein, M. le Sage ?" que je voudrais lui dire, moi, au NPC. Logique non? Bin, ne te tracasse pas tant, mon grand: une fois ce dialogue passé avec le Sage, pouf, tout d'un coup, les minish qui montaient la garde sont partis casser la graine ou je ne sais quoi. Champ libre. Et des comme ça, il y en aura des tonnes (oh!).

Wednesday, April 19, 2023

Fury 2 ?


Je dois reconnaître que celle-là je ne m'y attendais pas, mais figurez-vous qu'une équipe s'est mise en tête de nous faire une reprise de Fury of the Furries! Eh oui. J'en veux pour preuve une vidéo "playthrough" du nouveau château. Bon, je suivais à distance le projet depuis un moment, et je ne peux évidemment pas m'empècher (après Giana et Bubsy) de juger "ce qui marche et ce qui ne marche pas" dans cette adaptation.

Tout bon: le pixel art. Sur les décors principalement, bien que les sprites ne soient pas en reste. Les graphistes semblent avoir bien capturé le côté "di-chromie" du premier opus et s'en servent judicieusement (mur du fond dans les tons mauves et boiseries qui ressortent bien) ainsi que les "textures" variées des briques et autres éléments de décors.

A améliorer: la bande son. N'allez pas me faire dire que l'artiste en charge ne fait pas de belles choses, mais sincèrement, comparé au travail d'El Mobo sur l'original (remixé?), on se sent loin du compte. Ça manque de punch et de personnalité. J'aurais presque envie de dire à CJ "allez, je leur laisse Pikaboo Castle" (mais il est déjà dans Wolfling ^^")

Know what? One of the authors of the original Fury of the Furries game has started an indie Furry 2 project! From times to times, we get videos of in-progress game development - and recently, that was a full playthrough video of one of the environments: the castle. Great pixel art, improvable soundtrack and a mood that feels like keeping the genome of the original game. Let's see whether we can say more about game design...

Interpellant: la longueur des niveaux. Fury, c'était un 'puzzle platformer'. Pas que la dimension "casse-tête" soit majoritaire comme dans un "Toki Tori", mais enchaînant des niveaux plutôt courts les uns derrière les autres. ça donne la possibilité de se concentrer sur l'exploration/l'observation d'un des niveaux pour trouver les mécanismes à activer, les passages secrets, etc. Ici, on a une map plutôt grande concentrant tout ce qu'il y a a dire (?) sur le chateau, et dans laquelle on nous invite à trouver un bon millier de fruits et légumes cachés. Pas loin de 40 minutes pour une exploration exhaustive sans respawn, c'est long, comme session de jeu.

Levels are much *much* longer than in the original puzzle-platformer game. That somehow make sense since the 'puzzle' part seems to have been dropped in favour of a more regular "explore-platformer with some local puzzles to get extra things". When you're designing a puzzle, you want to expose as much of the items to your player's eyes, so they brains can solve the puzzle instead of trying to remember what they've seen elsewhere. You also need to ensure failing the puzzle (which often implies dying or retrying from the start in the original game) won't ask your player to redo too much again, and short levels help on that. Still, here we've got about 40 minutes of video for a collect-all-items playthrough.

Mitigé: les pouvoirs. Fury, c'est 4 pouvoirs différents et (le plus souvent), la possibilité de passer de l'un à l'autre. Jaune = attaque, Rouge = mange-mur, bleu = nageur, vert = acrobate à la corde ninja. Le principe est conservé ici, bien que si je ne me trompe pas, il est maintenant possible de changer de pouvoir instantanément, sans attendre d'avoir mis son personnage à l'arrêt (ce dont je ne me plaindrai probablement pas). J'avoue que le côté " vert se balance avec sa langue" a un côté dégueu-kid-paddle dont je me serais passé, mais je suis prêt à le concéder comme liberté artistique des nouveaux (?) auteurs au même titre que je passe l'éponge sur l'exagération graphique de Rayman Origins/Legends. 

Bleu peut maintenant cracher de l'eau, ce qui donne lieu à quelques interactions sympas (je ne vais pas appeler ça des puzzles) comme ici avec un canon qui perd toute sa vigueur si on noie sa mèche. Il faut toujours Jaune et ses kameah pour se débarasser de la plupart des ennemis, donc ça reste équilibré et ça atténue un peu l'effet "Bleu ne sert à rien, y'a pas d'eau" du jeu original.

Rouge a maintenant la possibilité de voler en pétant. C'est kid qui va être ravi. La mécanique est rigolote, le contrôle en vol plutôt correctement dosé (à vérifier manette en main) entre trop fort et pénible-à-la-Donkey-sur-un-tonneau-fusée, l'habillage (comprenez les prouts) bien en phase avec le personnage (dévore tout et n'importe quoi) et la fonction (mon filleul parlait déjà des prouts de Yoshi pour justifier sa faculté à voler un petit moment dans Yoshi's Island). J'ai un peu peur que ça ne rende les acrobaties de Vert fort anecdotiques, par contre. Pourquoi se prendre la tête à faire le ninja quand on peut léviter ?

The general idea of 4 characters with different abilities and a way to swap between them (you only ever control one avatar) has been kept and the overall ability scheme has been preserved: Yellow shoots, Red eats, Green swing and Blue swims. Well, Blue can also exhale water (solves some puzzles, and used as a weak weapon). And green can slurp bonuses at a distance (possibly swallow small baddies ?). The most modified one is Red. He can now fart-fly for significant distances. To what extent will that break platforming or rope-swinging, I'm unsure of. I understand this won't disappear because it was the core gameplay of a mobile game I've never played. It doesn't look very balanced on the video, but maybe that's because player has already collected some fart-more-power-ups or something similar (I see hints that Yellow has now limited shots despite the counter never goes down, either). Anyway, since there are already portals that forbid the use of one character for one area, there's a clear way to enforce player would use rope-swinging or jumping in some specific room if level designer decides so.

Enfin, et c'est sans doute lié au passage du puzzle à une approche plus "collect-platformer", les retraits de pouvoirs semblent plutôt rares. Le jeu d'origine nous mettait souvent dans un niveau avec seulement 2 pouvoirs, et un 3eme à réactiver dans une zone précise. Ici, c'est plutôt "les 4 font la fête" en permanence avec quelques passages "Regarde Maman! Sans les mains!" pour l'une ou l'autre salle bonus.

Très bien: le côté décalé. On a gardé les loups-garous qui se changent en lapinou, les parodies de Dracula, les clins d'oeil aux flippeurs et compagnie. Trop tôt pour dire si les cameos seront aussi nombreux que dans l'original (en particulier dans le niveau final. On dirait bien), mais on a ni renié ses origines façon préhistorik-supernes, ni sombré dans un excès qui perd toute saveur façon film de Jim Carrey.

A tester: la barre de vie. Fury était un die-and-retry. Tout comme Commander Keen et Rick Dangerous, tout contact y était fatal. C'était précieux pour certains puzzles mais ça pouvait devenir franchement gonflant quand le moindre pet de mouche dans la tronche vous éclate. Là, avec 6 points de vie en début de niveau et des ennemis qui explosent au moindre tir (le jeu d'origine vous demandait régulièrement de bourrinner le bouton de tir 5 à 10 secondes pour la moindre araignée) et distribuent généreusement des petits coeurs, j'aurais du mal à fixer un pronostic.

One last item that might be worth tuning is the amount of hitpoints for both baddies and furries. The original game had a die-and-retry aspect for the player while any baddies would require numerous shots to be defeated. Here this is the opposite: plenty of hitpoints for the player and most ennemies explode in one shot, often releasing a heart for the player to recover. I know I've been wondering whether some god mode was enabled while watching the video. But it wouldn't be too hard to make that customizable by the player, adjusting heart-drop-probabilities or baddies resistance to Yellow's shots through menu or collectibles. Don't let that prevent you from wishlisting the game!

Bref, vous l'aurez compris: Fury 2 n'est pas un remake de Fury 1, mais il reste fidèle à l'esprit et à l'ambiance de l'original. J'aurais envie de comparer l'évolution de gameplay à ce que l'on a pu voir entre Ori 1 et Ori 2. Je souhaite une bonne finalisation au projet et un grand merci au Cyrille qui bosse là-dessus de nous proposer un retour en Furries. Wishlistez bien ^_^

Tuesday, April 18, 2023

std::unique_ptr<>

Okay, it shouldn't be that hard to have a variable that gives access to an object RAII-style while allowing late initialization: I should use std::unique_ptr

RAII is neat. Despite having an impossible-to-use-and-remember-name (don't be surprised if I use 'Rabi' instead ;), it means that you spend less time catching/rethrowing exceptions, less time checking return codes (because you used exceptions) and can think of your software as something as reliable as a building instead of some Jenga game. Want to have some code dealing with some file, but don't want to forget closing the file handle if anything goes wrong ? Simple.

  • have a RabiFile class that encapsulate your file handler
  • have the ctor of RabiFile do *everything* that's needed to be working on the file
  • have the destructor always going to a clean state.

As soon as you've written something like RabiFile music("forest.xm", RabiFile::RO), you're good to go

  • either your RabiFile exists and all its methods are now valid
  • or you failed to create it and an exception has been thrown, taking you out

So with that approach, you'll never write any music->open("underwater.xm") nor any music->close() anywhere. That makes writing of constructors a *bit* more complicated, I admit, especially those for objects that contains some RabiObjects: they have to catch exceptions and rollback any 'personal' resource acquisition they performed because nothing invokes destructor on objects that haven't been fully constructed. If your constructor fails with an exception, it's up to your constructor to ensure it leaves no mines behind.

But that usually don't happens a lot. It does happen when I stretch the RAII fashion to long-lived objects like GameLevel that has LevelMap and SpriteSet(s), or to GobState that has GobTransitions, though. But Rabi* is mostly for temporary things that need us to hold something while something else.

There's one common drawback to both, though. Because my objects have to be allocated on the stack to benefit automatic cleanup on failure, it is tricky to benefit from polymorphism. Say I should either create an instance that reads the script from a file or an instance that reads it from a buffer received over WiFi by runME ... I can't just replace BufferReader reader(...) at the head of my function by


loaderCode() {
   if (fromFile) {
       FileReader reader(whichFile);
   } else {
       BufferReader reader(whichBuffer);
   }
   DoStuffWith(reader); // no such reader, dude.
}

because there, the condition-dependant objects are no longer valid when I then want to use them. And migrating the DoStuffWith into the condition will soon turn unpleasant as well because it is not DRY. So instead, I can have


loaderCode() {
   std::unique_ptr<InputReader> reader;
   if (fromFile) { 
      reader = std::unique_ptr<FileReader>(new FileReader(...));
   } else {
      reader = std::unique_ptr<BufferReader>(new BufferReader(...));
   }
   DoStuffWith(*reader);
}

Granted, that will not allocate the object on the stack but at least it guarantees that the created object gets deleted whatever happens during DoStuffWit(*reader). Given the size of the NDS stack, it might not be a bad move.
Maybe I could have done it with auto_ptr instead. It seems it was the way to do it before C++11 came, declared auto_ptr obsolete and unique_ptr as being the way to do this instead.

A few things to remember when working with unique_ptr

  • you must use `std::move(reader)` if you intend to return the unique pointer as function value
  • you must use std::move(aswell) if you want to capture some unique pointer you've received as a member of a new uniquely pointed thing

  •  

Wednesday, April 12, 2023

StyleHax on DSi

For (too?) long, I've staid aside of DSi homebrew. Partly because there's little the camera could offer to my projects and I don't really need more than 4MiB of RAM, but mostly because until recently, running homebrew on that device felt like an arm race with Nintendo. You need to break into the machine through exploits the same way you'd take control of someone else's server on Internet and that has a taste of dark side I'm not fan of. Plus, once you've managed to get into things, you then have to install more permanent modification if you want to be sure to be able to do the same the next day, since Nintendo could ban the vulnerable software you've used to gain access (if you were lucky enough to have it on your device on first place).

But one post on hackaday might well change all this. Nathan Farlow has found a way to use the web browser shipped with the DSi firmware to execute arbitrary code.

 He just had to get his code into the right spot. For this he employed what’s known as a NOP sled; basically a long list of commands that do nothing, which if jumped into, will slide into his exploit code. In modern browsers a good way to allocate a chunk of memory and fill it would be a Float32Array, but since this is a 2008 browser, a smattering of RGBA canvases will do.

Ultimately, that will execute a boot.nds file from the SD card, such as a homebrew launcher.

Octobre dernier, j'avais jeté un oeil un peu plus attentif à "Comment on peut faire tourner des homebrews sur DSi". S'en était suivi un graphe compliqué dans mon calepin indiquant quel menu dépend de quel installateur qui dépend de quel firmware custom. Mais au final, on finissait toujours par dépendre d'un "hax" pour prendre le contrôle de la console. Un vrai piratage en règle, quoi, et qui dépend en général de la disponibilité d'un logiciel vulnérable sur sa console.

Pendant toute la durée de vie de la bête, Nintendo a mené une chasse farouche à ce genre de vulnérabilité, supprimant jeux et applications du service DSiWare au fur et à mesure qu'elles étaient identifiées, étendant sa liste de cartouches "nds" interdites (dont les clés avaient été cassées et utilisées pour des linkers) lors de mises à jour système indispensables pour faire tourner les nouveaux jeux ou refaire un téléchargement sur le DSiWare. J'étais resté à l'écart de tout ça. Mais j'ai commencé à réviser ma position: un nouvel "exploit" (comprenez "mécanisme permettant de faire tourner son code à soi en exploitant une vulnérabilité) à été trouvée, et pour le navigateur intégré, tout simplement!

It should be trivial to try: install your favourite DSi-capable software as boot.nds on an SD card, direct your DSi browser to nathan's web page and voilà. Except nothing guarantees the contents of Nathan's page will stay the same over time, so I'd rather download that locally and study the stylehax source code before I start using that on my own device. 

At the start of the chain is an html document with some NDS binary embedded as "shellcode" in the html <script> part, plus some javascript to exploit the use-after-free bug of the browser. The payload.c featured in the stylehax repository contains actually just a little loop to inject the 'true payload' at some dedicated location (held as a blob into payload.h). And that true payload comes from another repository (well, actually, that second repository contains many exploits, but the one we'll use is the 'minitw' one, according to payload.h)

As soon as the 'payload.c' part is running, we already have access to most resources of the DSi. The goal of minitwlpayload is to setup the proper context for the default bootloader of the devkitpro project embedded into the .nds (yes, that comes from a third repository :P) The bootloader will mostly run on the ARM7 core, which is the only one to have access to SD card registers on the DSi. There will be many passme-loop-synchronization-points until we're ready to execute the NDS or DSi code contained in boot.nds, one for each ARM9 function we want to execute between two ARM7 functions.

Pour celui qui veut tenter sa chance, rien de plus simple! L'auteur de l'exploit a mis en ligne une page HTML (une seule) intégrant le code nécessaire pour aller exécuter le fichier boot.nds qu'on trouverait sur la carte SD de la DSi et le javascript qu'il faut pour faire bugger le navigateur au point qu'il se mette à exécuter un morceau de page web canvas HTML5 comme si c'était du code machine. Et surprise, c'est du code machine, cette fois-ci! Enfoncé, HTML Quest ;-)

One question remains, though: how does ARM9 exploit trigger execution of custom code on the ARM7 ? It looks like the answer lies within the following snippet of exploit.c:

    memcpy16(VRAM_C, decrbegin, decrsize); // writing custom code into VRAM at 0x6000000
	VRAM_C_CR = VRAM_ENABLE | VRAM_C_ARM7_0x06000000;
    // now inserting a jump at the start of ARM7-dedicated memory area.
	*(vu32*)0x2380000 = 0xE51FF004; // ldr pc, =0x06000000
	*(vu32*)0x2380004 = 0x06000000; // (constant for above)
	debug_color(0,15,0); // dark green
	IPC_SendSync(1); // MAGIC!

For some reason, when we trigger an IPC IRQ on the ARM7 with code 1 (out of 16 possible codes), it executes code from the start of the ARM7 area again, where we have installed our trampoline to VRAM. Why does it behave so ? I guess the reason lies in some line of code for the ARM7 code in the TWL sdk, sealed in a vault at Kyoto ...  I presume I'll have to accept it without further understanding.

Seulement moi, j'ai envie de comprendre un minimum ce qui se passe. Histoire entre-autre d'éviter que la page ne télécharge un jetaibieneu.nds au lieu de démarrer hbmenu.nds rebaptisé boot.nds, par exemple ?Et aussi pour enfin comprendre cette histoire de .twl et donner une meilleure chance à l'exécution de mes homebrews sur DSi. Donc je parcours les pages github, ma liseuse dans une main, mon calepin dans l'autre...

exomizer/exodecrunch

One notable action of exploit.c from minitwlpayload is to "decrunch" the embedded bootloader into VRAM where it will execute, that is, to undo the compression performed by the exomizer tool invoked by dsi/exploits/minitwlpayload/Makefile.

  • it packs the 'bootloader' from devkitpro github from 6KiB into a bit more than 4KiB. Unsure whether it was critical to use in this context :-P
  • ndsload.bin is just an 'objcopy' binary-extraction of the ndsload.elf
  • https://bitbucket.org/magli143/exomizer/src/master/src/Makefile could be the source for the exomizer tool, but I fail to get why it would need a 6502 emulator ... (I guess this is linked to the 'self-extracting for C64, Apple II and BBC micro' feature)

Possibly, such steps could make sense for larger payloads or for other exploits where the amount of data we can embed is limited (remember, there has even been exploits involving QR code readers)

Thursday, March 30, 2023

NDS Dev in 202x

Back in february, 19-yo Violet! posted an animated version of the low-poly DS collected 167K likes and over 380 comments, most of which going towards "my childhood" nostalgia. Clearly, the NDS is now a retro console. That may not bring a wave of NDS development the same way we saw people starting to do SNES development a decade ago, but that's enough to push some 23-y-o 3D artist to convert their model into a .nds just to check what it'd look like and animate their hair with more code.

I've been surpised to hear the snes-lady Skwirl reporting that she had first wanted to write homebrew herself back in the days but dropped it because it was "too complicated". (she started some 3DS port of tilemap town, btw)

There are some ongoing NDS projects on github. I spotted one that pushes the 'reimplement X on NDS' to the extreme, bringing support for GBA pokemons, no less. Codestudy started. Probably tricky to get into it. And given how 'mature' the device is now, chances are that we find more ambitious projects like this (or a minecraft clone or cave story port) than the tiny one-screen flash/arcade-inspired titles of the neocompo years.

Curieusement, on dirait que ça ce ravive un peu, du côté du dévelopement amateur NDS, ces derniers temps. On est encore loin de l'age d'or, mais voir une Violet de 19 ans poster un modèle 3D de DS lite et récolter 380 commentaires parlant de "c'est toute mon enfance" et 167 mille likes, ça pourrait bien indiquer que la console est finalement devenue rétro, après tout. Un autre, de 23 ans, à carrément décider d'exporter le modèle 3D de son personnage fétiche en une ROM pour animer sa chevelure. Tout ça m'a remis en mémoire un commentaire plus ancien qui demandait "comment on fait un homebrew NDS, au fait". Et je dois reconnaître qu'une grande partie des tutos et ressources de l'époque ne sont plus à jour: les procédures ont évolué, les bibliothèques aussi ...

Resources
It reminded me of a series of tweet where someone was wondering how to get into NDS development. I have a tutorial git of my own, where each commit presents a part of what I use to build games on NDS, but that's teaching you to use libgeds, not libnds. There are devkitpro examples, too. I can't really recall whether their coverage is convincing for someone who wants to kick in. I myself have learnt with a mix of that and open-sourced projects from fellow homebrewers, but these are likely obsolete and wouldn't build against recent devkit versions.

The closest I've found is magusti tutorials. Most 'readmes' are in spanish, unfortunately and the directory structure is a bit confusing. It uses modern libraries, including maxmod. It uses 'all your data are belong to 4MB of RAM' approach, though.

Mais quelque petites recherches github ont quand même donné quelques résultats intéressants. Si la plupart des projets actifs utilisant libnds sont des portages plutôt ambitieux (et trop gros pour pouvoir servir de tutoriels), si la NFlib a le défaut de contenir encore pas mal de commentaires en Espagnol, on trouve quand-même deux ou trois choses directement exploitable, comme la balle-qui-rebondit de Magusti ou une adaptation de '2048'.

Maybe the best way would be to use NFLib ? Like in 2048-nds, a small puzzle game with a codestudy-firendly size ? It does restrict what you can do but usually in ways that won't hurt your project (like assume you don't want to use 16-colors layers), it has understandable wrapper functions and meaningful abstractions like auto-adjust position of sprites when scrolling background planes or delay OAM updates until vertical blanking happens. It let you chose between "my files are in fat:/SOME_DIR" or "my fat are in magical NitroFS (within the .nds, but requires compatible loader).

If that's fine for you, the projects Snake-DS seems like a nice thing to check first.

Getting Started

The windows installer 3.0.3 is still there, and it seemed to install things properly on a Windows 10 machine. Or so it seemed. Likely the Antivirus quarantining 'padbin.exe' during the installation process did no good, but all I actually get (apparently) is an msys2 install in C:\devkitpro\msys2 (which hasn't messed with the other msys2 pre-installed on that system. yahoo!) and that's it.

I did get a 'choose your packages' box, but it did not produce anything apparently. Well, pacman -Sl dkp-libs did indeed report what's available and pacman -S libfat-nds libnds nds-examples was all I needed to get the devkitarm (now r59) and the make rules as well. Some dependencies were still missing to build NDS examples though, namely ndstool (and I also missed dswifi because it's not named ndswifi ^^"). That's not yet enough. I'm still missing 'default.elf' from the default-arm7 pacakge :-/

So make your life simpler than mine: read the doc to the bottom and pacman -S nds-dev to install the pre-selection instead ^^"

Pour la forme, j'ai même voulu vérifier que tout ça compilait bien avec la version la plus récente des outils devkitpro. Eh bien, ça marche, et contrairement à ce que j'avais cru plus tôt dans l'année, il y a toujours bien un installateur pour windows (version 3.0.3). Bon, on ne s'emballe pas, il se contente essentiellement de nous filer un msys avec un gestionnaire de packages modifié. Je découvre d'ailleurs en fin de parcours la présence d'un méta-package 'nds-dev' qui pré-sélectionne tout ce que j'ai personnellement téléchargé à la main ^^"

Edit: looks like there's even someone who's busy designing a R4 substitute based on dual-core Raspberry Pico ...

Sunday, March 26, 2023

3 rooms on 3DS

Novembre 2021, un rétro-fan nommé Fei m'invite à rejoindre un chat francophone qui accueille déjà un certain nombre de mes contacts twitter versés dans le rétro-coding. J'y ai fait quelques apparitions, mais je dois bien admettre que j'ai du mal avec la formule 'discord' qui a tendance à semer la confusion dans mon esprit.

Du coup, quand j'ai une nouvelle petite démo avec les 3-salles, bin je leur poste le lien, pour avoir un peu des retours. Sauf qu'à l'époque, la plupart des dev' qui ont un matériel compatible avec les homebrews NDS ont en réalité une DSi ou une 3DS... et là, bardaf: écran bleu.

J'en arrive assez rapidement à la conclusion que le fautif est le support des vieux linkers utilisant le port GBA (qui est absent des DSi et 3DS). Les tentatives de lectures sur la plage d'adresse prévue pour le "gba slot" provoquant des exceptions hardware. Mais même une fois ce code-là commenté, toujours pas moyen de faire tourner la démo. En fait, le code n'a pas trouvé les fichiers de données "intégrés" au .nds avec l'Embedded File System de Noda. Fei utilise le "Twilight Menu++" ... je creuse un peu mais fin de semaine, je n'ai toujours rien. Perso, ma 3DS n'a encore jamais vu de homebrew de sa vie (malgré l'acquisition d'un OOT3D) et j'ai oublié comment je faisais tourner des homebrews sur ma DSi avant que ses triggers ne me lachent. Le tourbillon du printemps 2022 va emporter tout ça au pays d'Oz dans l'oubli.

My bad. Really. It would not have taken me one whole year figuring out why my homebrew wasn't running on DSi and 3DS if I had not been messing with the ndstool settings in an attempt to avoid 'twl' headers. I started studying how any exploit on DSi seems to ultimately rely on something called 'generictwlpayload' to launch devkitpro/bootloader. But chances are that if it does not find some specific signature in the binary, it will fail to inject hooks to e.g. SD card reading.

Anyway, this is *so* satisfying to finally see someone else (Fei, this time) being able to run my demos again. I hope I will find a way to make it compatible with desmume as well and quickly manage to backport that 'fix' to some SchoolRush build... just in case.

Mais là, ce week-end, pour voir comment je peux m'y prendre pour faire des tapis roulants, je me rends compte que mon mercurial sur sourceforge n'est pas à jour. de plusieurs mois ^^". En regardant de plus près je retrouve une micro-branche qui reprend mes tentatives pour corriger le problème. La différence, c'est qu'entre temps, j'ai été démarché pour une version "cartouche" de SchoolRush. Et que j'ai commencé à essayer de comprendre comment faire tourner des homebrews DSi sans linker, encouragé par un post sur hackaday qui promet un mécanisme ne nécessitant ni jeu particulier ni modification permanente. Juste un p'tit serveur web et le navigateur intégré au firmware. (j'ai encore un peu de codestudy à faire dessus, mais on y reviendra).

Tout ça pour dire que j'avais une idée bien plus précise de "twl = indispensable pour démarrer quelque-chose sur DSi" qu'il y a 4 ans quand j'ai fait la mise à jour de mon devkit. A l'époque, j'avais d'abord remarqué un gonflement de tous mes programmes, puis une incompatibilité avec desmume. Le coeur du problème, c'était un changement de fonctionnement de l'outil ndstool mais en voulant "forcer" le support de desmume, j'avais aussi sabordé le support des consoles DSi et 3DS. Une petite marche arrière et Fei est maintenant en mesure de faire tourner la démo "three rooms" sur sa 3DS ^_^.

Voyons donc si je peux aider desmume à comprendre ces "nouveaux headers" si chers au TWLoader, sans lesquels il n'essaie même pas de faire le patch DLDI des softs qu'il charge... encore que ... en 2016, je pointais que desmume n'avait plus besoin de --gbaslot-rom pour faire tourner SchoolRush ... à creuser.


Monday, March 20, 2023

qmake (CMake and 'friends' pour une autre fois)

j'ai mis un tag 'tutoriel', mais considérez ceci comme juste un moyen pour moi de ne pas oublier les griffes que le chat m'a fait pendant que j'essayais d'apprendre à me servir d'un nouvel outil

Bon, des Makefiles, ça va faire plus de 20 ans que j'en mange. C'est plus ou moins la base des règles de compilation. Vous indiquez un nom de fichier à obtenir, puis les fichiers dont il dépend, puis les commandes à exécuter pour passer de l'un à l'autre.


Monfichier.o: Monfichier.c FichierImportant.h
	compiler Monfichier.c -o Monfichier.o
     
Et bien sûr, vous avez le droit de rendre ça plus générique:

%.o: %.c FichierImportant.h
	compiler $< -o $@

Bon, assez rapidement, ça devient plus complexe que ça, hein. Par exemple, il y a peu de chance que tout votre projet dépende de FichierImportant.h et seulement de ça. On risque plus d'avoir quelque-chose comme jeu.c qui dépend de jeu.h et sprite.h alors que sprite.c ne dépend que de sprite.h et menu.c dépend à la fois de menu.h sauvegardes.h et sprite.h, par exemple.

Si on a de la chance, le compilateur offre alors une option pour générer des fichiers de dépendances qui capturent ça (disons, jeu.dep, menu.dep et sprite.dep) et on s'en sort avec


%.o: %.c
	compiler $< -o $@ $(ET_GENERER_LES_DEPENDANCES)

-include *.dep

Mais restons-en là pour l'instant. Dès que le projet grossit, qu'il commence à dépendre de bibliothèque externes (pour les formats d'images, la gestion du son, etc.), et surtout, dès qu'on commence à vouloir le compiler pour plusieurs plate-formes différentes (linux et windows?), ça devient vite un beau gros sac de noeuds ... en particulier parce que si make est omniprésent dans le monde Linux, il est toujours boudé par Microsoft qui y va de son propre msbuild.exe qui manipule des fichiers XML déguisés en .vcxproj ... D'où l'intérêt de programmes qui vont générer des Makefiles à partir de descriptions plus haut-niveau du projet à compiler.

C'est le cas de qmake, notamment, qu'on va exécuter dans un répertoire vide en lui indiquant l'emplacement de nos sources et qui va créer un Makefile dédié à la compilation de nos sources dans ce répertoire-là en utilisant le fichier *.pro distribué avec les sources.


  mkdir build
  cd build
  qmake ../src
  
  • il y définit tous les programmes utilisés (compilateur, linkeur, etc.) dans des variables pour make;
  • il donne une règle indiquant quand re-générer le Makefile (est-ce que le fichier .pro a changé?) qui réappellera qmake;
  • il ajoute les règles pour préparer une distribution du programme, nettoyer les fichiers intermédiaires, etc.
  • et surtout, parce que qmake est lié au projet d'interface graphique Qt, il prend en charge tout ce qui concerne la génération de code pour l'interface graphique à l'aide de moc.

A travers son fichier .pro, qmake prend en charge le fait que les différents compilateurs ont des préférences différentes pour les macro-définitions (à ajouter dans DEFINES) ou les répertoires à explorer quand il tombe sur un #include (à ajouter dans INCLUDEPATH). Idem avec les bibliothèques à passer au linkeur. On trouve évidemment aussi SOURCES pour les fichiers à compiler et HEADERS pour les déclaration de classes contenant des annotations pour le système de signal/slot propre à Qt (qui en aura besoin pour générer du code et compléter les vtables).

Une autre grande différence entre make et qmake est que ce dernier se veut multi-plate-formes et indépendant du shell. ça se traduit notamment par une manière franchement confortable pour les éléments conditionnels, comme


linux {
    LIBS += pthread
    !isEmpty(ENABLE_GL) {
       SOURCES += backend/opengl.cpp
    }
}
windows {
    LIBS += direct3D
}

Et autres astuces du genre. Attention par contre: si il était possible de faire des Makefile qui incluent d'autre makefiles (notamment pour combiner les résultats de plusieurs répertoires), la chose qui s'en rapproche le plus avec qmake c'est le TEMPLATE = subdirs, mais qui à l'instar de makefiles récursifs, ne partage pas les variables d'un fichier .pro à l'autre. Oui, je sais, en lisant ça, ça semble évident, mais ça l'était nettement moins en écrivant le makefile. La solution, c'est de faire venir la variable de l'extérieur soit avec qmake ENABLE_GL=y sur la ligne de commande, soit avec Qt Creator


 Une approche pas toujours très confortable, cela dit, en particulier à cause d'une gestion des dépendances entre variables et fichiers .o pas toujours très claire ... je comprends pourquoi mes collègues lui ont préféré include (../projet.pri) dans les .pro-feuilles et qui contient les DEFINES += ENABLE_GL=yes et autre MODEL_EXPORTER_PATH=/usr/bin/export_to_gl

Friday, March 17, 2023

Conveyed ...

It's not a surprise (or it shouldn't) if there are waterfalls and sandfalls in the Three-rooms demo. And especially, if there is ground between two of them. I want my game engine to be able to 'push' you when you're on some ground, like it would if the ground was actually a conveyer belt. And it's been a few years now that using sand/water falls for that integrates better than mere mechanical belts in most levels. 

I have notes in my notebook for quite some times, too, about how the game script should indicate what those blocks do. They're not quite "special blocks" (that interact with collision code), but rather "physical types" that could be polled by frame-per-frame controllers.

I'm not completely satisfied with the proposed syntax, though. I'd rather use type %d {...} and is %s "%x" for level editor identification (like for block %d {} describing interactive special blocks), and I'd use props %x, like we already have for special blocks.

The truly innovative part is %s.%s = ... pattern to setup variables into controller factories. That one will be a bit more complex to get running, because the map of factories is actually managed directly by the GobState class, and I'd rather not re-inforce the Using... anti-pattern here.


Friday, February 03, 2023

Souldiers Pyramid

Je l'ai découvert à travers une vidéo "Game of the Year", mais dans la catégorie "jeux les plus décevants". Pourtant, Souldiers est carrément superbe. Et principalement réalisé en pixel art. Et enfin, cerise sur le gateau, il propose généreusement une zone du sable avec une pyramide cachée dedans! L'idéal pour aller à la pêche au pixels, en somme, d'autant que même la perspective du jeu est compatible avec ce que je veux faire dans Bilou's Dreamland.

I happily clicked on a "game of the year" video without realising it was mentioning the "most disappointing GOTY". But given how gorgeous the visuals were, I've been shocked that this was actually referred to as disappointing. Souliders is a piece of magic when it comes to pixel art. Plus it has a desert and a pyramid level, so while people are debating about its difficulty, I'll just increase the zoom level and see what I can learn from it.

Bon, malheureusement, un jeu récent sorti sur une machine haute-résolution (il sort des images 1920x1080 où on distingue clairement des sprites et tiles prévus pour du 640xqqch), je n'ai pas beaucoup de screenshots "propres" à me mettre sous la dent. C'est surtout des vidéos youtube qui vont me servir de base.
Premier élément à réfléchir pour Bilou: des toutes grosses pierres comme décor de fond, ça marche finalement mieux que des briques à la MegaMan & Bass ...

It wasn't that easy as I'd have liked, though: the game is running scaled up to 1920x1080 with only some effects/rotations using the increased resolution while sprites and maps are at 640xSomething. Add some touch of JPEG encoding of people's snapshots and you end up with 9 candidate colors per pixel, and little chance to see the same RGB value twice. So my pixelstudy session will be less precise than some earlier pixelstudies ...

On a un élément 'pente ensablée', juste le look qu'il faut pour du sol qui pousse Bilou dans une direction ou l'autre (ce qui n'a pas l'air d'être le cas dans ce jeu-ci). J'aime bien les motif en croissant de lune qu'on retrouve un peu partout dans le sable, que ce soient les paliers dans la pente ou les dunes au sol (petites ou grandes), ça passe nettement mieux que tout ce que j'ai essayé de dessiner jusque là.

Among the interesting items, I note a sandy slope, just the kind I need to implement 'conveyer belts' in Bilou's own pyramid. Note the moon-crescents patterns in the sands. We have such shapes almost everywhere, in background dunes, on the foreground sands etc. but I especially like how they make 'steps' here.

Another item I'm interested in is the sands-within-pyramid. Souldiers features both patches of sands that do not fully cover the ground and 'pools' of sands. Note how they used layers of sands to suggest depth and end up with a solid color rather than texturing the 'side' view of the sands area.

Et on a des zones ensablées dans la pyramide. Et du sable qui traine au sol sans couvrir l'entièreté du sol. Notez au passage la manière dont ces développeurs-ci ont utilisé une teinte nettement plus sombre pour faire "le sable au sol à l'avant plan" qui sert aussi de 'fond de sable' pour l'intérieur de la pyramide.
Allons-y, donc. Un des éléments qui m'intéresse dans tout ça, c'est la taille choisie par l'artiste (un crayon dédicacé à celui qui me trouve son nom/instagram/twitterID) pour les 'vagues' de sable au sol, selon qu'elles sont à l'avant plan, à l'arrière plan ou sur la trajectoire prévue pour le joueur. Verdict: on est avec un modeste 8 pixels de haut que ce soit sur le chemin ou à l'arrière plan, et seuls les dunettes à l'avant plan vont pouvoir se permettre de monter à 12 ou 16 pixels de haut. Les plus hautes sont principalement devant des structures en rocher, d'ailleurs.
 
Chose remarquable, par contre, un "dune au sol" (ici le modèle 64x16 de l'image ci-dessous) n'utilise au final que 4 teintes en plus du ton de base "sable sombre" utilisé pour le remplissage quand des espaces sans détails. Les pentes avant et arrière de la mini-dune sont linéaires (sauf au sommet) et utilisent le même angle par rapport à l'horizontale (contrairement à ce que mon croquis suggère ^^")

It took me some time before I could actually start and study those sand grounds, but a first item I could directly check on the raw picture: what sizes the artist has picked for those waves. I have noted that Souldier's main character is about 40-48 pixels high (roughly the size of pendats). The biggest dunes are 64x16, but that's only seen out of the character's path, as foreground element, but always put under the ground line so that it doesn't obscure the view. On the ground line, we only see 8-pixel high dunes.

I spot only 4 colours per dune, plus one extra 'darkest' tone that is also used as a filler. Of course dunes on another layer will use a lighter set of 4 colours. Another unexpected thing is that the slopes on each side of the wave is are almost linear (except at the top) and fairly symmetric (unlike what I drawn ^^").

There's so much more I could talk about. Those rock strucures, the bigger dunes in the background, the fallen statues... But there's not a chance I could use so much things in "Bilou's Dreamland" project ... so that will be for much, much later ;-)

Il y aurait largement de quoi étudier ces structures rocheuses, les dunes à l'arrière plan et les monstres, mais bon ... je n'aurai pas le luxe de tenter tout ça pour le jeu en cours: juste l'intérieur de la pyramide, ce sera déjà un bon début. Gardons donc ça sous le coude pour un prochain jeu ?

Niveau couleurs, j'ai fait quelques petits essais de superposition des morceaux de sable de souldiers dans la pyramide de Bilou ... c'est pas la grande joie. Les couleurs de prowler passeront probablement mieux. à vérifier.