Showing posts with label dr23. Show all posts
Showing posts with label dr23. Show all posts

Wednesday, January 10, 2024

Nager mieux

I like to release a demo on special occasions like new year or my birthday. It is twice unfortunate that I had none to show by the end of 2023 since it was the 30th anniversary of Bilou's original design. But there was something still lagging behind: for an untrained player, it takes countless trials to get out of the water in the green room. When the season holidays started, I thought I could just change the state machine a little bit so that Bilou would jump out of water if we hold the FOOT button.

Il y a un élément qui m'empêche encore de faire une nouvelle démo avec les améliorations de l'an dernier: c'est difficile de sortir de l'eau. Je m'en suis rendu véritablement compte en laissant un peu la démo actuelle entre les doigts de J.L.N ... Je m'étais donc donné la mission d'essayer de faire fonctionner ça pendant le congé, mais avant même le premier essai, j'ai compris que ça n'irais pas comme je voudrais. Parce que pour qu'on puisse "jaillir de l'eau si on a appuyé sur le bouton de saut près de la surface", j'ai besoin de pouvoir mémoriser que ce bouton a été enfoncé. 

It didn't work as I expected though. Maybe it could be adjusted with some cleaner input buffering, but since I intend to use some different SWIM mechanics to help the game being fun, it seems silly to hack something else first. So I went on, picked my animation editor and started crafting swim left, right and up animations, each split so that they player would have to chain button pressing to reach full speed and Bilou could come back to some "rest position" otherwise.

ça pourrait être réglé avec une peu de tuning sur l'input buffering, j'imagine. Mais comme j'ai aussi prévu de passer à un autre système de nage dans lequel Bilou reste dans l'équivalent d'un dash sous-marin pendant quelques frames quand on a appuyé sur "pieds", ce serait naturel que l'on jaillisse automatiquement si on est dans cette phase de dash. J'ai donc passé deux petites soirées à faire des animations dans MEDS pour que notre brave Bilou brasse mieux.

Manque de pot, une animation supplémentaire s'est invitée dans le fichier. J'ai prévu 4 "pages" d'animations pour bilou.spr, mais cette nouvelle animation est sur la page 7, décalant toutes les nouvelles animations

- code avant la nage avec les sprites avant la nage:
  - démarrer la pyramide: ok
  - passer de la pyramide à l'école: ok
  - sortie de l'école: gros crash.

Une petite modif' plus tard (là, ce soir) pour éviter que l'animation excédentaire soit à la fois sous le contrôle du jeu et sous le contrôle du reste (sinon, ça fout un chaos digne de Jurassic Park dans le gestionnaire de mémoire) et j'ai de quoi commencer à utiliser toutes ces jolies nouvelles animations. Sauf que ça fait bizarre de voir Bilou essayer de rejoindre la surface la bouche grande ouverte (animation "super jump" recyclée) puis fermer la bouche une fois qu'il arrive à l'air libre :-P Edit et il va falloir que j'en fasse une ou deux de plus pour permettre au joueur de quand-même avancer. Sur base des animations de Fury ? Pourquoi pas ...

But unfortunately, some bits got twisted, and I ended up with an undesired 'temporary' animation stuck in the bilou.spr file. Then I picked the wrong decision of ignoring it while loading rather than fixing the editor and saving the file again. It worked when I tried the animations in the green room, but when I tried to 'move' between rooms later on, everything blew up. I had HDMA experiments catching my attention, so I was all out of holidays when I finally understood why it broke and how to fix it. Sounds like you'll see the swimming another time ^^"
Edit: one more thing ... Dash-swimming might be fun, but I also need something to use when player just navigates with the DPAD. Maybe Fury's swim sheet will be the template I need for that ... 

Edit²: give it a try in the latest demo

Sunday, December 17, 2023

tapis roulant!

Nous y voilà enfin! Après l'eau-qui-pousse, je peux vous présenter le sol-qui-pousse. Le point délicat, vous vous en doutez, ça aura été de mélanger ça avec des sols pentus. Ce à quoi je ne m'attendais pas franchement, par contre, c'est que être poussé pendant qu'on est immobile sur le sol se révèle plus compliqué à gérer qu'être poussé pendant qu'on avance.

The natural step after water-that-push-Bilou was ground-that-push-Bilou. It started quite nicely and it is finally working, but to reach that nice behaviour, there has been significant ups and downs, and especially to get it working on sloped ground. But hey, sloped conveyor ground was my trick to have one-way halls without getting too mechanical.

