Showing posts with label monster edition. Show all posts
Showing posts with label monster edition. Show all posts

Sunday, October 31, 2021

Loading more, in LEDS

Bon, les soucis avec le moteur de jeu semblaient réglés, donc j'ai voulu vérifier que l'éditeur, dans sa version 1919 (une petite révision en-dessous du code sur mon laptop) présente sur Lime savait bien modifier des choses sans perdre d'information.

Je me rends compte que tenter de re-charger un deuxième niveau dans l'éditeur conduit au mieux à un iScriptException, au pire à un crash à une adresse venant du void*. Entre les deux, un "bon vieux" crash comme sur cette photo, qui pointe du doigt la boucle interne à Block::getnext()

Le 'pire crash' contenait quand-même quelques adresses vers du code dans la pile et suggère de regarder du côté de l'appel à Block::append() dans Level::ScanSubFile().

L'affichage des miniatures dans l'écran d'accueil, c'était pas terrible non plus. Plusieurs fichiers avaient juste des têtes de Bilou partout. Une résurgence d'un problème que je croyais cru réglé ? (ça se produit essentiellement avec greent.cmd et ses SimpleGobs, on dirait)

On dirait bien que ce n'est pas trop difficile de reproduire le problème avec 'cmdck' pour peu qu'on lui ajoute un moyen pour charger un 2eme fichier de commandes. Un peu plus de mettre le doigt sur le problème, puisqu'au moment du SEGV, on est envoyé à une adresse invalide.

Un cran plus tôt, on voit une liste de blocs dont le dernier a une table de méthode virtuelle totalement farfelue. Et ça, pour le premier bloc à traiter dans 'ink.gam'. Mais il y a eu un autre fichier .gam passé en revue avant, et le bloc farfelu est de type 'END'.

Je refais un 3eme tour de programme, je surveille tous les ajouts de blocs spéciaux. Et en réalité, le bloc "end" s'auto-ajoute dans la liste. Du coup, faire un 'delete' dessus parce qu'il n'est pas du type 'spécial', ça casse forcément tout.

edit: un petit 'ledsdbug.nds' qui reprenait les modifications utilisées en Avril pour corriger les miniatures, je remarque du coup que les miniatures sur NDS essaient toutes d'utiliser le sprite #0 ... louche ... la modif 'Anykey()' ramenée aussi et je constate que la ligne 'spr.more' n'a pas été correctement traitée. La faute à une valeur incorrecte pour la correction des numéros de pages d'animations (ouais. C'est technique hein ? j'aurais peut-être juste dû parler de 'pageshift', comme dans le code ^^). Rien de ce genre sur mon PC et pour cause: le code de LEDS a inversé le pageshift lors de la dernière sauvegarde ...

Par contre, du coup, l'outil de test cmdck -s greent.cmd > /tmp/green2.cmd me montre que toute la première moitié du script n'est pas ré-écrite après le traîtement. 'va falloir que je ressorte encore ddd. Euh non. ça c'était juste une interférence entre fprintf(stdout) et fopen("/dev/stdout"). 'faudra que je sois plus prudent avec ça.

re-edit: une fois ce pageshift corrigé, ça ne marche guère beaucoup mieux. Les miniatures ne sont toujours pas chargées parce qu'il y a des valeurs complètement incohérentes à la place des numéros d'animations dans le fichier. En fait, le fichier tout entier est sans-dessus-dessous dès qu'on atteint la section des animations et des miniatures.

Et ce n'est pas la première fois que j'ai des ennuis avec ces sections. Je ne peux pas exclure que c'est parce que j'ai utilisé une ancienne version de SEDS ou MEDS sur Lime. Mais je ne peux pas non plus exclure qu'il reste quelque-chose à corriger dans les éditeurs. (les éditeurs en version finale sont clean. Mieux: AnimEDS est capable de corriger un fichier foireux rien qu'en l'ouvrant et le re-sauvant).

re-re-edit: avec un fichier .spr valide, il me restait deux soucis à résoudre dans LEDS:

  • éviter les crash si on pointe dans le vide pendant l'édition des monstres (MonsterPropertiesWindow n'était pas prêt pour ça). Un 'simple' pointeur NULL, mais qui attire mon attention sur le fait que le hardware de la DS autorise qu'on lise à l'adresse 0 (en fait, tout une page de 32K).
  • avoir un affichage correct des miniatures produites par AnimEDS, et pas cet espèce de potée aux pixels que l'on voit pour Bilou. un simple '+2' qui manquait dans la version qui tournait sur DS.

Mais c'est compris et réglé pour attaquer le dernier week-end de congé. 'faudra que je note de comprendre pourquoi mon curseur de monstre a re-disparu, par contre.

Saturday, April 03, 2021

Where are my thumbs ?

I'm trying to add support for spr.more in LevelEditor (so that we keep seeing monsters positioning while editing DreamLand maps ;) but it doesn't work well. After some debugging, I realize that the values for the 'animation number' stored in the first word of the 'TINY' section make no sense. Animation #17000 ? with 48 animations per sheet ?

A simple test with ./sprdo confirms that even if I just load-and-save a file that has meaningful thumbs numbers, we end up with bogus numbers in the saved file.

Still wondering what thumbs I'm talking about ? They are still 32x32 images produced while saving animations in AnimEDS that are then used in LEDS for monsters edition.

Watching the memory backing my vector through execution, I finally found a suspicious set of `free()` statements at the end of the 'extra/unknown data blocks to set of MEDS animations' function.

