Game thing progress #5, but also a bit of other things that's been happening

Hey, long time no see, as if anyone reads these posts.

I've been doing some things on and off for the past several months, including working on the game, a bit of working on another code thing, and playing games (haven't done that in a while..).

The other code thing is a somewhat ambicious and experimental (as in I have not done anything of sort before) project, where I try to implement a virtual machine and a parser for the C language in Common Lisp. Surely it can't be that hard, right? The answer is indeed, it's not exactly that hard, just tedious. I return to it sometimes when I want to work on something else, but it's been lying on the shelf for some time now. You can find the code on github1, but it's very not done and poorly documented. Because of course it is.

Now for the game, there are quite a few news, the first one is the game project files finally changing name to the actual name and not the old one left over from my previous attempt at writing the game in rust with a somewhat different plot.

Now that the first puzzle room is somewhat finished, I added a small jingle that plays when it's solved, composed it myself, too (putting a few notes together in LMMS).

The DrawableSprite component has been made into Drawable and can now handle any drawable, it's been made so I could also draw text with it. Currently, the only text drawn with it is the text that says [E] to <action>, action being some unique thing encoded in the object, so for a button it'd be "press the button".

The story has been revised a bit again, somewhat. After playing Syberia 3 a bit I felt like I read something I wrote. Unlike the first two games, it's written very amateurishly and I think I saw a clear problem with my own attempts at writing there. It tells too much, reiterates on every thing and makes everything very clear, so the reader has no room for thinking about what was said. This is why it feels unnatural, people don't talk like that, and realizing that I rewrote parts of the story to remove uneeded details.

There were lines of text in the story that required no choice or anything of sort, but just an acknowledgement (by pressing space to progress). These has been reworked to accept "1" instead of space, because as of now most answers only have one choice and therefore 1 is the most logical key to use for acknowledgement. That said, pressing 1 now also skips text "typing" and prints it all at once, this also applies to responses (though there any numbered answer key can be used). So now you can skip the text printing out if you want to go faster.

Also, the first character you meet has finally been given name. It is an obvious cut out of a different word, but I think it does feel like a genuine name, and it fits quite well.

The dialogue construction file format has also been improved, an option called dialogue_between has been implemented, it allows not specifying characters if their pharses are alternating (one talks after the other all the time), while also also allowing to change the character that is currently talking in the dialogue by explitly specifying the character name. If the character is in the dialogue_between list, the next line is assumed to be by the next character, and if the charcter is not in the list, then the tracking ignores that line and doesn't change the currently speaking character, thus allowing intermissions like player - description - other character, with only specifying that the description is a description, and the system understanding that the the description is not a part of the dialogue. That allowed to remove about 30 lines of character specifications that I already knew, but had to be provided explicitly. I'm kind of proud of this dialogue system by now, it's quite flexible.

More of the story has been added to the actual game from the draft file, soon enough I'll probably be ready to make the first public release, probably on itch.io, since it seems to be a very nice site for indie game developers, providing a lot of benefits, allowing easy communication, etc.

Since I'm somewhat getting ready for the first release, I set up automatic builds with GitHub Actions! It builds the game for Linux and Windows curently, though I hope to extend that for MacOS also (I don't have a mac, but I know someone who might be able to help me with that). While building for linux is easy, since that's the platform I've been using all the time and I engineered the build somewhat well, so that it takes just two commands to build the whole thing, Windows was a bit of a different story. On Windows, I had to use MSYS/MINGW, that took some time to figure out. Then, I learned that you can't build lua on windows that easily (and can't link to it statically at all), so instead of using their makefile, on Windows I do the following:

gcc -O2 -DLUA_BUILD_AS_DLL=1 *.c -c && mv lua.o lua.e && gcc -shared -o lua53.dll *.o
gcc -L. -llua53 lua.e -o lua.exe

