Lesson 06.1: Creating the Quest Factory

Now that the player can move around, let’s start working on building quests for the player to complete.

 

 

Steps

Step 1: Create a new ItemQuantity.cs class, in \Engine\Models.

To complete these quests, the player will need to turn in items. For example, a farmer might ask the player to kill rats in his field, and return five rat tails to complete the quest.

So, we need a way to store which items need to be turned in, and how many of them need to be turned it.

To allow for more complex quests, the player may need to turn in more than one type of item, to complete a quest. This requires another List property, because there may be one, or more, items.

Our first class is ItemQuantity. It will store an item ID, and a quantity. You can think of it like a shopping list:

 

Quantity       Item

1                      pizza

6                      sodas

 

For this class, we won’t store a complete GameItem object. We can store the ID of the GameItem.

 

ItemQuantity.cs

namespace Engine.Models
{
    public class ItemQuantity
    {
        public int ItemID { get; set; }
        public int Quantity { get; set; }

        public ItemQuantity(int itemID, int quantity)
        {
            ItemID = itemID;
            Quantity = quantity;
        }
    }
}

 

Step 2: Create a new Quest.cs class, in \Engine\Models.

Next, we will create the Quest class.

This has string and int properties for the ID, Name, Description, RewardExperiencePoints, and RewardGold. The datatype of the ItemsToCompelte and RewardItems properties, will be List<ItemQuantity>, because they will hold a list of the new ItemQuantity objects.

 

Quest.cs

using System.Collections.Generic;

namespace Engine.Models
{
    public class Quest
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }

        public List<ItemQuantity> ItemsToComplete { get; set; }

        public int RewardExperiencePoints { get; set; }
        public int RewardGold { get; set; }
        public List<ItemQuantity> RewardItems { get; set; }

        public Quest(int id, string name, string description, List<ItemQuantity> itemsToComplete,
                     int rewardExperiencePoints, int rewardGold, List<ItemQuantity> rewardItems)
        {
            ID = id;
            Name = name;
            Description = description;
            ItemsToComplete = itemsToComplete;
            RewardExperiencePoints = rewardExperiencePoints;
            RewardGold = rewardGold;
            RewardItems = rewardItems;
        }
    }
}

 

Step 3: Edit Engine\ItemFactory.cs

Before creating the quests, we’ll need to create some more game items, to turn in to complete the quests.

Our first quest will be to kill snakes, and return with five snake fangs. The reward for that quest will be 25 experience points, 10 gold, and a rusty sword.

Inside the “static ItemFactory” function, add two more items to the _standardGameItems list: Snake Fang and Snakeskin.

 

ItemFactory.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Engine.Models;

namespace Engine.Factories
{
    public static class ItemFactory
    {
        private static List<GameItem> _standardGameItems;

        static ItemFactory()
        {
            _standardGameItems = new List<GameItem>();

            _standardGameItems.Add(new Weapon(1001, "Pointy Stick", 1, 1, 2));
            _standardGameItems.Add(new Weapon(1002, "Rusty Sword", 5, 1, 3));
            _standardGameItems.Add(new GameItem(9001, "Snake fang", 1));
            _standardGameItems.Add(new GameItem(9002, "Snakeskin", 2));
        }

        public static GameItem CreateGameItem(int itemTypeID)
        {
            GameItem standardItem = _standardGameItems.FirstOrDefault(item => item.ItemTypeID == itemTypeID);

            if(standardItem != null)
            {
                return standardItem.Clone();
            }

            return null;
        }
    }
}

 

Step 4: Create a new QuestFactory.cs class, in \Engine\Factories.

Now we can build the factory that will create the Quest objects. Like the ItemFactory, we will have a static variable (_quests) that holds all the Quests, and a constructor-like function that populates _quests the first time someone uses the QuestFactory class. We’ll also create the GetQuestByID function, to return the Quest object, wherever the rest of the program needs it.

