Containers

Note: This is an automatic translation from Russian. It looks good enough, but there may be inaccuracies. We are sorry for the inconvenience.

GameObjectCollection

TeravQuest uses objects of the GameObjectCollection class to store a collection of game objects. Objects of this type are stored, for example, the Objects properties of the Location class and the InventoryViewSource of the Game class.

The GameObjectCollection class has a number of methods for adding, finding and removing objects from the collection, and also ensures that more than one instance of an object with the IsUnique property set to true cannot be added to the game world. The class implements the corresponding interfaces of the C# Standard Library and can be iterated over using a foreach loop.

All methods of the GameObjectCollection class can be found in the API description .

Container

The Container class is designed to store game items that are represented by the Item class and its children. Objects of the container class are stored, for example, in the Inventory property of the NPC class. Also in the game you are creating in this tutorial, this class represents the PlayerInventory property of the MyGame class.

The advantage of using objects of the Container class to store in-game items is that it has a GetGroupedObjects method. It returns a GameObjectCollection collection, and if there are items with the same name in the container, they will be represented by one item in the resulting collection, the name of which will indicate the number of these items in the container. This is useful, for example, for displaying items in the inventory panel. Attention! The result of this method stores helper objects with references to the original, so use it for display only. To add or remove items, use the methods of the container itself, otherwise, unpredictable errors may occur. An example of using the GetGroupedObjects method is given later in this article.

The Container class also has an Objects property, which is represented by an object of the GameObjectCollection class, which allows you to use the appropriate methods, for example, to search for items in the inventory. We have already used this in the dialogue with the elf to retrieve the key: Key key = (Key) game.ActiveSpeaker.Inventory.Objects.GetObjectByName ("Shabby key").

Chest

The Chest class is a ready-made solution for adding chests, cabinets, etc. to the game. It can be placed on a location by adding it to the collection of location objects, and when the player clicks on it, the location panel will display the items stored in the chest, grouped by name, and in the list of actions it will be possible to close the chest.

Add a chest with a reward for the hero to the cave location, which he can inspect after defeating the enemy. But first, let's create an item that will be a reward. Create a Beer class with the following content:

using TeravQuest;

namespace MyGame
{
    [Serializable]
    class Beer : Item
    {
        private bool inPlayerInventory = false;
        
        public Beer()
        {
            Name = "Ale";
        }

        public override void Activate(Game game)
        {
            if (!inPlayerInventory)
            {
                game.MoveItem(this, game.ActiveContainer, (game as MyGame).PlayerInventory);
                inPlayerInventory = true;
                game.DrawLocation();
            }
            else
            {
                (game as MyGame).PlayerInventory.Remove(this);
                game.AppendLocationView("<p>It went nicely.</p>");
                game.UpdateInventoryView();
            }
        }
    }
}

Since, depending on whether the item is in the chest, or in the character's inventory, when the player clicks on it, it must react differently. To track this, a variable is declared: private bool inPlayerInventory = false; It is false, since, initially, the item is in the chest, and not in the inventory of the main character.

In the Activate method, we check where this item is currently located. If it is not in the hero's inventory, move it there, set the corresponding variable to true and update the location.

game.MoveItem(this, game.ActiveContainer, (game as MyGame).PlayerInventory);
inPlayerInventory = true;
game.DrawLocation();

When used to create chests of the Chest class, when opening the chest, the container with items is placed in the ActiveContainer property of the Game class, so here we have access to it.

And, if the item is already in the hero's inventory, then it is used for its intended purpose.

Now let's describe the class of the chest. Create a TreasureChest class:

using TeravQuest;

namespace MyGame
{
    [Serializable]
    class TreasureChest : Chest
    {
        public TreasureChest()
        {
            Name = "Chest";
            DescriptionOnLocation = "There is a chest in the far corner.";
            Caption = "<h1>Chest</h1>";
            CloseText = "Close the chest";
            Add(new Beer());
            Add(new Beer());
            Add(new Beer());
        }
    }
}

The Chest class inherits from GameObject, so properties such as Name and DescriptionOnLocation are available to us.

The Caption property is filled with content that will be displayed before the list of items. The CloseText property stores the text that will be displayed on the action to close the chest.

Using the Add method, add items to the chest.

The Activate method is not overridden in this case, since the base version already contains all the necessary logic.

Place the chest in the cave:

using TeravQuest;

namespace MyGame
{
    [Serializable]
    class Cave : Location
    {
        private TreasureChest chest;
        
        public Cave()
        {
            Name = "Cave";
            chest = new TreasureChest();
            chest.IsVisibleOnLocation = false;
            Objects.Add(chest);
        }

        public override void OnVisit(Game game)
        {
            if ((game as MyGame).QuestStage < 2)
            {
                Content = "As soon as you entered the cave, something incomprehensible and aggressive pounced on you from the darkness ...";
                AddOption(new Option("Fight", Fight));
            }
            else
            {
                ClearOptions();
                Content = $"<h1>{Name}</h1>" +
                    "Cute cave";
                chest.IsVisibleOnLocation = true;
                AddOption(new Option("Go to forest", ToForest));
            }
        }
...

Since access to the chest should appear only after winning a battle, we make it initially invisible.

chest.IsVisibleOnLocation = false;

The hero can now receive his reward.

Not grouped inventory

Please note that we have two identical items displayed in the inventory panel. We use the GetGroupedObjects method of the Container class discussed earlier to change this. Rewrite the Activate method in the Beer class.

public override void Activate(Game game)
        {
            if (!inPlayerInventory)
            {
                game.MoveItem(this, game.ActiveContainer, (game as MyGame).PlayerInventory);
                inPlayerInventory = true;
                game.InventoryVewSource = (game as MyGame).PlayerInventory.GetGroupedObjects();
                game.DrawLocation();
            }
            else
            {
                (game as MyGame).PlayerInventory.Remove(this);
                game.AppendLocationView("<p>It went nicely.</p>");
                game.InventoryVewSource = (game as MyGame).PlayerInventory.GetGroupedObjects();
                game.UpdateInventoryView();
            }
        }

Note that with this approach, the result of the GetGroupedObjects method must be assigned to the InventoryVewSource property after each container change, since they are no longer directly related.

The inventory panel now looks like this:

Grouped inventory