This builds both lua-the-shared-library and lua-the-interpreter. The first one I link with and copy it to the release directory, the second one is used for luarocks, which has a different setup script on windows (it's called install.bat). This script gave me a few problems when automating, since the GitHub Actions thingy that sets up MSYS2 doesn't inherit the PATH variable by defult, while the real desktop MSYS2 does, and since install.bat, when it detects that it's run in mingw, assumes the existance of both unix and windows utitilies, it wouldn't work on the automatic builder, but worked fine on my machine! This was an easy fix (setting path-type to inherit in the action config), but it took some time to figure out.

Other than a few path (/ vs \) problems, there weren't much more trouble with building, mostly thanks to the fact that all the major dependencies are provided as submodules and are in my control and, in release mode, I compile everything statically, which makes distribution a fair bit easier.

For the last thing, I also also added a small delay between terminal line output, so they don't feel too fast.

Looking at it, not that much work has been done, I've been mostly slacking off, but I did have some stuff to handle IRL, though that is mostly over for the time being.

Well, that's probably my longest post yet. That's pretty nice, hope you enjoyed it, and hopefully we'll meet again soon, thank you for reading my blog post!

Game thing progress #4

Getting straight to the point, since the last time a system for rooms to use other rooms as "prefabs" has been implemented, they can remove entities and resources they don't need, put new ones and override old ones. This has been used for a room that looks almost identical to the first one, except it has a door instead of a button, so many things can be copied over (like wall colliders and background).

While implementing this I noticed that colliders don't handle Tranformable scaling well, so I made a simple thing to adjust their position for this. Now, a door that has a mirrored texture has its collider put where it should be and not on the right.

The interactable objects also now use collisions to detect the player interacting with them instead of just their sizes, this allows for invisible interactable objects, for example.

The asset system has been reworked: now all the asset names are in one place instead of in the objects that use them. This make it easier to actually reuse things. Also, the asset table in lua now only stores the things that can actually be shared (textures and sound buffers instead of sprites and sounds) and components create their own instances of classes that use these shared assets. When the walking module is first loaded, it reads the assets file and puts everything in it into the known_assets table, which, through the __index metamethod on the assets table, is looked up when the asset cannot be found for a path to the asset. It is then loaded and put into the assets table for later usage.

A very simple and basic guessing "puzzle" was implemented for the ending of the first day. It is in the second room and involves three buttons interacting with each other. Along with that, I fixed a bug that was caused by not resetting the physics world when changing rooms (but resetting the ECS world).

Then, I separated the "can this thing be interacted with" and "what does it do" into two different fields in the interactable component. This will probably be used in the future to show a popup when something can be interacted with. It was added to all the objects that can have some condition where the object becomes non-interactable.

A small timeout has been added to object interactions in attempt to prevent interacting with multiple objects at once.

Getting back to the terminal text part, I implemented switching to different rooms from the terminal. In the process, I fixed YAML cutting off newlines (because I used > instead of |). This was a problem in some cases where lua requies newlines, but in some cases it wasn't noticable at all, so I didn't even discover it at first until a certain point in the script.

And that's it for now.. Thank you for reading my blog post!

Game thing progress #3

Since the last post, everything except the terminal part has been rewritten in moonscript. I'm still not sure if I like it that much better, but I like to think that the time spent dealing with the lua module linking hasn't gone to waste.

A simple change has been made in the walking part, to make it a bit longer, requiring the player to go back to the computer, where they previously were at the start, and then interacting with it to get back to the terminal, instead of just going there when the "puzzle" has been solved.

A simple prefab system has been implemented. It allows separating entities into different files and loading them from other scenes. This way, the player entity can be recreated in the new room easily.

A beginning of a second room, which will contain a simple puzzle, has been drawn and inserted into the game, along with a passage sprite to separate the rooms. When the player interacts with the passage, the ECS resets and loads the new room's contents. The passage is, in turn, implemented using the same mechanism the push button used, except it's been generalized for usage with anything interactable.

A simple collision system, using bump.lua, has been implemented, which replaced the "just compare sprite bounds" system. This allows entities without sprites to have collisions too, invisible walls (or walls that imitate the level geometry) can be made this way. If the entity does have a sprite though, the collider component can be told to use the sprite size as its bounds, or, as with those without the sprites, it can be told to use constant sized bounds defined in the entity description. The position for the boundaries is taken from the entity's transformable component (which is basically SFML's Transformable). If the entity has a sprite, then the sprite as used as a transformable, and if it does not, a new transformable is created and used.

