Lesson 25.1 – Select a random monster at a location

A reader asked how to have several different monsters that could be at a location – and randomly select one when the player moves to that location.

This is a little complex, so I created this lesson, to show how.


Planning the change

Currently, the Location class has a MonsterLivingHere property, which holds a single Monster object (or nothing, if there isn’t a monster at that location).

We use that property in the World class, to set the monster at a location. We also use it in the UI code, to detect if there is a monster at the location – so we can hide, or show, the combat buttons. And, we use it to instantiate a new monster to fight – using the NewInstanceOfMonsterLivingHere function.


To allow different possible monsters, we need to change the PopulateLocation function in the World class, to let us add multiple monsters to a list, along with the percentage chance for them to appear at the location.

Then, in the UI, instead of checking that single Monster property, we need to check if there are any monsters in the Location’s monster list.

Finally, we need to change the NewInstanceOfMonsterLivingHere function to get a random monster from the list, when it creates the Monster object for the Player to fight.


Making the changes

Step 1: Change Location.cs


We add a new private variable (_monstersAtLocation) that is a SortedList.

A SortedList is like a List, but lets you store two values per entry. These can be any types of values: strings, integers, your custom classes, etc.

For this SortedList, we will want to hold two integer values.

The first value is the “key”. You cannot have two items with the same key. The keys will be populated with the monster ID. So, we cannot add an entry to the SortedList that has the same key value (Monster ID) as an entry already in the SortedList.

The second value is the “value”. This is where we will store the percentage chance that this monster appears, when the Player moves to the Location.


To populate the SortedList, we add the “AddMonster” function.

This checks if the key (Monster ID) already exists in the SortedList, using the ContainsKey function. If the SortedList already has an entry with that key, it will replace the value for that entry with the new value. If there is no entry with the passed key, the function will add this entry to the variable.


Next, we add a new HasAMonster property, that checks if there are any entries in the _monstersAtLocation variable.

Before, when we wanted to check if there was a monster at a location, we used “MonsterLivingHere != null”. Since we’re replacing the MonsterLivingHere property with the new logic, we need a new way to check for monsters at a location.


The third step is to change the NewInstanceOfMonsterLivingHere function. Now, we need to randomly select one of the possible monsters, from the _monstersAtLocation variable, and create an instance of it.

This code sums all the percentages, and selects a random number that is less than, or equal to, that sum. This way, the function can handle situations when the total percentages do not equal 100.

Then, it loops through the list of possible monsters – adding the monster’s percentage chance of appearing to a running total variable.

If the random number is less than the running total, the function returns the monster. If not, it loops to the next monster in the list.


Finally, we can remove the MonsterLivingHere property – and its parameter in the constructor.


Step 2: Change World.cs


We were setting the location’s MonsterLivingHere property in the PopulateMonsters function. Now, we need to change it to use the new AddMonster function.

To set the monsters at a location, pass in the monster ID and the percentage chance it will be selected.

If you wanted rats to appear in the farmersField 20% of the time, and snakes the other 80%, your code could look like this:


farmersField.AddMonster(MONSTER_ID_RAT, 20);

farmersField.AddMonster(MONSTER_ID_SNAKE, 80);


STEP 3: Update the code that was using the MonsterLivingHere property.


There were three other classes that used the MonsterLivingHere property. We need to change them, to use the new property.



Inside the SetTheCurrentMonsterForTheCurrentLocation function (line 385), change this line:

To this:


SuperAdventure.cs (in the Windows Form UI project)

Inside the PlayerOnProperyChanged function (line 150), change this:

To this:


Program.cs (in the SuperAdventureConsole project)

Inside the AttackMonster function (on line 229), change this:

To this:


If you’ve modified the program, with your own changes, do a search for any other code that uses “MonsterLivingHere”, and replace them with the new property, or function.



When you modify an existing program, you need to find all the places where the current code is used. Then, plan how to get them to all use the new code.

