Press "Enter" to skip to content

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

using System.Collections.Generic;
using System.Linq;
namespace Engine
{
    public class Location
    {
        private readonly SortedList<int, int> _monstersAtLocation = new SortedList<int, int>();
        public int ID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public Item ItemRequiredToEnter { get; set; }
        public Quest QuestAvailableHere { get; set; }
        public Vendor VendorWorkingHere { get; set; }
        public Location LocationToNorth { get; set; }
        public Location LocationToEast { get; set; }
        public Location LocationToSouth { get; set; }
        public Location LocationToWest { get; set; }
        public bool HasAMonster { get { return _monstersAtLocation.Count > 0; } }
        public bool HasAQuest { get { return QuestAvailableHere != null; } }
        public bool DoesNotHaveAnItemRequiredToEnter { get { return ItemRequiredToEnter == null; } }
        public Location(int id, string name, string description, 
            Item itemRequiredToEnter = null, Quest questAvailableHere = null)
        {
            ID = id;
            Name = name;
            Description = description;
            ItemRequiredToEnter = itemRequiredToEnter;
            QuestAvailableHere = questAvailableHere;
        }
        public void AddMonster(int monsterID, int percentageOfAppearance)
        {
            if(_monstersAtLocation.ContainsKey(monsterID))
            {
                _monstersAtLocation[monsterID] = percentageOfAppearance;
            }
            else
            {
                _monstersAtLocation.Add(monsterID, percentageOfAppearance);
            }
        }
        public Monster NewInstanceOfMonsterLivingHere()
        {
            if(!HasAMonster)
            {
                return null;
            }
            // Total the percentages of all monsters at this location.
            int totalPercentages = _monstersAtLocation.Values.Sum();
            // Select a random number between 1 and the total (in case the total of percentages is not 100).
            int randomNumber = RandomNumberGenerator.NumberBetween(1, totalPercentages);
            // Loop through the monster list, 
            // adding the monster's percentage chance of appearing to the runningTotal variable.
            // When the random number is lower than the runningTotal,
            // that is the monster to return.
            int runningTotal = 0;
            
            foreach(KeyValuePair<int, int> monsterKeyValuePair in _monstersAtLocation)
            {
                runningTotal += monsterKeyValuePair.Value;
                if(randomNumber <= runningTotal)
                {
                    return World.MonsterByID(monsterKeyValuePair.Key).NewInstanceOfMonster();
                }
            }
            // In case there was a problem, return the last monster in the list.
            return World.MonsterByID(_monstersAtLocation.Keys.Last()).NewInstanceOfMonster();
        }
    }
}

ย 

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

