Friday, December 27, 2019

Leilani slopes

The tweet popped while I was doing mental checks on my revised tiled engine, and especially how it would handle more slopes than the previous one: Ishi(soft) finally decided to introduce slopes in Leilani's Island. And while I was looking for convenient ways to encode more than one array of tile_height[type][offset], I was surprised that his approach wasn't not requiring any such array.

Bon, le tweet est tombé comme un pavé dans la mare pendant que je réfléchissais aux problèmes que le mélange des pentes et des blocs à la physique modifiée risquait de poser: on peut aussi faire des pentes sans avoir pour autant besoin de modifier son moteur de jeu pour y mettre des fonctions du genre "doslopes". C'est ce que nous propose Ishi dans sa prose décrivant le moteur de jeu de Leilani's Island. Et que faire d'autre si on ne veut pas simplement faire un clone de Mario 1? Eh bien par exemple générer à la volée des "ascenseurs" sur lesquels les personnages vont se déplacer et dont la vitesse verticale donne l'illusion qu'on suit bel et bien la pente.

(C) Ishi
Instead, the world-collision engine would detect sloped ground and generate sort of "lift" objects that are tied to a specific X location, but adjust their Y location to the X position of the character that triggered them. It allowed enough flexibility to make me think twice before dismissing the approach. One of the benefit of the approach is that the code running the is-there-ground check still happily believes the character have solid ground all around them:

This fixes the problem with enemies turning around on slopes. When they do their collision check, they find solid ground in front of them - because as far as they're concerned, they're on a flat surface.

There was one major restriction, though: Ishi decided to go for a single type of slope (22.5°) where X-to-Y offsets function can easily be computed by shifts and additions. But I personally want more. More angles and some curved ground as well. We could still have that in Ishi's engine, but it would require picking a tile_height[offset] array according to the slope type and pass it to the "lift" object.


C'est séduisant, c'est sûr. Mais ce n'est pas allé sans mal pour autant, et à la lecture de tous les "corner cases" qu'il a dû prendre en compte, je ne suis pas certain d'avoir envie de jeter mon implémentation actuelle pour suivre son exemple. Entre autres, une des choses qui permet à Ishi d'éviter de devoir utiliser plusieurs types de pentes, c'est qu'il s'est limité à un seul angle (22.5°) pour lequel on peut facilement déduire la position verticale de l'ascenseur à partir de la position horizontale du personnage à l'intérieur du bloc. Mais moi je veux plus de variété. Il faudrait donc du coup plusieurs comportement d'ascenseurs et un code qui produit le bon à partir du type de pente que l'on a rencontré.

And the approach apparently has its own corner cases that need to be addressed, sometimes with special edge-of-slope types. The approach I'm using so far has its own corner cases, which were mostly resolved by using jump-through tiles (which are also walk-through) next to sloped tiles to avoid introducing artificial walls within the slope. That's precisely what needed more thinking when introducing "physics modifiers" tiles.

Well, anyway, don't miss the full post on Ishi's tigsource thread. It's very detailed and informative.

PS: it feels a bit odd to write a post about someone else's work here, but I've kept re-visiting the browser tab with the forum with the hope that I wouldn't forget it if there's anything important to find there ... for one week or so.


 Edit: apparently, there's one thing I understood terribly wrong: there is no 'lift object' generated. Just a collision rectangle returned by a function. A different rectangle every frame, but its lifetime is restricted to the duration of Character::think().

Sauf que ... en réalité il n'y a pas "d'objet-ascenseur" dans l'implémentation. Chaque fois qu'on demande au 'monde' s'il y a collision avec le sol, il nous renvoie un rectangle adapté à notre position. Ce n'est pas un objet-plate-forme qui aurait une durée de vie dynamique, juste une valeur de retour qui nous sert de contexte pour les prochains tests le temps d'un cycle de calcul des mouvements du personnage.

Tuesday, December 24, 2019

Ori and the difficulty curve

If you played Ori and the Blind Forest, I bet you've immediately recognized this place. If you haven't welcome to "how a simple puzzle can be as hard as beating a boss". It's not that the riddle is hard to figure out: there's a corner you'd like to reach locked with a door that you can only open by relaying some shot from the opposite corner of the room. You have the ability to redirect the shot, and there are corner-shaped things you've encountered before that redirect the shot as well.