Because we need to pass a list of ItemQuantity objects into the Quest constructor (for the itemsToComplete and rewardItems parameters), we’ll create variables, add items to the variables, and pass them as the parameters. In the refactoring for the Quest classes, we will change this a little.

 

QuestFactory.cs

using System.Collections.Generic;
using System.Linq;
using Engine.Models;

namespace Engine.Factories
{
    internal static class QuestFactory
    {
        private static readonly List<Quest> _quests = new List<Quest>();

        static QuestFactory()
        {
            // Declare the items need to complete the quest, and its reward items
            List<ItemQuantity> itemsToComplete = new List<ItemQuantity>();
            List<ItemQuantity> rewardItems = new List<ItemQuantity>();

            itemsToComplete.Add(new ItemQuantity(9001, 5));
            rewardItems.Add(new ItemQuantity(1002, 1));

            // Create the quest
            _quests.Add(new Quest(1,
                                  "Clear the herb garden",
                                  "Defeat the snakes in the Herbalist's garden",
                                  itemsToComplete,
                                  25, 10,
                                  rewardItems));
        }

        internal static Quest GetQuestByID(int id)
        {
            return _quests.FirstOrDefault(quest => quest.ID == id);
        }
    }
}

 

Return to main page

12 thoughts on “Lesson 06.1: Creating the Quest Factory

  1. If I add another quest to this factory with different quest items and rewards required for completion do I just add them to itemsToComplete and then in the _quests.add() use itemsToComplete. and then use index of added item in the itemToComplete list?

    1. Hi Trevor,

      The way I would do it is to add code like this:

      _quests.Add(new Quest(2,
      "Clear the farmer's field",
      "Defeat the rats that are in the Farmer's field",
      new List {new ItemQuantity(9003, 5)},
      25, 10,
      new List
      {new ItemQuantity(1001, 1)}));

      This will create the quest without using the temporary variables “itemsToComplete” and “rewardItems”. Your parameters are completely new lists, with the items you want for the quest. This is safer than trying to re-use the temporary variables.

    1. That is a difficult question to answer. This is a “philosophical” decision, with each programmer having their opinions and habits.

      For me, when I build objects with data from an external source (database, data files, etc.), I usually want to use a factory. I know I am going to change this game to get its data from files, so I started with factories. If you use an ORM (object relational mapping) library, like nHibernate/Entity Framework/etc., those have built-in factories to instantiate objects from the database.

      If I build objects that do not get their values from a database and will only be used in a small part of the program (or for a short time), I probably will not use a factory.

  2. i can’t get the new code from this lesson to compile, keep getting item can’t be null,
    even when copy and pasted still get same error. The code from the lesson before this
    worked. any idea’s

  3. Hi, Scott.

    Location, Player, and World classes have a property in type of a collection to store other objects as one of their member data. Location has QuestsAvailableHere to store Quest objects, Player does Inventory for gameItems, and World does currentLocation to locations.
    However, Player class initializes the property(collection) in its constructor while others do it quickly in the declaration expression.

    Is there is any difference between them?

    That is, can the below code safely replace yours without unwanted side effect?

    public ObservableCollection Inventory { get; set; } = new ObservableCollection();
    public ObservableCollection Quests { get; set; } = new ObservableCollection();

    // implicit parameterless default constructor

      1. Thanks, Scott.

        I thought you had a reason to define Player() constructor although its role is just initializing two properties with empty objects till now.

  4. so far so Good , i faced problem unable to call GetQuestByID function in QuestFactory.cs class , after i check each newly added class i found out that QuestFactory class were not inside Factory folder ,, haha finally i know how to revert to last Lesson using VisualVSN then code again in this lesson …
    thanks

    1. Cool! This is why most programmers use source control for even their small, personal projects. It’s easy to do something that causes a problems, and sometimes it’s easier to revert the changes and retry.

Leave a Reply

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