Showing posts with label gobscript. Show all posts
Showing posts with label gobscript. Show all posts

Tuesday, May 20, 2025

GobExpression debugger

Throwing apples does not work the way they should. There are likely bugs in the state machines transitions, and -- once again -- they are tedious to isolate. So here a screenshot of a first step towards a tool that would let me step through guards and actions at bytecode level.

The colored line just below shows those opcodes, one character at a time.

I've got such bugs to fix in the past, of course, but that's always been a pain. InspectorWidget can only break when a specific transition is taken, but not before its guard expression is evaluated. And something in my C++ code prevents gdb from stepping through the expressions evaluation function.

Juste avant de partir en vacances, j'ai fait des petits tests avec des lancers de pommes, mais il faut reconnaître que ça ne marchait pas comme prévu. Et faire du debugging de transitions, avec les outils actuels, c'est le truc le plus pénible (en dehors des 'guru meditation', peut-être). Il était temps que je m'attaque à un "side-project" esquissé pendant les valises: un véritable bytecode debugger dans lequel on sélectionnerait son expression comme si on explorait des adresses mémoire: n° de gob, n° d'état, type de transition puis choix de la transition.

On aurait ensuite la possibilité de mettre des breakpoints au niveau "instrution", mais aussi de faire du step-by-step dans les expressions pour voir si l'évaluation se produit comme prévu ou non.

La capture d'écran ci-dessus vous montre bien qu'on en est qu'au tout début: faire en sorte de pouvoir utiliser les caractères programmables de la DS pour pouvoir renseigner 'constante n° 2' ou 'décaler de 3 bits vers la gauche' sur le même espace qu'un des caractères ASCII.

So far, it's really the first baby steps: video mode switching and character reprogramming so that opcodes can each be shown on a single 8x8 character. Part of them could fit within the extended ASCII range, but there's some room consumed right after that for the 'screen maps' themselves, that cannot be assigned to any character.

But the idea is to half a screen of these expression, see a cursor step as you press the 'forward' DPAD direction, see the stack fill, the reads and writes from object variables, etc. Long is the road ;)

I don't want the expression evaluator to be slowed down by extra tests, so I'll do the kind of tricks 8086 debuggers were doing: inject breakpoint opcode at the next instruction and remember what was there instead. Next time the debugger is invoked, it will restore the saved opcode, show us the new state and tell the expression to retry current instruction when we give a green light.

Thursday, February 13, 2025

Hang on!

It's one of the key mechanics I'd like to bring into my new game: being able to catch hanging things and hanging as well. It was a fun move in Commander Keen, it was amazingly expanded in Fury of the Furries and Mickey Magical quest ... And last week-end, I went on to try and get it prototyped.

I started with a summary of how Bilou can hang below spongebops in School Rush ... something we don't see a lot in the blog, mostly because Bilou is supposed to *ride* the sponges, not hang under them. Hanging was added as an "else" branch of another "else" branch:

when you hit the "HANDS" button mid-air, Bilou does a roll move trying to grab what's nearby. that is AIRGRAB state. The original idea was to test what was *below* him and either pick-up a dumblador or ride a spongebop... but the move shows the hand grabbing in front and rear directions as well ... so it would feel natural that Bilou could grab a sponge even if he's on the same horizontal line, like in the sketch on the left. Yet, snapping Bilou on top of the sponge from this location felt glitchy.

Bon, j'ai un p'tit Bilou suspendu sur une page de mon cahier-agenda depuis le début 2024 ... j'en ai un autre sur la bannière du blog ... je crois que l'appel du pied est assez clair: il serait temps de prototyper un peu ça, d'autant que cette fois ci, ce ne sont pas les coins de niveau où s'accrocher qui manquent dans le livre-secret-du-level-design.

Et pour "commencer simple", l'idée était de faire subir à Bilou le comportement des éponges. tout en réutilisant un des états codés pour l'interaction avec les éponges: rester suspendu (la feinte). Chose qui m'a tout de même demandé de me replonger dans pas mal de gobscript, parce qu'à la base, il est prévu que Bilou s'accroche *par-dessus* les éponges, et pas qu'il pende en-dessous. Le côté "pendu" n'a été rajouté que dans les derniers 20% du projet, entre deux séances de playtesting parce "bin, oui, c'est vrai au fait: pourquoi Bilou ne pourrait-il pas rester suspendu s'il était trop bas pour escalader l'éponge au moment ou la joueuse a appuyé sur (B) ?"

So instead, Bilou will enter AIRHOP move, getting an automatic double jump and automatically tracking the sponge's position. That feels fairly naturally when playing, that gives you a sort of "fair second chance" but it might fail anyway: you may fall below sponge's position again without having reached the "riding spot", with no hope of catching up. and *that* is the time when we finally switch to HANGING. 

