Labyrinth: a hack-and-slash with a shifting map

This just keeps looking better and better – keep it up! :slight_smile:

3 Likes

It’s been a while. Update 6:

I mentioned at the beginning of this thread that I am not using tilemaps for this project. After some odd performance hits, I eventually decided to give tilemaps a try. Unfortunately, the way things are structured for this, rooms have to be dynamic, with tiles capable of changing after room creation. After making a branch and structuring the game to use tilemaps for rooms, I discovered a little too late that collider objects cannot be set to a live instance of a tilemap in a collection but only to a saved .tilemap file. Based on how collider objects are done in Defold, this isn’t that surprising, I just didn’t realize that would be a complication until far too late. Whoops.

Anyways, once I resumed standard development of Labyrinth I made some progress. I made some more room designs, but after that shifted more to focusing on combat:

  • I made a simple ‘bat’ enemy which occasionally swoops towards the player. A ‘trap’ room with a vertical entrance spawns these and locks itself upon entrance, not unlocking until all enemies are defeated.
  • The ‘dagger’ weapon shown in the inventory is working and deals damage upon collision with an enemy.
  • I set up a system so a text node floats above an enemy when they take damage.
  • I also added a ‘critical hit’ mechanic which doubles damage to enemies when the player strikes them from above. Critical hits will be triggered differently in the final product, that was mainly for debug purposes. The text effect for critical hits is also a different color along with a brief camera shake to provide feedback.

All of this combined leads to a game approaching playable:

The next update will have some combination of more enemies, more UI elements and more rooms.

11 Likes

I meant to post this yesterday but didn’t get a chance to, so this is recent as of yesterday. A little more progress has been made so far today but nothing substantial. I got a decent amount of work done in some other areas so here’s update 7:

Enemies:

  • I added a snail enemy, primitive AI right now but it works just fine for testing purposes
  • The enemy moves toward the player and does damage upon contact. I used sprite scaling to create a ‘crawling’ (I’m not sure if that’s the best term but I can’t think of a better one right now) effect that suits the snail enemy quite well.

Rooms:

  • I added several rooms and used vines in some to allow for the player to actually travel ‘up’ to higher rooms in the dungeon.
  • I did some experiments with connecting rooms in different ways in preparation for the procedural dungeon generation I’m starting to create. Right now rooms are manually arranged but eventually the map will automatically be filled with rooms that connect properly with each other.
  • Room connections are standardized to be on the middle two tiles of the edges of the 20x20 rooms. Therefore, the connective properties of a room can be represented by a boolean for whether or not it connects on each edge.
  • For the initial draft of procedural dungeon generation, an initial ‘seed room’ will be set to a special room type. Off of the borders of that room, random rooms will be generated that have the proper connective properties to appropriately link or not link with any existing bordering rooms. I’ll explain more about this once I have a working rough draft.

Demo of the new enemy and rooms:

I don’t think I’ll have procedural generation working in the next few days, but maybe by the weekend I’ll have a rough system working. I’ll probably post some other updates before that happens.

12 Likes

Looking really cool!! Love those retro graphics!

3 Likes

Update 8

Once again, this is recent as of yesterday, although I haven’t made too much progress today so far.

Surprise, I got dungeon generation working!

I made a lot more progress than I thought I would today. I added some basic elements to the UI, but nothing too fancy on that front. More importantly, I made a lot of advancement on the room system that lies at the core of Labyrinth’s gameplay:

  • Different room types were originally constucted by scripts, which was quite messy and only a temporary solution. Today, I put together a functional system for loading room data from a bundled folder of ‘room’ files and using it to construct rooms. If rooms were based on tilemaps this wouldn’t be necessary, but the solution I set up wasn’t that much of a hassle and allowed for some other important room data to be held in the same file as the terrain data. Originally, I wanted to use the .json format for rooms, but I found that Defold supports only decoding json text into a Lua table (json.decode()) and not the reverse process. Right now, only being able to load rooms isn’t an issue, but in the future might become a problem - I’m considering making a small ‘room editor’ program for my and others’ use to edit and create room files. If I make that, I will use it to design rooms more rapidly and give users a chance to create rooms and submit them to potentially be used in the game if they are of interest. Anyone who’s interested let me know, more on this once I’m closer to making it.

  • Once I had an organized room format created, I added data specifying the ‘connections’ I mentioned earlier. Using that data, I scripted a simple procedural generation system that fills the map with tiles that connect properly. In order for the system to work I needed at least one room for each of the 16 combinations of connective properties, so the majority of the dungeon’s current room designs s (16 of the 21) are simple placeholder tunnels. Nevertheless, I now have a surprisingly decent procedural dungeon that is approaching the amount of difficulty to navigate that I’m aiming for. At this rate, I’ll probably have some simple earthquakes working soon.