using System.Collections.Generic;
using System.Linq;
namespace Engine
{
    public static class World
    {
        private static readonly List<Item> _items = new List<Item>();
        private static readonly List<Monster> _monsters = new List<Monster>();
        private static readonly List<Quest> _quests = new List<Quest>();
        private static readonly List<Location> _locations = new List<Location>();
        public const int UNSELLABLE_ITEM_PRICE = -1;
        public const int ITEM_ID_RUSTY_SWORD = 1;
        public const int ITEM_ID_RAT_TAIL = 2;
        public const int ITEM_ID_PIECE_OF_FUR = 3;
        public const int ITEM_ID_SNAKE_FANG = 4;
        public const int ITEM_ID_SNAKESKIN = 5;
        public const int ITEM_ID_CLUB = 6;
        public const int ITEM_ID_HEALING_POTION = 7;
        public const int ITEM_ID_SPIDER_FANG = 8;
        public const int ITEM_ID_SPIDER_SILK = 9;
        public const int ITEM_ID_ADVENTURER_PASS = 10;
        public const int MONSTER_ID_RAT = 1;
        public const int MONSTER_ID_SNAKE = 2;
        public const int MONSTER_ID_GIANT_SPIDER = 3;
        public const int QUEST_ID_CLEAR_ALCHEMIST_GARDEN = 1;
        public const int QUEST_ID_CLEAR_FARMERS_FIELD = 2;
        public const int LOCATION_ID_HOME = 1;
        public const int LOCATION_ID_TOWN_SQUARE = 2;
        public const int LOCATION_ID_GUARD_POST = 3;
        public const int LOCATION_ID_ALCHEMIST_HUT = 4;
        public const int LOCATION_ID_ALCHEMISTS_GARDEN = 5;
        public const int LOCATION_ID_FARMHOUSE = 6;
        public const int LOCATION_ID_FARM_FIELD = 7;
        public const int LOCATION_ID_BRIDGE = 8;
        public const int LOCATION_ID_SPIDER_FIELD = 9;
        static World()
        {
            PopulateItems();
            PopulateMonsters();
            PopulateQuests();
            PopulateLocations();
        }
        private static void PopulateItems()
        {
            _items.Add(new Weapon(ITEM_ID_RUSTY_SWORD, "Rusty sword", "Rusty swords", 0, 5, 5));
            _items.Add(new Item(ITEM_ID_RAT_TAIL, "Rat tail", "Rat tails", 1));
            _items.Add(new Item(ITEM_ID_PIECE_OF_FUR, "Piece of fur", "Pieces of fur", 1));
            _items.Add(new Item(ITEM_ID_SNAKE_FANG, "Snake fang", "Snake fangs", 1));
            _items.Add(new Item(ITEM_ID_SNAKESKIN, "Snakeskin", "Snakeskins", 2));
            _items.Add(new Weapon(ITEM_ID_CLUB, "Club", "Clubs", 3, 10, 8));
            _items.Add(new HealingPotion(ITEM_ID_HEALING_POTION, "Healing potion", "Healing potions", 5, 3));
            _items.Add(new Item(ITEM_ID_SPIDER_FANG, "Spider fang", "Spider fangs", 1));
            _items.Add(new Item(ITEM_ID_SPIDER_SILK, "Spider silk", "Spider silks", 1));
            _items.Add(new Item(ITEM_ID_ADVENTURER_PASS, "Adventurer pass", "Adventurer passes", UNSELLABLE_ITEM_PRICE));
        }
        private static void PopulateMonsters()
        {
            Monster rat = new Monster(MONSTER_ID_RAT, "Rat", 5, 3, 10, 3, 3);
            rat.LootTable.Add(new LootItem(ItemByID(ITEM_ID_RAT_TAIL), 75, false));
            rat.LootTable.Add(new LootItem(ItemByID(ITEM_ID_PIECE_OF_FUR), 75, true));
            Monster snake = new Monster(MONSTER_ID_SNAKE, "Snake", 5, 3, 10, 3, 3);
            snake.LootTable.Add(new LootItem(ItemByID(ITEM_ID_SNAKE_FANG), 75, false));
            snake.LootTable.Add(new LootItem(ItemByID(ITEM_ID_SNAKESKIN), 75, true));
            Monster giantSpider = new Monster(MONSTER_ID_GIANT_SPIDER, "Giant spider", 20, 5, 40, 10, 10);
            giantSpider.LootTable.Add(new LootItem(ItemByID(ITEM_ID_SPIDER_FANG), 75, true));
            giantSpider.LootTable.Add(new LootItem(ItemByID(ITEM_ID_SPIDER_SILK), 25, false));
            _monsters.Add(rat);
            _monsters.Add(snake);
            _monsters.Add(giantSpider);
        }
        private static void PopulateQuests()
        {
            Quest clearAlchemistGarden =
                new Quest(
                    QUEST_ID_CLEAR_ALCHEMIST_GARDEN,
                    "Clear the alchemist's garden",
                    "Kill rats in the alchemist's garden and bring back 3 rat tails. You will receive a healing potion and 10 gold pieces.", 20, 10);
            clearAlchemistGarden.QuestCompletionItems.Add(new QuestCompletionItem(ItemByID(ITEM_ID_RAT_TAIL), 3));
            clearAlchemistGarden.RewardItem = ItemByID(ITEM_ID_HEALING_POTION);
            Quest clearFarmersField =
                new Quest(
                    QUEST_ID_CLEAR_FARMERS_FIELD,
                    "Clear the farmer's field",
                    "Kill snakes in the farmer's field and bring back 3 snake fangs. You will receive an adventurer's pass and 20 gold pieces.", 20, 20);
            clearFarmersField.QuestCompletionItems.Add(new QuestCompletionItem(ItemByID(ITEM_ID_SNAKE_FANG), 3));
            clearFarmersField.RewardItem = ItemByID(ITEM_ID_ADVENTURER_PASS);
            _quests.Add(clearAlchemistGarden);
            _quests.Add(clearFarmersField);
        }
        private static void PopulateLocations()
        {
            // Create each location
            Location home = new Location(LOCATION_ID_HOME, "Home", "Your house. You really need to clean up the place.");
            Location townSquare = new Location(LOCATION_ID_TOWN_SQUARE, "Town square", "You see a fountain.");
            Vendor bobTheRatCatcher = new Vendor("Bob the Rat-Catcher");
            bobTheRatCatcher.AddItemToInventory(ItemByID(ITEM_ID_PIECE_OF_FUR), 5);
            bobTheRatCatcher.AddItemToInventory(ItemByID(ITEM_ID_RAT_TAIL), 3);
            townSquare.VendorWorkingHere = bobTheRatCatcher;
            Location alchemistHut = new Location(LOCATION_ID_ALCHEMIST_HUT, "Alchemist's hut", "There are many strange plants on the shelves.");
            alchemistHut.QuestAvailableHere = QuestByID(QUEST_ID_CLEAR_ALCHEMIST_GARDEN);
            Location alchemistsGarden = new Location(LOCATION_ID_ALCHEMISTS_GARDEN, "Alchemist's garden", "Many plants are growing here.");
            alchemistsGarden.AddMonster(MONSTER_ID_RAT, 100);
            Location farmhouse = new Location(LOCATION_ID_FARMHOUSE, "Farmhouse", "There is a small farmhouse, with a farmer in front.");
            farmhouse.QuestAvailableHere = QuestByID(QUEST_ID_CLEAR_FARMERS_FIELD);
            Location farmersField = new Location(LOCATION_ID_FARM_FIELD, "Farmer's field", "You see rows of vegetables growing here.");
            farmersField.AddMonster(MONSTER_ID_SNAKE, 100);
            Location guardPost = new Location(LOCATION_ID_GUARD_POST, "Guard post", "There is a large, tough-looking guard here.", ItemByID(ITEM_ID_ADVENTURER_PASS));
            Location bridge = new Location(LOCATION_ID_BRIDGE, "Bridge", "A stone bridge crosses a wide river.");
            Location spiderField = new Location(LOCATION_ID_SPIDER_FIELD, "Forest", "You see spider webs covering covering the trees in this forest.");
            spiderField.AddMonster(MONSTER_ID_GIANT_SPIDER, 100);
            // Link the locations together
            home.LocationToNorth = townSquare;
            townSquare.LocationToNorth = alchemistHut;
            townSquare.LocationToSouth = home;
            townSquare.LocationToEast = guardPost;
            townSquare.LocationToWest = farmhouse;
            farmhouse.LocationToEast = townSquare;
            farmhouse.LocationToWest = farmersField;
            farmersField.LocationToEast = farmhouse;
            alchemistHut.LocationToSouth = townSquare;
            alchemistHut.LocationToNorth = alchemistsGarden;
            alchemistsGarden.LocationToSouth = alchemistHut;
            guardPost.LocationToEast = bridge;
            guardPost.LocationToWest = townSquare;
            bridge.LocationToWest = guardPost;
            bridge.LocationToEast = spiderField;
            spiderField.LocationToWest = bridge;
            // Add the locations to the static list
            _locations.Add(home);
            _locations.Add(townSquare);
            _locations.Add(guardPost);
            _locations.Add(alchemistHut);
            _locations.Add(alchemistsGarden);
            _locations.Add(farmhouse);
            _locations.Add(farmersField);
            _locations.Add(bridge);
            _locations.Add(spiderField);
        }
        public static Item ItemByID(int id)
        {
            return _items.SingleOrDefault(x => x.ID == id);
        }
        public static Monster MonsterByID(int id)
        {
            return _monsters.SingleOrDefault(x => x.ID == id);
        }
        public static Quest QuestByID(int id)
        {
            return _quests.SingleOrDefault(x => x.ID == id);
        }
        public static Location LocationByID(int id)
        {
            return _locations.SingleOrDefault(x => x.ID == id);
        }
    }
}

