Status
SteamPeaks
ChartsSalesUpcomingPatchesNewsCalculator
New on SteamEvery app, DLC and depot the minute Steam creates itAppsEvery app on Steam, newest change firstPackagesSubs and bundles, and what each containsDepotsDepots, manifests and install sizesTagsSteam's user tags and the games under themDevelopers & publishersCompanies and their cataloguesTechnologiesEngines, SDKs and anti-cheat found in the filesChange historyEvery PICS changelist as it lands
SignalsNineteen readings of the whole catalogueCompareAny games side by sideRecordsAll-time peaks and the days they were setReportsWeekly and monthly write-upsAlerts & newsroomWatch a game, get told when it movesSteam statusIs Steam up, right nowWeb API explorerTry the endpoints in the browser
NewsSteam's own announcements and the sales calendarCalculatorWhat a Steam account is worth, and its pile of shame
/
Sign in
/
SteamPeaks
The ultimate resource for Steam data.
ExploreChartsSalesSales and festsUpcomingPatchesNewsRecordsTrendingSignals
DatabaseAppsPackagesDepotsTagsDevelopersTechnologiesChange historyMasterworked.GG
ToolsCalculatorCompareSearchAlertsSteam statusAPI
SiteMethodologyFAQDiscordSupportSign in via Steam
Not affiliated with Valve or Steam. Game names and artwork belong to their owners. All times UTC.
PrivacyCookiesFair useStatus
Warlords Battle SimulatorNewsDevelopment blog: Making a roguelike map movement system
Community

Development blog: Making a roguelike map movement system

Warlords Battle Simulator · published 6 Feb 2024, 13:12 UTC

All newsPlayers around this dateRead on Steam

Because whenever I read any kind of writeup, I love to see where we're heading straight away, so here's what we're trying to achieve:

In this thread, I will discuss how we created a procedurally generated roguelike map and my thought process going into it. This isn’t really a tutorial, more of a post about how I got to it, how I did it, and why I did it this way. I’m not saying that my way is the right way, nor that it is the most performant way (definitely not), but I’m hoping it might be useful to some of you who are new to game development. This is a relatively short write-up, where you’ll need to fill in some of the blanks, because again, it’s not exactly a step-by-step tutorial. Lastly, before we start, I’ll be often referring to people by their nicknames because it’s much easier than typing DESIGNER, CODER etc. So. Kiwi (designer), Gothim (programmer and UI guy) and me (Komisarek, a code monkey).

Defining the requirements


One of the most important stages before I even got to coding was defining the requirements needed for the entire thing to work. We had three people working on this, me doing the generation, one colleague doing the UI and all the code associated with displaying the map (which means he’d be using my data to create the visual map), and a designer who created all the requirements for this task. This post will focus mostly on the generation part.

Generation requirements were:

  • The map needs to be divided into X number of acts, with each act having between Y and Z MOVES. A move represents a step forward in the act, onto a node
  • The map will start with a single “Start” node and a single “End” node
  • Except for the above, each node has a type. It can be one of: “Shop”, “Event”, “Combat”
  • Each move can have between 2 to 3 nodes for the player to choose from
  • Nodes can only be connected diagonally, and in straight lines. For example node 2 in a move with 2 columns, cannot connect to node 1 in 3 columns (because it’s too high).

We don’t care about what happens when we step on a shop for now, or anything like that – implementing this will come later. For now, I visualised what we needed, and came up with more or less something like this:

Coding


I started with creating an “Act” class. This is a simple class that holds the basic data, which the designer can modify.

public class Act
{
public Vector2 Events;
public Vector2 Shops;
public Vector2 Moves;
}

He can set a min and max number of Events, Shops and Moves in each act. I could implement some gatekeeping logic here to prevent errors (like no max moves less than max shops + events), but I decided that it’s simply not necessary. It’s something that Kiwi, can keep in mind himself.

Then, of course, I created a List that holds the acts, so he can add as many or as few as he likes. We’d use this list to generate the map.

Now that we had the data we needed (num. of acts, moves, shops etc…) we could get to generating.

Next part was to create the data structure for nodes and columns (moves). The idea is that each column will know which act it is in, which move it represents, and has a list of nodes that are on it.

public class RGNodeColumn
{
public int Act;
public int Move;
public List<RGNode> Nodes = new List<RGNode>();
}


