Thursday, May 27, 2010

NitroTracker

Nitro-tracker, the homebrew soundtracker running on the Nintendo DS written by 0xtob and based on libntxm has been open-sourced a few monthes ago. Sounds like it is time for me to embrace the open-source spirit, grab the sources and port the improvement I made locally to a wider audience. That should be my "hobby-priority" once Apple Assault is released.

L'auteur de la libntxm a publié les sources de son NitroTracker il y a quelques mois. Ce serait justice que de m'assurer que la gestion des effets que j'ai ajoutée pour mes besoins personnels y soient également disponible ... et pourquoi pas en profiter pour ajouter un bouton "quit to runme" ? Allez, je boucle "Apple Assault" et je m'y mets.

Tuesday, May 25, 2010

Als de klok thuis tikt ...

Howdy. You've noticed the small bullet point on improper music replay on my latest "todo" post, haven't you ? That means more cscope-powered lunch times ahead, and I've just had one of them. I must add that, while I've been coding or hacking around players for tracked music for over 10 years now, I've got virtually no experience of the .MOD format itself and very little background on the .XM format. S3M and IT were what I worked with until I opted for 0xtob's libNTXM as the media player for GEDS. So my documentation of the current player state got stuck on question such as "Is there a 'volume column' at all in .MOD files ?" Screenshot from Wikipedia suggests it hasnt, and format 'specs' from wotsit confirms.

J'ai choisi la libNTXM d'0xtob pour sonoriser les aventures de Bilou, il y a bien 2 ans, maintenant. Pourtant mon expérience du fasttracker II est virtuellement nulle, et ma connaissance des .MOD (les fichiers produits par son précurseur) franchement aléatoire. Un exemple ? Hier, j'aurais été incapable de vous dire s'il y avait ou non une colonne dédiée au volume dans un .mod. Dans un .S3M, ça oui : c'est le format que mon "modplayer" en assembleur pour DOS prenait. Dans .IT aussi -- c'est le format que mon frère a utilisé le plus pour ses productions. Eh bien, il n'y en a pas en .MOD. Tout au plus un effet "set volume" (Cxx) et quelques variantes (portamento + vol slide, etc.

    _____byte 1_____  _byte2__  ______byte 3_______  _byte4_