C'est que pendant qu'on avance, on appelle régulièrement la fonction do_slopes, voyez-vous. Et même si le sol annonce "je te pousse de deux pixels sur la gauche" alors qu'on est sur une pente, on finira bien 2 pixel à gauche et 1 pixel plus bas si nécessaire. Mais rien de ce genre dans l'état "immobile sur le sol". Et notre fonction do_slopes est prévue pour aller directement corriger la vitesse et les déplacement retardés du personnage, or l'idée du "sol qui pousse", c'est justement de manipuler la position dans un premier temps et de laisser le code qui traite la vitesse tranquille. Sans ça, sur un tapis roulant trop rapide, vous verrez Bilou se retourner pour marcher tout seul vers la gauche plutôt que de le voir s'échiner à avancer vers la droite tout en reculant malgré tout vers la gauche. Pas terrible.

Dans Rayman, j'avais pu voir qu'on avait deux types de pentes: des normales et des glissantes. Chaque angle et chaque offset est dédoublé avec un second type pour gérer les deux types de physiques. J'avoue que je souhaite plus de souplesse pour Bilou. Pas tant que j'ambitionne de faire un jeu plus complexe que Michel Ancel, mais surtout parce que je n'ai pas l'occasion de planifier l'ensemble du jeu. Je suis incapable actuellement de garantir qu'il n'y aura jamais plus de 2 types de sol dans un niveau ni 2 types de pentes. Le projet était donc d'utiliser des emplacements séparés pour encoder la pente et les propriétés du sol. Et quand je me suis mis à coder, c'était encore plus simple que prévu: quelque soit la position de Bilou sur une pente, il est toujours à l'intérieur du tile "pentu". Et donc pour trouver le tile avec le type du sol, il suffit toujours de regarder 8 pixels plus bas. Enfin ... presque toujours.

And that was the final test for my "neat idea" to "simplify" the level editor: use separate tiles to indicate the slope (if any) and the ground properties. Ground properties are always on the 'plain' part of the ground and the slope types sit on top of it. A new getGroundType function can probe through that slope layer to find the actual ground and return its numerical type. Then, each controller use that to index to retrieve relevant parameters, such as push vector, friction, or whatever needed. It mostly worked except on one spot: the precise location where Bilou's hotspot has just been shifted out of the lowest 'sloped' tile of a slopes stripe. It is now mid-air, with one 'slope' tile below it and the ground properties one more below. Then Bilou stops because it is no longer pushed but doesn't fall either. And if I try to hack that around, I end up with Bilou moved horizontally instead of following the slope because there's no slope to follow.

Comme toujours avec les pentes, les problèmes commencent quand le sol n'est plus dans le prolongement horizontal de celui utilisé une frame plus tôt. On se retrouve dans ce cas-là soit avec Bilou qui s'arrête au milieu de la pente, soit continuant à se faire pousser, mais à l'horizontale. 

Yet, when you walked on the sloped ground rather than letting yourself pushed by flowing sands, you wouldn't get any issues. The trick is that walking state already has a call to the do_slope function that keeps Bilou in contact with sloped ground despites of motion. The extra shift happened prior that call, so it would still re-align when Bilou's own velocity is applied. But there was no such call in "idle, standing" state so far. And the do_slopes function wasn't meant to affect anything but character's speed, while it should now sometimes affect temporary variables used to immediately apply a coordinates update.

And just in case that seemed too easy, a typo in the first coding sprint made me change accumulated step instead of actual speed, ruining the whole thing with Bilou digging into the ground. Then a bug introduced in the level editor broke some part of the level without me realizing it and I started suspecting wrong properties for the ground here or there.

But there we are. It's working, even though it cost me hopping back in time with Mercurial and re-doing the sprint one dash at a time, checking, committing, re-importing maps and the like. And about one week later, I finally shot a decent .gif of the behaviour so I could post it and write this stuff. It's been planned for so long. It has been through so many preliminary work and so that I'm gonna call it a milestone.

Bref, inspector widget, ddd, j'ai pu sortir toute la panoplie. Presque (pas les unit-tests, cette fois). Mais l'éditeur de niveau m'aura bien cassé le rythme.