The above exposed a bug with render Z sorting: before, everything was drawable, so this wasn't a problem, but introduction of things that are not to be drawn also introduced gaps into the entity array of the drawing system. This means that virtually all built-in things that work with arrays do not work anymore. Particularly, sorting either silently fails or passes nil to the sorting function, because it assumes there are no gaps. It took me about a day and a trip to the #lua channel on IRC to figure that out. Now, there's a separate array of entities to be drawn, sorted and without gaps, which is updated when entities are added or removed.

Synchronizing collisions betwen the physics world and the actual trasnformables and moving things accoring to those collisions also turned out to be a non-trivial thing to do. I implemented a debug overlay that draws red boxes around colliders, this overlay will probably be easily togglable in the future. The positions of the objects are now synchronized with the physics world in the following way:

  • If the entity wasn't in the world before, put it in there with the position of the transformable minus the origin/pivot point
  • If the entity was there already, put the position from the world plus the origin/pivot point, and update the size of the entity in the physics world from the sprite

And that's how things have been in the past few days, I think that's enough for this post, so I'll end it here. Thank you for reading my blog post :)

Game thing progress #2

Since the last post, some more of the story text got ported to a format that the game understands. The entire first day is now there, and, maybe, it will stay there as it is, unless i decide to change the beginning details again. This day includes a feature that I implemented a long time ago but didn't have any chance to use, which is showing answers to questions depending on some state. This state is actually determined from a lua script in the story file. Here's how one of them looks like:

return not TerminalModule.state_variables.narrator_house.hub.living_room

This "script" is put into the "condition" part of the reply and the reply is therefore only shown if, in this particular case, the living room has not been seen yet. I'm thinking about maybe simplifying hubs like these in some way, but I'm not sure about it yet.

Another thing that's been done is that for the "walking" part of the game, all the components for the entities and the assets to use are now written out in a TOML file (because that's a simple format and there's a native parser for lua), and are loaded from that file instead of being hard-coded. Here's how that file look like right now:

[assets.sprites]
computer_room = "resources/sprites/room/room.png"
button = "resources/sprites/room/button/button.png"
mainchar = "resources/sprites/mainchar/mainchar.png"

[assets.sounds]
footstep = "resources/sounds/footstep.ogg"

# Background

[entities.background.drawable_sprite]
sprite_asset = "computer_room"
z = 0

# Button

[entities.button.drawable_sprite]
sprite_asset = "button"
z = 1
position = [1020, 672]

[entities.button.animation]
sheet = "resources/sprites/room/button/"
playing = false
playable = false

[entities.button.button]
initial_state = "disabled"
state_map = { disabled = 1, enabled = 2 }
callback_name = "switch_to_terminal"

# Player

[entities.player.drawable_sprite]
sprite_asset = "mainchar"
z = 2
position = [420, 820]
origin = [44, 176]

[entities.player.animation]
sheet = "resources/sprites/mainchar/"

[entities.player.player_movement]
footstep_sound_asset = "footstep"

All of the assets are loaded and stored first, and then components reference them by name. At component creation time, references to assets are taken from the storage and put into the components. This allows them to never be GCd, which is a good thing since dependencies between values passed from C++ like textures are not tracked by the lua refcounter.