In conclusion, here’s the current state of the dungeon:

The location indicator is only a debug feature, there’s a good chance that it won’t be visible to players in order to keep the maze difficult to navigate.

7 Likes

I don’t have any progress that amounts to a full update, but just something you might find interesting:

I’ve tried to make a full-on game many times before, but in reality I’ve never released anything that’s amounted to an actual playable, enjoyable, quality product. I got the idea for Labyrinth about a month ago, and started to make it with the expectation that it would not succeed. To my surprise, after a few weeks of development it started to look like it might go somewhere. As I’ve continued developing it, I am now regretting that I programmed it with the expectation it would fail, because many aspects of the code were written in a messy way that was only intended as a proof-of-concept. Now that I’m actually making this, I have to recode pretty much every aspect of the game. Darn. While this might be unfortunate, I’ve gained both a valuable lesson and an opportunity to make the game’s core code much more efficient from this experience. Hopefully any other discouraged gamedevs that are reading this can benefit off of this by writing their code right the first time without the expectation that they will ‘abandon ship’ in the future.

6 Likes

Cool, it’s nice to hear that you have faith in your game. It looks great and I’m looking forward to playing it!

3 Likes

As I’m doing the recode I’m fixing some old issues that prevented varying the aspect ratio. Now that I’ve got that all fixed up, I have the option to add touch screen + variable aspect ratio support for Labyrinth, which in theory gives me the ability to release the game on phones.

Quick poll: Is this is a game that seems like it would be playable on a phone? Or is it better kept to desktop mode?

1 Like

It would play well on mobile. See Super Dangerous Dungeons or Sword Of Xolan as a reference for control schemes.

3 Likes

Alright, thanks for the feedback! I’ll take a look at those for UI design, and I’ll also try to figure out if the circular inventory can be made easy to use on phones.

1 Like

Due to some other obligations I haven’t had much time to work on Labyrinth recently. In the future I’ll be able to make progress a lot more quickly, but for the upcoming few weeks I’ll be on and off. I’m working on the new room script right now, once that gets back to where it used to be and then some I’ll move on to the player system.

Update 9:

Unfortunately, I still haven’t been able to make much progress on the recode. Due to some obligations this weekend, I won’t be able to make a ton of progress in the next few days either. I did manage to get room loading and postprocessing (adding grass, corners, etc.) to work with much cleaner code, and thanks to a modified render script the game now works fine at various resolutions and aspect ratios:

I’m also starting work on Labyrinth’s icon. Here’s a (very, very) rough draft of a potential icon:

After this weekend there’ll be some more progress, and by mid-May I should be making pretty rapid progress.

5 Likes

Would you be willing to share your render script?

2 Likes

I don’t have access to a laptop right now, but when I do I’d be glad to share the script. I haven’t made any heavy modifications to the default script right now, so far I’ve just done some scripting on the view matrix. Anyways, I’ll share the render script as soon as I can!

2 Likes

@Pkeod, here’s my render script; it’s only different from the default render script in one area. This script creates a camera view 160 pixels across horizontally, centering around the virtual coordinates <80, 80>. Hopefully this is useful to you.

function init(self)
    self.tile_pred = render.predicate({"tile"})
    self.gui_pred = render.predicate({"gui"})
    self.text_pred = render.predicate({"text"})
    self.particle_pred = render.predicate({"particle"})

    self.clear_color = vmath.vector4(0, 0, 0, 0)
    self.clear_color.x = sys.get_config("render.clear_color_red", 0)
    self.clear_color.y = sys.get_config("render.clear_color_green", 0)
    self.clear_color.z = sys.get_config("render.clear_color_blue", 0)
    self.clear_color.w = sys.get_config("render.clear_color_alpha", 0)

    self.view = vmath.matrix4()
end