/ / / /
0000 0000-00000000 0000 0000-00000000
| | | |
Upper four 12 bits for Lower four Effect command.
bits of sam- note period. bits of sam-
ple number. ple number.
LibNTXM code is not a maze, but it has to work around the glitchy sound hardware of the nintendoDS: NDS sound hardware doesn't allow us to alter the volume directly (if we do, it produce sound glitches), so we have to perform fading from the current volume value to the next one, requiring some actions to take place ahead of time. So you'll encounter things such as
 if(state.tick_ms >= song->getMsPerTick() - FADE_OUT_MS) {
// we are shortly before the next tick. (As long as a fade would take)


As a consequences, EFFECT_SET_VOLUME (Cxx), EFFECT_VOLUME_SLIDE (Axx) or EFFECT_E_NOTE_CUT (ECx) do not directly affect the channel volume, but record a request to set the volume in effstate.channel_setvol_requested, altering state.channel_fade_target_volume jointly. So what happens to the "volume column" of an S3M (yes, it had it when converted into an XM ?)


Pourquoi ce regain d'intérêt soudain pour libNTXM ? eh bien parce que le "mod" que je voudrais utiliser pour Apple Assault ne passe pas correctement sur DS. Je soupçonne fort "mon" player de ne pas tenir compte des changements de volume. Je creuse donc les sources, mais le code n'est pas d'une lisibilité exemplaire. En cause, le fait qu'il est nécessaire de baisser progressivement le volume d'un canal audio de la DS avant de changer le son en cours... faute de quoi, un "tic" se fait entendre. Le code de "EFFECT_SET_VOLUME", "EFFECT_VOLUME_SLIDE" et autre "NOTE_CUT" (que je connais sous leur 'nom' dans scream tracker, pour simplifier) va donc indiquer son "intention de modifier le volume" de sorte que le player puisse commencer le changement de volume suffisamment en avance ... De plus, le code de gestion des effets est découpé en 2 parties: ce qui s'exécute sur le premier "tic" d'une ligne et ce qui s'exécute sur tous les autres tics.
// Separate volume column effects from the volume column
// and put them into the effcts column instead


if((vol >= 0x10) && (vol <= 0x50)) {
u16 volume = (vol-16)*2;
if(volume>=MAX_VOLUME) volume = MAX_VOLUME;
ptn[chn][row].volume = volume;
} else if(vol==0) {
ptn[chn][row].volume = NO_VOLUME;
} else if(vol>=0x60) {

// It's an effect!
// decoded into ptn[chn][row].effect2
// and ptn[chn][row].effect2_param
// see XMTransport::load() for details.

}

Another thing I'll have to keep in mind while hacking around is that ProTracker effects (and i guess, their XM derivatives) are only supposed to be executed on non-row ticks. At least, that's the case for volume slide and portamento to note, and that explains many if (tick==0) return; in libmikmod's code. I blogged that a few years ago. That explains the separation between handleEffects() and handleTickEffects() in libntxm. Hence the name of this post: ticks are the ultimate measure of time in MOD-inspired formats. Final note for today: in .MOD and .XM, the same "Fxx" command is used to set speed (#of ticks per row, 0..31) and tempo (that is, BPM, 32..255)

next time: update of Player::playRow so that it affects volume in the same way as playNote(..., chn->cell.volume,...) does when note==EMPTY_NOTE. Stay tuneD.


Bref. Je vais devoir m'assurer que l'équivalent du code de "set_volume" existe lorsqu'une "cellule" contenant du volume mais pas de note (sinon playNote(...) aurait fait son boulot :P)

Monday, May 24, 2010

Smashed Apples ...

Nous y voilà : coup de poing et applemen projetés ... Plus qu'à bricoler des arènes et Apple Assault pourra voir le jour.
Here we are. Got the pixels drawn. got the moves "coded". Got collisions checked ... only some arena should be needed now. Apple Assault coming soon ?

PS: ma fée a quand-même bien ri en voyant un des "bugs" de la journée: j'avais oublié de préciser que le sprite destiné à faire le "coup de poing" devait disparaître une fois l'animation terminée. Résultat: Bilou pouvait s'éloigner pendant que son point restait sur place en donnant des coups et anihilant tous les applemen passant par là ...

Saturday, May 22, 2010

Jump!

Quelque-chose me dérangeait dans la manière dont j'ajuste la hauteur du saut de Bilou au timing du bouton de saut. Je passe les "classiques" en revue:

  • Sonic, qui ne semble avoir que deux hauteurs de saut et fera son saut maximal même si le bouton ne reste pas enfoncé
  • Mario, à l'opposé, peut faire de petits bonds ou de long sauts. Il faudra alors tenir le bouton enfoncé jusqu'au bout ... ou courir.
  • Giana Sister (version DS), peut comme Mario interrompre son saut, mais l'arrêt est alors plus brutal.
Tout ça pour constater que le problème vient très probablement du fait que le changement de direction, aussi bien que le relâchement du bouton de saut, provoque la fin du saut de Bilou :P

Apple Assault : 90% done ?

Okay, code is done and working on real hardware. You can stomp and bounce on applemen, you can "shoot something" when you've bounced enough. And applemen can be respawned, and their number can be dynamically defined. We're definitely getting closer to a "AppleAssault" release.

Oh yeah, I know. You've been hearing that since the start of the month. That's quite typical from a hobby project.

Why ain't I not releasing, then, might you think... Data, that is it. I need a few more things do be done:

Ca se rapproche, ça se rapproche. Ma petite démo ressemble de plus en plus au gameplay prévu pour Apple Assault. Au niveau du code du moteur de jeu, on y est, en fait. Reste une animation par-ci, par-là (coup de poing de Bilou, Appleman projeté ... Twinbee et Bonkers ont accepté de poser pour un résultat plus réaliste). Ensuite, il faudra que je m'attaque à quelques "arènes" pour ces moments épiques.

Note that the level editor is still in a "broken" state (that is, it won't be able to add monsters to a map in its current shape), so I hope this will not prevent me from doing arenas anyway.

Monday, May 17, 2010

Marble Fire Madness ...

La médiathèque du coin me permet d'emprunter des jeux DS à un coût de 2€ par mois ... alors je ne me fais pas trop prier. En attendant mon vol pour Uppsala, j'ai pu m'essayer un peu aux Sonics auxquels je n'ai jamais vraiment eu l'occasion de jouer étant gamin. La MegaDrive étant squattée par les "grands" à la maxithèque, je me rabattais sur la version "Master System" (où j'arrivais fièrement à passer le boss de la zone de la jungle).

Ce devait donc être la première fois que j'affrontais la "Marble Zone" pour de vrai, et là, le parallèle avec ce que mon frère tentait de faire dans la "fire zone" de Calimero, dont j'ai retrouvé dernièrement le niveau 1 devient évident ... le tout premier niveau dessiné par mon frère qui me le tape sur ma planche à LEGO en disant "tiens, code moi ça sur C64, plutôt"...

Les mouvements de Sonic sont clairement mieux adaptés aux pentes et arrondis de la Green Hill qu'aux escaliers de la Marble Zone, chose que l'on observe pas tant dans les Mario. j'avance avec prudence ... ça aussi, ce n'est pas "naturel" pour Sonic. Je suis aussi impressionné de voir que, si tôt dans la série, Sonic tirait sa force non seulement de sa vitesse, mais aussi du réalisme de son moteur physique. Ecrabouilleurs, des pans entiers du niveau qui s'écroulent ou s'enfoncent ... On est clairement à un niveau différent d'un Super Mario où les "objets" sont généralement d'un seul bloc.

Du point de vue d'un programmeur, Sonic restera un exemple impressionnant. Je serais vraiment curieux de mettre la main sur le code Genesis de ce jeu ... ne serait-ce que pour la gestion des plate-formes mobiles... et des loopings, combinés au sol qui croule et aux blocs destructibles...

PS: avant de conclure que "la marble zone n'est pas vraiment Sonique, mais pourrait se retrouver dans n'importe quel jeu de plate-forme, je vous invite à regarder ce speedrun où la plupart des séquence "sur un bloc flottant sur la lave" sont court-circuitées en prenant de la vitesse et en enchaînant les sauts avec précision... Ou en choisissant de se faire toucher par un écrabouilleur-à-picots pour sauter un peu plus haut.

Friday, May 14, 2010

Debugging Industriel ?

Finalement, j'ai opté pour une approche toute neuve (pour moi) pour ce problème de malloc. C'est toujours via gdb que j'attaque le problème, mais je laisse tomber la sur-couche graphique (ddd) au profit d'un petit script perl. Celui-ci va placer les breakpoints à l'entrée et à la sortie des fonctions "malloc" et "free" (ainsi qu'à des emplacements stratégiques dans les opérateurs new et delete du C++) et enregistrer consciencieusement la pile d'appels, le contenu des registres du processeur ARM9 et le contenu du tableau __malloc_av_ dont j'ai parlé précédemment, et qui représente la "partie visible" de l'état maintenu par malloc.

