Monday, August 29, 2022

SuperMario 3D World

Oui, côté topic réchauffé, j'avoue qu'on fait difficilement mieux sans tomber dans le retrogaming. Mais voilà. Après l'avoir essayé sur la WiiU de mon frère il y a déjà quelques années, j'ai fini par craquer pour la version Switch, dans une tentative pour introduire un nouveau jeu intergénérationnel dans la maison.

Donc, oui, il y a eu la partie-de-papa (sur laquelle les enfants sont invités), la partie-de-J.l.n, la partie-de-*deline et enfin la partie-de-papa-sans-les-enfants. Parce que si le jeu à 3 est sympa, il est aussi assez décousu et ne permet pas franchement de se faire une idée de ce que le level design a dans le ventre.

Granted, there is little chance I can say anything fresh about a Mario game that has been released about 8 years ago. It's not like I had not played it until this summer, but a 4-player game in my bro's living room or a 1-player game in a quiet place doesn't feel the same to analyse the game's design. And even that way, I must confess that so far, I'm mostly writing down observations that my 9-year-old mentioned to me after he saw me playing.

And one of them connects quite well to a weird feeling I had about how the levels were 'disconnected' from one another. I mean, here are two pictures of 4 different levels. Each picture picks levels from one of the worlds: desert world or ice world. Can you relate one picture to one word ? To be honest, if you had asked me like 2 months after I'd been playing those levels, I don't think I could have answered.

