Lesson 26.2 – Hiding Unvisited Locations on the World Map

Now that we have a map, it would be nice to only show the images for the locations the player has visited. That is what we’ll add in this lesson.

 

NOTE: In the last lesson, there wasn’t an image for the Bridge location. That’s because I used the images from the WPF version of these lessons, and there is no Bridge location in that game.

So, if you finished Lesson 26.1, and you don’t have a Bridge image (and you don’t have six columns of PictureBox controls), please go back to Lesson 26.1 and make the changes to add the missing column. This will require downloading the location images again (to get the Bridge image), and changing the WorldMap form (to add the new column and display the Bridge image).

 

STEP 1: Add FogLocation.png to SuperAdventure\Images

After adding it, set its properties to:

Build Action: Embedded Resource

Copy to Output Directory: Do not copy

 

Right-click and select “Save as”, to download

 

 

Step 2: Edit Engine\Player.cs

We’re going to store the ID of every location the player visits. We’ll save the IDs in a new List property named LocationsVisited (line 69).

Because this is a List property, we need to initialize it, otherwise it will be null, instead of an empty List. We’ll do that in the constructor (on line 80), where we initialize the other list properties.

Now, when the player moves to a new location, we need to add its ID to the property – if it hasn’t already been added. We do that inside the MoveTo function, on lines 167 to 170. If the LocationsVisited property does not already contain the ID of the location, we add it to the List.

 

Player.cs

 

Step 3: Edit SuperAdventure\SuperAdventure.cs and SuperAdventure\WorldMap.cs

In order to display the correct image for a location (the fog, or the location’s image), the WorldMap form needs the current player object, to know which locations the player has visited. So, we need to pass it from the SuperAdventure form, into the WorldMap form – like we do with the TradingScreen form.

In WorldMap.cs, we need to add a Player parameter to the constructor (line 13).

In SuperAdventure.cs, we pass the current player when we instantiate the WorldMap form (line 225).

 

Now, we can hide the unvisited locations by displaying the FogLocation in the PictureBox for any locations whose IDs are not in the player object’s LocationsVisited list.

I’ve done that by using the ternary operator inside the calls to SetImage (lines 17 through 25). If LocationVisited contains the location’s ID, we pass the name of the location’s image file. If the ID is not in LocationsVisited, we pass “FogLocation”.

 

WorldMap.cs

 

SuperAdventure.cs

 

Step 4: Edit Engine\Player.cs

We want to remember the player’s LocationsVisited values between game sessions. So, we need to update the code that saves the player’s data to the saved game file – and the code that creates the player object from that file.

In the ToXmlString() function, we’ll add a new section that creates nodes with the ID values in LocationsVisited (lines 349 through 361). This is like the code to save the InventoryItems and PlayerQuests.

We create a LocationsVisited node, with a child node named LocationVisited, to hold the location ID.

In the CreatePlayerFromXmlString() function we add code to read those values from the saved game file (lines 116 through 121).

 

Player.cs (with changes to save/read LocationsVisited from saved game file)

 

Step 5: Edit Engine\PlayerDataMapper.cs

We also need to save the LocationsVisited values to the database, and read them when loading a saved game from the database – if you are using a database to save the game data.

To save the location IDs, we’ll create a new table named LocationVisited. It will only have a single column “ID”, whose datatype is “int”, and does not all nulls. The script to create it is below.

 

 

Next, we need to update PlayerDataMapper to save the values into this table, and read the values from it.

The code to do this is like the code for adding and reading the values for the InventoryItems and PlayerQuests.

 

We save the Location IDs to the table in the SaveToDatabase() function, at lines 286 through 307.

The code to read from this table is in the CreateFromDatabase() frunction, at lines 111 through 131.

 

NOTE: I noticed a bug with the readers not closing. So, inside each “using” block of code in CreateFromDatabase, I’ve added a “reader.Close();”. These are on lines 58, 85, 108, and 130.

 

PlayerDataMapper.cs

 

Step 5: Test the game.

Now, as the player moves to new locations, the map will display more images – instead of the “fog” image for unvisited locations. The map should start to look like this (for example):

 

 