Each node has a type. If we wanted to be SUPER DUPER ROBUST we could create a base class, and then derive others from it. Like ShopNode, BattleNode etc… but let’s be honest. This game is not massive. I decided to just do it with an enum.

public enum NodeType
{
Start, Unassigned, Combat, Shop, Event, End
}

public class RGNode
{
public bool Victory = false;
public NodeType NodeType = NodeType.Unassigned;
public RogueArmyData ArmyData; //holds the enemy army on this node
public BattleReward Reward; //this is the reward structurepublic string GetName() {} //returns localised name
public string GetDescription() {} // returns localised description
public string GetCombatRewardDescription() {} // returns description of rewards
}

I know what you’re thinking, not every node will need army data or a reward… that’s fine. I’ll just ignore them when necessary. My room is a little bit messy, nothing will happen if this is a little bit messy too!

So, to the actual generation part. I created a monobehaviour called RGWorldGen. It holds a list of Nodes and Columns, so all data should be available when Gothim wants to ultimately access it, when creating the visual map. We also have a static instance (yes, singleton!)

public static RGWorldGen instance;
public List<RGNodeColumn> Columns = new List<RGNodeColumn>();
public List<RGNode> Nodes = new List<RGNode>(); private void Awake()
{
if (instance == null)
instance = this;
else
{
Destroy(gameObject);
return;
}
}

So I’m going to paste a bunch of code now, it’s all pretty simple. But just in case I added lots of comments in. Idea is pretty simple: Generate the world by going through X acts, and for each of those create as many columns as there are moves. We also assign node types here.

Mind you, this is quite long, so I threw it on pastebin. There is no syntax highlighting here and too much white reminds me of the times I used to edit code in notepad. I don't like those memories.

Now that we have the data structure generated every time we fire up the roguelike mode, we can start playing around with it.

First of all we need to give UI easy access to the following:

  • The current node the player is standing on
  • The node the player wants to travel to (destination node)
  • List of visited nodes (so we can draw connections between them in different colour)


We also need to get connected nodes to each node. You might remember I mentioned the connections early on in the post. I REALLY, REALLY could not be bothered to work out the algorithm for this, even though I assume it’s really simple, so I just… hardcoded it. IT’S FINE, IT WORKS.

pastebin link!

I'm not going to give you too much detail on the UI side, because that's a whole another write-up, but the final result looks more or less like this:

Let's cover some of the important things you might be curious about though:

Node icons


Since we have node types we can easily assign icons to Shops and Events, as well as Start and End (they are always the same). Each COMBAT node has an ArmyData object you might have spotted before. This object also contains an enum for the enemy race. We set an icon based on that enum.

public enum Race
{
Greenskin, Undead, Barbarian, Kingdom
}

Node descriptions


Each node also features a description. The RGNode class contains a method called GetDescription which returns a string. We have a hardcoded string for each race, such as "battle_desc_greenskin", or "battle_desc_barbarians", and of course for all other node types, for example "ui_prompt_info_end_desc". The localisation manager returns the localised string from an XML file.

The icons are hardcoded sprites as well. If you're curious, you can see the code for that HERE

You can see the final outcome in action, here: https://www.youtube.com/watch?v=BYEtN7qMyl4

Lastly, if you liked this post, please follow the game's development, and make sure to WISHLIST here!

More from Warlords Battle Simulator

Other announcements

All news
Game update3 Jan 2025New Year update!Hi everyone. Just a little follow up from the last update. · Heroes will now have trophies next to them after beating bosses. · Orc and Undead perks now stack correctly (before this patch secondary bonuses were not applied correctly). · Difficulty levels will now have perks and penalties applied correctlyGame update2 Jan 2025Minor PatchHi guys. Just a minor patch rollout that mainly tries to address issues some players have with unlocking content. How it worked previously: Pretty much everything was based on Steam achievements. If you did a campaign mission that unlocks a hero, you'd get an achievement, and that achievement was checked for when seeing if your content is unlocked. This currently doesn't work for some users (no…In-game event31 Dec 2024Difficulty Modes in Adventure!Hi everyone. A brand new patch (last this year, I hope, as it's already 16:46 here and I want to get off my PC) just hit the floor. We've added the very first iteration of difficulty modes in Adventure Mode . This is very much a TRIAL before the main thing rolls out. For now, you're able to select the difficulty between Normal (current version), Hard and Impossible. Impossible IS possible, alth…