On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags

Mystery Dungeon Hand - Balatro x Shiren

A topic by kyle kukshtel created Mar 02, 2024 Views: 384 Replies: 4
Viewing posts 1 to 5

MYSTERY DUNGEON HAND

I've had an idea gestating for a while that I realized I wanted to make for 7DRL - the idea is to make a mystery dungeon style game (basically... a traditional roguelike) played through cards. But unlike other card action roguelikes like Slay the Spire or card-driven roguelikes(likes) like Forward: Escape the Fold or Card Crawl, I wanted to focus more on the deck itself and treat it like a "Room" that you encounter and "move" through. Then also trying to do things where you're seeding/upgrading a deck over time based on deck "primitives". It's maybe weird to explain but in practice I think works really well.

Another game worth mentioning that was a bit of an inspiration is https://store.steampowered.com/app/2338240/Searching_For_Rest/ - a (based on reviews) mostly unplayed game that I think has some really good design ideas in it.

So I put together some paper ideas for the game:



I don't expect too much of those docs to be legible, but thought they were fun to share anyways!

Dinghy

The other big part of this is that I'm doing all of the game in my own C# 2D game framework called Dinghy. It's a fully custom thing, built on top of Sokol + STB, and I think it's pretty cool. Here's some demo videos of what the engine itself can do:

The whole idea of the engine is that it's meant to be something like Heaps/Cermaic/HaxeFlixel but for C#. And it's specifically targeting trying to be able to quickly put games together with nice-to-have basics that can then tease out to be more robust. So for an example here's how easy it is to get a sprite on screen:

//Program.cs - seriously this is it
Dinghy.Engine.Run(new (800,600,"dinghy",
    () => {new Sprite()},
    () => {Console.WriteLine("update");}
));

It's got a fair number of nice things as well, including source-generated integration with Depot files and Heaps-style type-safe access to res/ files, meaning you can type-safely reference stuff like this (imagine you res/monster.png exists):

new Sprite(Res.Assets.monster) // no strings!

I'm using 7DRL to really put the engine through its paces for the first time, and am learning/adding a lot along the way already. Dev updates will probably be split between engine/game stuff as well to just talk about both as they evolve.

The biggest stretch goal for me for this jam is seeing if I can get it all compiling to wasm for the web. The tech stack allegedly supports it, but the compile toolchain for it all will likely need a days worth of time to even figure out if I can do it. But here's hoping!

Current state

Here's where we're currently at. I've got all the data hooked up with Depot which is populating the cards above:


I also got text rending working in the engine, but tbh on tying it up properly.

My current task is making the logic ECS proc properly in a card-action type way. Dinghy's backend ECS uses Arch so I'm tying into that a bit, but also doing some nice "I have access to Depot" type stuff for Logic event procing:

What this means is that from code I can then directly emit a "logic" event based on the type-safe logic triggers I get access to from Depot:

And then you can emit these events like:


What's nice is that again the logic event is type safe, so you can easily check Card keywords that respond to the logic events with a direct equality check, no strings required (they are literally the same "logic"!).

I'm excited now, but this is also the third iteration already of trying to get this stuff to work so I'm a bit in the doldrums of converting the old logic code to this. However once it's all setup, there's no code maitence required for incorporating new words/triggers. The only thing I'll need to do is just emit the logic even where it happens and everything else will just work as normal!

Off to coding, and hoping to report back more later tonight!

will it have a balatro style trailer? 馃槅 cool idea!

Well that was an adventure. I just coded a Hearthstone-style rules system in a few days, only taking d8 psychic damage in the process.

But I think it's pretty cool. Here's the basic "entry" for the rules logic for a given action:

public void Wait(Action onComplete = null) {
    Depot.Generated.dungeon.logicTriggers.wait.Emit(Systems.Logic.RootEvent, postExecution: e =>
    {
        Dungeon.Track.Act(e);
    }, onComplete:onComplete);
}

This is doing a few things. First is that that long string of Depot path is the type-safe data emitted from my Depot integration that allows me to reference whatever I put in this, directly:


On top of that I then wrote extension methods that actually "Emit" the event into the action graph (again channeling this blog). I had a stroke of genuis for this bit that I think is worth a proper standalone blog about, and maybe as its own follow up to that blog I'm referencing, but basically what I do is bind the keywords themselves to a static dictionary that maps to "loose" static IEnumerator functions.

"Adding an event to the graph" then basically creates and underlying Event object that saves the references IEnumerator, and then invokes it when it's the events turn. The other thing I did on top of this was tie in sensible event lifecycle callbacks to modify the action graph as it runs, primarily through OnExecuted and OnComplete.

OnExecuted runs when an action graph node has been executed... which is a perfect time to add side-effect nodes as children of that node! The event polling system finds the new children, and executes as normal. What's also cool is that when you use the Emit function, it scans for matching keywords from cards such that it will also then tack on the "Will Do X" events to a node before execution, ensuring they update first.

OnComplete is called when a node is FULLY complete, as it has no more un-executed children. Tying into here can basically surface top-level callbacks for actions that may have had hundreds of reactions, etc. Here's a more robust example of how to trigger the "basics", and keeping in mind that under the hood all the graph event stuff is being automatically generated!

public void Move(Action onComplete = null) {
    Depot.Generated.dungeon.logicTriggers.move.Emit(Systems.Logic.RootEvent, postExecution: e => {
        Depot.Generated.dungeon.logicTriggers.discard.Emit(e,new Systems.Logic.EventData(Dungeon.Track.Cards[0].ID), 
            postExecution:e =>  {
                Dungeon.Track.MoveTrackCardsToLatestTrackPositions();
                Depot.Generated.dungeon.logicTriggers.draw.Emit(e,onComplete: () => {
                    Dungeon.Track.MoveTrackCardsToLatestTrackPositions();
                });
        });
    }, onComplete:onComplete);
}

The idea is that you have a unified entry point for all the main actions in the game that have pre-determined steps that execute, but at the same time any cards with interesting keywords/effects can automatically tie into this to build out a more robust graph of effects!

What's ALSO cool is that, to debug stuff, I tied in a simple way for the graph to report itself using Mermaid, so I can basically (visually) peek at the internal graph at any time. I'll set a breakpoint in the middle of that function above, and here's what the graph spits out:

flowchart TD 0[_0]-->7[move_7] 
7[move_7]-->9[discard_9] 
9[discard_9]-->10[draw_10] 
0[_0]-->8[DeathReap_8]

Which visually resolves to:


This is a pretty simple state to visualize but it's cool to see it all working!

With this in place a ton of things are now possible so I'm hoping to actually crank on content between now + deadline and get some real gameplay in. Oh and I guess and inventory... and some spells.... we'll see!

Couldn't resist getting this setup - I set up VS Code to have a Mermaid extension in the preview window. Open up the folder, debug the game... and now I have live-updating visual debug logic tree:

At time so submitted!

https://kylekukshtel.itch.io/mystery-dungeon-hand

Submitted as incomplete and a very obvious prototype but imo the formula kind of works, and I'm proud of what I've got going on the backend. 

Not a ton of major changes since the last update, main thing is that I really nailed out bugs with the logic system and it's really cool. Another cool thing I realized is that I could wrap up standard coroutine-y stuff into their own "logic" nodes that exist in the graph, meaning I can emit and wait on animation. events directly inside the execution graph.

The major feature thing I did was to get inventory / drops / loot-ability working, all with the logic system. When something dies, it checks its droptable to see if it has a valid drop. That gets dropped in place where the thing died. The only example I have in game is that enemies can drop food when they die.

That food is marked as lootable, meaning that when it discards it goes to the player inventory instead of discard.

Then, as the food is marked "eat", when you "use" it your inventory you gain fullness, and then the card is sent to the graveyard.

All this works with the same logic tree/graph, and I didn't need to code any of the "system". I just implement eat / use indepdendently, and it all works together based on the data in Depot.

I also ended up implementing stackful coroutines for something that I ended up not using, but they're now working in the engine!

All in all, really glad I took the time to work on this for 7DRL. It was both a great test of my engine, as well as helped me really hone in on something I've been wanting to program for a while (the rules engine).

Give the game a try and let me know what you think!

https://kylekukshtel.itch.io/mystery-dungeon-hand