Pourtant, c'est J.l.n qui m'a fait observé que, même si on a droit à un monde du désert et un monde des glaces (d'après la carte), une fois dans les niveaux, ce n'est vraiment pas évident qu'il y ait le moindre élément thématique. C'est vrai au niveau du choix des ennemis, de l'habillage des objets, du décor de fond ... tout. Le premier niveau de chaque monde est jusqu'ici (monde 3) le seul à vraiment jouer la carte du thème.

Tenez, chacune des images de ce post correspondent à quatre niveaux venant d'un même monde. Lequel est celui de la glace ? Lequel est celui du désert ? Il faut déjà avoir bien mémorisé le jeu pour pouvoir répondre.

Now let's be honest: there have been desert and ice worlds in Mario games since SMB3. They are present in NSMB, NSMB2 (checkme), NSMBWii and NSMBU. It is perfectly fine that we (at least?) see something new and different. It is perfectly fine that levels are located in the game according to their relative difficulty rather than according to some arbitrary theme. But still, the game designers insisted that we're taken back to a map showing snow or sands after we crossed that moving-grids-aerial-level. It isn't bad enough to break the player's illusion that it shares the adventure with Mario, but if you ask me on the next day "oh, so you're just in front of the Ice Castle. Was there anything interesting in the Ice World, then ? I can only remember of the 1st level in that zone" ... well ... I'll be pretty much in the same situation, because none of my memory of playing world 3 really relates to "snow" or "ice" or whatever.

Alors est-ce que le jeu est moins fun pour autant ? Pas forcément. Les niveaux restent bien trouvés et intéressants, mais il se peut que ça contribue au sentiment de "design décousu" que j'ai en reprenant le jeu.


Thursday, August 11, 2022

Funghi v2.0

 Bon, il est temps que je blogge les cogitations de nouvelle année, vous ne croyez pas ? Je m'étais installé pépère dans le canapé pour récupérer un peu, agenda-cahier sur les genoux histoire de faire un peu le tri de ce qui doit encore être dit et ce qui doit être transférer dans la nouvelle année, mais c'était compté sans J.L.N.

Le p'tit bonhomme venait de se rendre compte qu'une SpongeBop, si on la repeind en vert, ça fait comme une branche d'arbre et que donc on pourrait en mettre dans la forêt! Il n'a pas tort, mais j'ai voulu vérifier ce qu'il connaissait déjà des autres habitants de la forêt de Bilou. Après tout, à part quelques parties de Apple Assault, il n'a pas encore eu beaucoup de contact avec la green zone.

Il y a Funky Funghi, notamment. Le champignon sauteur. Si je permets de ramasser les applemen à la manière des dumblador dans School Rush, que se passe-t-il quand on lance une pomme sur un Funghi ?

Sketches in this post were actually drawn on January 1st, while I was brainstorming Green Zone with my nearly-9-y.o. son. He's been mostly playing the School Zone so far, and only bits of Apple Assault. So little that he suggested I put a green spongebop as a spidey-branch platform of some sort.

At my first drawing of Funky Funghi, he'd almost immediately suggest that we can jump on his hat and use it as a platform to climb higher. Not a bad idea, but completely incompatible with the Commander Keen-inspired, decades-old setting where the whole funghi is highly toxic and that any contact should be avoided. But that was at a time where a Manic-Miner clone was considered.

L'idée de base, celle de la version BASIC, c'était que le champignon était ultra-toxique. Tiré assez directement de Commander Keen, je dois bien dire. Le moindre contact est dangereux pour Bilou. On ne peut même pas lui sauter dessus.

Mais bon, avec le recul, pour un premier niveau, c'est peut-être un peu exagéré. Je n'irais pas pour autant jusqu'à proposer que l'on puisse monter sur la tête de Funky Funghi et s'en servir comme plate-forme. Ce n'est pas franchement dans le caractère du personnage d'accepter ça.

Pas question non plus de permettre à Bilou de simplement 'pousser' Funky Funghi comme il le ferait avec un encrier (même si je n'ai toujours rien implémenté de la sorte ^^")

Faire se déplacer un ennemi sauteur invulnérable en visant correctement, voilà qui promet d'être fun. En tout cas, c'était fun dans Rayman.

I still would like to avoid a platform-like behaviour -- I even already have another mushroom design for that. And I now have throwable monsters in the rest of the game, meaning it would be good that we could pick up and throw applemen in Green Zone (that was the plan from long ago, before I came up with Apple Assault mechanics). That means something fun is bound to happen if you throw an apple at a funghi. Right ?

I bet being able to knock the otherwise-camping mushroom to an alternate location should be both fun and interesting (I take it from Rayman). But beware, said my bro: we don't want to end up locking the progression by poorly "chosing" where we put the funghi. And with an all-toxic one, chances that we lock ourselves are much higher. Especially with limited supplies of applemen in the level.

Mais contrairement à Rayman, on joue ici avec des "munitions" limitées. Il ne faudrait donc pas que l'on bloque un Funghi dans un coin du niveau où il peut nous empêcher définitivement d'avancer! Une solution pratique contre ça, évidemment, c'est de placer un 'trou à champignon' au bout de la zone-bac-à-sable dans laquelle il est prévu de le déplacer. Quoi qu'on fasse, il ne peut aller plus loin et on a remplacer "passer par-dessous l'obstacle" par "passer par-dessus l'obstacle", qui est en théorie plus accessible.

Final thought: if Edward is true claiming that goomba were picked mushroom-bumper-shaped because that should suggest the players they'll be safe jumping on them, making my bouncy-funghi hurt you when you stomp him won't be a neat way to teach the players they're in a traditional platformer when they're playing level 1 of Bilou's Dreamland. (not to mention that mushroom=bumper has been over-used in all platformers after that, to the point that it's almost more natural to try using them that way than trying to eat them and expect to grow or shrink, nowadays).

So let it be bouncy. But only his hat (foot is still toxic and you shouldn't try to push Funghi like you'd do with inkjet). And bumping you to the side as well. If you want to have a Funghi ride, you'll have to master it first. Else all you'll get will be a funghi-rodeo. 

Mais tout ça devient secondaire si - une fois déplacé - Funky Funghi devient utile. Bon, comprenons-nous: ça reste un PNJ peu fiable et dangereux sous les bords. Disons par exemple que son pied reste toxique (on ne le pousse pas à la main) et redoutable (on ne reste pas trainer en-dessous). Mais son chapeau, lui, avec sa jolie tête de bumper pourrait ne réserver aucune vacherie et agir effectivement comme un bumper. Mais un qui soit susceptible de nous propulser vers l'arrière ou vers l'avant si on ne le prend pas bien comme il faut. Bref, un bumper qui amène une touche d'imprévu pour le joueur inexpérimenté, une touche de challenge pour le paddawan, et une variable supplémentaire à intégrer pour le Maître.

A méditer.


Saturday, August 06, 2022

scanf %ms ou scanf %n?

 it should have been quickly done. Almost trivial. Edit "DHud.cpp", revive calls to the MuadDebug class, change the GOB number so that it tracks the scorpeye rather than Bilou, and make it rewind time

  • when the shell's horizontal speed turns to zero
  • after it has been thrown (there's a flag telling us that)
  • after it has been picked up (need a new flag for that).

Except that however I tried to do it, and regardless of the amount of instruction-by-instruction stepping I made, I couldn't get the second condition to trigger. The optimizer did a great job at packing the expression evaluation code -- I mean there, that you can't tell anymore where you're in the source while doing it, and thus mostly can't set a breakpoint on a specific instruction.

Hopefully, there's the B opcode (not D, which is for Detach) to break at the script expression level. That one at least allowed me to realise that the expression for that second condition was indeed evaluated. But for some reason, the part I was interested in, with flag-setting, never got executed. A 'all done, bye' opcode always showed first. Mysterious...

Faire des essais dans le jeu pour essayer de reproduire un 'trick' pas fréquent, et avoir programmé un critère d'arrêt qui nous permet de passer en débugging pas-à-pas mais en revenant une image en arrière dans le temps, donc en revivant exactement l'évènement qui fait que tout est parti de travers en bullet-time pour pouvoir le comprendre et savoir ce qui doit être corrigé. J'appelle ça le muad-debugging, et c'est vachement pratique.

Enfin, surtout quand ça marche.

It turned out the reason was the expression got truncated even before it was turned into bytecode. Such a thing is not viable to extend the game to "Bilou Dreamland" level.

The code to parse an expression basically looks like

siscanf(base+cont,"[%64[^]]] (%64[^)])",predicate, axion)

And both 'predicate' and 'axion' arrays are static-sized. I know there's another way you could do the 'same' thing, but with any-sized arrays. That'd be something like

siscanf(base+cont,"[%m[^]]] (%m[^)])",predicate, axion)

in which case the siscanf function will allocate some memory by itself and you'll have to invoke free(predicate) when you're done with it. It is sure the way to go in most of your production code, but to be honest, I do not want this within GEDS script parsing code. Parsing a level already takes too much time. Adding dynamic memory for strings processing will only make things worse. Not to mention that it will fragment memory with temporary strings requested while we're making big blocks for the 'tank'.

But there's another trick I could (and likely will) use:

%n Nothing is expected; instead, the number of characters consumed thus far from the input is stored through the next pointer, which must be a pointer to int. This is not a conversion and does not increase the count returned by the function

Combined with the fact that %*s scans a 'string', but does not store it, I should be able to write

siscanf(base+cont, "[%n%*[^]]%n] (%n%*[^)]%n)", &predistart, &prediend, &axstart, &axend);

Now, I'd have start and end pointers for both expressions (if they're actually there), plus that would be copy-less and I would directly perform ascii-to-bytecode conversion. The conversion code would have to be adjusted, of course, so that it takes two pointers instead of one and that it does not expect a terminating \0 character at the end of the expression. So that will be a new "todo" item for when ongoing things are cleared and time is right to start something more experimental.

Oh, yes. And I could just grow the static arrays, of course. That's what I did in the past when they were 32-character long each, and I thought "well, 64-bytes predicates ought to be enough for everybody, right ? ". In another context, I could pick ridiculously large arrays (like 2048 bytes each) and forget about the issues for decades until it strikes back with some auto-generated script. But that would be a bad idea on the Nintendo DS with its fairly small stack. And nah, I won't make the arrays global either. That would sure break so many things in the ability to run that code out of its current context that it has even been considered as an evil thing to do by the former generation of developers.