There will be other objects to hang to in the game, especially bookmarks, vines, and the like. Some of them could be entities like spongebop (maybe moving a bit less), but sometimes it might also be nice to have tiles you can hook to. I have code to make some tiles react to collisions with the player, but that was not enough: the hitboxes used to detect spongebop explicitly search for baddies. And because I'm testing 3 areas over the course of the animation, I'm out of options to also test for friendly areas (which has been the case for all pick-ups so far ^^"). But hitting a tile can trigger an animation, and it can spawn an entity with proper hitboxes so that Bilou could hook to *that*. You couldn't really hook to a tile, anyway, because the BlockArea created during the collision test is immediately recycled, so you could not attach to it. 

Mon idée était ensuite d'utiliser un type de bloc spécial pour servir de point d'accroche, et d'aller "poker" quelques blocs de ce genre à des endroits bien sentis de la map "green" de la démo. ça non plus, ça n'a pas fonctionné comme j'espérais. Entre autres parce que quand Bilou cherche à s'accrocher à quelque-chose pendant sa pirouette-en-l'air, il cherche parmi la liste "EVIL" ... et les blocs interactifs, eux se présente automatiquement comme des "HERO".

Bon, ce n'est pas bien grave: puisque je voulais que Bilou s'attache au point d'ancrage, il fallait de toutes façons que j'introduise un GOB (game object) généré à partir du bloc spécial, un peu comme les scintillements qui suivent Bilou quand on ramasse un smiley bleu. Ce gob pourra être dans la liste que l'on veut. Pour éviter qu'on ne génère des quantités ridicules de gob-points-d'ancrage, on va (enfin) utiliser une animation de la map elle-même pour désactiver puis réactiver les propriétés du bloc pendant que le gob est présent. Exactement le comportement des blocs-question de SuperMario quand on les coup-de-boulise.

So the plan was the following: a new special block that would react to GRAB action. When that happens, it trigger the animation that actually leaves graphics untouched (with "spr0 ffff") but turns the physical tile into "plain fall-through" for 5 frames, and then restore the tile as "block 07" (thus interactive) again. Quite the kind of thing that SuperMario question blocks do on a daily basis, actually.

(It turned out meanwhile that using sprite 'ffff' did not work as intended, so I ended up adding a bbox statement to that animation -- effectively making it a special 8x8 tile rather than a 16x16 special block -- and fixed the code that plays mapanim so that "props" instruction is executed immediately rather than cached until the next "spr0" instruction)

anim9 spr:2 {
   spr0 7      # visuals for the "tile replacement",
   delay 5     # here just a Bilou hand ...
   done
}

state63 :anim9 {           # behaviour for that hand :
   using freemove          # just stay there, 
   area 0 (0,0)-(8,8) 1010 # accept GRAB hits.
}

state62 :anim9 {   # should have been the persistent state when Bilou hooked...
   using freemove
}

state63->state62 on found 0 [wc $1010 ?] (t) # detecting the actual hook-up hit
state63->nil on done                         # and disappear otherwise

using shgob(state63 is e:=1:6) as 6          # a special action to spawn the hooked hand
                                             # oh ... and it's an evil hooked hand.
block 06 {
    is gndhook "ff3c7cfebe2a2a00"  # the special tile 
    area (0,0)-(7,7) 1010          # will also detect GRAB hits
    on hit [t] (x6) :anim6         # and the 'x6' will invoke shgob registered 'as 6'
    props fc0
 }

That should have done it. I could see the hand showing up, and getting away, but I couldn't ever hook to it. I couldn't get an evidence of why it failed, but I believe it is rooted to the fact that bilou's own bbox must overlap the tile in order to trigger the "on hit" transition. but since all the hitboxes with the GRAB flag tend to be off-center, the second collision (with state63 entity) doesn't occur because there's no overlap.

Alors oui, je vous balance le script directement dans le browser. Parce que ça ne fonctionne pas. Enfin, après avoir corrigé la gestion de l'animation pour ne pas dépendre des changements d'affichage (qui expliquent le bout de terre manquant dans le screenshot là-haut), ajouté une "bbox" à l'animation (pour que le code sache qu'il ne doit manipuler que 8x8 et pas 16x16), je finis par avoir un objet qui est créé tandis que le bloc devient inactif puis se réactive ... le hic, c'est que il faut pour ça que le "corps" de Bilou soit en contact avec le bloc interactif alors que ce sont ses *mains* qui vont essayer de l'attraper. Et la zone à attraper est réduite à 8x8, de toutes façons mal positionnée... bref, je n'y arrive que 1 fois sur 5-6, donc le joueur n'y arrivera pas.

Il vaut bien mieux que je me décide à dessiner un graphisme dédié (une racine ?) et que je fasse juste un GOB dédié... il y aura des import/exports mercurial à faire ... 

At some point I thought "hmm. I'll just have the little tile be a 8x8 hitbox in the middle of a 24x24 special box, that will do the trick", but it wouldn't work either: that 24x24 region around the corner of a pillar (for instance) would have "jump-through ground" on one tile, and "air" everywhere else... how could a single "props" instruction fix that ? okay, I could introduce "props(-1,0) fc4" to change one specific tile of the block but that sounds like the coding equivalent of "just stab me now" ... 

So, likely, I should forget about that "hook to tile" thing, accept that there's very little hookspots in the Dreamland levels anyway and kickstart my NDS to draw some root sprite in green.spr ... 

Saturday, July 06, 2024

Test tile type from state transition

As I update an old post to explain that yes, cando() finally managed air/water transition I note that the whole blog is still missing a key explanation of how this was made possible. It happens in state machine transition expression (in the predicates, actually) and says "tell me the properties of the world n pixels above my hotspot".

$FALL->$INWATER on fail [2 H WATER ?]
                        (v1 v5 + 2 / :1 0 :5);

$INWATER->$RBOUNCE on event0 [D_FOOT 8 H AIR ? &]
                        (800 ~ :1);

You see it there. H is the new OP_HOTTILE for the GobExpressions. it picks the value on top of the stack, the constant 2 or 8 here. We can then test bits against the constants WATER or AIR with the ? operator.

Without that, the trick about a slice of tiles that are both water and air is useless, because you couldn't make the difference between falling into water and falling on the ground anyway.

(and yeah, I still have to add those get-worldwide-flag and set-worldwide-flag opcodes as well as the roll-the-dice opcode)

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.


Tuesday, December 14, 2021

PlaySFX vs. PlayInst

 

Je refais un peu d'explication-BD parce que là, c'est un brin compliqué. Le mécanisme que j'avais ajouté pour faire des effets-par-pattern, je l'ai poussé un peu trop loin.

Au départ, c'était bien vu: un ensemble de flags permettant de dire si on doit suivre la "partition" normale du module ou continuer à avancer dans la piste en cours jusqu'à trouver un effet 'pattern break'. Mais il y a un cas que je n'avais pas en tête à ce moment-là: les effets 'normaux' fait d'un p'tit sample tout seul. Eh oui.

Dans ce cas-là, la piste sur laquelle on met le sample n'a normalement pas de contenu. Depuis le nouveau code, elle est ignorée puisqu'au-dessus du nombre de pistes définies dans le fichier .xm. La seule exception à cette règle, c'est pour les pistes qu'on a attachées à un morceau de piste d'effet.

