Backpack

Diseño y programación – Mochila del concursante

Introducción

En este post contaré cómo hemos diseñado y programado la mochila del concursante a alto nivel, desde un punto de vista conceptual, hasta un nivel más técnico viendo algo del código.

El juego cuenta con múltiples objetos, pero para crear un ecosistema que funcione hay que etiquetarlos y diseñarlos pensando en conceptos reales.

backpack-schema

 

Objetos inventariables (artículos)

La mochila solo permite almacenar objetos inventariables.

Este tipo de objetos tienen un peso y una propiedad que indica si pueden apilarse. Por ejemplo, las flechas pueden apilarse en el inventario, pero los arcos no.

Las propiedades que se configuran para un objeto inventariable son:

  • Tipo: tipo de objeto inventariable, que lo identifica de forma única en el ámbito de los inventariables.
  • Peso: el peso que tiene el objeto en el juego expresado en gramos.
  • Apilable: indica si este objeto puede apilarse en las ranuras de inventario.
/// <summary>
/// <para>Cualquier objeto que puede formar parte de un inventario o que puede inventariarse.</para>
/// <para>En la mayoría de casos, serán cosas que podrán llevarse en el cuerpo o en algo 
/// que permita almacenar, como una mochila o saco.</para>
/// </summary>
public interface IInventariable
{
    /// <summary>
    /// Tipo de objeto invnetariable. Actua como identificador único.
    /// </summary>
    InventariableType InventariableType { get; }
 
    /// <summary>
    /// Peso de este objeto (en gramos).
    /// </summary>
    float Weight { get; }
 
    /// <summary>
    /// <para>Indica si el objeto se puede apilar en las ranuras de un contenedor de inventario.</para>
    /// <para>Por ejemplo, las armas por norma general no se pueden apilar en una misma ranura.</para>
    /// </summary>
    bool Stackable { get; }
}

Contenedor de inventario

El contenedor de inventario es el recipiente donde se pueden guardar los objetos inventariables.

La característica más particular de estos contenedores es que están formados por ranuras de inventario. En estas ranuras es donde se almacenan los artículos u objetos inventariables. Pueden contener un número ilimitado de artículos o un tope a partir del cual no deja apilar más objetos.

backpack-slot-schema

Las propiedades que se pueden configurar para un contenedor son:

  • Capacidad: cantidad de ranuras de las que dispone.
  • Límite de ranura: indica la cantidad máxima de artículos por ranura.
  • Peso: peso máximo que puede soportar.

Por ejemplo, un contenedor que tiene 10 ranuras y un límite de 5 artículos por ranura tiene una capacidad máxima de 10×5, es decir, como máximo podrá contener 50 artículos.

Además dispone de varias propiedades y métodos de utilidad, que permiten consultar el estado del contenedor y gestionarlo. Estos son los más relevantes:

Métodos de extracción

  • Coger X: permite coger una cantidad específica de un tipo de artículo. Se da prioridad a las ranuras con menos artículos, con el fin de intentar liberarlas.
  • Coger X no rotos: hay artículos que se rompen con el uso, este método solo coge artículos que aún no están rotos.
  • Coger todos: saca todos los artículos de la mochila. Queda vacía.

Métodos de inserción

  • Poner pila de artículos en ranura con espacio: añade al contenedor una pila de artículos del mismo tipo. Busca una ranura con espacio suficiente, dando prioridad a ranuras que ya contengan artículos del mismo tipo. Solo realiza la inserción si puede colocarlos todos en una sola ranura. Tiene en cuenta el peso, impidiendo la inserción si se sobrepasa.
  • Poner lista de artículos: añade al contenedor una lista de artículos que pueden ser de tipos diferentes. Se añadirán al contenedor tantos como quepan en el orden en que vienen en la lista.
  • Poner un artículo: añade el artículo al contenedor (si cabe). Tiene en cuenta el peso, impidiendo la inserción si se sobrepasa.