Summary

This uses hard-coded values for placing the images in the PictureBox, which isn’t the best way to create a map. This would be much more flexible if we used X and Y coordinates for the locations. Then, we could do things like having the map always centered on the player’s current location, and showing a 5 x 5 (or larger) grid of the surrounding locations.

If you follow the “Build a C#/WPF RPG” lessons, that is how we are building that world.

 

Source code for this lesson

Source code on GitHub

Source code on Dropbox

 

Previous lesson: Lesson 26.1 Displaying a World Map

All lessons: Learn C# by Building a Simple RPG Index

12 thoughts on “Lesson 26.2 – Hiding Unvisited Locations on the World Map

  1. Hi!

    The same problem, that causes the Quest bug, adds the last saved location to the empty LocationsVisited list, and that causes duplicated rows in the LocationVisited data table after you start the program multiple times.

    I think Lesson 99.1 Bugfix will solve this problem.

    1. Correct, it should solve that problem. If anyone has duplicated locations in their data, they should probably delete the second (or more) instances of the locations. It might also be nice to create an AddLocationoLocationsVisited function (like we do for adding items to the player’s inventory) that would prevent adding a duplicate location. Although, it’s always best if we have code the doesn’t let the data ever get into a bad condition.

  2. Hi Scott! Thank you very much again i need help :C
    I managed to do everything and i was happy, then i got the final step of localisation so other programmers would understand the code in my contry, and i started… I was happy that everything worked and went away from the project for a while and then i realised that the project is not saving the player.. only creating new one every time when a user tries to play. I tried to pin the problem down but everything was in vein, the program doesn’t show any sign of mistakes but still doesn’t save the player. So i just tried to return all the names of the functions and methods and try again, there were no mistakes but still nothing new.. Could you pls help me with my final step of finishing the game. If you can’t spot the problem that’s all right i will just delete this function of saving from the game, i’ll be happy to any help!

    https://www.dropbox.com/s/dyf5bhm9tpkkmrn/ForgottenLand2.rar?dl=0

    1. I think I found the problem. In ForgottenLand.cs, starting on line 89, the “else if” code is probably not running the way you want. There are lines after the “else if”, but before the {} code block. When code is like that, the compiler pretends there is a pair of curly braces around the next one line – and not any lines after it.

      There are some notes here: https://gist.github.com/ScottLilly/8516a40ec115b3c512da096e2cf8b8ea

      Because there is not an “if” or “else if” before line 8 (in Before.cs), that code always runs. So, the program always creates a new player.

      If you want to see what is happening, you can use the debugger. Here is a lesson on how to use the Visual Studio debugger. You can set a breakpoint on line 89 and see what is happening when you run the program.

      1. Thank you! This is unbelievable how only one line can stop all the work of the application. it is difficult to imagine how people can work with multiple projects with over 1000 lines in it.

  3. I could not run the SuperAdventure because I was hitting an error about the Quests containing more than one entry for the same quest, ID = 1.

    I fixed the problem in my copy of the game by doing the following in the Player.cs file.

    Above the PlayerDoesNotHaveThisQuest method I added the following method:

    Then I changed the GiveQuestToPlayer method by adding an if statement at the top:

    There may be a better way of fixing this error, but this particular way worked for me.

    1. Hi Ferlin,

      There was a bug if the player exited the game at a location with a quest. When they restart the game, it gives the player the quest from their saved Quests list, and tries to give the quest again since they are at the location. Your fix will prevent the game from crashing – or, you can use the changes in lesson 99.1.

  4. I was looking back on the site and was surprised to see all the changes to the application and some integration with the WPF project! I was going to do some expansion and add images to the original program only to find that it has already been done! Great job on this Scott. It has been a real help to a lot of folks.

    1. Thanks Stan! This started out as a little project in my spare time, but has grown larger than I ever thought it would. It always great to hear from people who have learned something from the lessons.

      I just started a new series this week to refactor the SuperAdventure code into higher-quality code that will be easier to work with and modify.

Leave a Reply

Your email address will not be published. Required fields are marked *