The history of that function is that it abstracted something from `spr.load` script statement so that `spr.more`can exist. When it is done with them, the GameScript used to dispose of remaining really-unknown data blocks. Such as thumbnails. They won't be needed while we play the game and they eat up significant amount of memory.

Then, there was the code of SpriteEditor, the FileModel, to be precise. It had been updated so that it could invoke the same sort of 'FromExtras' function, but since we're not interested in MEDS animations while editing sprites, template magic makes this whole function unused. And then is the new tool, that tries and use code meant to be for GameScript but in a load-modify-write setup. That one ends up with freed pointers in its array and then try to save back those opaque data in the file. This is where my 17000 comes from.

The perplexing thing is that AnimEditor itself doesn't seem to read back the TINY thumbnails, but still when I load a file from OtherFileWindow, I get no thumbs on my top screen. Not until I edit something and move to another page, that is.

It took me time to check everything, and it finally revealed that, yes, makeThumb() are properly invoked even for OtherFileWindow, as soon as we call MetaWindow::data_reloaded() to signal things were updated. But the setup ThumbsWindow::restore() installed to show and use the TileTable showing the thumbnails got screwed up by OtherFileWindow's default layers settings. forcing a call to THW::restore() within data_reloaded finally fixed the issue. So that means bilou.spr on lime DS is likely fine and I can download it to resume development of LevelEditor's support for spr.do ^_^ (erhm. Next time. It's quarter-to-midnight and I've got meetings tomorrow ^^")

edit: unfortunately, after retrieving bilou.spr and school.spr that had been edited by AnimEditor on the NDS, they still contain rubbish TINY sections... I'll have to work on a medsdo tool to study that...

Saturday, July 13, 2019

Monster selector

Okay, it's not much and it wouldn't even be posted alone despite I haven't been posting about tools development for weeks. I have now the 'selected monster' arrow showing you where on the map is the selected monster if you happen to get it off-screen.

But it is not alone. Changing that required more refactoring that I kept track of, and I'll have to break that giant "stash: okay, it works" commit into proper commits. It's not just that the code was old or buggy: my coding standards have quite evolved by working in a team where most of the code you have to deal with has been written long ago (and likely not by yourself).

It's not that much about the lack of comments. Having something like

// use sprite tile 0 for the cursor
sprites[CURSOR].attribute[2] = 0;

only works if this is the only place in your code where it can happen, and without coding habits that guide you towards

const oamno_t CURSOR_TILE=0;
sprite[CURSOR].attribute[2] = CURSOR_TILE;

you're very unlikely to get it done at a single place.

I had to fix things like that, as over time, I ended up with two widgets deciding to use sprite tile 0 for their own special purpose. I had to migrate the 'sprites granularity' into the recently-introduced "UsingSprites" class as I realised that more and more parts of the code now depended on that granularity (like whenever you're allocating sprite tiles) and top-screen and bottom-screen might not be using the same granularity...

Among the ugly things in LEDS code, I shall name a global "UI-like" object -- QuickGo -- that renders the level into a small bitmap, so that we can quickly navigate the level. That object is created in the 'MapEditWindow', but it is never inserted as a widget. It seems to be a global pointer, which is accessed through LayersWindow -- the part of the application where it actually shows up -- and even there, it doesn't directly reacts to user inputs. Instead, there is a 'QuickWidget' object that doesn't show anything on screen, but that does produce events on stylus touches that MapEditWindow will further catch and process.

Such a "code-that-works" approach that breaks almost every coding convention in the "GEDS" library is tricky to reason upon. You see QuickGo in a window, so you should be allowed to assume it is part of the UI, and used in that window. 

Tuesday, July 09, 2019

Toolset and MonsterPropWindow

I've got less free-reading time during my vacation this time, and likely, I should be not blogging right now, but cleaning up all the mess that going to vulcan(eifel) and back brought to the house. When I did get some time, I spent it clarifying some obscure parts of my Level Editor which puzzled me when I initially tried to add some additional monster edition features, like being able to decide within LEDs whether a given game object belongs to the 'hero' cast, the 'evil' cast or none of them.


I refactored a few things to make that possible already and I hope I'll get some time to continue working on it within the upcoming days.

I also took some time documenting one of the more elaborate (but arcane) widget that I might have to use to do that: the 'ToolSet' meta-widget, which is truly a bunch of buttons hacked to behave like the typical radiobuttons of point-and-click GUIs.

Friday, July 18, 2014

rbegin(), deadend.

I know that C++ has pitfalls and numerous weapons for you to shoot at your own feet, and the behaviour of iterator has already puzzled me a couple of time, but I wasn't expecting *this*. I wasn't expecting the position returned by a single there = list.rbegin() to be different depending on when you de-reference it. Like, pushing 3 items, invoking there = list.rbegin(), pushing 3 more items and figuring that *there now let me access the 6th item 0_o. I had to check the source code of stl_list.h to understand how reverse_iterator is just a façade calling operators of a companion bidirectional-iterator as expected to be appropriate, and how the 'end' of a list is materialized by a 'guard' node hosted by the list itself (an academic workaround), meaning that when you ask for rbegin(), you're actually positioned exactly on the same node as with begin(), but now operator* finds it smart to move one position backward just before returning the target.

Eh bien, on dirait que le C++ me réserve encore quelques surprises. Il m'avait semblé judicieux de réécrire une partie de la gestion des monstres de LEDS en utilisant une std::list et des itérateurs, mais je me suis rendu compte hier que du coup, éditer un niveau détruisait systématiquement toutes les informations sur les monstres. Un effet qui est resté caché lors des tests en émulateur, vu que desmume ne modifie jamais de façon durable les fichiers. Je vais donc laisser tomber le "rbegin" ('donne moi le premier élément de la liste lue à l'envers') au profit d'un --list.end() ('donne moi l'élément juste avant y-en-a-plus') parce que ce dernier, au moins, est évalué au moment où on le demande et pas à la volée au moment où l'on s'en sert.

