I need to pause on Re-Hoard.

I have been working on the translation over 2 weeks almost straight, not including the experimentation with HTML5. Then there is the lot of time I have spent with the original pico-8 version.

I think I need to rest some time.

Advertisements

Re-Working, Re-Thinking

You are probably wondering what has happened to me during this silent period.

After I left Lua, I assessed my current options. Because a job opportunity already led me to learn JavaScript, a rather delightful language, I decided to first use melon.js, then Phaser. I later realized, though, that not only would me needing to use a web server be a problem with the turnaround when working on the game, but there is also the risk that way too many people would try to play my game at once, causing server overload (and high costs). Meanwhile, I worked on a ChaiLove version. The problem is that ChaiLove does not have that much documentation – the only ones that were helpful to me were the cheatsheet and this tutorial – which meant that I had to request Rob Loach’s aid… which would be the first time. Despite that, ChaiLove seems to be a great alternative, what with the ease of use and better functionality. I mean, not only can I use actual classes, but the audio system has more control.

The tricky thing, however, is re-thinking how the game should work in ChaiLove… especially given that some algorithms are specific to the pico-8.

I may have to go back to being silent, though, because the concentration on this work is intense.

My hoard is nil.

if ecs_single_entity["actor"] == "fireball" or "arrow" and ecs_single_entity["location"] ~= nil then
    if ecs_single_entity["location"][1] > 1 and ecs_single_entity["location"][2] > 1 then
        if ecs_single_entity["sprite"] == sprite_fireball_up or sprite_arrow_up then
            if {ecs_single_entity["location"][1], ecs_single_entity["location"][2] - 1} == value then --the location is nil
                ecs_single_entity[key] = nil
            end
            elseif ecs_single_entity["sprite"] == sprite_fireball_down or sprite_arrow_down then
                if {ecs_single_entity["location"][1], ecs_single_entity["location"][2] + 1} == value then
                    ecs_single_entity[key] = nil
                end
            elseif ecs_single_entity["sprite"] == sprite_fireball_left or sprite_arrow_left then
                if {ecs_single_entity["location"][1] - 1, ecs_single_entity["location"][2]} == value then
                    ecs_single_entity[key] = nil
                end
            elseif ecs_single_entity["sprite"] == sprite_fireball_right or sprite_arrow_right then
                if {ecs_single_entity["location"][1] + 1, ecs_single_entity["location"][2]} == value then
                    ecs_single_entity[key] = nil
                end
            end
        end
    end
end

re-hoard_still-nil

Even after I explicitly checked if the location is nil, I get this error.

In fact, my main issues are how Lua and pico-8 deal with nil values. They always seem to come up and cause big problems every step of my way, no matter how close I get.

I think that my game cannot handle Lua.

I was prepared in case this happened. I am aware that C, being a lower-level language, invites more problems, but I do not think that I can profitably go farther using Lua at this point.

I am not quitting Re-Hoard. However, right now, I shall search for a different game engine. Meanwhile, I shall take a look at the underlying algorithms that run Re-Hoard. Even if this issue is just another algorithm problem, I still plan on switching languages because Lua is just giving me too much grief.

Hoarding on the Dragon

(Disclaimer: a lot has happened here quite some time ago, which means that I have forgotten details here. While the order of the text and the order of the pictures is correct, the pictures might not accurately reflect the state of the game at their point of time in the text.)

 

I was wondering why all of the opponents were knights instead of the variety of emotions I planned. I decided to check the value of the name of one of the opponents.

That value was nil.

In fact, all of the opponents had nil components set to nil values!

The entity-component system that I used is actually a couple of functions that run over tables (the entities) that have a set of keys and values (the components). Other functions (the systems) run on those keys. Because tables are the basis of this entity-components system, I figured that I had two solutions:

  1. Turn the component keys into integers instead of strings. I might also switch from pairs() to all(), which requires a gapless sequence of integers but runs the components in order and had a bigger guarantee of working.
  2. Rewrite the entities, components, and systems in a way that gets rid of syntactic sugar.

I picked the 2nd option, other than my run with Scheme making me distrustful of syntactic sugar, because making the underlying structure more apparent should make the game work better.

That did not work, though the whole thing is actually more understandable.

I then found that the populator accidentally overwrites the dragon entity. I fixed that.

re-hoard_half-opponents

…”local ‘c’ “? That variable did not exist in the code!

After some struggling with online searching and looking at the code again, I found that my conversion to the new format was incomplete. Aside from the table-related deletions still using syntactic sugar, the string keys, by virtue of being strings instead of numbers, needed quotation marks, plus the key/value pairs needed commas at their ends. I did all these fixes.

re-hoard_full-opponents

Everyone is there now! The game actually looks playable!

…”looks”. Nobody could move!

The code seemed fine, but the dragon’s movement values were nil! This time, the dragon was completely empty (excluding one entity that had the value of… an empty table)! I tried manually setting the value of the dragon’s movement, but the dragon moved in place once then stopped. I ended up writing a lot of debugging code that printed the value of both the dragon’s vertical movement and position after I manually set the movement, but the movement seemed to go nowhere.