Sometimes, this can be difficult – especially in larger programs. However, the time you spend in preparation will almost always save you more time, once you begin making the changes.


Source code for this lesson

Source code on GitHub

Source code on Dropbox


Next lesson: Lesson 26.1 Displaying a World Map

Previous lesson: Lesson 24.1 – Make the SuperAdventure source code easier to understand and modify

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

39 thoughts on “Lesson 25.1 – Select a random monster at a location

  1. This is great stuff!! I am thinking about adding a map you can open where it will populate as you travel throught the world.. And also a “delete save” function. So you can start over.

    I really have enjoyed these! πŸ˜€

    Thank you so much for the effort.

    1. You’re welcome!

      I’ve seen a couple other people modify the game to show a map. But, I haven’t seen one where it only shows the locations the player has visited. That would be a cool feature to add!

  2. Hi!
    I Like your tutorial. It is Great.
    Here is my thoughts to my future improvments:
    I want to add a NPC class, and add them to locations.
    NPC can give you a quests.
    I want to add something like dialogue system, through that system player can take a quest from NPC.

    P.S. sorry for my English, I’m from Russia =)

    1. Hello Den,

      Thank you. I’m glad you like the tutorial. I will think about how to add a dialogue system for an NPC. However, I won’t be able to work on it until April (I have a big project I need to finish this month). If you want to start looking for ideas before then, you might look for code about “state machines”.

      P.S. Your English is good. It is better than my Russian. πŸ™‚

  3. Thanks! I’ll try to understand and realize state machine.
    I have another one question.
    My game grows in size, the number of items, monsters, locations increases. So I thought, is it correct to keep the identifiers of all objects in constants? How is it stored in big games? After all, as the content of the game increases, the number of constants will grow and it will be easy to get confused in them.

    And second question.
    I want to move all my world description in the xml, and add this xml file to resources.
    How do you thnk, it is good idea, or not?

    1. You’re welcome! I’ll answer your second question first, because it is easier. πŸ™‚

      Yes, using an XML file (or database) would be a good way to build a larger game – more items, monster, locations, etc. You can use Visual Studio to edit the XML file. It can help ensure the XML does not have errors.

      For your first question, you do need to have a unique identifier for each monster/item/quest/etc. The large games also use IDs the same way. If you store the World data in an XML file, you will still need to use an ID. However, you could remove the constants from the World class. We only need the IDs in the World class to help us connect things – like which quest to have at a location, where we use “alchemistHut.QuestAvailableHere = QuestByID(QUEST_ID_CLEAR_ALCHEMIST_GARDEN);”. In the World XML file, you could manage that the same way the Player XML file manages its inventory and quests. Each Location node could have a “child” MonstersHere node, which has “child” nodes for each Monster (with its ID). This would look like the /Player/InventoryItems/InventoryItem nodes in the Player XML.

      Do you see how that could work? If not, please tell me.

      1. I was thinking in this direction. I understand how this should work, so I just confirmed my guesses =).

        Sorry for bothering =)

  4. Hi Scott, I get this error after this lesson, and I can’t solve it :/
    Error CS1061 ‘Monster’ does not contain a definition for ‘NewInstanceOfMonster’ and no extension method ‘NewInstanceOfMonster’ accepting a first argument of type ‘Monster’ could be found (are you missing a using directive or an assembly reference?)
    Can you help me?

    1. Hi Daniel,

      Check that your Monster class has this change from Lesson 24.1. It adds the “NewInstanceOfMonster()” function that creates a new instance of the Monster, with all its hit points, and new (random) loot. If that is already in the class, make sure it is set to “internal”, so other classes in the project can use it.

      If that does not fix the error, please let me know.

  5. Thanks, i did it and then the program runs, but i didn’t do the 24.1 completely before 25.1 so the battle button appear when im in a no monster area, and also no monster spawn where they should… So i reverted back to an older version.
    I don’t want to copy&paste SuperAdventure code because a changed a bit. I find it a little difficult to implement 24.1 and 25.1 (code has changed a lot in two lessons) without having to ruin the whole thing.
    Can you do a tutorial about show an item sprite before the item name in the inventory and vendor inventory?
    Sorry for my ban english!

  6. Your tutorial are grate. Thanks you so much. You opened my way to become a good programer. I think to start to learn how to use unity now. Maybe in one day you will play my own game.
    Good luck in all what you do.
    And sorry if my english is bad.

  7. Hi Scott,

    I’ve found it really hard to work through Lesson 24.1 – I didn’t even realize I had missed a bunch of changes until I had errors in 25.1, then saw the prior post you made about needing more changes. That code dump given in 24.1 is brutal, going back to it, because there are some very large files that only have sporadic, few changes.

    It would help a lot on these lessons, I think, if the code files showed a diff view of the old and new code, so we could zero in on the areas that have changed. I don’t like just copying in the entire file because then I don’t learn – I much prefer having to manually enter in every change, but manually searching to find the changes is rough.

    That being said, these tutorials are great! Thanks for developing them.

    1. You’re welcome Mike,

      That’s a good idea – showing the changed code for each lesson. I haven’t seen a good tool to do that on the web pages and videos. I’ll look around and see if I can find anything for future lessons.

  8. Hi Scott,
    I been trying to modify the spawning to have a chance to not spawn a monster and that chance to be different on every tile of the map. You think, you can help out

    1. Hi William,

      I can try helping. To get you started, did you add a new property to the Location class (let’s call it MonsterApperanceProbabilty), to store the percentage chance a monster appears? If so, you can make a change to the NewInstanceOfMonsterLivingHere() function, in the Location class. After checking if the monster is null, do another check where you get a random number from RandomNumberGenerator. If the random number is higher than MonsterAppearanceProbability, then return null – since that would mean no monster appeared.

      Let me know if you make that work, or if you need a little more help.

  9. Hi Scott,
    I have got it working, but due to a different bug. It constantly is spawning after a monster is killed or in this case not spawning. I looked and looked all of the code link with the monster and i believe it matches but I seem to be overlooking something; if you do know why and how i can fix it, I would appreciate it.

        1. I created a Gist page with the changes that should fix the problem: https://gist.github.com/ScottLilly/7ce401c77b6942cb4d40e067222fede3 There are changes to Location.cs, World.cs, and Player.cs.

          In Location.cs, I added a new property “MonsterAppearanceChance” on line 16. This holds the percentage for if a monster should appear. In the NewInstanceOfMonsterLivingHere function, I added some lines (57-71) to check if there is a value in MonsterAppearanceChance. If there is, the program gets a random number between 1 and 100, and sees if that number if lower than the MonsterAppearanceChance. If it is not, it returns null. If it is, it continues with the rest of the function, to create the monster object.

          In World.cs, I added line 115 – to set a value for the MonsterAppearanceChance property for the Forest location.

          In Player.cs, I made changes in the movement functions (lines 225, 241, 257, and 273). These need to check the CurrentMonster property instead of the HasAmonster property, because it is now possible that HasAmonster is true (because it checks if there are any possible monsters at the location), but there is not really a monster there (because of the new MonsterAppearanceChance test).

          Please tell me if you see any problems with those changes, or if you have questions about them.

  10. Hey Scott,
    It works perfectly, I just like to thank you. I just need to set the attack button to turn off after a creature dies now, so a errors doesn’t pop up when there is no monster to hit. Thank again.

  11. Morning Scott!
    Could you explain what this thing does?

    1)_monstersAtLocation[monsterID] = percentageOfAppearance;


    _monstersAtLocation[monsterID] = percentageOfAppearance;

    1. “_monstersAtLocation” is a SortedList. So, each entry in that “_monstersAtLocation” holds two values – a “key” and a “value”. Think of this like a shopping list, where each entry stores an item and a quantity, like this:

      Shopping List
      Eggs 12
      Pizza 1
      Coca Cola 6

      If we created a SortedList for the shopping list, it could be: “SortedList _shoppingList = new SortedList();” – because the key for our shopping list (the item we want to buy) is a string, and the value (the quantity we want to buy) is an integer.

      If we want to change the value for the “Pizza” entry from 1 to 2, we would use code like: “_shoppingList[“Pizza”] = 2;”. If we wanted to know the quantity of eggs, we could write “int quantityOfEggs = _shoppingList[“Eggs”];” That would get the value of the entry with a key of “Eggs”.

      Does that make sense?

      1. Sorry for silly question but i have a very stupid problem..
        I created a Windows Form inside SuperAdventure with a wish to create a Menu,
        but when i press F5 only SuperAdventure.cs is opening, not a starting menu from which i want to open SuperAdventure with a button.

        1. Or when i’m trying to connect a button to open a SuperAdventure form from the Menu like that
          SuperAdventure superAdventure = new SuperAdventure;
          The errors say that SuperAdventure cannot be used like a type, while we could do the same with the WorldMap.
          I guess it’s because the SuperAdventureForm is like the main Form in the project that cannot be created from other forms ?

          1. Do you have:
            SuperAdventure superAdventure = new SuperAdventure;
            SuperAdventure superAdventure = new SuperAdventure();

            You need to use the second one, with the parentheses, because you are instantiating a new object – the SuperAdventure form object.

        2. If you want your UI project to start with a different form, you need to change the project’s Program.cs file. Look for the line that says “Application.Run(new SuperAdventure());” and change it to “Application.Run(new YourNewForm());” – using your new form’s name for “YourNewForm”.

      2. I was trying to add some new fetures but i faced a little problem with this:

        1) in the button use for weapon we have :
        // Get the currently selected weapon from the cboWeapons ComboBox
        Weapon currentWeapon = (Weapon)cboWeapons.SelectedItem;

        2) BUT inside the the comboBox of the Weapon we have it :

        private void cboWeapons_SelectedIndexChanged(object sender, EventArgs e)
        _player.CurrentWeapon = (Weapon)cboWeapons.SelectedItem;
        3) The question is why we have CurrentWeapon when we have currentWeapon or insted why we have currentWeapon when we have CurrentWeapon?

        (Looks like my brain has a delay :C )

        1. In the first one, we are getting the currentlt-selected weapon from the combobox, to know what weapon we should use to attack the monster. In the second one, we are updating the player’s CurrentWeapon property, so it can be saved if the player stops running the game.

          We could eliminate the first one, and say “Weapon currentWeapon = _player.CurrentWeapon;” because the player’s CurrentWeapon property should always be reset when the combobox’s selected item changes. That would be a good “refactoring” to do, to clean up the code. There are many more places that could be improved in the program, if you want to continue expanding it.

          1. Hi Scott!
            I on last days i faced a problem that i tried to solve for 2 hours..
            I created form1 and created there

            public string NewOrNot1 {get; set:}

            and then in a button created

            NewOrNot1 = “Word That I want To Pass”;
            using (form2 formM2 = new formM2 ())
            formM2.NewOrNot2 = NewOrNot1; // in the second form i also
            } // created NewOrNot2 {get; set;}

            So i wanted to pass the string value to another form, and the program is not showing any errors but when i start and press the button the new form is not openings ( only 1 flick of something ). I tried formM2.Show() in a using. And i have in form2, form2.Show();

            if you need more information or the whole code i can provide you, i’m just writing now not from the best place.

          2. Try using “formM2.ShowDialog();” inside the “using”. ShowDialog will show the form as a “modal” dialog box. Or, if you only want to display a message (the form does not need to do anything else), you might want to use MessageBox.Show(), instead of a new form.

Leave a Reply

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