Hello everyone, welcome back! This fortnight’s update is entirely about some huge developments on hunting down treasure in the game. First though, here’s where things currently stand on 0.11 development, with BLUE items being things which are finished and GREEN items being those that are partly developed. As you can see, things are cracking along nicely:
ITEM INTERACTIONS
- You can drop items
- You can pick items up
- You can use items (whatever that means for the item in question)
- You can destroy items (very rarely an item might have something hidden inside it…!)
- You can show items (e.g. to someone else)
RELIGIONS
- Generate the images for all 25+ archetypes of religious relics, and place them in religious buildings
- Update and transform things like religious beliefs, things religions dislike, information about religious histories, holy books, etc
- Enable the player to join and leave a religion
- Far more complex interactions with priests etc
- Develop proper model for “membership” in religions, nations, cults, etc
TREASURE MAPS
- Treasure maps spawn in shops just like non-treasure maps do
- Zoomed-out treasure maps correctly show relevant locations
- Zoomed-in treasure maps correctly show relevant locations (far more complex)
- High-value items are, indeed, under the ground where a map points!
- These will become, for now, the game’s primary objective (rather than, as they will be later, one aspect of many)
MORE OPTMIZATIONS / BUG FIXING / POLISHING
- I currently count 48 known bugs / issues – I want this down to 0
- Further speed up map generation
- Further speed up turn-by-turn rendering
- Try to speed up world generation as well?
Today though it’s all about treasure maps – for a game about solving procedurally-generated mysteries, puzzles and riddles, having the player be able to hunt down specific areas on such a vast world map is an essential part, and will be part of the win condition finally finding its way into URR in 0.11! As ever, if you enjoy these posts, please do think about sharing them around the internet, or on forums, or tweeting it out, or to fellow gamers who might be interested – it takes a heck of a lot of effort to write these things up and I really do appreciate it a ton.
Treasure Maps
It is with not a little pride that I can say this update is focused entirely on the initial implementation of working treasure maps. At time of writing the player can actually hunt down a location containing treasure, use a shovel to go at the tile in question, and dig up the chest in question! This is pretty fantastic and really starts to hit some of these core gameplay mechanics I’m interested in – deduction, puzzle-solving, finding and hunting down things across a vast and complicated world. In this section I’m just going to talk a bit about the process and the various aspects of getting treasure maps to work correctly in game – in wilderness areas, at least – and what went in to getting such a complex and demanding game mechanic to actually work.
So, the fundamental issue is this: how can we have the game generate a treasure map that correctly shows a location in an area that has not, itself, actually been generated yet? All the areas of the game world only generate when the player first enters them, but the player might get hold of a treasure map showing a treasure’s location long before that location is actually generated. Therefore, how can the game create a map that accurately shows a spot in a location that – in terms of the game’s code and storage – doesn’t actually exist at present, while making sure that when the actual in-person area is generated (by the player setting foot in it) there is something which looks like the treasure map that might have been generated hours, days, weeks ago? This took quite a bit of thought but I’ve found a number of possible solutions, and the first of these is to begin with treasures buried in wilderness areas. The good thing about these areas is that by virtue of lacking buildings and more complex things it is relatively easy to generate a map on some general lines for a given area of the world (an empty desert, a dense jungle, an ice sheet, etc) and then put the treasure map area seamlessly into the generated area later on. What I actually mean by this will become clearer as we go on – don’t worry!
The very first step is to get the game tracking treasure locations. As for what’s actually in them, that’s a more complex thing and can come later, but the first objective is to get the game selecting locations on the world map (and then figuring out locations on the local, human-scale map) where something is buried, and make sure the game tells the player that they have indeed found something when you use a shovel or a pickaxe on the area in question. To begin with the game now selects three locations in each quarter of the world map, and ensures each location is pure wilderness, i.e. contains no settlement or structure or any type, but also doesn’t include mountains or volcanoes or things of this sort. Since rivers and coastlines don’t take up a lot of a map grid on the human scale those are still considered valid for the generation at the moment. Once it has selected these tiles a new list is then filled up with the world map x/y coordinates for this location, and the game has chosen its wilderness areas to place treasures in. Here is an example of a world map where I’ve highlighted the locations that the treasure maps are pointing at:
I then spent about half an hour on one day going around all the terrain types, both with and without a “forest” on them (snow biomes can’t have forest, while in desert it registers as an “oasis”) to take close note of exactly how packed things like trees and flowers and the like are in each one. This is important because I don’t want the game to generate treasure locations that look any different from the rest of the map grid, otherwise a player even without a treasure map might notice and start digging. They need to blend in perfectly, in other words. Once I’d established what a normal area in each possible climate area both with and without forest cover and the like looked like, I had a good sense of roughly how many trees should appear on a treasure map, for example, or how many plants should appear. This was of course modified and modulated according to the size of the treasure maps – at present they can display 11×11, 8×8, or 5×5 human-scale map tiles – and this yielded a debugging output which looks something like this for the various maps:
With this in place, the game can then create a treasure map item in the game which randomly selects one of the treasure map grids created at world generation and puts it into physical form, with each of these letters representing a symbol that might appear on the map. Here, for example, is me checking out a map I’ve just bought:
And here’s a number of treasure map grids I purchased shown alongside their associated treasure maps:
At the same time the game can of course still generate a world-scale treasure map (rather than a human-scale treasure map) showing the map grid that treasure might be found on, and these look like this:
So the game is now generating maps that are correctly pointing at where something buried can actually be found. The world-scale maps show you which tile on the map you should be looking at to find some treasure; the human-scale maps show you exactly where on that map tile you will find the thing you’re looking for – even though that place hasn’t actually been generated yet! So now we face the challenge of making sure that whatever’s shown on the treasure map actually appears when generating the map grid it’s supposed to appear on. By starting with wilderness treasure locations I have been able to pursue a (relatively) simple model here, because I know a wilderness map grid is essentially 200×200 tiles of randomly-placed trees, plants, hills and elevations, and so forth. The idea is then to generate the map, and then once the map is generated the game checks whether a treasure map points to this map grid or not. If the answer is “no” then the game just moves on, but if the answer is “yes” then the game pauses and selects the appropriate location on the map grid for the treasure location to be. Once it has found the right x/y coordinates that were selected for each treasure at world generation (so maybe a treasure is on map tile 124/51, and within the 200×200 grid of that map tile, it’s on human-scale tile 39/101), it then transforms whatever had been randomly placed there to make it match the treasure map. This is the key secret, essentially – building a system to transform a part of a wilderness map grid into something that matches the treasure map, so that from the player’s perspective it looks like it was always there! Here’s a pic of the game identifying the correct area:
So, to do this the game selects the location and then takes a look at the 11×11, 8×8, or 5×5 area that needs to be transformed to match the map. It deletes any plants in the area before then adding in any plants shown on the treasure map. It then moves onto the far more complex part of the whole thing which is adding trees, as these as multi-tile structures and you can have multiple tree canopies, for example, on the same map tile. The game looks for any trees already in this area and deletes them, before then working through all their leaves and deleting those as well (so that leaf tiles aren’t just floating above the ground with no tree trunk). The game then checks for foliage tiles in the treasure grid area that don’t show up on the treasure map and recursively works through all the tree foliage tiles until getting back to their tree of origin, deleting the tree, and deleting all the remaining foliage tiles around it. This is a slightly weird thing to have the game do and took a bit of time to figure out (and some time to refamiliarise myself with the relevant code) to ensure it doesn’t delete the wrong bits of tree / foliage, and that it does completely delete what it’s meant to. This then clears out the required area of trees and plants, and has added the required plants. The final step is to add whatever trees are shown on the treasure map, which just uses the normal tree placement system, and then as if by magic a part of the wilderness map grid has been transformed to look like the area on the treasure map, but because it’s all wilderness, the area doesn’t look out of place at all! Without the treasure map in hand the player would just walk past it without a second thought.
Here, for example, are some areas containing a treasure map area – can you tell which area has been snuck in after generation, and which areas were made by the normal map generation? Without a treasure map to hand, I certainly can’t.
But with a treasure map, however…
I can see where the treasure is in each case! And then you take your shovel, ‘u’se your shovel…
…and voilà!
And it also leaves behind a hole where you have dug, as we noted last time:
So, you dig up a chest which you’ve been able to hunt down, on a single map tile, in a game world of 2,500,000,000 map tiles. If you ask me that’s pretty damned cool, especially as we begin to combine it with more types of clues, more types of hints, stuff to actually be found in the chests (a rather important part!), and so forth. But right now the game can place treasure locations in wilderness areas, and create a logical treasure map for that place, and ensure the actual location looks correct when the player finds it, even if it hasn’t been generated at the time of the map’s creation! This is now the basis for all the local-level treasure map generation. Others – such as those involving being inside settlements and those involving being inside buildings – are going to be far more complex, but the foundations are there. I’m so excited by this and I hope I gave a good sense of how the whole thing works at the moment. I’ll be coming back to treasure maps throughout this release development, but this is a tremendous start for the time being.
You’ll also notice that I decided to go back and make the treasure maps look a little bit “richer”. For the world map level maps I think these quite sparse and subtle maps work nicely, but for the human-scale maps I think that much denser colours are very attractive and really work well. I therefore went back into the print_map function and put in quite a bit of extra work on the map graphics, and eventually ended up with a generator that was only lightly modified from the previous one, but I thought created maps that looks much more intense and vivid (and removed the possibility of any confusion between the world-scale and the human-scale treasure maps).
And so, there we have it! There will be many other ways to hunt down hidden items – saying code phrases to NPCs, solving riddles, and more – but for now, this is a tremendous development in the game and really starts to get some of this core procedurally-generated puzzle solving coming together for the first time. I’m really excited to continue building this more and more so that 0.11 will finally have a proper, core, objective, upon release! Thanks so much for reading this everyone.
(And as a final, final point – I plan to have the plant colours in the treasure map reflect the actual plant colours on the treasure area, but that’s the one thing I haven’t yet done here. Soon, though!)
What next?
More relics, more new religion detail, more new item interactions, more treasure maps, more bugfixing – see you all next time! (Update: comments are closed due to spam on this particular entry, please leave a comment on another post instead!)
Hey Mark, love this update. Treasure maps continue to be a feature I’m very excited about.
Just curious, because I constantly ponder both game design and procedural generation when I daydream about projects I may one day actually work on, why did you take a reversed approach to the treasure map sites as opposed to using the procedural generation to do the work for you?
For instance, what if you had just picked those three locations in the corners of the world, generated that world tile only, took a “snapshot” of a certain area in that tile, then used that snapshot for the map? Then you just save the pre-generated tile to disk and load it in, “skipping” generation for that tile while moving onwards. If you’re using the same seed all the way through, in theory, it should even naturally generated all around afterwards without creating an artifact in the terrain.
Was wondering if you just thought your method would be easier to implement or if there was some other consideration. Love the project as always and keep it up!
Eric, thank you! Yes, me too, and I will have some other equally exciting stuff on this front to reveal in a few updates’ time, so do stay tuned for that :).
Yes, that’s a really good question. The main reason is because all the generators for all the places, areas, lands, buildings, settlements, etc, were made *before* I then went back and made the treasure maps. This meant that if I wanted to do it the other way around, I would have to potentially make serious changes to every single possible generator, and abstract that code away from where it’s currently used to generate these areas into something more abstract which can generate maps without actually generating the physical terrain it’s on. So much of the code goes the generating by reading from the *actual terrain* that the combination of these two would have meant a genuinely towering amount of extra work! I’m not averse to very demanding things if the outcome is going to be great, of course, but this method just seemed infinitely easier.
I also thought about having the game pre-generate treasure locations, but then the player could look in the save files on their hard drive and see that “124×55” had been generated, so they’d know that that map tile must have treasure on it. So that one also had to go in the bin. As such, this is the slightly strange system I settled on in the end!
Couldn’t you generate the treasure tile, use that to create the map, then “ungenerate” the tile again, so it doesn’t appear in the save file? Assuming your generators are running on a specific world seed, when the player does visit the tile, it should generate the same way? I suppose it could look different when generated in a different order to adjacent tiles?
Hi Jeremy, thanks for the comment! I… suppose, actually, yes, that would be another way to do it, but you’re right, depending on the sequences that things are generated in things might indeed be different the next time around. It would need less refactoring than some options, but would still need a signifcant amount of refactoring to ensure that the generator works the same each time around depending on when you enter a given tile.
Finding a new URR post is always a highlight! I check often! 🙂
This treasure map update sounds (and looks) amazing. You ascii assets remain the best I’ve seen….and they’re procedurally generated!? Very, very cool.
I enjoyed hearing the journey to solving the map being generated well before the area it describes. From your grabs, it certainly looks seamless to me.
Assuming this ain’t hurting you, know that we’re cheering you on. Keep it up, good doctor!
Thank you Shane! I so appreciate the kind words, thank you. Really glad you like them. And in the past I did feel a bit of “pressure” but actually it’s the opposite these days, any kind of positive feedback really encourages me and gives me more energy for coding, so it’s all good and very much appreciated :).
I like the look of those treasure maps! Really cool ancient treasure quest feel. 🙂
By the way, as I was catching up on a few of your posts today I spotted a number of code screenshots where there seem to be several hefty usages of long if..elif chains. I’ve been refactoring some of my own code recently to apply the Command design pattern, which is essentially just about having small data container classes for item types (e.g. Arrow, Book, etc) and then a polymorphic interface that these all implement that point to the command methods they all have in common (e.g. Describe, Use, Throw). So the calling code wouldn’t need to know what the item actually was, it just knows (thanks to the interface) that it can call Item.Use() and have it do something. Command pattern is great for chopping down lengthy enum switches and if..elif chains, so I thought I’d mention it since it’s fresh in my mind and it might be worth keeping in mind next time you do refactoring.
Thanks so much for the comment Shannon! I’m so glad you like them.
Yep, absolutely, that is one of the weakest parts of my coding – I am still a fan of the long if chain! I tend not to do much refactoring unless it actually seriously affects performance; I acknowledge the value of clean code and my modern code is better – while still not being outstanding – but for me the priority has always been speed of coding + speed of game, rather than elegance of code. I guess the other thing is that there are so many interwoven pieces that a refactor can often break something which presently works, and that generally discourages me as well unless there’s a clear performance issue. I have been doing some big refactors recently for speed, though, and I still have this mad idea that one day I might literally just hire someone, freelance, to spend a few months cleaning the entire code…!
Sure, absolutely, the process that works for you is the process that works for you. I have my own coding quirks, one of which is probably going too far in the other direction of polishing/optimising my code obsessively, which comes at the expense of losing some of that “move fast and break things” productive speed. To each their own! And the heralding of a mythical freelancer to save us all in the end.
(I really, really wanted to work a treasure map metaphor in there somewhere, but my mind is blanking on me lol.)
“move fast and break things” – you have described my coding style to a tee, honestly! I do rarely get into a mood for polishing, but for me I think it has to be “polishing that leads to a meaningful improvement in performance”, whereas polishing to reduce the number of lines, for instance, or make it just cleaner or more efficient without a noticeable difference for the player, I definitely find tougher to persuade myself to do. That said though, I found a lot of the speed optimization for the previous release *very* gratifying, and I hope to get some in this one, too…