Ce côté "totalement automatique" permet d'obtenir une trace exhaustive (64Mo) de ce qui s'est passé au niveau gestion de la mémoire pendant l'exécution du programme, que je peux ensuite traiter avec d'autres scripts. Par exemple pour vérifier qu'il y a bien eu un appel a free avant chaque delete operator, et surtout qu'il y a un malloc()=X pour chaque free(X), et l'emplacement dans la trace de la dernière opération sur l'adresse X dans le cas contraire.

Avec un peu d'ajustement, je devrais même être capable de trouver les malloc() pour lesquels il n'y a aucun free .. et donc potentiellement d'identifier les fuites de mémoire dans mon code. Bien sûr, il existe d'autres programmes pour faire ça, et ce de manière plus automatique (valgrind, si ma mémoire est bonne). Il y a aussi des implémentations qui intègrent ce genre de tests (cf. la variable d'environnement MALLOC_CHECK_ sous Linux) ... je profite du côté "hobby" de ce projet pour découvrir et tester des hypothèses, me construire de petits outils et tout ça ...

Et j'ai finalement mis le doigt sur le problème ... une bête initialisation manquante dans la classe GobState. C++ n'aide vraiment pas le programmeur, de ce point de vue-là. Je devrais m'habituer à compiler avec -Weffc++, s'il ne contenait pas une quantité ridicule de warnings qui me sont inutiles.

Wednesday, May 12, 2010

0_o still 42 zombies

Making funghi-generated applemen EVIL wasn't such a hard task. Now I need to ensure they are properly destroyed when the level is over. It looks like it's something I had postponed when writing ClearDynGobs() ... Resulting in an enigmatic "still 42 zombies" message in the middle of parsing logs.

I don't like zombies. They believe their iWorld still exists but it doesn't. They mess up with the real world instead, poking and peeking here and there. They follow NuLL pointer ... chaos and madness await them at its end.


Avec 3 jours 3/4 de congé commençant ce soir et les bouts de code qui s'assemblent comme des pièces de puzzle, je sentais bien une pre-release d'Apple Assault avant de décoller pour la Suède ... Un petit message "42 zombies restant" fixé sans trop de soucis (en nettoyant convenablement les "dyngobs"), enfin la possibilité d'assommer les ribanbelles de pommes. Let the sun shine, quoi.

Ouais. C'était compté sans malloc et ses effets à retardement. Cette fois, même gedsdemo, la version autonome du moteur de jeu utilisé pour les release, est affecté. Desmume lui même en reste sans voix: impossible d'effectuer du débugging puisque le "serveur GDB" qu'il intègre semble s'être bloqué lui-même. J'ai quelques pistes à tenter, mais ça prendra du temps, du papier et quelques tasses de thé. Je vous tiens informés.

Btw, 42 apples in a level doesn't slow the engine down in any way, and it produce funny "wiggler" or "lemmings" behaviours. And it sure keeps the doctor away ^_^

I see coming more "debugging sessions darker than night itself", though. For some unclear (yet) reason, the current game engine crash in bowels of the memory manager after two runs. I can only guess this means something is freed twice or some similar issue. The codebase has grown since the last (not so succesful) attempts, and today not only runme is affected, but also the game engine demonstrator. It's the most stable memory bug I've seen ever, and I hope I'll at last be able to pinpoint what's going wrong.

Tuesday, May 11, 2010

Round 1! Fight!

Première véritable tentative de passer à un "véritable" Apple Assault, avec les Funky Funghi dans le rôle (intérimaire) du générateur-de-pommes. Ils maintiennent un nombre à peu près constant d'applemen sur la zone de jeu par l'intermédiaire d'un compteur partagé -- le même mécanisme que celui qui permettait aux "portes" de "s'ouvrir" lorsque Bilou a ramassé 16 pommes ou de relancer le niveau lorsque Bilou n'a plus aucun point de vie.

First attempt to build the 'Apple Assault mod' out of the game engine, using the Funky Funghi as appleman throwers ad interim. A global counter enforces a maximal amount of applemen on the playground (so if you manage to dismiss one, a new one will appear near a Funghi) -- that's the same mechanism that already allowed key-and-doors mechanism in the .999 release or that triggers a level restart when Bilou runs out of energy, actually.

The 'doh-of-the-day', unfortunately, is that dynamically-generated GOBs all have the same cast "DYNGOB", while it is required that they bear "EVIL" if we want Bilou to stomp them. Pretty hard to try out bops-and-shots in that setup, thus. Oh, and by the way it's also the first (fairly succesful) test of release-jump-button-to-modulate-jumps-height feature.

Petit soucis, par contre, le moteur de jeu considère ces applemen comme des "dyngobs": de simples "tirs", qui ne figurent pas dans la liste des monstres pouvant être écrabouillés par Bilou, et qui du coup sont invincibles. Il faudra ajuster ça. Probablement en fixant la caste d'un monstre au moment de l'importation des états vers le script-maître.

Oh, et c'est aussi le premier test avec un saut dont la hauteur peut être modulé en relâchant le bouton de saut ... ça peut expliquer que j'ai eu un peu de mal à m'y retrouver.

Monday, May 10, 2010

Bounce & Shoot

Petite tentative de mettre en image les petits essais de ce midi, dans le cadre du mini-jeu "Apple Assault". L'idée est d'avoir une jauge d'attaque que Bilou peut remplir en rebondissant sur des monstres (représenté par les 2 transitions en haut de l'image) et qui lui permet de lancer une étoile (à remplacer par un coup de poing).