Disclaimer first: many of the things I'm now doing with libntxm are far beyond what it was designed to do, that is running a soundtracker on the NDS. When thigns go wild because I stop a song and replace it with another before the Player class on the ARM7 get any chance to run its every-millisecond handler routine, I'm worse than a TAS-speedrunner producing superhuman inputs on the software. Right ? Same when I feed the Player with two patterns simultaneously and going 'yeah, play pattern 9 on channels 1-12 while at the same time playing pattern 27 on channels 14-16, will you ?'. None of this was part of the original plan, and that's perfectly natural. It's up to me to make it work if I want it to work.

Ça, ça marche bien pour le moment 'lecture de la nouvelle cellule (note) dans la ligne'. Mais si on veut qu'un p'tit son puisse être joué, il faut qu'on continue à faire des calculs d'enveloppe, la gestion des registres de volume. C'était déjà un peu à ça que servait le flag channel_active. Il reprend du service.

Bon, là je me suis limité à un p'tit test tout bas, parce que ma fée est pas ultra-fan de StrTrk et que c'est l'heure de la p'tite série qu'elle aime bien à la télé.

So a few weeks ago, I fixed a crash in the ARM7 code with 'oh, I know: I'll give NTXM player a NULL song first, and then delete-and-create another one'. Not a bad idea in itself, but I missed a few things that the player has to do even when I change the song, like progressively muting the channels when there are no more notes to do. Obviously, I shouldn't prevent it from doing so even though we activated the NULL song.

I also finally implemented an if-block for the GobScript (it turned out quite simple thing to do) and tried to use that so that we'd use pattern-effects only when playing a tune with such patterns, and play plain-samples-effects otherwise. Nice try, but it turned out that no sample was playing at all. That was because of my previous changes again: when playing such lone samples, we're perfectly right to do so on channels that are not present in the song. (Actually, we should better do so). But the code from SchoolRush would only start playing a sample if there was a pattern Cell to back it up. Either a regular one, or one from a pattern-effect. I had to identify the tasks where that test was mandatory (e.g. when processing effects or advancing to the next row) and tasks where it shouldn't be used (process volume envelopes, start the sample, turn down the volume when done playing the sample, etc.)

Reste à espérer qu'avec les corrections de 'quelle musique jouer quand' et 'okay, on a plus de partitions, mais continue quand-même à baisser le volume', ça suffise pour que les musiques de la prochaine démo soit un peu plus sous contrôle.

Ah oui. Et au cas où, ces choses en forme de tentacules (Bob) sont des statues-registres.

Friday, August 03, 2018

Rolling Random Number.

I decided to map every known token of the GobExpressions onto the ASCII charset some other day. And it struck me that there was a pattern like "upper case for mostly side-effect actions" and "lower case for mostly functional actions". To some extent, the x being used to call eXtra functions such as spawning new game objects, triggering sound effects and the like should be replaced by X. Tracking that in all the command files won't be trivial, though.

It could be tempting to use S(for score) as a function since we have i(ncrement counter) and d(ecrement counter), but unlike game counters, the score cannot be read again. It's more a side effect to change it than really some functional extension to the core arithmetics.

So where should "roll a dice to get a random number" go ? likely r. What should be used to "get/set game state bit %n". That could be g and s. Pretty handy map.

edit: For long, I have delayed introducing OP_ROLL, and that was partly because I had no clear idea of what implementation to pick. Here is the one from Tetris, according to some gamedev talk. What is good for Tetris is good for me.

Monday, April 09, 2018

Tutorial revision goes on.

I reached the point where you can write very simple scripts and have them processed on the "tutorial" branch. Of course there isn't much follow-up at the moment despite the 3 forums on which I comment stuff. That doesn't really matter, although I'd love to get feedback on whether it reads well.

What is really interesting here is that it forces me to get rid of many odd things. Hopefully, that will lead to a code base that will be easier to extend. Things like "rules.gam", for instance.

Being busy reviewing the expressions system, for instance, make it obvious that some static array could be gone now that I have the gob collision structure. the "game counter al so cry for a refactoring out of the GameScript class. And making the 'guns/controllers' system easier to understand (esp. by automating the registration system) made it obvious that I need to re-think the way classes access the Camera object.

Thursday, March 01, 2018

__lock_jiffies

Perdu dans un petit coin d'un driver Linux, une fonction sympa qui augmente un des verrous avec un chronomètre ... histoire de voir combien de temps on est resté en section critique.

Je devrais peut-être bien ajouter quelque-chose de ce genre avec parseLine() dans GameScript, tiens.

Et capturer le numéro de ligne des transitions crées, ce ne serait pas mal non plus.

Friday, November 17, 2017

most vexing (gobscript) parse


My scripting language is far from perfect, but this one is really the most vexing issue I have encountered so far. I mean, it is hard to debug, counter-intuitive, and it feels more like a question block turned into a thWomp and smashed you. It gave no clue, no warning, you did nothing wrong, but still get smashed.

The core of the issue lies in how collisions are handled:

  • There is always an active "collider" and a passive "collided" object. 
  • collider area is allowed to expand to any size (within int size limits). 
  • collided area will only be checked if the object's boundary box intersected with the active area.

The third constraint comes from performances considerations. But unfortunately, it means if you create a passive area larger than the object's own size, nothing will complain, you will see boxes intersecting, bar no collision will ever occur.

I'll need to take some time to add warnings

Sunday, June 25, 2017

Behaviour Edition ... brainstorm

Prochain outil pour le projet "dsgametoosl" ? Je ne sais pas encore. Mais j'aimerais beaucoup construire quelque-chose qui permette de manipuler les machine d'état définissant le comportement des personnages dans les jeux qui tournent sur mon moteur "geds".