Turns out that everyone still used the old components which split the movement and positions into horizontal and vertical parts. I updated them to the new components that used its own table. In the meantime, I also got rid of some redundant code that checked the current phase in some modules, even though the modules themselves only run in their respective phases.

re-hoard_gang-up

While the opponents can move, they were supposed to touch the dragon if the dragon was next to them. Moreover, the dragon was supposed to lose after one of the opponents touched the dragon, but the game keeps going…

Why not make a game from scratch?

When I was learning C, I figured out that, because Libretro also uses C, I could simply target Libretro instead of the Game Boy Advance. Not only would this route free me of the restrictions of the Game Boy Advance but also gives me far more flexibility with what libraries and other functionality I could include (say, movies). Plus, the whole point was that I wanted to actually take advantage of the unique features Libretro has.

However, I found out that there are some good reasons why I would target the Game Boy Advance… at least at the current time.

  • Drawing graphics in the Game Boy Advance actually appears to be more straightforward compared to from scratch (even in Libretro), especially since…
  • …I only have to deal with one screen resolution (240 * 160 pixels) and refresh rate (~60 fps), whereas writing a game from scratch would have to account all of the screen resolutions and refresh rates of any device that might play my game.
  • Trying to work within the restrictions of the Game Boy Advance would lead me to write more concise, more efficient code.
  • The whole point of Libretro is that I would write a program that targets Libretro, then the program would run in any platform that runs RetroArch or similar forntends. The problem is that, in practice, I would still have to consider the different platforms when compiling. A couple examples are that, while a Libretro program would be a .dll file in Windows, the same file would be a .so file in Lignux. There is also the fact that compiling to a game platform would require a difficult-to-set-up toolchain. Keeping track of all these platforms is complex and invites significant error and confusion. In contrast, while targeting the Game Boy Advance, itself a game platform, requires a difficult-to-set-up toolchain, I have to compile to only one platform: the Game Boy Advance itself. The resulting .gba file can be played in any platform that can play Game Boy Advance games.
  • Speaking of platforms, the list of platforms that have a Game Boy Advance emulator overtakes the platforms that support Libretro. More specifically, I can also play Game Boy Advance games through native BSD, BeOS, Dingoo (along with a lot of similar devices), Microsoft DOS, Nintendo DS, Windows Mobile Pocket PC, Windows Phone 8, Windows Phone 10, and any platforms that supports the Java Virtual Machine. Then there is the obvious: I can put my game in a linker that plays the games in an actual, real-life Game Boy Advance.
  • Documentation on the Game Boy Advance, including hardware to writing homebrew, is plentiful. Documentation on Libretro development is far less so. In fact, the “go-to” tutorial of targeting Libretro is literally incomplete, unexplained in many areas, and apparently abandoned since years ago.
  • The documentation on the Game Boy Advance actually helped me understand programming using Libretro. The “pitch” (in graphics programming) and makefiles (in program compilation) were nebulous concepts until I read the documentation on homebrew of the Game Boy Advance.

There is also the fact that there is that simple appeal of playing in my actual Game Boy Advance a game that I have made myself.

 

I am still going to support Libretro directly later (maybe make a Game Boy Advance “Lite” version and a Libretro “Deluxe” version). Right now, though, I believe that targeting the Game Boy Advance would be a better next step when moving from the pico-8.

 

Hoarding Opponents

Shortly after I wrote my previous post, I wondered if I made a mistake when drawing the map. After all, while testing the game, I hard-coded the dungeon plan, which meat that the dungeon-builder was not at fault. Sure enough, I found that the mset() functionality merely says which sprite goes where in the map but does not actually draw the map itself.

I added actual drawing-code.

re-hoard-unbuilt_dungeon

The dungeon actually drew itself. From what you (cannot) see, though, the treasure is gone. Even after I replaced the dummy data with the actual dungeon-generator, the treasure is still invisible. I messed with the treasure’s location.

Turns out that the game draws the treasure before the dungeon, hiding the treasure. I only noticed that because, when the treasure was moved behind a brick tile, I could see part of the treasure. (Black is equivalent to transparent in the pico-8. I saw the treasure through the black lines of the brick tile.) I rearranged the order of the drawing code.

re-hoard_stuck-knights

One thing is that, not only does the dungeon-builder actually work in practice, but I also can see every opponent and the dragon! The problem, though, is that just about everyone is stuck inside the walls. Looking into the code revealed that the sprite-drawing code positioned everyone off by 2 pixels. That worked, but some were still stuck in walls. I then realized that the dungeon-building code had the internal cell-order of “vertical, then horizontal” instead of the more intuitive “horizontal, then vertical” order I used. I switched the order of the opponent-placer to “vertical, then horizontal”.

re-hoard_free-knights

Everyone is now located properly!

(You can see that I restored the original color of the treasure’s metal part.)