Another change worth noting is porting most of the code to moonscript, because it's quite a bit better as a language (except maybe the indentation syntax, the features are nice). That was a pain to set up, because i've always been linking lua statically to my executable, but lua modules expect that the dynamic symbols of the lua library can be looked up. Didn't know about that for a while, but when i figured that out, it was as easy as adding -Wl,--export-dynamic to the linker arguments to expose those statically linked library symbols to the modules. After that there was a bit of a fight with luarocks, that ended up in moving the modules from build.modules to build.install.lua, as it was trying to build .moon files as if they were C sources. For some reason, sources in install don't have that problem.

So, that's what I was up to in the last few days, I'm hoping to maybe continue rewriting things in moonscript and then work on the second room in the walking part of the game.

Thank you for reading my blog post :)

Game thing, initial story

I've had an idea of making a game with a story inspired with several things I loved a lot for a while. So I started actually doing it about a year ago, in rust, because that's the "popular thing to use". I wasn't sure about the plot at the time, just focused on the engine, it had a big field for the story text and a few buttons with answers at the bottom. It was in that state for a while, I used a different story as an possible intro, since I wasn't sure about where to start. And then I kinda burned out on the whole thing and forgot about it for half a year or something like that.

I remembered about it when playing another game, not related to the original inspiration, but also with a good plot. That day I started thinking really hard about what to do and decided to scrap the previous story prologue and start a new story unrelated to it. I was super determined initially and spent a few evenings writing the basic, very rough prologue into an Org file. It was a few thousand words when I finished that session, and I thought "Well, I guess that's pretty good for my first serious writing attempt, right?", and went to sleep, because it was super late already :)

I spent a few days adding things to it after that and then tried changing the engine to suit it better, since it was more like a terminal with inputs now, and not a screen with a buttons. In the middle of that process I got tired of fighting rust over simple things and decided to rewrite the whole thing in C++ because "I know what I'm doing, just let me do the thing", so that took a few days too. But not that long, because I was just porting a few simple algorithms and using the same graphics library1 I was using in Rust except in C++ it was natively available.

After finishing porting the needed parts (only the terminal one, basically), I worked on the other planned part of the game, where the character is supposed to actually walk and interact with things and not just read a lot of text and respond to it. Doing that in C++ sure was a pain. So I thought maybe I could do it in Lua, and turns out there's a great library full of magic2 that greatly simplifies using lua with C++. After some painful CMake-ing, I was able to connect luarocks and CMake to pull all the lua dependencies and put them into the _buid/resources directory, where the game could find them at runtime. The next few days were spent porting things from C++ to Lua. I thought about using ECS for the terminal initially, but turns out it doesn't work very well in that particular case, so I resorted to just having two functions called from the C++ part that were called every frame to draw and update things. When the porting was done for the terminal, though, it looked a lot better than C++ version and I wasn't that much ashamed of it.

After that came a bunch more writing. I set up a goal of finishing at least two days worth of text (two game days). That took a lot of time, and I'm still revising what was done there, but it's a good amount of text for now, about 8k words. It's not well written, it is my first big writing experiment after all, but I'm somewhat proud of it. I had some people read it and give me suggestions on what I should improve about my writing, so let's say that is in the works.

At some point, I decided to draw a few small pictures to explain surroundings a bit. They are small because I'm not very good at drawing pixelart and there's not much space on the screen for them anyway. I drew the first two for now, but more will come later, I already have them planned in my head.

With the first few days finished, I thought I'd also port the "walking" (that's how it's called in code) part of the game ported to lua too. Now that part did actually work well with ECS, so I used a ECS library for lua3 to do everything in the first walking scene, using some global values provided from the C++ part (like the drawing target). I'm not sure about how it performs, but it was once again better than what I had with C++. I added a lua coroutine wrapper thing that would simplfy working with them and implemented fade-in/out transitions to switch between the terminal scenes and walking scenes, that took about a day, but coroutines will surely be of use in the future. And all of that is built as simply as just typing make, thanks to past me doing all the awful CMake writing.

As of now, I'm continuing to work on the story, refining and rewriting it, making it seem more natural and belivable. Stay tuned for some more progress news, hopefully.