Je sens de plus en plus le besoin de tests systématiques de mon code, mais en environnement émulé, j'ai du mal à percevoir comment les mettre en place.

Sunday, September 22, 2013

7 days left.

I got LEDS working almost fine on the green DS with the expanded and reordered animation sheets. I went through trivial fixes such as "animation X shouldn't loop since we depend on it to be done before switching to another state", etc. In-editor GOB linking did worked, although producing curious effects: a Spongebop was found moving through the level backwards because the nail it's pinned on was cloned from an inkjet and therefore had an initial horizontal speed :P

Cette fois, c'est reparti. L'éditeur de niveaux et le moteur de jeu intégré à "runME" sont capables de gérer les nouvelles pages d'animations correctement. Je peux ajouter les petits raffinements du style "faire faire demi-tour à la gomme" ou "se relever après s'être ramassé un coup", etc. Il me reste 7 soirées pour compléter le comportement de la gomme, de l'encrier et du crayon et dessiner le "bas" du niveau anniversaire.

At some point, it will save time to be able to see the initialization expression of one GOB, clear it if needed (and later, to write it from scratch, of course). A way to have self-initializing spongebop would also help: currently, they do not preserve the "energy" they're given initially, and so their speed is "reset" to a given swing value when they're crossing the vertical line. Unless this is fixed, sponges with a too-short thread will break loose and sponges with a too-long thread won't swing as far as the level design states.

I still wish Bilou could get more height when leaving a grabbed sponge, too. And then, there's pendat.cmd virtually inexistant, verso.cmd hoping for more freedom, inkjet.cmd needing to settle down and some cork-platform to be drawn.

Sunday, September 15, 2013

Ready? School!

one whole line of airborn (trans)anims!
Aah ! Ça fait du bien! J'ai réussi à mener à bien les modifications synchronisées de l'éditeur de niveau et d'animations pour permettre jusqu'à 6 fois plus d'animations. Du coup, ma DS "green lime" est prête à reprendre le flambeau et me permet d'enfin bricoler ce "niveau anniversaire", qui a d'ailleurs bien avancé ^_^ Le fait que toute la famille ait été à moitié K.O.-microbes pendant le week-end n'y est pas tout à fait étranger ^^"


I'm kinda proud of this week-end's progress. LEDS, AnimEDS and runME have been syncrhonously updated to allow more animations (up to 6 sheets) and thus more flexibility in behaviours. That will give me plenty of headroom for "thrown by an inkjet", stunned by a blador and all other interactions that should make the School Zone interesting to play. As a result, I have doubled the content of the "anniversary school zone" level which I hope to be ready by the end of the month.

Btw, be ready to hate the bopping eraser. It will not exactly "chase" Bilou, but it's gonna turn back or adjust the height of its jumps depending on your position 0:-°

Friday, September 13, 2013

Missing monsters

A bug in pokemon. Here be
a no-such-monster exception
Latest tests seems to claim that upgrade LEDS is trust-worthy enough to start manipulating my precious level maps (or at least their Lime copy -- more to come soon). There's one last thing I would like to fix: making sure that we can detect missing monsters, which may occur if some "input/import" statement has been stripped from the GobScript .cmd file that describes the level. Right now, they'll likely don't show at all, giving you very little opportunity to select them (you don't know where to click) and change their type.

Lors des dernières tentatives de donner vie à la "school zone, level 1", je m'étais heurté à deux problèmes: des incohérences entre le nom de fichier de la map et celui réclamé par le script définissant le niveau et l'absence de certains monstres faute du fichier de comportement correspondant. Le deuxième point était d'autant plus gènant que le moteur de jeu refusait alors tout simplement de faire tourner le niveau ... et que l'éditeur de niveau rendait impossible l'édition de ces "monstres manquants" pourtant présents dans le
niveau. Vous suivez toujours ?

Bien. Trop occupé à faire le design du "langage secret" employé sur la planète 24.3-lxxix ce midi, je n'ai pas encore codé les dernières retouches sur LEDS avant de repartir dans les comportements de monstres et le dessin des maps, mais j'ai au moins pu dégrossir le terrain. Voilà qui présage un week-end bien rempli :)

It would be neat to take advantage of this last fix to also ensure that a decent number of animations can be used, without forcing them to be all on "sheet 0".

  • the pages corresponding to the .spr file need to be present in RAM so that we can tell location of "old" animations in VRAM. That's handled completely with Set::Load;
  • the code handling "anim%ano = spr:%sno" could allocate thumbno++ rather than using %sno directly, and then set thumbrq[%sno]=thumbno.
  • that allows animeds thumbs to be cherry-picked when thumbrq[%sno] is defined.
  • either the page carrying thumbs is created and filled *after* parse()
A missing .cmd file will result in monsters with no corresponding states[no] entry. We can prepare the states[] array so that all entries are ready to display the "missingno" icon. We should ensure at display() that the OAM is not in 'disabled' state, though.

Here comes the week-end ... take the dishwasher to warp 4 and let's make it so.




Tuesday, July 16, 2013

Yearling