Eh bien, elle m'aura donné du fil à retordre, cette salle de Ori and the Blind Forest! Pas tant que l'énigme soit difficile, mais l'exécution doit être impeccable. Si passer du premier au deuxième point de "rendez-vous" n'est pas trop tendu vu la vitesse (lente) du projectile qu'on relaie, le deuxième est déjà plus délicat: les sauts sont plus longs et s'attacher aux "lanternes" demande d'abord un double saut. Bref, il aura fallu que je maîtrise la nouvelle compétence du 'BASH' pour y parvenir. D'abord en prenant conscience que je n'étais pas obligé de me trouver au-dessus du projectile pour l'envoyer vers le haut. Je peux me positionner tout simplement au sol, là où il est possible de bien voir le moment où je dois dévier le projectile.

Et si vous n'avais jamais joué aussi loin à Ori, sachez que le "bash" est le signature move de votre personnage. La mécanique de jeu originale consistant à geler le temps à proximité d'un ennemi ou d'un projectile pendant que vous choisissez la direction à donner à ce projectile. Pour faire bonne figure, votre personnage sera propulsé dans la direction inverse. 

No, what makes it tricky is that it requires precise (perfect?) execution with proper timing despite that nothing in the level layout provides you grids or guides to time or aim things. And you've got to be quick because every time you move from one spot to another, you're racing against the same shot, all over the room. In other words, you enter the room with knowledge of how the BASH mechanics work, and you only leave the room when you have mastered the mechanics.

Through my quest to mastery, I discovered that (1) I wasn't required to be mid-air over the shot to throw it up, and (2) that I wasn't forced to use the analog stick to redirect the shot.. Standing still and pressing the BASH button with the right timing does the job. And the 'dpad' of the left joycon provides higher precision under stressful conditions (like re-doing it for the 20th time), when all you need to do is directing something vertically or horizontally. For the rest of the game, having an analog stick is precious, but puts too much stress on the dexterity of the player.


Le deuxième truc, ça a été de me mettre à utiliser le "d-pad" de la switch plutôt que le stick directionnel pour diriger le projectile avec assez de précision même quand le stress est là. Parce qu'évidemment, si les "monstres" présents sur cette scène ne sont pas bien méchants, il finissent toujours par "repopper". On aura donc qu'un temps limité pour boucler la séquence si -- comme moi -- on ne se sent pas assez balèse pour passer d'une traite malgré la présence de limaces à pics.

I was expecting some relief after such a boss-time but actually no! It's even getting worse: I got trapped in an escape event that requires full mastery of almost all the mechanics we've been taught so far. And I failed. A lot. Like 30 to 40 times, split along a whole week. I started believing that the BASH mechanics lacked proper tutorials and that I'd have preferred to face a "GAME OVER" and start the game afresh to find secrets that might bring me there better prepared.

Saturday, December 07, 2019

Rush to Flatworld

It took me some time to get all the special block types working again with the new collision engine for GEDS. Partly because there were places in the scripts that referred to some block identifiers which I had to squeeze from 8-bit to 6-bit. I managed to make it a lossless squeeze, but some bits had special meaning. 

Like all the blocks identified 1xxx or 3xxx triggering actions even when collided by a monster while 0xxx and 2xxx would require a HERO cast. Or like the second character of the id xXxx indicating whether the block would act as sky (x0xx), water, block (x2xx) or floor when not interacting.

Avec le recul, ça me fait un peu penser à un donjon Zelda où on viendrait de trouver l'objet-clé. J'ai un nouveau mécanisme pour gérer les types de blocs sur DS, conquis après une lutte opiniâtre contre les vilains bugs (pensez aux wall masters) et les émulateurs récalcitrants (grosses statues à déplacer vers une dalle-interrupteur au hasard dans la pièce).

Et du coup, je dois retrouver les différents endroits où je peux à présent reprendre le développement. Editeur de niveau amélioré ? Programme de conversion vers un nouveau format de map ? Plus de types de pentes ? chacune de ces "petites clés" supplémentaires seront nécessaires au final pour pouvoir faire une version jouable de la maquette "desert zone" que j'ai en tête, et un bon nombre est aussi utile pour débloquer une conversion correcte de la "green zone". Mais là, je suis dans la salle avec trois portes et je dois choisir la bonne.

But well, it finally works fine for level 1 of school rush, and adapting other levels should now become easy as I patched the "rules.gam" shared file instead of patching directly the level commands.