(wow. Le soir est tombé, j'ai encore une machine à faire tourner et je n'aurai pas fait plus de homebrew de cet aprèm' libre que de vous raconter tout ça :-P)


Thursday, November 23, 2023

Making swim fun

Somewhere last year April 2023, I had been reading something a neogaf thread about water levels in platformers and whether it might be possible for them to be actually fun. Because, well, I don't think I'll manage to copy the awe DKC sharks may produce and I can't ask my brother to come up with something like David Wise's Aquatic Ambience for my Nintendo DS title.Yet there will be water.

There have been some platformer titles over the last years that came with fun-to-play water levels. I think about "20000 lums undersea" level in Rayman Legends and some level of DKC: Tropical Freeze. Swimming in Ori and the Will of the Wisps was pretty pleasing as well. All these games share something: they benefit from analog stick and they depart completely from their 8-bit and 16-bit counterparts by letting you target any direction freely. Your character will typically need some time to turn himself towards the direction you want though, which works fairly well.

Les niveaux aquatiques dans les platformers, ça a mauvaise réputation. Vu le nombre d'échecs que Vanilla Lake Forest of Illusion 2 m'a infligé, je ne peux pas franchement leur donner tort. ça a commencé à aller un peu mieux avec Donkey Kong Country, d'abord parce que la musique de David Wise était aussi époustouflante que l'animation des requins, mais surtout grâce à Enguarde qui permet de cesser de se battre continuellement avec la gravité... Et d'avoir une chance contre la poiscaille.

Plus proche de nous, Tropical Freeze et 20000 Lums sous la mer l'Océan des Songes de Rayman Origins nous ont donné un nouveau mode de fonctionnement d'un personnage de jeu de plate-forme qui tombe dans l'eau. Ils sont maintenant capables de se diriger dans n'importe quelle direction (indiquée par le stick analogique), accélèrent dans la direction correspondante si on appuie sur un bouton. C'est souple. On voit son perso se tortiller pour faire un demi-tour ce qui donne l'impression d'être dans l'eau ... On peut chercher une certaine forme d'élégance dans les trajectoires qu'on prend ... d'une certaine façon, ça se contrôle un peu comme un jeu de micromachines avec beaucoup de dérapages. Ou un avion qui fait des loopings.

Dans chacun de ces cas, exit la "blind box fonctionelle"  d'Enguarde: on peut attaquer dans n'importe quelle direction, à n'importe quel moment.

But the part I prefer is how you get a speed boost into the direction of choice with the JUMP button, and especially how you jump out of water in Ori and the Will of the Wisp like you were a true dolphino. I could certainly do something alike in Bilou Dreamlands. (at first, I wanted to make it an unlockable move so that you could discover secrets later on when you've unlocked some ability ... but that's something for Bilou's Adventure instead).

Of course, with the NDS DPAD, I can't truly have free aiming like in the switch/wiiu titles, but maybe I can find something approaching:

  • when you hit FOOT, you get a speed boost in the one-of-eight direction you're aiming with the DPAD.
  • during that move, you can modulate your direction (say, +/-15°) around that main direction with the DPAD
  • the swim animation eventually comes to a slow down step where you can chose a new main direction and hit FOOT again to keep speeding.

Une des choses que j'ai préférées dans ces jeux plus modernes, c'est la manière dont notre personnage peut jaillir hors de l'eau si on fait une "attaque" près de la surface. J'avoue qu'au départ je pensais utiliser quelque-chose inspiré de la physique de nage de Fury of the Furries, où on ne sait sortir de l'eau que s'il y a une berge suffisament "à niveau". Puis offrir le "mode dauphin" avec un level-up, comme le fait Ori and the Will of the Wisps. Mais bon, l'objectif ici, c'est un "dreamland", pas un castlevania. Pouvoir bondir hors de l'eau dès le début du jeu, pour le fun, c'est l'objectif. Première chose à tenter, donc: permettre à Bilou de jaillir de l'eau si on tente de sauter près de la surface.

Deuxio, prévoir un dash-swim qui propulse Bilou dans une direction indiquée par le DPAD si je tente de sauter dans l'eau. ça pourrait être intéressant d'en profiter pour essayer de faire l'équivalent aquatique d'un wall-jump parce que j'ai toujours trouvé plus simple de se propulser en utilisant le bord de la piscine que de nager à proprement parler.

Tertio, le dash sera normalement suivi par une période de "retour au repos" pendant laquelle le bouton saut n'aura aucun effet, mais ça pourrait être intéressant de prévoir d'enchaîner sur un dash si on utilise plutôt le bouton "ramasser". On nagerait alors à la vitesse maximale en enchainant pied - main - pied - main avec le bon tempo... Pas d'attaque aquatique prévue pour l'instant, mais je n'ai pas non plus un bestiaire aquatique débordant ... 

Quarto(?): permettre d'infléchir la trajectoire vers le haut ou le bas avec des petits coups de DPAD pendant un dash horizontal. Et vice versa ... et diagonalement. Ce sera l'équivalent à la croix directionelle des mouvements libres au stick analogique.

One other fun thing that might be worth experimenting is doing the equivalent of wall-bounce underwater. At least, it might be the easiest way to get a first experience with wall jumps in my engine.

And finally, one possibly terrible (or fun) idea would be to keep moving fast underwater with properly timed FOOT / HAND / FOOT / HAND button pressing
 

Saturday, November 18, 2023

tile 4 { swim.flow = (256,0) }


Enfin! Je m'attaque enfin à la dernière face de mon 'newmeta/newmap': les tiles spéciaux. Il y avait déjà les blocs spéciaux, auxquels on peut attacher des zones de collision et des actions. Il y a les pentes qui se passent de commentaires et les tiles aux propriétés "directes" qui permettent de combiner jusqu'à 6 propriété distincte (eau, sol, air, lianes, ...) librement. 

Dans le cas des "nouveaux" tiles, le script définissant le jeu va pouvoir encoder librement les propriétés voulues pour chaque type de tile. J'aime y penser comme à un accès indirect, mais c'est probablement parce que j'ai fait trop d'assembleur :-P. Et ça ne s'arrête pas là: je veux les utiliser pour les tapis roulants, le sol qui glisse, et ce genre de choses. Chaque type permet donc aussi d'aller paramétrer des valeurs le concernant auprès des différents contrôleurs.

En pratique, on va définir dans un fichier script.gam

tile 8 {
    is flowdn "0055220000552200"
    props fc8 # WATER
    swim.flow = (0,512)
}

La première commande dans le block, is est utilisée dans l'éditeur de niveau, pour fixer le graphisme représentant le type de tile. La commande props donne les fameuses propriétés indirectes. Ces deux-là existent aussi pour les blocs interactifs. La dernière sera passée à une nouvelle fonction SwimControllerFactory::setTileVariable() qui s'occupera de tout ce qu'il y a derrière le signe = et enregistrera le "mouvement forcé" à appliquer dans GobSwimController::think() quand on se trouve sur ce genre de tile.

Hello. I've been writing notes about how to have conveyers and flow-in-water since at least 2020 2019. Now is the time to get it done. At last. The syntax of the new "tile" description will look as much as possible to that of the "block" description, used for collectibles and more. Like them, it can describe the value of cando() flags to be used by controllers with a props statement.

What only they can do is set values for 'controllers variable'. Say that we want water to be able to push Bilou in some direction while he's swimming, we need a table of swim::flowx and swim::flowy that indicate how much Bilou should be moved at each frame if he stays on the given tile.

After some refactoring of the iWorld class and some more code in the script parser, this is finally possible. And with some bugfixes on the level editor, I've got a test case coded for the "three rooms" demo where Bilou is moved away from the waterfall because tiles say so.

Quelque jours plus tard, j'ai enfin ajouté le code qu'il faut au contrôleur utilisé pour la nage, téléchargé une nouvelle map avec des tiles "pousseurs" sous la cascade, et voilà: une première mise en application du concept où Bilou, tel un Fury, se fait embarquer vers le fond, puis un poil sur le côté avant d'être recraché vers le haut par les remous...

Tuesday, October 31, 2023

zik.more "bilou.xm"

Bon, après avoir peiné à permettre de mélanger deux jeux d'images dans la mémoire vidéo, il est probablement temps que je regarde à faire la même chose au niveau des sons. En particulier parce que pour l'instant ma démo "three rooms" est obligé de partager la même musique pour l'école et la pyramide. Et si CJ ou Piek me débarquait là tout de suite avec une musique plus pyramidale, je serais obligé d'aller copier-coller les samples et les pistes de 'bilousch16.it' par-dessus, sans quoi je risquerais d'avoir des effets bizarres.

La manière la plus simple de procéder serait de charger d'abord "pyramid.xm" puis d'aller piocher dans "bilou.xm" les pistes et les samples que je veux importer. ça a par contre l'inconvénient qu'à chaque fois que le jeu va charger un nouveau monde, il doit commencer par oublier tous les sons, charger les sons spécifiques au nouveau monde puis enfin remettre les sons communs par-dessus. Avouez que c'est un peu idiot.

Une alternative à creuser, ce serait de faire en sorte que les sons et pistes d'effets de Bilou.xm restent en mémoire, et que la musique "pour le monde en cours" sache y faire référence sans essayer d'en gérer directement le contenu. En somme, que ces samples aient un statut "d'invités" et que supprimer le morceau en cours n'ait aucun effet sur eux.

  • bonne nouvelle: XmTransport, la classe chargée d'ouvrir les fichiers XM, est déjà capable de travailler avec un NTXM::Song& donné.
  • [done] vérifier qu'il n'y a pas "d'effet Singleton" dans ::Song
  • [done] extraire le bilou.xm hors de la musique de SchoolRush

Saturday, September 30, 2023

Un p'tit coup de marteau ...

Mes dernières sessions avec AnimEDS s'étaient toutes soldées par des "guru meditation". Peu de travail perdu, heureusement, le problème se produisant généralement soit juste au début, soit juste après une sauvegarde. L'impact sur ma motivation à continuer à animer mes p'tits persos en prenait quand-même chaque fois un coup: j'aurais pu effectivement perdre un travail précieux.

Alors j'ai noté de faire du debugging de tout ça. Il y a un bail. D'abord refaire une version précise de l'animateur (rH2021) et garder le .nds et son .elf à côté des fichiers re-générés à tout bout de champ quand je bricole du homebrew, histoire que quand le problème se présente console en main, je puisse effectivement utiliser la valeur du registre PC pour retrouver un numéro de ligne dans le code. C'est le cas depuis Juin.

I can't help wondering whether this is worth translating. It's another epic (?) showdown between me and my code to see who's got the StrongARM. But well, it has been bugging me since January, crashing the animation editor almost every time I used it. I'm just lucky I never lost anything important but motivation to work on some cute things over the evening. The "guru meditation" screens I got during the first half of the year were almost useless: the AnimEDS build on my 'lime' DS was so old I did not have the matching .ELF file for my debugger anymore. Believe it or not, the RealLife (tm) has turned so intense that I even had to write down an agenda check list with "rebuild ; keep .elf apart ; upload .nds to lime" to actually get it done.

Pas de chance: c'est un de ces bugs où l'adresse ne dit pas grand-chose parce qu'on est a suivi des pointeurs de fonctions qui ne voulaient rien dire. Mais! Bonne nouvelle, avec les nouvelles animations pour l'Appleman, le bug est plus facile à reproduire: il suffit d'essayer de copier une frame d'animation juste après avoir chargé la première animation du fichier dans l'éditeur.

Alors j'ai ressorti mon émulateur et mon débuggeur et là, bonne nouvelle, ça foire aussi dans l'émulateur. Différemment, mais ça foire, donc c'est débuggable. Et là, j'ai galéré pendant des heures ... Des structures qui ne veulent rien dire, des vptr complètement à l'ouest ... Il faut dire que pour encoder la ligne du temps utilisée par l'éditeur, j'ai pris une std::list de std::pair de classe dérivées. Bref, on se perd sous les couches de templates, de membres dépendant de l'implémentation et d'optimisation du compilateur qui prend un malin plaisir à rendre inaccessible les variables dont on aurait besoin.

With that done, I wasn't much more lucky. It's one of those bugs where you try doing a virtual function call on something that isn't truly an object and thus end up in the middle of nowhere, especially where memory contents doesn't match any valid opcode. Hopefully, while trying to get the crash to write down registers values, I realised that the bug was actually easy to reproduce (just open one animation and try to copy the frame before selecting any frame) and a bit later, that it also happened with the same file in my emulator. At least I could save the evening where I navigate DS memory to reconstruct objects on paper this time.

J'allais jeter le gant puis un soir j'ai noté dans mon calepin "fait du débugging indirect en surveillant les constructeurs". Bah oui: la variable aura beau avoir été optimisée, il faut bien qu'il soit construit à un moment où à un autre, le TIFrame qui explique pourquoi j'ai du n'importe quoi dans les registres au moment de copier cette première frame.

Sauf que ... non. Les "TIFrames" sont construites vides, puis au fur et à mesure que l'AnimationParser traite la liste de commandes destinées à GEDS -- le moteur de jeu -- il complète les coordonnées, les numéros d'images, etc. J'étais sur le point de laisser tomber (traduire: réécrire tout avec ma propre classe 'liste' plus propice au debugging) quand je réalise que je peux "figer" l'afficher d'une ou de TIFrame dont je capture l'adresse à la construction, et suivre leur évolution jusqu'au bug (l'animation en question ne contient en réalité qu'une frame et une commande de contrôle). Et là, surprise, tout va très bien (Mme la Marquise :)

 It did not save navigating memory altogether though. The way gdb handles std::list and std::pair combined to the amount of variables that were actually "optimized away" when they would be critical to have around still turned that debugging session into some guru meditation. I first thought that it was because of some incoherent animation instructions into the animation itself, but inspecting how the animationParser processed them shown everything should be fine. Yet, if I tried to see what frame was triggering the bug, all I'd get was garbage non-sense. I was about to replace all that std::* by some pype::* when I realised that I could just break on constructors of the contained TimeItems to see what the list contained.

That did not work either, unfortunately, because when TimeItems are added to the list, they are "blank" items that will be modified by the yet-to-come UPDATE animation commands. What did help, was creating some static watches at addresses discovered in a constructor-breakpoints so that I could look at the state of my list just before the offending call. That and realising that the list was just fine, thanks, but that I was using an iterator that might not have been updated since the previous list had been trashed and replaced by the new one :P

Une seule explication restante: c'est l'itérateur qui devient invalide au bout d'un moment. Je sors mon cahier A4 histoire de me faire une map UML de tous les bouts de code impliqués et c'est bien ça: quand on choisit une animation à éditer, la liste est vidée, mais l'itérateur reste inchangé, ce qui est invalide.

Ah oui. Vous vous demandez "pourquoi le marteau" ... et vous n'avez pas Thor. C'est à cause de cette blague d'ingénieur où un consultant rentre une facture de $100000 pour une intervention et ça rouspète chez le client parce que "vous avez juste donné un coup de marteau!". Et le consultant de reconnaître qu'il a fait une erreur et de préparer la facture suivante

  • 1 hammer hit: $10
  • knowing where to hit $99990

(bon, c'était vraiment une grosse machine très chère qu'il fallait dépanner). Bin c'est un peu la sensation que ça me fait sur ce bug-ci. Sauf que je ne vais pas recevoir des cents et des mille et que je ne devrai pas les débourser non plus :P

 

Saturday, August 26, 2023

Apple Bop ?

Une pomme qui vous fonce dessus, ça fait mal. Forcément. Une qui vous tombe sur la cafetière également. Surtout quand elle est aussi grande que vous. Si vous lui tombez, dessus, là, c'est elle qui tombe dans les pommes. En disant ça, j'ai couvert 75% des interactions possibles entre Bilou et les Applemen dans Apple Assault.

If you get an apple on your head, that hurts. If you're thrown an apple in the face, that will hurt too -- especially if the apple is almost as large as yourself. Chances you won't get hurt if you stomp on an apple while falling but that will likely do the apple no good. All these cover 75% of the interactions between Bilou and the Applemen in Apple Assault.

Now, what if Bilou run into an apple. Should he still get hurt ? I know it is the default behaviour in platformers: if Mario runs into a koopa, it gets hurt no matter what direction the koopa was facing. You can only kick a koopa shell if you stomped the koopa first. You can create stories about it like "yeah, as you kicked the koopa, it clawed to the ground, turned back so fast you couldn't react and bite you. You're dead", and by '86 standards, they would work. But does it mean we should continue like that ? Can't we find something more fun ? It's an apple, after all. It's not all covered with deathly spikes!

Mais si c'est Bilou qui arrive dans le dos de la pomme ? Est-ce que le choc devrait aussi lui faire mal (à Bilou) ? dans Apple Assault, la réponse est "oui". Dans la plupart des jeux de plate-formes, la réponse serait "oui" aussi. Un koopa qui bouge, c'est un blesse, qu'on l'approche par-devant ou par-derrière. Mais est-ce que c'est forcément la bonne chose à faire ? Est-ce qu'il n'y a pas moyen d'être plus *fun* que ça ? C'est une pomme, après tout. Pas une chataigne couverte de pics.
En plus, maintenant, il y a un précédent avec les Pendats de School Rush: on est blessé quand ils nous foncent dedans ou quand on tombe sur leur pointe. C'est plutôt logique. on est blessé aussi quand ils font demi-tour, mais de nouveau, vu la violence du mouvement, un mauvais coup est vite parti. Par contre, tant qu'on est dans leur dos, on pourrait presque les pousser, on ne se fait pas mal. Alors si c'est vrai avec des soldats, ça devrait aussi être vrai avec des pommes, non?

When you consider that, in School Rush, pendats -- despite their aggressive and spiky appearance -- will not harm Bilou (only block him) if he comes in their back, it would make the whole game more coherent if we would equally have the back of Applemen harmless. Plus, it could actually be pretty fun to see an Appleman slightly bounce forward on such a collision, as if Bilou had kicked him in the back while walking, and only then actually turning back, being surpised to see Bilou and finally attacking.

Voilà donc ce que j'ai envie de faire pour Bilou: Dreamland (et la green zone dans le futur): si Bilou bouscule un Appleman dans le dos, c'est l'Appleman qui fera un bond et Bilou ne subira aucun dommage ... dans l'immédiat. Parce qu'en revenant sur le sol, l'Appleman fera alors demi-tour et, voyant Bilou, lui foncera dessus (comme il fait depuis 2009). Ce qui n'empêche pas le joueur de sauter par-dessus l'Appleman, peut-être même suffisamment tôt avant que celui-ci ne se retourne, et qu'il ne se rende donc compte de rien. ça,, ça a le potentiel pour être bien fun ^_^

Thursday, July 20, 2023

class AppleWalls : public WallTest, UsingApple

So I had managed to get shell/walls interaction mostly fixed. I thought it would be wise to get it run 'unit tests' to ensure nothing got broken by the fix, but it would do more than a few seconds of tests before failing. In Walls/Walk test , a fairly recent addition to MapTests.cpp, for which I do not seem to have any commit where that test is a success.

The test failure will produce patterns such as

--(120,0)+<-508,0>:[-224,0]     's/7f.f, S/fe08.0, '
--(118,0)+<-512,0>     's/7f.f, S/fe08.0, '
--(116,0)+<-516,0>     's/7f.f, S/fe08.0, '

--(114,0)+<-520,0>:[-232,0]     's/77.f, S/fdf8.0, '
--(112,0)     's/77.f, S/fdf8.0, '
--(110,0):[-240,0]     's/77.f, S/fdf8.0, '
--(108,0)     's/77.f, S/fdf8.0, '
--(106,0):[-248,0]     's/6f.f, S/fdf8.0, '
--(104,0)     's/6f.f, S/fdf8.0, '
--(103,0)+<248,0>:[0,0]     's/6f.f, S/fdf8.0, '

which I, too, find cryptic. I should at least inform future self that they are from saved "last state" array, dumped on exception like assert failure during the test.
  • (%{x}, %{y}) is used to report new coordinates
  • <%{vx}, %{vy}> is used to report a new speed
  • [%{dx}, %{dy}] is used to report new delayed step

Together with the arena layout defined by walls1, it can start making sense. For instance, x=104 is the lowest valid position before entering the two-# "wall" of the upper platform, where the Gob-under-test is walking. Granted, this is arcane and confusing, and it would deserve a review fix even if it is only for me. Possibly even more confusing are the `s/%x.%x` codes reported between quotes afterwards. These are generated from ChiefInspector::report() call from the WalkerController.

  • lowercase s indicates that there has been a change of tile considered in doslopes(). hex values are the coordinates of the hotspot used to decide that.
  • uppercase S indicates that doslopes() reported we're on sloped ground. hex values are the speed defined during doslopes().

Even with that wrong report of sloped ground fixed, I still have my 'follower' capable of entering walls. It happens when we stop next to the wall, but do not cancel the 'step value still needed'. More investigation needed.

With some break getspeed and cond BREAKNO (x >> 8) < 105 && cdata[GOB_XSPEED] == -520 in an epic gdb session, I could trace precisely what happens, one GobController call after another, and how the speed, delayed steps and the like where further processed.

The issue turned to be linked to the 'maxmove computation', an engine feature added in 2020 that scans the animation commands ahead of think() calls, so that we don't ask doslopes() to check for something different than what we'll actually do, else the vertical move and the horizontal move won't match and we'll end up out of the slope. The code that decides what to do when we detect a move must be cancelled assumes that the motion debt stored in the 'delayed step' variable has already been validated by controllers. In case of failure, we're safe to move *at least there*.

That was true with the "school rush" engine, where it was helpful to ensure we actually get close enough to the walls for the testpoints to work as intended. It is no longer true if maxmove shortened some of it. How precisely this can be fixed still has needed a bit more time to be figured out.

PS: while looking for the 'inspector codes' in my 2020-notepad, I got my eyes caught by a character stating "this is contemporary to maxmove introduction"... The line below reads "hopefully, no need to tweak things (how many loops are allowed while processing animation instructions, btw) too much. All it takes is building an animation list with enough 'I_MOVETO(+1, *)' commands before it loops."

PPS: of course, fixing things here broke things there, but mostly because code was wrongly assuming that some things (like vertical position or FAIL counter)

Saturday, June 24, 2023

area %d = anim %x

Bon, franchement, je me demande depuis quand ce problème attend que je m'y attelle... que ce soit mon blog, twitter ou mon calepin, impossible de trouver une date correspondant à "oui, la plate-forme se reclappe, mais quand je me mets plus sur sa gauche, je passe à travers.

J'imagine que ça fait partie des choses qui m'ont motivé, fin du mois dernier, à faire en sorte qu'Inspector Widget marche à nouveau dans la démo "3 rooms". Et la visualisation des zones de collision m'avait porté à croire que j'avais mal défini les zones de collision dans l'éditeur, sur NDS.

At some point, I noticed that InspectorWidget wasn't working anymore in Dreams.nds ... not quit ideal when you're trying to add bits of gameplay to your years-long-under-development demo. Especially not when you barely manage to get more than 1 hour of gamedev at once. And after I managed to get it back, I suddenly remembered that seeing Bilou falling through the flipping platform was nothing new and that it was linked to the platform having bad hitbox.

Parce que oui, ayant la flemme de définir les zones à la main, cette 'crocforme' est (avec la branche-qui-rebondit), un des premiers objets du jeu à importer une zone de collision passive directement avec area 0 = anim ${FLAGS}, plutôt que d'en fixer explicitement les coordonnées comme j'ai fait avec Apple Assault et School Rush.

Sauf que sur DS, les hitbox sont parfaites. Rien à redire. Je re-transfère le fichier (le lendemain), des fois que j'aurais oublié que j'avais déjà corrigé le soucis. Je recompile (le surlendemain), je teste. Pas d'amélioration. On est samedi, je sors l'artillerie lourde: une nouvelle instruction `break` pour le langage de script histoire de pouvoir mettre un breakpoint dans ddd et commencer à inspecter le comportement du parseur de script pile là où ça coince: dans la boucle qui lit les commandes 'Define' de l'animation où on fixe les coordonnées de la zone solide et des hitboxes pour l'animation.

At first, I thought I had just messed the hitbox within AnimEditor for DS. But a few checks and WiFi transfers later, it turned out that no: the data (as far as the editor was concerned) was ok, but the game still would use the bad hitbox. Checking that on the parser would be darker than night -- or so I thought. This is how the parser now supports an additional "break" instruction that is a noop where you can set a gdb breakpoint while parsing things. These are not meant to be committed, of course, but it definitely helped.

A few 'next instruction' later, it turns out that my engine is using tool-absolute coordinates for the hitbox instead of object-relative coordinates. That is, when you edit an animation in AnimEDS, you don't only store how to use it in-game, but also what you need to edit it again in the editor, and that includes where to put each component on-screen in the editor. The character's center and the edition widget center may match, but they don't have to.

Le code qui gère ça n'est pas tout neuf: il a permis d'améliorer les hitboxes pour attraper les objets dans SchoolRush il y a près de 10 ans. Mais j'ai commis l'erreur de copier-coller le code plutôt que de faire une fonction "importArea" qui serait utilisée des deux côtés: pour les zones actives et les zones passives.
ça n'aura donc pas été trop compliqué de faire en sorte que les deux utilisent le même bloc de code (le bon) et d'avoir une plate-forme qui fait *enfin* ce qu'on attend d'elle :-P

J'espère que je passerai un peu plus vite de la découverte du problème à la recherche de sa solution, la fois prochaine ^^"

If I want to fix that, I need to track not only hitbox#n ORIGIN coordinates, but also those of the 'solid' box -- the one used to decide whether the object cando() something. then when one 'ORIGIN' instruction shows up for the area we're interested in, we create relative coordinates. Hopefunny enough, it turns out I already have code that does precisely that, because I have both active and passive hitboxes and I failed to avoid repeating myself when writing the original parsing code ... and later failed to fix both copies when I realised things were not going as they should have.

Sunday, May 28, 2023

special properties

While thinking at 'how to implement conveyer belts', I end up investigating some currently incomplete code in special blocks handling. They have correct implementation of their collision areas, but since the migration to the new tile types engine, they can no longer act as 'doors' because they all have the same cando() properties.

Au départ, je réfléchissais à "comment coder des tapis roulants" et je me retrouve à creuser du côté du traitement des blocs spéciaux: ils ont bien un support des zones de collisions, mais depuis le passage au nouveau format de maps, on ne sait plus les utiliser comme des "portes". Je m'explique.

Au moment de coder la démo 9-9-9, je voulais mettre un système "clé-et-porte": un petit bloc devait rester ... bloquant jusqu'à ce qu'on le touche avec au moins 9 pommes, auquel cas il disparaissait et devenait du "ciel": on pouvait passer. Mais pour faire ça, il faut qu'une pomme (à travers laquelle on peut marcher, tomber, etc) ait des propriétés physiques différentes d'une porte. C'est ce qui manque actuellement. 

Euh ... attendez. Non: on a déjà un scanf qui ferait ce qu'il faut ... reste à le tester. 

There's some code in the parser to change that, and some code in 'FlatWorld' to return BlockInfo::props when we encounter one ...

Wait ... BlockInfo::props is precisely what this last `siscanf()` updates ... so ... that means the code is not incomplete at all ? it just needs to be tested ?

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.

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.

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.

With --slot1 R4 --slot1-fat-dir /tmp/fat
/tmp/fat/Dream.nds
, it now works.

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.

edit: 3/4: With some GameInfo::isHomebrew() function located, I thought it wouldn't be too hard to allow dsi-enabled .nds to run with desmume, but that was not the problem (not with 0.9.14, at least). The problem was more with DLDI patching. Unfortunately, the handlers checked by slot1_GetCurrentType() are not yet registered into slot1_list[] when you invoke desmume --slot1 R4 Dreams.nds. Adding slot1_Init(); slot2_Init(); at the start of CommandLine::process_addonCommands() definitely helped.

Edit: 8/4: For some reason, slot1 is super-slow in the emulator. I'd like to try slot2 instead, but the GameInfo structure is full of zeroes since NDS_LoadROM() has been called. A preloadROM function finally allowed using --gbaslot-rom and have it recognized as a homebrew, but that still did not allowed libfat to find files.