Last year's "holiday todo list" is thus finally completed. The last touches to SpongeBop's behaviour were introduced mid-June, multi-palette support was released in March, ink hurts (when configured in that way) and (of course), we do have compound-animation for Bilou since September 1st, 2012, and I fixed much more than just the "landing bug".

What's up for this year's holiday ? I've got pendats animation (well, not the full set of actions, just the simple walk) and a first draft of verso-the-eraser. I just have used the last 70$ of NeoCompo prize to order a replacement part for my fridge (15°C cooling is not funny), and I still have much drilling and sawing to do outdoor to make the garden kids-friendly.


Si sur le devant de la scène je prend enfin le temps de mettre en ligne toutes ces réflexions sur le gameplay de Mario World, en coulisses, la révision de l'éditeur de niveau atteint le stade "tests intensifs". L'objectif ? Pouvoir enfin ajouter de manière fiable des monstres, encore des monstres, et pouvoir les lier les uns aux autres (indispensable pour SpongeBop) sans devoir passer par le PC et son éditeur de texte ... Mais le reste (clonage de niveau, wizards, etc) devra attendre: la Neo Compo 2013 approche à grands pas et j'aimerais pouvoir y présenter une première version de Deep Ink Pit ... si libntxm veut bien ... et la VTJ aussi.

The critical item to get started on a "deep ink pit" map right now is the level editor, which must be capable of cloning monsters that are attached to other monsters without messing up the level commands. It's still in "testing and fixing" stage, though. Then, I'll need something that makes the ink moving up ... that will involve a custom effect of some sort, as that's not something the state machines will easily handle.

I hadn't got much luck with musics so far: Both Piek's and Piet's tune play weirdly on the Nintendo DS... Deep ink pit will feature yet another song and if that one doesn't work properly either, fixes/level-up will be required on libntxm.

Will this be completed for NeoCompo 2013's deadline ?


Oh, btw, translation of Saturday's post is at last completed. 

Wednesday, July 10, 2013

Refactoring level loading in LEDS


If you want monsters in a level with libgeds, a .map file isn't enough. You need to provide a companion .cmd GameScript file that contain commands parsed by the engine to instanciate dynamic object, set rules that guide them, etc. Most of that content is left unmodified when you edit your map: only some 'GameOBject number U will start with behaviour B at coordinates (X,Y)' is modified. So far, I thus only extracted those coordinates into Monster objects and would use the updated Monster list to patch the .cmd file when you click [save]. Yes, I really mean patching. Scanning the original file, and deciding for each line whether I keep it as is or replace it with something coming from monster[i].writeback().

But the approach couldn't work properly when we start inserting new gobs that mess up the line numbering in the ouptut. Plus, it is excessively limited, while I'd like to be able to include more features in the level editor (such as linking spons with pins, picking the background image, etc.)

The new approach thus keeps a complete copy of the .cmd file in memory, divided in blocks. Each block holds lines that should be edited together, like the full configuration for one monster, the settings for one plane, etc. I'm almost done. All that's left to fix is spacing in generated lines.

I quite liked how the former version of LEDS had some "controller" class (WelcomeWindow) that provided an empty vector to LevelModel so that it'd fill it with the objects the view (MonstersManager) would use. Unfortunately, in the new version, the relationship between code blocks and in-editor structures (like the Monster instances) got more complex, and a flat vector no longer works well. The new Block class offers the ability for any part of the code to crawl through the sequence of Block instances that were generated while parsing the level's cmd file. Unfortunately, this dependency between LevelModel and MonstersManager has turned implicit while it was nicely explicit before.

There's just one design decision I wish I could validate with a magic crystal ball. Any line on a GameScript that starts with a 'g' is now packed into a GobBlock unless that GobBlock refuses it (if so, a new GobBlock is created). That means the gob%.focus had to be changed into focus=gob%, for instance. Not exactly what I'd recommend in any serious project, but the syntax of GameScript is fairly simple and should not receive further significant extensions.

Friday, June 28, 2013

Incoming week-end ?

I cannot tell yet whether there will be significant development this week-end. I had a sort of marking-hangover last week-end and I barely managed to fix some bounding boxes and import some "school2.cmd" level to support the 20-year-old map I want to revive.

Ça ne donne pas l'air de bouger fort, hein, ces dernières semaines. En fait, j'avais commencé à ajouter les gommes sauteuses le week-end dernier pour me rendre compte qu'il y a toujours un sale défaut dans l'éditeur de niveau qui a tendance à bousiller la liste des monstres à chaque sauvegarde. Si je parviens malgré les valises que je traîne sous mes paupières à faire un peu de développement sur le projet Bilou ce week-end, ce sera donc très certainement dans 'LEDS' pour modifier la structure de données qui maintient le "gobscript" en mémoire ...

I'd have loved to include the pendat over the week, but I still have a dormant bug in LEDS which has been left pending since Easter's fixes. Now I need it fixed or I'll have to beam level scripts to the PC and back after every save to fix duplicate "gob% := state% (x,y)" statements.

Meanwhile, I had a nice idea for editing such blocks of text in a convenient fashion ... which led to an alternate design for processing the .cmd files that could solve the issue with monsters duplication. So if something happens in the svn tonight, it's likely to take place here.

Friday, April 05, 2013