Quelque-chose à moitié graphique (je clique sur un bilou en train de sauter, on me montre tous les états qui sont accessibles depuis celui-là. J'en choisis un, je vois les conditions et les actions annexes pour la transition correspondante si elle existe), et à moitié texte (je peux en éditer une ou en créer une nouvelle, sans s'embêter, rien qu'en écrivant les expressions que je veux).

Quelque-chose de pratique (je clique sur une variable, je vois tous les états où elle est manipulée).

Pour permettre la réalisation de "School Rush", j'ai introduit pas mal de fonctions à coup de macros qui permettaient de "nommer" des tests du genre "est-ce que la direction choisie sur le DPAD va vers la droite ?" Pour que l'éditeur soit pratique, il faudrait qu'il permette de conserver ce genre de simplifications, et ne pas imposer à l'utilisateur de mettre dans chaque test "est-ce que le bit 5 de la variable numéro 3 est à 1?"

Du coup, l'éditeur doit à la fois savoir que l'expression utilise la variable 3 (l'état du dpad) pour qu'elle soit listée dans "toutes les expressions qui dépendent du DPAD), mais aussi qu'elle s'affiche avec un "DRIGHT". et pouvoir indiquer à l'utilisateur ce que fait DRIGHT au cas où ce ne serait pas suffisamment explicite.

ça demande d'une part de faire avec assez de précision le tour de tous les éléments de scripting qui ont besoin de pouvoir servir de "critère de tri", mais aussi de faire en sorte que le logiciel tournant sur DS puisse comprendre la version "évoluée" du script et puisse en tirer la version "interprétable par le moteur de jeu" pour essayer les modifications dans runME.

Un défi qui n'est pas à prendre à la légère, en fait. J'avais envisagé pendant mes congés de l'an dernier de refaire l'équivalent du préprocesseur de gcc, mais je me rends compte aujourd'hui que je suis parfaitement libre d'utiliser autre-chose que des macros C pourvu que je sois en mesure de produire le fichier voulu. Par exemple en produisant un fichier .h à partir de ce que le nouvel éditeur utilise pour connaître les variables, les compteurs, les types de collisions, etc.

Ce sous-projet m'intéresse pas mal, et commence à consommer de plus en plus de feuilles quadrillées dans mes petits carnets, même si je traîne un peu à blogger tout ça ces derniers temps. J'ai ma petite idée sur la saisie de texte (à mi-chemin entre pixel-art et reconnaissance d'écriture manuscripte), par exemple, ou  la possibilité de représenter sous une forme d'arbre les expressions mathématiques du style "v6 := min(v6 - 32 , -400)" qui s'écrit en GobScript "v6 32 - 400 ~ m :6" pour être plus facile à traiter par l'interpréteur.



Monday, February 02, 2015

Les limites du GobScript

Ramasser des éponges alors qu'on pouvait déjà ramasser des tailles-crayons ... ça n'aurait pas dû être aussi difficile que celà n'a été. Le fait que les objets soient numérotés plutôt que nommés empèchait toute possibilité de détecter les erreurs. L'autre, c'est que le copié-collé est le seul moyen de réutiliser une région substantielle de code.

Current GobScript is meant to be human-readable bytecode for the state machine. Yes, truly. The most obvious way to realise that is how everything is *indexed* rather than *named*. The upgrade introducing C pre-processor in the make process to avoid code duplication in the behaviour script has been generalized to all monsters of the School Zone.

Yet the recent modification of these behaviour show that it becomes quite tricky to maintain coherency when adding new features. For instance,

  • [done] picking up spongebops was missing triggers and repeated bugs about stuck carried GOB when hit.
  • [done] Pendat rushing don't hurt Bilou when stomping him because I haven't copied hitboxes when creating the new state
  • [done] Stomp-and-grab blador in one move kills Bilou's horizontal speed and creates bogus camera conditions.
  • [done] We may enter walls when trying to pick something on the ground, because of a missing 'stopper' micro-controller
  • [done] stomping a blador while doing a air-grab allowed to move through because of a missing hitbox.

All this claims that I should now work on a higher-level language for editing, where I could express that "SpongeBop allows carry_me" and "blador allows carry_me" and that would imply some new states added to state machines, specific hitboxes in other states, transitions and such. Don't be surprised if I take that excuse to practice some Haskell, as there is currenty a professional incentive to master that language.


Je me vois mal utiliser les macros du pré-processeur C pour reproduire un motif du genre "une zone de collision dans l'état X permet de passer dans un état $CARRIED puis dans un état $THROWN jusqu'à ce qu'on heurte le sol, ce qui nous ramène dans l'état Y." par la seule ligne "using CARRY_ME(X,Y)" ou quelque-chose de ce genre. Dans le meilleur des cas, celà reviendrait à définir CARRY_IN=X et CARRY_OUT=Y avant un #include "carry_me.cmd.h" ... pas franchement le plus élégant, mais sans doute préférable à une duplication du code...

Ne vous étonnez donc pas si je prends le prétexte d'un convertisseur d'une version "maintenable" du GobScript vers sa version "machine" pour mettre un peu d'Haskell en pratique dans les prochains mois, hein ;)


Wednesday, July 30, 2014

Ink Swamp Worm


The ink swamp worm
Now *this* is looking better. It tooks two "masters" (one to the left, the other to the right) to ensure that I have a chain of wavelets GOB looking still, and looking to occupy the whole level. I will definitely need a wider GOB for the wavelet so that the spawn rate is enough to cover the screen even when the camera accelerates, and I'll need to investigate the camera offsets (currently, some values curiously refuse to work). But at least I get spawning and termination of wavelets as expected.

Hmm. J'aime mieux ça ^_^ Un "maître" de chaque côté, qui s'assure qu'il n'y ait plus de vagues à une extrémité et qui en génère une nouvelle s'il n'y en a pas au-dessus de lui. Il me restera des règlages avant que ça n'ait une belle tête de mer d'encre, mais on s'approche de l'objectif.
Il faudra aussi que je trouve un moyen d'arrêter la montée implacable de cette encre, au fait ^^"

Thursday, June 12, 2014

InspectorWidget could be more helpful ...

Ok, je suis toujours dans les ajustements supposés permettre à Bilou de soulever un taille-crayon dans un empilement. DDD, cybook, tout le monde y passe ... j'aurais bien aimé avoir un peu plus progressé sur l'Inspector Widget pour me simplifier la tâche, mais il faut faire avec ...

Après avoir envisagé la possibilité d'envoyer un message "à tous les objets qui me sont attachés", j'opte pour une approche plus pragmatique: une variable de la plate-forme que les objets transportés peuvent tester via leur contrôleur (on écrira donc onpath(when w7.1)) pour demander un avertissement si la 7eme variable de la plate-forme voit son bit 0 retomber à 0.

At last, Bilou can pick up any blador even when they're stacked up. One glitch fixed. I hope the same technique will allow me to have Bilou's carrying/non-carrying state more coherent. What technique ? Well, at first I thought collisions would be suitable to notify that one can no longer stand on a platform. E.g. when the platform fades into the background, I'd have a state with a collision area triggering a $STAND->$FALL (Detach) transition. But that's not working properly, because gobs might get collisions from another object that the one they're attached to. Tweaking areas so that the collision is properly received by all the gobs in all their states also turns into a sort of nightmare. 
Instead, I updated 'onpath' controller so that it optionally polls one of the target's variable and raise an event when this variable states "I'm no longer a solid platform, go away." That was the least resistance path. Plus, the polling is virtually free since we had to check that target for presence, position and speed anyway.

What about inspector widget and the picture ? well, it's an additional todo list to remember everything InspectorWidget should have been able to do to ease the (completed) debugging session.

On dirait bien que le problème est maintenant résolu. Glitch suivant: les mains de Bilou qui disparaissent dans certaines conditions (lancer un taille-crayon près d'un taille-crayon isolé, p.ex).

Saturday, April 19, 2014

La p'tite encre qui monte, qui monte ...

Quelques notes sur l'encre-qui-monte que je numérise avant de passer à l'implémentation. L'idée étant d'utiliser soit un polygone soit la fonction "fenêtre/cadre de vue" pour la partie noire ("Master Ink" dans mes notes") et des sprites pour la partie animée (les "wavelets").
Si je veux avoir quelque-chose à proposer pour la NeoCompo de l'été, il est temps que je m'y remette, n'en déplaise à Gomez et son fez. Donc:
  • [done] permettre au contenu de la mémoire vidéo des sprites de recevoir des étapes d'animations successives à un seul emplacement (par copie à chaque étape) pour une synchronisation parfaite des ondelettes;
  • [done] ajuster CopyCoords pour que MasterInk ne suive la caméra qu'horizontalement et que les wavelets ne suive MasterInk que verticalement.
  • Mettre en place la machine d'état avec ces éléments-là
  • Faut-il une "caste" spécifique pour les vaguelettes (pour réduire le coût des collisions) ? 


Thursday, March 13, 2014

encre ? yeah !

Je ne veux pas me contenter de flaques d'encres immobiles pour la School Zone de Bilou. Je veux pouvoir en profiter pour construire des puzzle autour du principe des vases communiquants. Je veux aussi pouvoir utiliser un seul niveau qui monte pour tout le niveau, histoire de proposer des niveaux à terminer dans un temps limité.

So far the ink is static. Puddles where you shouldn't fall in or some bottom-of-level deathly trap. I want to go beyond that. One of my favourite puzzle elements in Fury of the Furries was involving sand or water level that you raise or lower to transform the level. It re-appears in two of the levels I had envisioned for the School Zone, and it would be the base for both deep-ink-pit and rush-to-completion arcade-games I have in my furnace. Several elements need to be sorted out to get that working, though: rendering, interaction with monsters and implementation of the levels equilibrium mechanics.


Côté implémentation, j'ai écarté l'utilisation d'un plan dédié (je n'en ai plus). Combiner une ligne de sprites pour l'animation de la surface avec un remplissage de l'arrière-plan par des pavés tout noirs pourrait faire l'affaire quand l'encre va toujours vers le haut, mais pour des vases communiquants, qui pourrait "réparer" la map quand l'encre descend ? ... A moins que la map complète en mémoire ne soit jamais dégradée, et que seule sa copie à l'écran ait des pavés noirs. Mais Infinimap n'est pas conçu dans ce sens-là. Des gros sprites partout, ça ne marchera pas bien. Le plus souple, ce serait sans doute de règler ça à coup de polygones.

Rendering still has a few options possible on the DS, but in my current setup, the constraints start to pile up. I cannot have a dedicated BG layer. I don't have any left since I introduced 3D objects, which need their own layer. I might be using 3D polygons when the ink sits "between" the two layers of the playground -- that's for Deep-Ink-Pit and Bilou's Adventure. When the ink rise up through the whole level as in Rush-to-Completion, I'd better use the "window" feature (or possibly a raster interrupt to affect background priorities on the fly). For the animated surface of the ink, sprites are the best I can think of.

Ensuite, il y a les personnages. En remplaçant les codes du bloc d'encre sur la map du niveau d'*deline, j'ai pu faire en sorte qu'un taille-crayon lancé dans l'encre fasse des gouttes. C'est aussi un premier pas pour faire en sorte qu'une éponge qui tombe sur l'encre flotte (enfin, à moins que Bilou ne se tienne dessus assez longtemps).

Spongebops and other monsters might have to interact with the ink. I made a first step with Dumblador getting "killed" when touching the ink and generating droplets. That required to change the tile properties so that monsters can also interact with the ink. Initially, I thought making spongebop floating on contact would require some intermediate "liquid" tile before the interactive "kill-Bilou" blocks, but it turns out that the same interactive block can also trigger state transitions such as fall/float for spongebop. neat.
That's actually very good news. It means I don't *need* the ink to be tiles in order to trigger the floating. A GOB with the appropriate collision mask will transparently produce the very same kind of state transition.

Je pensais au départ ajouter une couche de tiles "flotteurs" par-dessus les tiles qui blessent Bilou, mais le même code "F_INK" peut à la fois se traduire en une transition tombe/coule pour Bilou et tombe/flotte pour l'éponge. En en faisant une collision ordinaire (et pas un type de tile spécifique, ce que je garderais pour l'eau), ça veut dire aussi qu'un GOB qui possède la bonne zone de collision F_INK peut interagir avec les éponges, y compris pour l'encre qui monte et qui descend.

A partir de ce moment-là, je peux commencer à concevoir les réactions "vases communicants". Un objet-déclencheur peut forcer les deux GOB-surface à se mettre en mouvement. On peut aussi les lier l'une à l'autre pour leur permettre de s'équilibrer, avec une variante du contrôleur qui ajuste la distance entre Spongebop et son pivot. Reste un élément délicat: comment faire en sorte que les niveaux d'encre se mettent en mouvement simultanément ? Un coup de sonde à l'aide d'une zone de collision bien calibrée pourrait faire l'affaire (de la taille du bouquin central, par exemple), sauf que les zones de collision sont normalement définies pour un état donné, pas pour un objet donné.

If the part of the ink that's animated is allowed to be a GOB, the use case of ink-level-equilibrium turns closer to something GobScript can readily handle: it mostly comes down to two ink-surface objects that adapt to each other so that they reach the same level, possibly assigning them top speed that depends on the width of the pit they're in. Then, I have to figure out a way to kick them so that they start moving simulatenously (e.g. after Bilou opened some path between the two pits). I could build something inspired from Badman II boss rooms in RSD Game-Maker, with a lock-type block that shoots invisible trigger-sparks moving along the same kind of directional block as those controlling inkjets.

J'ai donc commencé à construire quelque-chose qui fait fort penser aux boss de Badman II: faire en sorte que la serrure génère des "étincelles déclencheuses" (invisibles) vers la gauche et la droite et placer le même genre de blocs-directionnels que ceux qui contrôlent les déplacement des encriers pour faire en sorte que les GOB-étincelles atteignent simultanément les deux surfaces pour les mettre en mouvement.

If that's funny to demonstrate that the a-priori simple engine can handle elaborated things, it would quickly turn into a nightmare for any sophisticated situation, like the twin-corks in Remi's level (where each cork might trigger raising ink from the other's position). You'd end-up crafting chuchu-rocket puzzles on your map and hope the timing you computed in your mind is correct when launching the level. As a second thought, I'd rather have the surface GOBs both attached to the "lock" and start raising when this link is lost. Yet, it would be nice to have test-zones that can be defined through GOB variables so that the level editor can precisely define a trigger region for each of them (but it's unclear I have true *need* for that in any of the level I designed so far).

C'est rigolo un instant, et ça fait plaisir de constater que les abstractions construites jusqu'ici sont suffisament Turing-esque (comprenez, expressives) pour ce genre de déviance. Mais soyons clair: si on veut passer à un niveau un tout petit peu plus élevé -- les bouchons jumeaux du niveau de Rémi, par exemple -- on peut rapidement se retrouver à construire un puzzle à la chu-chu-rocket qui va promettre de sérieuses migraines au moment de la mise au point.

Il y a bien une autre possibilité: quand un Gob (ici, la surface d'encre) est attachée à un autre (la serrure), il reçoit un évènement si le Gob de référence disparaît. On peut alors faire monter le niveau d'encre suite à la disparition du bloc-serrure -- ou du bloc invisible qui s'est fait détruire par le bouchon-qui-s'enfonce. La bonne nouvelle, c'est que l'éditeur de niveau supporte déjà ce genre d'approche. La moins bonne, c'est qu'un Gob ne peut être attaché qu'à un seul autre. On saura donc faire des niveaux avec 2 bouchons, mais pas avec trois. Cela dit, ça mériterait vérification, mais je pense bien que je dois pouvoir m'en sortir avec juste "2 bouchons" dans tous les niveaux dessinés jusqu'ici.

Friday, January 03, 2014

Fixme list

"Don't Repeat Yourself" supported.
The few feedback I got for the "upgraded" school zone level provide some fairly interesting hint: some gameplay features will not be properly managed by players unless they're sufficiently pure. Give any reason for the player to believe that holding B could let him run and he'll fail to discover that he's got to hold R for that to work. So to get pure gameplay in Bilou, I still need:
  • [blador:ok] Introduce monsters/ink interaction
  • [MEDS] Dedicated animation when Bilou picks up something
  • [done] Never "randomly start running", no longer run-on-turn-back, only run-on-land when one had running speed in the air.
  • [done] no invisible ceiling to kill your jumps.
  • [done] visual clue to tell whether you'll bounce or not.
  • [done] visual clue that chalk may break when sufficiently stressed.
  • [wish, D.I.P] bouncy-idle-sponge vs. low-bounce-swinging-bops.
  • [patch] HUD with life meter. (and speed meter ?)
  • [wish, adventure] visual hint for dead ends (?)

Il y a une leçon qui réapparaît à travers les divers retours que j'ai reçu de ma démo de la school zone: les éléments de gameplay ne sont correctement maîtrisés par les joueurs que s'ils sont suffisamment purs (les éléments, pas les joueurs). Par 'pur', j'entends 'le mécanisme se déclenche sur base des inputs, à chaque fois et uniquement dans ce cas-là'. Qu'il y ait la moindre raison que le joueur puisse se mettre à courir en appuyant sur B et celui qui en arrive là lors de sa première partie n'essaiera pas de maintenir R enfoncé pour courir. Par contre, il nous indiquera que 'ouais, courir, des fois ça va, des fois ça va pas'.

Le même genre de leçon peut être tirée pour les animations: une action différente du personnage sur son environnement réclame une animation différente. Le joueur doit pouvoir "voir" que son personnage fait quelque-chose de particulier. Pas d'animation "essaie de ramasser" quand il n'y a rien à ramasser, et il n'y a pas de raison que le joueur essaie de ramasser un truc quand enfin il s'en présente un.

(old) non-symbolic gobscript

All this (and further introduction of power-up with more conditional state transitions) will require more work on the state machine for Bilou which is already fairly complex (32 states, 170 transitions 0_0). 

I don't want to make the parser smarter on the DS side (e.g. I still don't want symbol tables or function definitions) but it's truly time I adapt the Makefiles so that e.g. GCC's pre-processor can be used to produce the "compiled" state machine out of more symbolic description.

(new) symbolic gobscript

Mais pour pouvoir modifier tout ça, il va falloir que j'aille replonger dans la machine d'états de Bilou, qui est déjà pas mal complexe (32 états, 170 transitions).

Je n'ai pas trop envie de devoir augmenter aussi la complexité du parseur qui tourne sur la DS (pas de table de symboles ou de définitions de fonctions, par exemple). mais je pourrais arranger quelque-chose pour utiliser le pré-processeur C pour me convertir une description plus 'symbolique' vers le texte brut compris par le code DS...

Saturday, December 21, 2013

Shrink and Grow!

http://www.flickr.com/photos/68105287@N03/7941244748/in/photostream/
It's somewhat time to allow my game "objects" to take the "drink me" bottle and to change size as the game goes. Well, they can already do so. Well, they can already change size, but it usually result in characters stuck in walls, since the coordinates doesn't change.

Jusque là, si M. Miyamoto était revenu me voir après sa pause-déjeuné au Palais du Champignon Chinois, j'aurais bien tiré la tête en l'entendant dire "Mario géant, on ne va pas le faire géant dès le départ: il aura une taille comme dans Mario Bros. et il deviendra géant seulement après avoir attrapé un champignon. C'est bien connu: ça pousse vite, les champignons". Si mon éditeur d'animations me permet effectivement de changer la taille de la zone occupée, le fait que la position du personnage (de son coin supérieur gauche, pour être précis) reste identique l'envoie plus souvent qu'à son tour se faire emmurer.

So despite I'm feeling as dizzy as an egg, I managed to update the game engine so that you can now indicate which edge of your character should stay in place when coming from a state where the major hitbox (the one which is used to test collisions against the world) has a different size. At last, that allows Bilou to adjust his position as the inkjet shrinks while preparing a "blow".

Mais cette fois, j'ai enfin intégré une amélioration du moteur de jeu trop souvent retardée: premettre d'indiquer sur quel bord/coin de sa zone le personnage doit s'aligner. Ça me permettra prochainement d'ajouter de sympathiques animations pour l'aterrisage de Bilou (comme Lazycow l'avait suggéré). Plus directement, ça me permet enfin de décaler légèrement Bilou vers le bas quand un encrier se prépare à le propulser en l'air ^_^

Merry christmas.

Thursday, December 12, 2013

Hands'up !

Once we know blador would stick to a wall while Bilou can slightly move below it, our options to show Bilou's hands while carrying something narrow down to only one option: hands are actually part of the "blador" sprite while Bilou's real hands will remain hidden. That perfectly fits the fact that Blador lost its feet, by the way.
Hiding Bilou's hand is somewhat similar to the flickering mechanism introduced for inkjets, in the sense that there's a set of limbs which we'd like to selectively hide while the rest of the character remains displayed.


Je m'attaque enfin à la position des mains, ce qui fera plaisir à Pierrick. Finalement, pour être cohérent avec la façon dont dumblador est transporté, le truc consistera à faire en sorte que les mains de Bilou soient directement visibles sur le sprite de Dumblador. Je reporte à plus tard des mécanismes de "costumes" qui remplaceraient les images en VRAM pour que Bilou porte des lunettes de soleil, par exemple: ça ne m'aiderait pas pour ce problème-ci. Il me reste du coup à cacher les "vraies mains" de Bilou, ce qui ressemble fortement au problème de transparence partielle qu'Inkjet avait introduit, sauf qu'ici les sprites désignés comme transparents doivent en réalité être invisibles.

wrong ...
The first difference with inkjets is that here, hands are *permanently* hidden, bypassing the "counter" used for flickering. The other difference is that many states are involved (actually, most of Bilou's state machine) and that the set of limbs may be different depending on the animation (hands aren't always on the same "layer"), which stems for a more automatic approach (inkjet had sets defined directly within GobExpressions).

Ideally, those "sets" would be defined graphically in AnimEDS. The second best thing I can do is to define them only once, when the animation is imported in the GobScript. That's manageable for a prototype. As a last refinement, I had to alter CopyCoords controller so that it produces an event whenever horizontal direction changes, so that the animation can be changed to avoid odd displays where Bilou seems to have "crossed his arms" while turning back (see the 'wrong' picture).

Je conserve donc le concept de sélection de sprites attachés à un personnage (flickermask), des animations elles-même de sorte que si je réutilise la même animation dans plusieurs états, elle définit en même temps le masque à utiliser. Ce serait encore mieux si je pouvais définir cette sélection directement dans AnimEDS, mais je n'y suis pas encore. D'ailleurs, c'est la raison d'être principale du "GobScript": pouvoir expérimenter les techniques du game engine avant d'en automatiser la gestion par les éditeurs graphiques.
mais les expressions de la machine d'état ne seront plus le seul moyen d'y accéder. Je le complète avec une annotation au niveau de l'animation elle-même.

right!
Je suis obligé, par contre, de passer à 2 états distincts pour le transport de dumblador (un vers la gauche, l'autre vers la droite) de façon à utiliser la bonne animation au bon moment. Il faut du coup que j'ajoute au contrôleur copycoords la possibilité de provoquer un changement d'état quand le personnage transporté subit un changement de direction (ça sera intéressant aussi pour Bilou-sur-l'éponge, tiens).

One drawback with this approach, though: Blador follows the move, but he stays at constant height while Bilou walks/run despite the fact that Bilou's body moves up and down.

Monday, December 02, 2013

The art of Blading

For g88 release, carrying bladors was functionally correct, but still a bit cheap, as the carried blador could move through walls, ceilings and such. It would be an acceptable simplification of physic rules in-game if that did not meant you could "throw" it while it overlap solid structures, which would let it "stuck" there as soon as regular physics takes over to let it be thrown away. I want the game to feel professional, and demonstrate that a simple scripting approach does not imply half-baked behaviours. It's thus time to prove it.

Un taille-crayon qui passe à travers tout pour qu'on puisse le lancer n'importe où, ce n'est pas très réaliste, mais c'est acceptable dans un jeu vidéo. Un taille-crayon qui reste bloqué dans un mur quand on le lance, ça, ça fait tache. Surtout quand le joueur aurait théoriquement dû pouvoir le ramasser et l'emporter plus loin. Premier pas pour corriger ça, interdire à mon taille-crayon de passer à travers les murs (ce qui demandait un réglage spécifique du contrôleur freemove). Résultat: Blador suit maintenant Bilou "de son mieux", ce qui amène parfois des résultats curieux comme un saut quantique à travers un mur contre lequel il est resté scotché pendant 15 secondes lorsque Bilou émerge enfin d'un passage secret.

The first step is to make blador using freemove(1) instead of using freemove(0) when carried. Freemove is the controller that allows free movement typical from airborne ennemies such as the berrybat. The only thing it does before validating the desired movement is checking that you cando(m) where m describe some properties of the environment - like being something you can MOVETHRU. 0 is considered as a special case where you can always do what you want -- perfect for ghosts or sparkles that will fall of the screen. So now, blador can't get into walls, but curious things occur when Bilou gets into narrow passages where Blador cannot follow: Blador just sits where it is, and if at some point Bilou reaches a new position where Blador could fit above its head, Blador gets quantumagically teleported to that new location (yes, again).

Pour corriger ça, il faut que Bilou puisse être informé de la situation du Dumblador qu'il transporte. En '97 j'avais donc pensé augmenter mon "ulitmate game maker" d'un système de messagerie entre les personnages. En fait, ici, on va faire beaucoup plus simple: tout comme l'encrier possède une zone-test (hitbox) qui repousse Bilou lorsque celui-ci l'approche, le Dumblador transporté va disposer de murs invisibles qui empècheront Bilou de s'en éloigner de trop tant qu'il le transporte. Eh oui: c'est aussi simple que ça.

Enfin, presque. Il y a quand-même une subtilité dans le cas du saut. Si je me contente de forcer Bilou à faire du sur-place au milieu d'un saut juste parce qu'un taille crayon a la tête dans le plafond, ça fout en l'air la crédibilité de la physique du jeu pour donner un effet Badman/RSD Game-Maker carrément pathétique. Il faudra donc que je traite cette collision aussi du côté de Bilou en forçant une remise à zéro de la vitesse, exactement comme le contrôleur "gravity" l'aurait fait si on s'était vraiment retrouvé directement dans un mur.

I like how Bilou/Blador distance is stretched or squished when colliding the environment, but I need more control over it. And that's just a perfect fit for the "repel" action introduced with solid inkjets. To avoid having Bilou going too far away from the carried Blador in a narrow tunnel, I just need some repelling-areas acting as virtual walls at appropriate distance from its position.
The same approach allows Blador to take some room between Bilou and the ceiling, but here I need some additional action. "Repelling" simply adjusts coordinates and does not affect speed. The result was that Bilou would keep floating along the ceiling as if he virtually kept jumping. Even weirder: he might have resumed moving up as soon as he'd have moved away from what was blocking Blador (just as in Badman II, actually).
To avoid this, Bilou needs to react on the collision and clear vertical speed. Additional tests are needed to ensure the blocking object (active GOB) is indeed over Bilou's head and that he's still moving upwards. The center-to-center distance (dy) is available through GobScript wf variable read and is positive when the active object is above the passive object (and yes, the sign of dx and dy is related to passive/active roles as the distance is defined only once, and then reused in both passive and active objects transitions).


Il me reste donc "simplement" à corriger l'ordre d'affichage de Bilou et du dumblador (tant qu'à faire, prenons-le en mode "hotte de père Noël" plutôt que "Sac de courses devant le nez") et à m'assurer que les mains de Bilou apparaissent sur le taille-crayon pendant qu'on transporte le crayon.

It leaves me with one last aspect to fix: having Bilou's hand properly shown as grabbing the blador while it is carried, knowing that hands will have to be aligned with blador rather than with Bilou if we want to keep stretch/squish.

Tuesday, October 22, 2013

Toujours plus haut!

S'il y a une chose à quoi les éponges de la School Zone ne sont pas encore prêtes, c'est bien à servir d'ascenceur dans "Deep Ink Pit". Plate-forme mobile par-dessus l'encre, ça oui. Avec un bon timing pour s'y accrocher puis sauter, on peut prendre pas mal de vitesse horizontale -- et c'est assez fun. Mais monter, ça ... c'est une autre histoire. Au point qu'aux endroits où il fallait passer d'une éponge à quelque-chose d'autre, j'ai fini par amener la trajectoire presqu'au-dessus de l'objectif, qu'on ait plus qu'à se laisser tomber.

With the anniversary level, Spongebop no longer directly hurts. There binding to a pin can be modified by the player too, to the point I wonder whether I should allow Bilou to carry them along. There's one thing they're still terrible at: allowing you to climb up! Too bad: that's what they should provide in Deep Ink Pit. With training, one can get significant horizontal speed when letting the sponge loose, but if you manage to find an idle sponge in the level, you may have hard time gathering the bonuses that float over it. 

While investigating the reasons, I figured out that the rule that makes SpongeBop "slightly bounce when you land on it" interferes with Bilou's attempt to use bop'd ennemy's vertical speed as an extra bounce impulse: first, the sponge gets a push downwards due to its collision with Bilou and then Bilou's jump strength is defined as default_impulse + other.yspeed. I cannot change that ordering: it is defined by who's active (Bilou) and who's passive in the collision. What can do, however, is to arrange a variable where the vertical speed of the monster is preserved (or an impulse bonus of some sort) and ensure that vertical speed is only used as a booster, never to reduce the impulse.

En fait, j'ai presque tué toute possibilité de rebondir au moment où j'ai ajouté la règle "l'éponge reçoit un choc vers le bas en cas de contact" pour donner un effet plus "élastique". Bilou ne peut du coup plus bénéficier de la vitesse montante de l'éponge pour sauter plus haut puisqu'il vient de lui forcer une vitesse descendante! J'imagine que c'est le genre de couac qui a poussé les auteurs de Frogatto & Friends à construire leurs scripts à grand coups d'expressions-qui-renvoient-des-affectations. Ici, il ne me restera qu'à sauver l'ancienne valeur dans une autre variable de Spongebop pour que Bilou puisse y accéder.