function update(self)
    render.set_depth_mask(true)
    render.clear({[render.BUFFER_COLOR_BIT] = self.clear_color, [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0})

    render.set_viewport(0, 0, render.get_window_width(), render.get_window_height())
    render.set_view(self.view)

    render.set_depth_mask(false)
    render.disable_state(render.STATE_DEPTH_TEST)
    render.disable_state(render.STATE_STENCIL_TEST)
    render.enable_state(render.STATE_BLEND)
    render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA)
    render.disable_state(render.STATE_CULL_FACE)

    -- The only thing different from the default script right now
    local orth_h = 160 * render.get_window_height() / render.get_window_width()
    render.set_projection(vmath.matrix4_orthographic(0, 160, 80 - orth_h / 2, 80 + orth_h / 2, -1, 1))

    render.draw(self.tile_pred)
    render.draw(self.particle_pred)
    render.draw_debug3d()

    render.set_view(vmath.matrix4())
    render.set_projection(vmath.matrix4_orthographic(0, render.get_window_width(), 0, render.get_window_height(), -1, 1))

    render.enable_state(render.STATE_STENCIL_TEST)
    render.draw(self.gui_pred)
    render.draw(self.text_pred)
    render.disable_state(render.STATE_STENCIL_TEST)

    render.set_depth_mask(false)
    render.draw_debug2d()
end

function on_message(self, message_id, message)
    if message_id == hash("clear_color") then
        self.clear_color = message.color
    elseif message_id == hash("set_view_projection") then
        self.view = message.view
    end
end
3 Likes

Not quite a full update, but I’ve made some progress!

Room transitions are back to the state that they used to be in; camera motion is a little choppy due to the framerate drop caused by having a large number of sprites - once Defold 1.2.80 is released this problem should be fixed, as it’s an internal issue that was fixed by a compiler flag that should be set in the next release. Vine climbing is handled in the entity script now, meaning that enemy AIs will be able to take advantage of vines if I figure out a good way to do that.

Next thing to implement / re-implement is a basic enemy AI, after that I’ll work on a UI and hopefully start making progress beyond the pre-recode game. I also need to re-implement the lava particle effects and map generation - all of the room files from the old version are migrated to the new one but the map generation algorithm hasn’t been recoded yet. It’s fairly simple so hopefully by update ten that’ll be ready.

4 Likes

Wow, update 10:

After recoding Labyrinth for a while, the game is finally starting to approach where it used to be and beyond.

The previous item management / inventory system that I wrote was, like many aspects of the previous game, temporary and limited in nature. I now have a more advanced inventory system.

In-world items are entities which the player absorbs into the inventory upon contact:

The way the item system is, there will be different ‘categories’ of items which all do a similar task with different parameters, such as ranged weapons, swords, and status affecting items.

Right now items aren’t back to the point of being usable; when they are, things will start to get interesting. I’m planning on making some fancy projectile weapons.

5 Likes

Just a quick post today, not quite enough progress for a full-fledged entry:

I finally got items to start working well. I’m experimenting with two different weapons right now, a hammer and a bow. Once I do some code cleanup, I’ll be able to add some other item types and variations. The bow is a typical ranged weapon, with some recoil and (eventually) a cooldown rate. The hammer is a physical weapon that behaves a little differently than the standard sword will:

As shown here, using a hammer causes the player to crash downward at high speed, and also lunge forward a little in the process. When the player finally hits the ground, some visual effects play and the player’s horizontal velocity is zeroed out; these effects are used to give a sense that the hammer is ‘heavy’ and also open up possibilities for interesting combat strategies.

7 Likes

Alright, update 11!

I made a ton of progress today. Some major code cleanup paid off: I now have a fully extensible item and combat system.

The main weapon that I’ve been focusing on is, as in the last post, the hammer. In Labyrinth, different categories of items such as hammers / axes, swords, daggers, etc. will have more than just different balances of damage and attack rate; each weapon category will affect the player’s mobility in some way to allow for unique strategies and add some challenge to the game. As far as the hammer goes, it’s more of a reckless weapon: hammers do a great deal of damage, and also cover some ground if used in-air; however, the crashing movement of the hammer can easily bump the player into the enemy in a way which causes both entities damage. Also, most hammers will have quite long cooldowns, which could temporarily leave a player defenseless, directly in front of the enemy if they don’t have a backup weapon ready.

This demo should give you an idea of how the use of a hammer affects gameplay:

If you look at the damage text effects, you’ll notice that a smaller ‘10’ damage appears with the ‘280’; the player crashed into the enemy while landing the attack.

I’m planning on doing some more work on weapons, and then after that doing some work on enemy AI. Once enemies have a primitive AI to test combat against, things should get really interesting.

8 Likes

Another quick little post:

Now that I have enemy spawning and map generation working well, I’m starting to focus on the actual workflow for the game.

I added support in the room parser for designated spawning spots; a particular entity type can be specified in the field where “enemy” is entered. The “enemy” lets the parser know to pick a random enemy type to spawn upon entering the room, so once I have multiple enemy types they will show up.

Here’s the parsed result of the configured room, with two enemies spawning in the configured positions and the room locking until both are defeated:

8 Likes