LEDS update

    Mon filieul (12 ans) -- de passage chez moi -- s'est assez rapidement désintéressé de ses projets de monorail LEGO et m'a laissé entendre qu'il avait son AceKard 2 en poche pour que je lui fasse une mise à jour d'Apple Assault. Après des semaines passées sur Minecraft DS, il était mûr pour se lancer dans du dessin de niveaux et j'en ai donc profité pour lui transmettre l'intégralité de mes outils de développement, avec les spritesheet dernier cri... pour me rendre compte qu'il y avait un très désagréable "guru meditation" au moment de charger les derniers niveaux (variable non-initialisée dans TileTable).

    Je contourne le problème en lui installant plutôt la dernière version "stable", et après avoir essayé les techniques de contre-attaque d'Apple Assault, le voilà lancé dans la réalisation de "Thom.map". Il maîtrise plutôt bien les techniques de base pour placer des blocs sur la map, et a rapidement pigé le fonctionnement du "plan de collision". Le voilà donc qui place quelques blocs-code pour que ses crayons blessent et s'émerveille devant son premier niveau qui prend vie.
      Mon seul regret aura été de ne pas avoir pu lui permettre de placer des monstres sur sa map. J'ai un couac avec l'édition des monstres dans LEDS. Ils apparaissent complètement tordus malgré mes meilleures tentatives pour importer les "miniatures" d'AnimEDS. Après une série de tests en émulateur ce matin, j'arrive à la conclusion désagréable que le SpriteSet est trop grand pour pouvoir accueillir les 48 miniatures d'animation (1Ko chacune) ajoutées au sprites de base.
      One of my lil' nephews asked me to install my map-design tools on his DS earlier this week. And this led to some more flaws to be identified in the version of LEDS I shipped on my birthday. After I fixed a guru meditation error on level loading, I still had to figure out why monsters thumbnails couldn't work right. It's a bit tricky to tell a 12-year-old "don't trust those pictures you see in the level: this is a blador, and that's a sponge bop although they both look like shrunk-down version of Bilou".

      C'est un peu ridicule, parce qu'il n'y a pour l'instant qu'une dizaine d'états initiaux pour Bilou et les 3 monstres de la school zone (spons, blador et inkjet). Mais l'éditeur de niveaux ignore tout ça et tente de faire tenir toutes les miniatures d'animation plus toutes les images individuelles (des fois qu'une animation à l'ancienne les utiliserait). Je pourrais bien sûr rationaliser tout ça et faire en sorte que chaque byte en VRAM soit utile dans l'éditeur ... Mais si la DS n'est pas capable de traiter plus de 1024 images différentes pour les sprites, elle est en revanche parfaitement capable de traiter des sprites plus gros. Jusqu'ici, j'utilisais DISPLAY_SPR_1D_SIZE_64, soit 64 bytes par sprite (8x8 pixels ?). En passant à DISPLAY_SPR_1D_SIZE_128, je perds la possibilité d'utiliser des micro-sprites, mais je peux faire tenir mes 64K d'images dessinées dans SEDS plus jusqu'à 64K de miniatures rajoutées par AnimEDS sans compromis notable puisque SEDS travaille au minimum avec des sprites de 16x16 (donc DISPLAY_SPR_1D_SIZE_256 pourrait même s'envisager).

      After sufficient time was invested into documenting the multi-pass parsing of the command files in the editor, a lunch-testing-session revealed the naked truth: the amount of sprites supported by the video hardware was once again the root cause of the issue. More precisely, it wasn't an issue with the amount of VRAM (the game engine accepts up to 64K of sprites and all the thumbnails won't take more than 48x1KB) nor with the number of objects the hardware can handle, but merely with the number of individual graphics that can be addressed by the OAMs.

      Hopefully enough, it was a mere matter of re-configuring the GPU so that it uses coarser indexing. So far I could precisely point at a single tile (8x8 pixels) in sprite memory, which only used the first 64KB. Yet, it's possible to use up to 16x16 pixels per index (the size of the smallest sprite generated by SEDS. Even with an intermediate and conservative setting of DISPLAY_SPR_1D_SIZE_128, I immedately recover the ability to preview the monsters on the level. Too bad: my nephew is gone :P

      But that's not too much of an issue: as soon as his DS gets in range of a friendly WiFi, all he's got to do is launch his current version of LEDS, and press SELECT while holding L+R on the welcome screen (instead of clicking <go>. That should grab ledsgama.nds, the latest build.


      Seuls les pieds et les mains de Bilou tiennent dans une taille de 8x8 à l'heure actuelle, et ils ne représentent que 12 sprites sur les 1024 autorisés. Une perte d'espace probablement acceptable. Si nécessaire, je pourrais envisager de les transférer en 8x16 plutôt qu'en 16x16 ... on verra ça une fois que je saurai quelle quantité de mémoire vidéo la 3D me demandera ...
      • [done] fix guru meditation when entering edit mode
      • [done] keep VRAM for thumbnails
      • [done] properly render monsters
      • [todo] ignore duplicate GOB entries when reading a level
      • [done] ensure we don't generate duplicate entries.
      • [done, runme] ensure we can read the log as soon as there's an error.
      • [todo, runme] ensure we can move back to SEDS/LEDS/download mode at all time.
      • [wish] "generate .cmd" wizard window.
      • [done] clearer view of which file is loaded/saved/renamed

      Wednesday, May 23, 2012

      LevelModel | MonstersManager

      wow. Loading a level with monsters in LEDS is far from being a simple story, right now. LevelModel is the internal representation of your level content, and it can extract the "gob% = state% (%,%)" statements, but it has no idea how to render GOBs. That's the job of the MonstersManager ... and to some extent, it makes sense.

      Once this is laid out, it comes more naturally that LevelModel should also be responsible of storing gob% := (<expression>) statements in Monster instances it creates, so that MonstersManager no longer has to worry about that... And I'm glad I took the time to map the situation, because my previous (jump-to-code) "design" would have been a highway to hell.

      Sunday, April 11, 2010

      Une bière fraiche ! Dans un verre propre, nom de ... !

      Avec le départ du professeur Ribbens, c'est sans doute une page de l'histoire de Montef' qui s'est tournée, quelques années (mois ?) seulement après le début de ma carrière. Comme pour beaucoup des cours que j'ai suivi, j'ai peut-être bien eu la chance à avoir reçu du maître du Scheme en personne une initiation aux techniques des continuations et du "data-driven programming". Et sa rétrospective sur les LISP machines et techniques de garbage collection était tout simplement magistrale.

      In my 3rd year at University, I've finally been taught a programming language that forced me to re-think everything I thought I knew : LISP (and I practiced mostly its Scheme dialect). Beyond the charismatic character of Pr. Ribbens whose motto could more or less be translated in "brew sana in f**ing bottlore sano", it introduced me to "data-driven programming" and design of language-specific processors. To make a long story short, "data-driven" is what you feel you should be using when you start going beyond 10 rooms in a Lone-Wolf game : keep the code short, simple and generic, and have it proceed through structured data in order to obtain the desired result.

      C'est de "Data-driven" justement, qu'il est question ici, puisque j'ai décidé de ne pas directement *coder* la logique de mon jeu, mais de la décrire par des structures de données traitées par un moteur qui reste plus simple et plus générique. Une approche qui me ralentit peut-être par moment mais qui me titille : je veux en avoir le coeur net et vérifier par moi-même si oui ou non il sera possible de construire un jeu de plate-formes sophistiqué de cette manière.

      Les "livres dont vous êtes le héros" sont sans doute le meilleur exemple possible de "data-driven programming": tout programmeur qui a un peu roulé sa bosse "sent" bien qu'il y a moyen de faire mieux que

      sub Salle42
      print "au détour d'un couloir obscur vous entendez un bruit sourd ..."
      print "1. vous dégainez Voleuse de Vies"
      print "2. vous vous avancez dans les escaliers"
      input "votre choix"; choix
      if choix = 1 then Salle44()
      if choix=2 then VousEtesMort()
      . Programmer une fonction par salle / évènement (en fait, par numéro de "chapitre" dans le livre) serait extrèmement pénible, la logique du jeu se retrouverait noyée par des éléments de second ordre comme "est-ce que le clic se trouve dans la zone s'avancer dans l'escalier ou dans dégainer la Voleuse de Vies?" Sans parler de la difficulté à gérer les modifications du scénario. On préfèrerait de loin pouvoir stocker toutes les "données" du jeu dans un format à part ... un fichier texte avec des "macro-commandes" pour les vieux patchs de mon genre (et leurs mentors) ... un document XML pour les afficiandos de l'UTF-8 et autres codeurs post-moderne. Une S-expression pour les fans de "recueil de petits problèmes en Scheme", je présume. Peu importe, finalement, la forme: ce qui comptera, c'est le fond, la sémantique, le modèle sous-jacent.

      My "game script" and the state-machine-based-monsters is deeply influenced by this technique. Unlike your regular scripting language (Lua ?), building a data-driven game engine means that you're building with line of code a software microsystem that will process data in a specific context and for a specific purpose. You are free to define the line between code-bound function and data-driven function, and placing that line at the right place will be the key to efficient processing.

      Bref. Pour mon jeu de plate-forme, le "modèle" de données est un peu plus complexe, fait en partie de pixels et de commandes qui décrivent les machines d'état des différents intervenants. Une réminiscence du cours "Ingénierie du Logiciel Orienté-Objet" et de ma confrontation avec l'UML, et dans une moindre mesure, avec le formalisme des automates à états fini. La frontière entre data-driven programming et langage de script complet (cf. microLua pour DS) est sans doute ténue ... Elle explique sans doute la décision parfois curieuse de garder certaines choses "en-dehors du script". La détection des collisions, notamment, ou la prise en charge des animations. J'ose espérer que cette volonté de "rester juste un cran en-dessous d'un DS-Basic" me permettra de garder un moteur de jeu suffisamment efficace.

      Avec mon "InspectorWidget" désormais opérationnel, on se rapproche aussi d'un éditeur graphique pour ces machines d'état ... Et certains "défauts" du modèle actuel deviennent flagrant. Par exemple, Bilou saute, cours, nage, attend ... autant d'états que je dois déclarer et que je relierai ensuite les uns aux autres par des transitions, p.ex. "lorsque les boutons changent, si le bouton 'saut' est enfoncé, alors modifier la vitesse verticale et passer dans l'état 'saute'". Par contre, pour que l'appleman puisse être assomé quand Bilou tombe dessus, il faut que tous les états de l'appleman mêne vers l'état "appleman assommé" si la 'bonne' collision se produit. Il est facile d'en oublier l'un ou l'autre en cas de modification, et c'était d'ailleurs en partie la raison pour laquelle il était si difficile de s'en défaire dans les démos précédentes du jeu.

      UML state machine formalism was one of other things I learnt that very same year, and it looked like a powerful way to express behaviours while avoiding repetitive boilerplate code to be described ... So my current data model for GOB behaviours is mostly implementing that. So far, it has the limitation that a transition is always flowing from exactly one state (to exactly one other state). It's expressive enough (that is, there isn't a behaviour you cannot implement with that), but it's not code-friendly in that when you actually want to implement a transition that should apply from (almost) all other states to a specific state, you can quickly forget something. It actually occured with the Appleman that couldn't be stomped when walking to the right simply becaused I missed some state20->state33 on hit0 [t] statement.

      Je cherche donc depuis quelques semaines à ajuster le modèle de manière à pouvoir justement exprimer "depuis tous les états, ..." ou "pour tous les états où X est au sol, ..." et donner une transition unique qui s'applique à un groupe d'état d'entrée. Comme prévu de longue date, d'ailleurs (cf. ce schéma du comportement de l'Appleman). Outre l'économie de temps de parsing au démarrage du niveau et de quantité de mémoire (à mon avis négligeable), celà permettrait de pouvoir d'un seul clic dans le débuggeur forcer un point d'arrêt sur "je vais me faire blesser", quelque soit l'état actuel de Bilou. Pour l'instant, avant le debugging, il était nécessaire de passer tous les mouvements de Bilou en revue pour cliquer sur "transition vers l'état n° 15" depuis chaque état. Pas franchement folichon.

      I haven't added such "group transitions" yet, but at least I made a nice step towards it by letting states and animation be defined in the context of .cmd files, so that each monsters' state machine can be written without having any knowledge of what other monster do and how many animations they use. Then, only a small amount of the states are "imported" by the master (level) script.

      I'll have to adapt the level editor accordingly, but it should make InspectorWidget more friendly to use as the 'kind' of monster is now part of the state's name. Moreover, once the group transitions are in, activating a breakpoint on "jumping->hit" should equally activate "standing->hit", "walking->hit" etc. as long as you used a group transition for "{jumping, walking, standing, ...} -> hit"


      Un premier pas dans cette direction, ç'a été de donner à chaque fichier ".cmd" (généralement un par personnage ou ennemi) son propre "répertoire" d'états et d'animations, désormais indépendants de ce que font les autres. Seuls certains de ces états seront "importés" par le script qui définit le niveau en cours pour placer les intervenants sur la map. L'éditeur de niveau devra être adapté, bien sûr (eeh oui. Faire et défaire ... that's the question), mais ce sera pour un mieux: plus besoin de passer en revue toutes les positions de Bilou, ni de savoir lequel des états de l'appleman convient comme état initial. Les groupes de transitions devraient s'y greffer plus agréablement.

      Et InspectorWidget lui aussi en bénéficie, puisque les monstres sont maintenant nommés "fu01", "ap07" ou "wo00" plutôt que d'être représentés avec un simple nombre.

      Friday, January 29, 2010

      Des Bilous Partout!

      I pretty much love when the template code is written down and that you only have to add a few lines to increase functionalities. Enthusiast Coding, do I call it. I managed to have monsters moved around in LEDS yesterday evening, and today, I just hacked around to change their state, which includes changing which character or monster you have. I'm still missing "clone" and "kill" to be done.

      I hope I'll find the time to write down "save monsters position to file" this week-end, between my Brother's Birthday, planned concert and crafting of my fairy's rocking chair.


      C'est le moment que je préfère ... et difficile de contenir mon enthousiasme. La structure est là, les questions pénibles sont réglées, il n'y a plus qu'à ajouter quelques lignes par-ci par-là pour voir les fonctionnalités apparaître... un clic, je sélectionne un GOB, je maintiens L enfoncé et je le balade à la pointe de mon stylet ... A et B me permettent de choisir l'état suivant ou précédent (comme on 'naviguait' dans les samples sous Scream Tracker III), et voilà: y'a des Bilous partout!

      Avec un peu de chance, j'aurai le temps de coder la sauvegarde de tout ça ce week-end (à caser entre le montage du rocking-chair et le concert de Witloof Bay, quand-même).

      Wednesday, January 27, 2010

      MonsterWidget ou MonsterWindow ?

      Bon, voilà. J'ai fini de me créper le ch(amp)ignon avec "j'ajoute une fenêtre ou juste un widget". J'ai rajouté une fenêtre, et je peux commencer le "vrai" boulot, à savoir sélectionner, déplacer, dupliquer mes monstres, et tout ça. Si je me suis planté, ben au pire, vous me lirez râler d'ici peu. J'avais commencé à rédiger toute une tartine sur les options possibles, etc. mais finalement, je n'ai pas du tout suivi cette approche. Ce sera donc pour les plus English d'entre vous.

      Well, finally i opted for giving monster edition its own, full-blown window "stacked" over the regular level editor. I've been struggling with OO design, once again. It's getting sort of a habbit. At last, I can start with "real" coding: positioning monsters over a level. I guess if I've been mistaken, you'll read me complaining about the odds of OOP once again :P Here follows the rant about the possible alternative that has not been retained.

      The GUI of ?EDS tools mostly consists of "windows" through which you navigate. "Window" is a poorly chosed term, i admit. It should rather be "screens" or "layers" (since you can overlap them) that acts as container of widgets and contains the "application-specific logic". Again, the GUI toolkit is "in-house" development and grows together with the apps that grow together with the game projects.

      When extending my applications on the DS, I recurrently stumble upon the same problem: I need some widget to be drawn or not, to receive events or not depending on the "application state". An obvious example is the "pop up buttons" I've added to the level editor recently. Another one is the "edit" button of the level editor's main window that only appear once you've got both a tileset and a map loaded in RAM.

      I don't have (yet) a very clean and satisfying way to handle such situations. I mostly take advantage of the sequential testing of widgets within a window and alter Window->nbWidgets at run-time to make widgets appear or leave. I typically "snapshot" nbWidgets at various spots in the window initialisation code so that I can later "enable" a widget with something like

       void update_edit_buttons() {
       if (level->isready()) {
         nbWidgets=showedit; // show [edit] button.
         edit->render();     // force redraw
         // ... more actions if needed
       }
      }


      It is not alway possible to make it "that simple", though. Sometimes, the new widget will "shadow" an existing one. Edition of tile properties, for instance, needs the "regular" map to be drawn (a MapWidget), but additionally, the MetaLayer (widget that manipulate meta-information) and the MetaButtons (widget that let you pick a specific block type from a panel) become present. Plus, the MapWidget should no longer receive any event to prevent mess up of tiles graphics while editing tiles properties.

      I then maintain metamap and normap, two copies of the window's nbWidgets that indicate the respective indexes of the MapWidget and MetaLayer. Window::swap(i,j) can help me moving around objects in the widget list so that we change the priority of both widgets. But this is tricky to extend cleanly to a "third state" as required for monsters positioning. Most likely, I'll have to keep track of the "current state" better than through the value of nbWidgets, and I'll need a reset_widgets() that returns from any state to the default (post-ctor) state so that I can properly alter stuff.

      The alternative would be to add "hidden" and "disabled" states explicitly into the GUI engine, so that it can "skip" some objects.

      Hopefully enough, the GUI engine also supports "stacked" windows where you can add a layer of widgets on top of another one, but it's a bit overkill to do that for every "dynamic" widget of a complex window. It *does* help when not only the display of a widget, but the whole controls layout (DPAD and physical) is redefined in the new state.

      Wednesday, January 13, 2010

      Monsters!

      The "model" part is written down : the level editor is now capable of parsing a .cmd script, investigate sub-scripts for state and picture information, and the MapWindow can invoke MonstersManager::display() to spice up the rendered level scene with monsters. Now to the "controller" part and ensure that I can move object around, change the state they use, etc.

      C'est bon. Le côté "modèle de données" pour les monstres est fini. L'éditeur est capable de lire les scripts .cmd et d'en extraire les informations nécessaires pour dessiner les monstres aux positions adéquates sur la map. Ca n'aura finalement pas été si terrible dès que je me suis remis en tête la différence entre setupOAM, setOAM et changeOAM dans (ma propre classe) SpritePage :)

      Maintenant, il faudra passer au côté "view & controller" pour véritablement déplacer et éditer tout ça ... et je sens bien que le côté "réécrire le fichier .cmd avec les modifications" promet des moments ... intéressants.

      Wednesday, January 06, 2010

      Edition des monstres ...

      Petit moment sympa pendant le congé de Noël : j'essaie d'imaginer la manipulation des monstres sur la map dans le Level Editor... En général dans ces cas-là, j'essaie de me servir au maximum des contrôles de la DS pour garder le maximum de l'écran pour la "zone de travail" (en l'occurence la map).

      Somewhere between shovelling of the driveway and wrapping up of gifts, I sketched up a possible user interaction for monster edition mode in my level editor ... I thought you might find it funny to see. My aim was to keep mostly the screen clear of buttons, and use the natural control (that is DS buttons and DPAD) to keep the touch screen purely as a "workbench" to select and move monsters.

      LEDS-prepare This completes a former document that was more of an overview of the monster management process.

      Dans le même genre,mais un poil plus ancien, une réfléxion plus complète sur "comment savoir quel monstre mettre où", etc.

      Tuesday, December 29, 2009

      Xmas Checkpoint

      Je ne vais pas vous faire le coup du changement de palette pour avoir une forêt aux feuilles toutes blanches de neige, ni affubler Bilou d'un gros bonnet rouge et blanc ... Mais joyeux noël et d'avance une bonne année 2010 quand-même.

      A défaut de "release", voici l'état actuel de mes trois applications:

      • le sprite editor, dont j'ai un brin amélioré l'édition de palettes.
      • le level editor, avec son interface revue et corrigée.
      • le moteur de jeu, avec support des pentes. J'ai aussi essayé d'un peu ajuster les collisions avec les ennemis. Vous pouvez donc vous amuser *aussi* à écraser les appleman.
      I'm afraid I'm not going to show you Bilou with a red, floppy hat in snowy woods this time. I'm afraid you won't even consider as a "release" the snapshot of my three major DS tools available from the link above. Okay, the Sprite Editor has (slightly) improved palette editor, featuring readable RGB and HSV values. Okay the game engine better supports slopes and appleman has an improved behaviour.
      But before I go for a "real" release (that is, an extended level that really showcases slopes), I'd like to improve my level editor so that it could handle .cmd files -- and especially graphically represent and edit monsters.

      Je voudrais parvenir assez prochainement à vous faire une "nouvelle release" (comprenez, greendemo avec un 3eme stage) qui fasse profiter pleinenement de l'ajout des pentes. Mais pour ça, il faut que je progresse encore un peu sur l'éditeur de niveau, histoire de lui permettre de gérer les monstres présents dans le niveau. Grosso-modo, il s'agit de permettre graphiquement l'édition des lignes du genre "gob4 :state8 (300,240)" qui positionne un nouveau monstre et définit son état initial. La bonne nouvelle, c'est que puisque j'ai "extrait" les comportements dans des fichiers .cmd séparés lors de la dernière release, celà devrait réduire le nombre de lignes à traiter. L'inconvénient, c'est que je n'ai pas de liaison entre "state8" et une image représentant, p.ex. Funky Funghi à moins de me retaper le parsing de ces fichiers séparés (pour identifier les images associées aux animations associées aux états :P)

      There's just a silly little thing to figure out for this: how am I going to know what graphic should show up as "state8" in a gob-description-line without importing the whole GameObject.cpp file into the level editor ...