Cette jauge n'est toutefois pas permanente: quand Bilou est au sol, elle retombe progressivement à 0, réutilisant le même mécanisme que celui qui fait ralentir les Applemen. Cette "vue synthétique" de toutes les interactions faisant intervenir une variable donnée pourrait bien venir compléter d'autres fenêtres de dialogue plus "classique" (éditer toutes les transitions de / vers un état) dans un éditeur de comportement tournant directement sur la DS ... à venir plus tard.

This week-end has seen virtually no code written for the engine, but massive update and improvement on the "gobscripts" defining Bilou and the Applemen. Bilou now actually 'bounces' when he stuns appelmen, and you can bounce higher by pressing the jump button with the proper timing. In an attempt to get closer to the "apple assault" milestone, I was toying this lunch time with a bounce counter that could be use to enable the "shot of some placeholder" that is meant to become a "finishing move" that would clear the area of stunned (or running) applemen.

Here's a conceptual mockup of how that job would have been done if the "behaviour editor for DS" was already implemented... or at least how you could visually provide hints on what is using that bounce counter by identifying all the predicates and action that use "variable #6". In order to add that behaviour, you'd have to start with the "falling" state, alter "monster found" behaviour, then browse neighbour states and decide what to do with v6 in order to implement the "depleting counter" that only let you "throw a star" shortly after you've hit the ground.

Thursday, May 06, 2010

Do I smell release ?