ย 

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.

 

Player.cs

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

RaiseMessage("You see a " + location.MonsterLivingHere.Name);

To this:

RaiseMessage("You see a " + CurrentMonster.Name);

 

SuperAdventure.cs (in the Windows Form UI project)

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

if(_player.CurrentLocation.MonsterLivingHere == null)

To this:

if(!_player.CurrentLocation.HasAMonster)

 

Program.cs (in the SuperAdventureConsole project)

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

if(_player.CurrentLocation.MonsterLivingHere == null)

To this:

if(!_player.CurrentLocation.HasAMonster)

 

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.

 

Summary

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 Comments

  1. Dennis
    Dennis December 1, 2016

    Thanks ๐Ÿ™‚

    This helped a lot ๐Ÿ™‚

     

    Greets, Dennis

  2. Philippe
    Philippe December 30, 2016

    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.

    • Scott Lilly
      Scott Lilly December 30, 2016

      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!

  3. Den
    Den March 11, 2017

    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 =)

    • Scott Lilly
      Scott Lilly March 11, 2017

      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. ๐Ÿ™‚

  4. Den
    Den March 14, 2017

    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?

    • Scott Lilly
      Scott Lilly March 14, 2017

      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.

      • Den
        Den March 14, 2017

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

        Sorry for bothering =)

  5. Daniel
    Daniel April 4, 2017

    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?

    • Scott Lilly
      Scott Lilly April 4, 2017

      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.

  6. Daniel
    Daniel April 5, 2017

    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!

    • Scott Lilly
      Scott Lilly April 5, 2017

      You’re welcome. I can do a lesson for adding graphic to the inventory, although it might be a couple weeks before I can write it.

  7. Maybe...
    Maybe... May 16, 2017

    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.

    • Scott Lilly
      Scott Lilly May 16, 2017

      Thank you! If you create a game, please tell me about it. Good luck working with Unity, and learning more about programming!

  8. Mike Leiter
    Mike Leiter September 20, 2017

    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.

    • Scott Lilly
      Scott Lilly September 22, 2017

      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.

  9. William Nichols
    William Nichols January 22, 2018

    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

    • Scott Lilly
      Scott Lilly January 22, 2018

      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.

  10. William Nichols
    William Nichols January 25, 2018

    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.

    • Scott Lilly
      Scott Lilly January 25, 2018

      Can you upload your solution (including the directories under it, and all the files in those directories) to GitHub or Dropbox, so I can look at it?

        • Scott Lilly
          Scott Lilly January 26, 2018

          I was able to download the code from GitHub, and it looks like it’s all there. I’ll look at it this weekend, to try to find the source of the bug.

        • Scott Lilly
          Scott Lilly January 28, 2018

          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.

  11. William Nichols
    William Nichols January 29, 2018

    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.

  12. NOTaROBOT
    NOTaROBOT February 10, 2018

    Morning Scott!
    Could you explain what this thing does?

    1)_monstersAtLocation[monsterID] = percentageOfAppearance;

    IN

    if(_monstersAtLocation.ContainsKey(monsterID))
    {
    _monstersAtLocation[monsterID] = percentageOfAppearance;

    • Scott Lilly
      Scott Lilly February 11, 2018

      “_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?

      • NOTaROBOT
        NOTaROBOT February 12, 2018

        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.

        • NOTaROBOT
          NOTaROBOT February 12, 2018

          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 ?

          • Scott Lilly
            Scott Lilly February 12, 2018

            Do you have:
            SuperAdventure superAdventure = new SuperAdventure;
            or:
            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.

        • Scott Lilly
          Scott Lilly February 12, 2018

          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”.

          • NOTaROBOT
            NOTaROBOT February 12, 2018

            HAhahahah IT WORKED ! THankyouu! This is amazing, when you feel like something just worked. ohhohoohh))!

      • NOTaROBOT
        NOTaROBOT February 12, 2018

        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 )

        • Scott Lilly
          Scott Lilly February 12, 2018

          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.

          • NOTaROBOT
            NOTaROBOT February 15, 2018

            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.

          • Scott Lilly
            Scott Lilly February 15, 2018

            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 to NOTaROBOT Cancel reply

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