Despite these fixes, there are a couple of problems. The first is that they are all of the knight sprite when there is supposed to be only one knight. The second part is that no one, not even the dragon, is moving. I thought that may have to do with the entities not having a variable that declared what opponent they are. (After all, the knight sprite is the default sprite.) However, had the opponent-declarer not worked, neither would the opponent-locator worked either because both are functions! Trying to access the details of the world was fruitless, either.

I let go of this problem right now.

That’s my Hoard!

At this point, I am incredulous to any feeling I have that I would never finish my game. After all, every time I get that feeling, I get a success that deals with the cause of that feeling.

The first part was that I realized that mset() from the pico-8 API merely alters the map data but does not actually draw the map. The second part was that, out of some reason, I wrote code inside the dungeon-building function that explicitly does not run during the setup phase. (Between those two, I ended up fixing a lot of other bugs and even simplified the code a lot. That was a happy side-effect of figuring out the problem with the map not drawing.)

After those fixes, I got this:

re-hoard_single-treasure

That screen was blank yesterday.

I have never been so glad at seeing a single treasure chest.

Cleaning up my Hoard…

After struggling with finding out a possible entry point in solving my problem, I wondered if the dungeon that has the array was too big. I did a couple of dungeons: one normal-sized and one half-sized.

No.

I then wondered if the opportunities were at fault, since the game runs out of memory after printing the opportunities.

No.

I then wondered if what was at fault was the function that runs at an intermission, since everything prints right before running out of memory. After selectively commenting out sections, I found that the music-handling system was at fault.

I looked the code itself.

music_stop = function()
 music_stop()
 music_playing = false
end

…doh. I meant to write music(-1), instead. The game was stuck in an infinite loop.

I fixed that, but I got a new problem:

re-hoard_too-big

I read about that restriction a lot, but this is the first time this has happened to me.

I got rid of the commented-out debugging code in testing the dungeon, but the fact that I am extremely close to hitting that restriction is surprising and frightening.

 

Despite this, the game still does not go to regular gameplay. I wonder what now?

Re-Building Dungeons

I thought that all I needed to resolve was dealing with the old algorithms. However, because running computer programs reveal only one error at a time, I found out that the dungeon-building code crashed the game because the code apparently tried to index a nil value. That burned me so much; I did not want to deal with the game until late at night, even though I fixed the previous problems early in the same day.

Because the dungeon-building code is based on my own implementation of the Recursive Backtracking algorithm, I decided to pick someone else’s implementation, instead. I ended up getting one from Rosetta Code. I then took my time adapting the code closer to my code style and integrating the code into my game. Subsequently, I brought the imported code to a separate cartridge because I wanted to make sure I understood the algorithm and got the algorithm right. I took this time in changing the all() iterator to ipairs() because all() is very slow, from what I have read at Lexaloffle. all() guarantees order in iteration, but this is a randomly-generated dungeon; the risk of iteration out-of-order would only add to the randomness in generation.

From what I expected, the adapted code broke, too. After doing more adaptions in light of the pico-8 environment, the pico-8 still got problems with nil values. Turned out that a constant table that had the corners needed to be renamed.

After the algorithm ran without a problem, I decided to try to get a dungeon preview. The problem is that trying to print only one character at a time led to the pico-8 printing one newline after character, which meant that my preview was just a column of wall characters. I could concatenate the characters, but, because the dungeon is generated, I would not be able to know what character goes next. Though I could put the characters in an array, I also found that the pico-8 does not print a newline after a character if I specify the screen position, which I can calculate from the sentinel values the algorithm uses when iterating over the rows and columns the dungeon structure has. After adjusting the positions, I got the preview.

However, that used an all-wall dungeon. Once I started using an actually-generated dungeon, the nil error returned. I decided to comment out all of the code, write a few lines of debugging code that mentioned the values certain variables had, then uncomment the code one piece at a time. I found that, because I used pico-8’s random-number generator instead of the one in the table library, I risked getting a value of 0, even though arrays in Lua start at 1 by default. Adding 1 to that value solved that problem.

I ran the generation algorithm, which ran without a hitch. However, the dungeon seemed to not have a wall perimeter despite there being code that specifically printed some. After inspecting and testing that the code ran correctly, I wrote new debugging code that printed the variables of any factor that could affect this mistake. I started by printing the dungeon’s dimensions then adjusting them accordingly. There was no correlation. I then tested the arguments that were fed to the walk() submodule, that is, the part that actually generated the dungeon’s path. (A positive side effect of this technique was that I separated into their own variables the complex calculations that went into walk(). ) After going through 20 test cases, I found that the dungeon had the wall perimeter only when the values were fed into walk() were both even. After writing code that made those values even, the generated dungeon now has a proper perimeter every time.

 

These changes did not work even when fed back into the main one. I tried hard to check how the code was different, even switching between the ipairs() and all() iterators, but to no avail. Turns out that, while copying the algorithm to the actual game, I forgot again about renaming the constant that had corners, since the game put all of the constants at the top of the source code… away from the algorithm which was closer to the middle.

The game now stopped whining about nil values.

 

What comes next… the game fails to switch from the title screen to the intermission screen, failing to set the variable that activates the intermission phase.

I want to take a break from this, too, until I figure out how to even define the problem.