On s'approche d'une nouvelle démo. La quasi-totalité des petits soucis ont été corrigés ... Depuis ce soir, InspectorWidget est capable de me montrer quel monstre (sur l'écran de jeu) correspond à quel objet (sur l'écran de debug) et en cliquant sur ses coordonnées on peut le "mettre en veille" jusqu'à nouvel ordre. Avec ça, je devrais être paré pour comprendre comment mes applemen se retrouvent suspendus en l'air ... hopefully.

We're getting closer to a new demo. The only todo item that I have to take care of is the appleman's behaviour. And with tonight' updates on InspectorWidget, I can more precisely identify GOBs (a touch on their debugging area make them flip) and I can disable those who disturb debugging (such as the berrybat, atm).

-- edit: doh' Problems with applemen obviously arise when they leave the "stunned" state for more hunting. I haven't any button to set breakpoint on such transitions ... not yet.

Tuesday, May 04, 2010

statW20->statI4 on fail


Après relecture du code "la tête froide" (comprenez, en prenant un Ice Tea sur la terrasse du Fagotin de ma fée entre la grèle et la panade pour *deline), il s'avère qu'aucun état "on pousse" n'est nécessaire pour règler les problèmes de collisions. Une meilleure sémantique pour la décision "FAIL" des contrôleurs a suffi, et je devrais pouvoir encore simplifier un peu la sauce. Cf.


I managed to find some time to study back my engine code this week-end, and it turned out that no "push" state was actually required to solve stuck-on-corner issues I observed last week. Quite good news, somehow: that means I won't need extra states when it'll come to do climb/walk transitions.

Btw, I applied to Bilou the same kind of "reuse impact speed for bouncing" than I previously used for little stars... It's pretty much working, but it is tedious to maintain the state of variable: when hitting a wall first, and then the ground, we should still bounce with the floor-hitting vertical speed (rather than wall-hitting speed).

It isn't obvious to implement at the moment, requiring to manually reset the "impact speed" in every transition that enters "Fall*" state. I'd prefer to have "impact_y:=0" defined as a "state initialisation action" ([todo] a part of the UmL state machine model I'm still missing). But first, I'll have to check whether removing the extra "landing" delay will affect it or not.

0.7.3 > 0.9.4 > 0.9.5

Fin janvier, j'étais bloqué dans le développement de mon level editor. par un bug crapuleux de desmume 0.9.4 (version par défaut dans Karmic Koala) qui m'empèchait d'utiliser le touchscreen dans mes dernières productions. J'avais heureusement toujours les sources de la version 0.7.3 (qui, elle, fonctionne sans soucis de ce côté) sous la main.

Là, je viens de réinstaller Lucid Lynx sur Beetle et rebelote pour les soucis: l'EFS de Noda essaie de fonctionner en mode "DLDI" plutôt qu'en mode "GBA".

I'm stuck to desmume-cli version 0.7.3 on my Ubuntu alambics... Karmic Koala ships a 0.9.4 release that somehow ignores clicks on the touchscreen ... 0.9.5 shipped with Lucid Lynx is even worse: it seems to misunderstand EFS attempts to read the ROM file through GBA slot (a technique known as "NitroFS", afaik) and falls back to DLDI-powered crawl through a (non-existant) filesystem. I filed a bug report on the desmume sourceforge project ... Hope it'll be of some help.

Edit: some tip from Ludo: install 0.9.6 and use desmume-cli --gbaslot-rom=mybrew.nds mybrew.nds

Saturday, May 01, 2010

Les test-points sont à revoir

Décidément, le mécanisme des test-points ne cesse de me jouer des tours >_<. Une fois encore, alors que j'ajoute un état "pousser" pour débloquer Bilou des murs, ils me "lachent" de nouveau. L'idée, quand j'étais passé au mécanisme cando(), était de conserver les test-points en complétment, pour permettre de choisir la bonne action à effectuer. En clair "ok, tu ne sais plus marcher, mais je fais quoi ? je pousse ou je tombe ?"

It looks like I definitely can't make test-points and cando() tests live together well. Despite a new state for "wall-pushing" that should have solved Bilou's behaviour, I still observe that you might end up unable to advance up to the wall for the simple (?) reason that the test-point that should enable "walk->push" transition haven't seen the wall yet due to a cancelled move. I fear controllers and test-points are too much desynchronised, and most likely, I'll have to print out some code on paper to re-think the whole problem and come with a better, integrated, system.

Mais ils sont évalués indépendamment du mouvement qui a été tenté, et donc, régulièrement, le "point-détecteur-de-mur-devant-Bilou" ne détecte rien du tout parce que le mouvement qui aurait dû conduire Bilou dans le mur a été annulé. Bref, ça sent l'impression de code et le débugging à coup de fluo et de tasse de thé :P