Métodos de utilidad

  • Peso actual: suma del peso de todos los artículos que contiene.
  • Reforzar: aumenta el peso máximo que puede soportar el contenedor.
  • Comprobar artículo: indica si un artículo cabe en el contenedor.
  • Comprobar artículos: indica si una lista de artículos cabe completa en el contenedor.
  • Contar artículos: cuenta los artículos de un tipo concreto que hay en el contenedor.
  • Contar artículos no rotos: cuenta los artículos no rotos de un tipo concreto que hay en el contenedor.
    /// <summary>
    /// <para>Contenedor de inventario que permite transportar o guardar cosas.</para>
    /// <para>Tiene una capacidad y peso máximos que puede soportar.</para>
    /// <para>Permite ser reforzado para aumentar el peso máximo que soporta.</para>
    /// </summary>
    public abstract class InventoryContainer GameObject
    {
        #region Constructor
        protected InventoryContainer() : base()
        {
            InitSlots();
            MaxWeightSupported BaseWeightSupported;
        }
        #endregion
 
        #region Properties
        /// <summary>
        /// Indica si el contenedor es un clon, es decir, se ha creado
        /// a partir del método "Clone()'.
        /// </summary>
        private bool IsClone { get; set; } = false;
 
        /// <summary>
        /// <para>Número máximo o límite de artículos que caben en las ranuras del contenedor.</para>
        /// <para>El valor '0' significa ilimitado.</para>
        /// </summary>
        public abstract uint SlotLimit { get; }
 
        /// <summary>
        /// <para>Capacidad base del container, equivale al número de ranuras de las que dispone inicialmente.</para>
        /// </summary>
        public abstract uint Capacity { get; }
 
        /// <summary>
        /// <para>Peso base máximo que puede cargar (en gramos).</para>
        /// </summary>
        public abstract float BaseWeightSupported { get; }
 
        /// <summary>
        /// Acción que se lanza cuando el contenedor de inventario sufre alguna modificación
        /// que cambie su contenido, normalmente cuando se añaden o quitan artículos.
        /// </summary>
        public ActionEvent OnInventoryModified { get; set; }
 
        /// <summary>
        /// <para>Peso máximo que puede cargar.</para>
        /// <para>Se puede reforzar aumentando su limite de peso.</para>
        /// <para>Si no ha sido reforzado coincidirá con el peso base máximo soportado.</para>
        /// </summary>
        public float MaxWeightSupported { get; protected set; }
 
        /// <summary>
        /// Ranuras de inventario del contenedor (las ranuras pueden apilar objetos u artículos del mismo tipo).
        /// </summary>
        public List<InventorySlot> Slots { get; protected set; }
 
        /// <summary>
        /// Peso total del contenedor en base a la suma de pesos de todos los artículos que contiene.
        /// </summary>
        public float CurrentWeight
        {
            get
            {
                float totalWeight = 0f;
 
                if (Slots != null)
                {
                    totalWeight = Slots.Sum(slot => slot.Weight);
                }
 
                return totalWeight;
            }
        }
        #endregion
 
        #region Extraction
        public Stack<IInventariable> Take(InventariableType type, uint amount)
            ...
 
        public IInventariable TakeUnbroken(InventariableType type)
            ...
 
        public Stack<IInventariable> TakeUnbroken(InventariableType type, uint amount)
            ...
 
        public Stack<IInventariable> Take(int slotIndex, uint quantity)
            ...
 
        public Stack<IInventariable> TakeAll(int slotIndex)
            ...
        #endregion
 
        #region Insertion
        public bool Put(Stack<IInventariable> articles)
            ...
 
        public List<IInventariable> Put(List<IInventariable> articles)
            ...
 
        public bool Put(IInventariable article)
            ...
 
        public Stack<IInventariable> Put(Stack<IInventariable> newArticles, int slotIndex)
            ...
 
        public Stack<IInventariable> Put(IInventariable article, int slotIndex)
            ...
        #endregion
 
        #region Information & Utils
        public void Reinforce(float weightIncrement)
            ...
 
        public bool CheckSpace(IInventariable article)
            ...
 
        public bool CheckSpace(InventariableType articleType)
            ...
 
        public bool ContainsAllUnbrokenIngredientsAndTools(CraftingRecipe recipe)
            ...
 
        public bool CanFit(List<IInventariable> articles)
            ...
 
        public List<ICraftingTool> GetAllUnbrokenTools()
            ...
 
        public HashSet<ICraftingTool> GetUnbrokenTools(HashSet<CraftingToolType> toolTypes)
            ...
 
        public bool IsFull()
            ...
 
        public InventoryContainer Clone()
            ...
 
        public uint CountProjectiles(Type projectileType)
            ...
 
        public uint CountArticles(InventariableType type)
            ...
 
        public uint CountUnbrokenArticles(InventariableType type)
            ...
 
        public List<DrinkContainer> GetAllDrinkCotnainers()
            ...
 
        public List<DrinkContainer> GetDrinkCotnainers(Type drinkType)
            ...
        #endregion
 
        ...
    }

Mochila del concursante

En definitiva la mochila del concursante es un contenedor de inventario, que tiene una capacidad de 16 ranuras y un límite de 5 artículos por ranura. Puede soportar un peso máximo de 8 kilos.

public class BasicBackpack Backpack
{
    #region Constructor
    protected BasicBackpack() : base() { }
    #endregion
 
    #region Game Object
    public override string Name { get; } = "Basic backpack";
 
    public override string FlavorText { get; } = "The simplest and free backpack provided by the show.";
    #endregion
 
    #region Backpack
    public override uint SlotLimit { get; } = 5;
 
    public override uint Capacity { get; } = 16;
 
    public override float BaseWeightSupported { get; } = 8000;
    #endregion
}

Eso es todo (a alto nivel) en cuanto a diseño y programación de la mochila del concursante.

Si tienes dudas o curiosidad por saber el detalle de alguna cosa ¡espero tus comentarios!

 

 

Leave a Reply

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