And while working on it, I also mapped the whole set of "tile properties" that were in use in School Rush. That will help for introducing the new tile types. In addition to the 'special' blocks, some will have direct flags for the 'cando' operations and some will have indirect flags (i.e. you have to lookup a table to know their properties). The idea is to reduce the comparison stress for the 'normal', 'empty' tiles. We may keep the 'monster cage' tiles, but they would be seldom tiles compared to plain 'air' tiles and allow an extra table lookup.

Well, that last part is not yet done. And to be honest, I feel so tired that I'll need to go back to my little "remember you had no blog" notebook to see what has unlocked with the newest achievement and which target should be my next goal. I can't even dare to do that on screen :-/


Friday, December 06, 2019

FlatWorld.artmap

Bon, bin j'ai finalement réussi à trouver les erreurs que j'avais glissées dans mon code et à les corriger. Je peux donc reprendre une des "maps" de School Rush et la convertir à la volée pour qu'elle tourne avec la nouvelle classe "monde" et ses données de collisions séparées des blocs de VRAM du fichier .map. Par contre, ce faisant, j'ai cassé la possibilité d'animer le contenu de l'écran en réaction à une collision avec un bloc spécial.

Je dois reconnaître que j'y vais à tous petits pas, là. Entre autres à cause de la faible quantité de temps libre qu'il reste après les transports et les opérations de maintenance domestiques ... et encore, je suis loin du compte.

Bref, on va commencer par ré-établir un lien entre la "méta-map" et les map-à-images puis il faudra ajuster les changeblock() et autres clearblock() pour que tout passe. Mais caser ça avant St-Nicolas ? Je dirais qu'on est à un niveau d'improbabilité de 3.14 ou pis.

Wednesday, December 04, 2019

visual ...

  • objdump equivalent in VS world is DUMPBIN. mingw objdump (elf) will not work on msvc objects (coff), and djgpp's coff tool won't work either (MS-DOS executables no longer running on 64-bit systems).
  • /GL is your archenemy if you planned any guru meditation on .obj files. It will kill about everything the file may have as meta-information, including symbol tables. How one can still link such files has not yet been researched. dumbpin will shout File Type: ANONYMOUS OBJECT at you if that flag was present when compiling.
  • If you ever see 'Rtl*' in some windows code, stop thinking about RealTech(Tek?) network cards. This is just Run Time Library, doing weirdo wrappers around standard functions such as memcpy for whatever reasons. Have a look in wdm.h or google the function name to find where it is defined.
  • it may be sensitive to MIDL_PASS, _MEMCPY_INLINE_ and _CRTBLD
  • There is no /Ldirectory_path in (my setup of?) link.exe, but it is sensitive to the %LIB% environment variable.
  • The amount of information /VERBOSE produces on link.exe explains why we don't see anything in the map file unless things went ok
  • There's no stuff & command to send stuff in the background but we can use start stuff to have the same effect
  • Speaking of linking, DUMPBIN /imports exe_or_dll is what you'll use instead of /usr/bin/nm and DUMPBIN /dependents when trying to figure out what DLL is preventing your stuff to run. Note that DLL-loading function will return the same error code whether the requested module or any of its dependencies has an issue, and that will be a 'no such file' for you, sir.
  • It looks like sysinternals is now an official MS resource for system monitoring. It's just ... Dudes, did you really had to sell its documentation ?
  • I won't ever maintain a .sln file in parallel with the SConscript that built the binary I want to debug because I know devenv /debugexe TakeOverTheWorld.exe
  • don't even try to use a Debug binary over a machine where there is no visual studio installed. It will miss its MSVCP140d.dll no matter how many attemps vcredist.x64.exe does at populating c:\windows\system32 with release dlls.
  • don't forget to disable VS Code tracking & telemetry options if you dare to use that.
  • I wish so much I had heard of c:\windows\system32\where.exe, the equivalent of /usr/bin/whereis ...
  • There's no killall, but if you have powershell running, there's taskkill /IM buggy.exe /F
  • If you want to know which #defines are still present after pre-processing your code, add /d1PP to your /P or /E switch
  • If your compiler still pretends that some symbols are undeclared while pre-processing output clearly shows they are there: that sounds like stdafx.h is messing things up. When doing a 'regular' compilation, the compiler ignores whatever #include statements you have and instead blindly use pre-compiled headers that are built only using stdafx.h !

And I still have to check the details of pdb files, dump files and remote debugging... edit: hopefully, one can invoke dumpbin from MSYS shell too. I'll just have a disasm bash function to wrap that around from now on:
disasm() {
   /c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio\ 14.0/VC/bin/amd64/dumpbin.exe -disasm $* ; 
   }