package com.jonathan.survivor.hud; import java.util.HashMap; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.ui.Image; import com.badlogic.gdx.scenes.scene2d.ui.ImageTextButton; import com.badlogic.gdx.scenes.scene2d.ui.ImageTextButton.ImageTextButtonStyle; import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.badlogic.gdx.scenes.scene2d.utils.SpriteDrawable; import com.badlogic.gdx.utils.Array; import com.jonathan.survivor.Assets; import com.jonathan.survivor.inventory.Inventory; import com.jonathan.survivor.managers.CraftingManager.Item; import com.jonathan.survivor.managers.ItemManager; /* * Displays a table with six item slots and one crafted item slot. */ public class CraftingTable { /** Stores the amount of columns of ItemCells in the table. */ private static final int NUM_COLUMNS = 3; /** Holds the number of items that can be placed inside the crafting table. */ private static final int NUM_ITEMS = 6; /** Stores the width and height of each item button. Note that this is the size of the ItemCells' backgrounds, not of the item's image itself. */ private static final float BUTTON_WIDTH = 32; private static final float BUTTON_HEIGHT = 32; /** Holds the horizontal distance between each item button. */ private static final float BUTTON_PAD_RIGHT = 15; /** Holds the vertical distance between each item button. */ private static final float BUTTON_PAD_BOTTOM = 7; /** Holds the color of the item box which acts as the small box behind each item sprite. */ private static final Color ITEM_BOX_COLOR = new Color(0.5f, 0.5f, 0.5f, 1); /** Stores the color of the text displaying the quantity of each item in the crafting table. */ private static final Color TEXT_COLOR = Color.WHITE; /** Holds the ItemManager instance from which the Sprites for each item is retrieved. */ private ItemManager itemManager; /** Holds the inventory from which the list of player items is retrieved. */ private Inventory inventory; /** Holds the universal Assets singleton used to retrieve the visual assets needed to create the inventory list. */ private Assets assets = Assets.instance; /** Stores the ClickListener used by the CraftingHud. All button clicks in the table are delegated to this listener to be handled by the CraftingHud. */ private ClickListener buttonListener; /** Stores the table containing all of the items in the crafting table. */ private Table table; /** Holds an array containing the six cells which hold an item in the table. */ private Array<ItemCell> itemCells; /** Stores the ItemCell displaying the item that is crafted as a result of the items in the crafting table. */ private ItemCell craftedItemCell; /** Holds the image displaying the arrow below the grid of items. */ private Image arrowImage; /** Maps an Item subclass with a item cell displaying this item. */ private HashMap<Class, ItemCell> buttonMap; /** Accepts the itemManager from which to retrieve the items' sprites, the inventory from which to retrieve the player's items, * the ClickListener to which button clicks will be delegated, and the height of the list. */ public CraftingTable(ItemManager itemManager, Inventory inventory, ClickListener buttonListener) { //Stores the parameters in their respective member variables this.itemManager = itemManager; this.inventory = inventory; this.buttonListener = buttonListener; //Creates and populates the crafting table, along with the widgets inside it. generateTable(); } /** Called when the crafting table's widgets and its table must be created. */ public void generateTable() { //Creates the table which will contain all of the crafting table's widgets. table = new Table(); //Instantiates the array of ItemCells which contains the six cells in the crafting table. itemCells = new Array<ItemCell>(); //Populates the itemCells array with as many cells as there are items. for(int i = 0; i < NUM_ITEMS; i++) { //Creates a new, empty item cell to put in the crafting table. ItemCell itemCell = new ItemCell(); //Populates each element with an empty item cell. itemCells.add(itemCell); //Adds the ItemCell's button into the table. Sets the width and height of the button to ensure that the cell takes up the right size in the table, //and adds appropriate padding to the right and the bottom of the cell's buttons. table.add(itemCell.getButton()).width(BUTTON_WIDTH).height(BUTTON_HEIGHT).padRight(BUTTON_PAD_RIGHT).padBottom(BUTTON_PAD_BOTTOM); //If we have filled up a row if((i+1) % NUM_COLUMNS == 0 && i != 0) { //Removes the padding on the right of the button if it is the last button in the row. Like this, no extra width is added on the table. table.getCell(itemCell.getButton()).padRight(0); //Skip a row. table.row(); } } //Creates an Image instance for the picture of the arrow. Created using a sprite inside the hud atlas which is stored inside the hudSkin. arrowImage = new Image(assets.hudSkin.getDrawable("CraftingArrow")); //Resize the arrow image to ensure that, no matter the atlas size chosen, the arrow takes up the same size on the screen. arrowImage.setSize(arrowImage.getWidth() / assets.scaleFactor, arrowImage.getHeight() / assets.scaleFactor); //Creates a new empty itemCell holding the item which is crafted using the items currently in the crafting table. craftedItemCell = new ItemCell(); //Registers the buttonListener to the craftedItemCell's button. As such, since the buttonListener belongs to the CraftingHud, it will be notified of a button click. craftedItemCell.getButton().addListener(buttonListener); //Add the arrow image to the center of the table, making it span as many columns as there are in the table. table.add(arrowImage).colspan(NUM_COLUMNS).padBottom(5).center().width(arrowImage.getWidth()).height(arrowImage.getHeight()); //Skip a row. table.row(); //Add the craftedItemCell's button to the table, make it span as many columns as there are in the table, and center it to the middle of the table. table.add(craftedItemCell.getButton()).colspan(NUM_COLUMNS).center().width(BUTTON_WIDTH).height(BUTTON_HEIGHT); //Set the table to fill the stage. If excluded, the table does not appear. table.setFillParent(true); } /** Adds the given amount of the item inside a cell in the crafting table. */ public void addItem(Class itemClass, int quantity) { //Cycles through each item cell in the crafting table. for(int i = 0; i < itemCells.size; i++) { //Stores the ItemCell the loop is iterating through. ItemCell itemCell = itemCells.get(i); //If the item of the given class is already inside a cell of the crafting table, simply increment the quantity of items in the cell. if(itemClass.equals(itemCell.getItemClass())) { //Add the specified quantity of items to the cell. itemCell.addQuantity(quantity); return; } } /* If the above for loop did not return the method, the given item is not already in the crafting table. Thus, add it there. */ //Cycle through all the cells in the crafting table until an empty one is found. for(int i = 0; i < itemCells.size; i++) { //Stores the ItemCell that is being iterated through. ItemCell itemCell = itemCells.get(i); //If the cell is empty, put the item inside this cell. if(itemCell.isEmpty()) { //Sets the cell to display the given item belonging to the given item class. itemCell.setItemClass(itemClass); //Add the given amount of items to the cell. itemCell.addQuantity(quantity); //Only one itemCell has to be filled with the given item. return; } } } /** Sets the item displayed in craftedItem slot. This is the ItemCell where the crafted item is shown. If null, the slot is emptied. */ public void setCraftedItem(Item craftedItem) { //If the crafted item is null, empty the craftedItemCell if(craftedItem == null) { //Empty the craftedItemCell. craftedItemCell.empty(); } //Else, if the given item is not null, populate the craftedItemCell with the correct item. else { //Empty the craftedItemCell so that the quantity of the item is reset to zero before calling the 'ItemCell.addQuantity()' method. craftedItemCell.empty(); //Set the contained item class of the craftedItemCell to that of the Item argument. Ensures that the correct item is displayed inside the cell. craftedItemCell.setItemClass(craftedItem.getItem()); //Add the given quantity to the craftedItemCell so that the correct amount of items are displayed in the craftedItemSlot. craftedItemCell.addQuantity(craftedItem.getQuantity()); } } /** Called when the user leaves the crafting menu. Removes all of the items in the crafting table, and places them back into the player's inventory. * If the boolean argument is true, the items inside the crafting table are put back in the player's inventory. If not, they are lost. */ public void emptyTable(boolean transferToInventory) { //Cycles through all of the cells in the crafting table. for(int i = 0; i < itemCells.size; i++) { //Stores the ItemCell that is being cycled through. ItemCell itemCell = itemCells.get(i); //If the itemCell is already empty, skip this itemCell. if(itemCell.isEmpty()) continue; //Holds the class representing the item placed in the cell. Class itemClass = itemCell.getItemClass(); //If the items have to be transfered back to the inventory if(transferToInventory) //Increments the amount of items in the inventory by the amount that were inside the crafting table's cell. Effectively inserts the items from the cells //back into the inventory. inventory.addItem(itemClass, itemCell.quantity); //Empty the item cell so that no item is displayed on it. itemCell.empty(); } //Empty the cell where the preview crafted item is placed. craftedItemCell.empty(); } /** Returns the table containing all the widgets in the crafting table. */ public Table getTable() { return table; } /** A cell representing an item as a button in the crafting table. */ private class ItemCell { private Class itemClass; //Stores the Item subclass represented by this cell. private int quantity; //Holds the amount of items of the same type contained in the cell. private ImageTextButtonStyle buttonStyle; //Holds the style which dictates the look of the button. private ImageTextButton button; //Stores the button which displays the given item and its quantity in the crafting table. private Image itemImage; //Holds the image displaying the item on the cell. Image itemBoxImage; //Stores the image displaying the grey box background to each button. /** Creates a default ItemCell with no item inside. */ public ItemCell() { //Creates the button style which dictates the look of this item cell/button. buttonStyle = new ImageTextButtonStyle(); buttonStyle.font = assets.moonFlowerBold_38; buttonStyle.fontColor = TEXT_COLOR; buttonStyle.pressedOffsetX = 1; buttonStyle.pressedOffsetY = -1.5f; //Instantiates a button for the image cell using the button style created above. The button will be empty by default. button = new ImageTextButton("", buttonStyle); //Registers the buttonListener to each button in the crafting table. As such, the CraftingHud will receive all button clicks from the crafting table. button.addListener(buttonListener); //Creates the box which is placed behind the item sprites. Acts as a background to each item image. Uses the "ItemBox" sprite from the HUD atlas. itemBoxImage = new Image(assets.hudSkin.getDrawable("ItemBox")); //Sets the color of the itemBox's image itemBoxImage.setColor(ITEM_BOX_COLOR); //Sets the size of the item box so that all of them are the same size. itemBoxImage.setSize(BUTTON_WIDTH, BUTTON_HEIGHT); //Adds the itemBoxImage before everything else in the button. button.addActorAt(0, itemBoxImage); //Creates the image which will display the item on top of the button. itemImage = new Image(); //Adds the itemImage before the text in the button. button.addActorBefore(button.getLabel(), itemImage); } /** Adds the given amount of items to this cell. Can be negative */ public void addQuantity(int amount) { //Updates the quantity of the items held in this cell. quantity += amount; //Updates the button's text to display the new quantity of items in the cell. button.setText(Integer.toString(quantity)); //If the quantity of items in the cell is one or zero, don't display a number on the button if(quantity <= 1) { //Empty the text on the button. button.setText(""); //If the item has just been removed from the crafting table if(quantity == 0) { //Empty the cell. empty(); } } } /** Updates the button to display the image for the given class. */ public void setItemDrawable(Class itemClass) { //If the itemClass passed as an argument is null, remove the image from the cell, since no item belongs on it. if(itemClass == null) { //Remove the drawable from the itemImage so that no item picture is displayed on the cell. itemImage.setDrawable(null); return; //Return this method, since the below statements would cause NullPointerExceptions. } //Optimization. /*if(itemImage.getDrawable() != null) { SpriteDrawable previousDrawable = (SpriteDrawable) itemImage.getDrawable(); itemManager.freeSprite(previousDrawable.getSprite()); }*/ //Retrieves the sprite corresponding to the item class, wraps it into a SpriteDrawable, and stores it in a local variable. SpriteDrawable drawable = new SpriteDrawable(itemManager.getSprite(itemClass)); //Changes the image being displayed on the button by changing the drawable on the itemImage. itemImage.setDrawable(drawable); //Sets the size of the image to correspond to the size of the sprite. Like this, the item image is always the same size as the item's sprite. itemImage.setSize(drawable.getSprite().getWidth(), drawable.getSprite().getHeight()); //Sets the item image's position so that it is at the center of the button. itemImage.setPosition(BUTTON_WIDTH/2 - itemImage.getWidth()/2, BUTTON_HEIGHT/2 - itemImage.getHeight()/2); } /** Resets the ItemCell to an empty cell. */ public void empty() { //Set the quantity stored in the cell to zero. quantity = 0; //Empty the text on the button. button.setText(""); //Remove the itemClass being held by the cell, since the cell is supposed to be emptied. setItemClass(null); } /** Returns true if this cell is not filled with an item. */ public boolean isEmpty() { //The cell is empty if it does not hold an itemClass. return itemClass == null; } /** Sets the item class held by the cell. */ public void setItemClass(Class itemClass) { //Updates the item class held by the cell, which dictates which item the cell is holding. this.itemClass = itemClass; //Updates the item image on the button. Retrieves a sprite corresponding to the item class, and places it on the buttonwraps it into a SpriteDrawable, and passes it to setItemDrawable(...). setItemDrawable(itemClass); } /** Returns the item class held by the cell. */ public Class getItemClass() { return itemClass; } /** Returns the button which displays the item held by this cell. */ public ImageTextButton getButton() { return button; } } /** Returns true if the given actor is an item button inside the crafting table. An item button is every button in the grid of six buttons the crafting table. */ public boolean isItemButton(Actor actor) { //Cycles through each itemCell contained in the crafting table for(int i = 0; i < itemCells.size; i++) { //Return true if the actor corresponds to one of the buttons in the itemCells. if(buttonEquals(actor, itemCells.get(i))) return true; } //If we are here, the actor is not an itemButton in the crafting table. return false; } /** Returns the class belonging to the given button. That is, each itemButton in the crafting table represents an item class. This method returns the class represented * by the button. */ public Class getItemButtonClass(Actor actor) { //Cycles through each itemCell contained in the crafting table for(int i = 0; i < itemCells.size; i++) { //Return true if the actor corresponds to one of the widgets in one of the itemCells. if(buttonEquals(actor, itemCells.get(i))) { //Returns the class which represents the item held by this button. return itemCells.get(i).getItemClass(); } } //Return null if the button is not contained inside the crafting table. return null; } /** Returns true if the given Actor is the craftedItemButton. That is, the button which contains the preview of the item crafted from the items in the crafting table. */ public boolean isCraftedItemButton(Actor actor) { //Returns true of the given actor is the same as the button in the craftedItemCell. return actor.equals(craftedItemCell.getButton()); } /** Returns true if the given item is contained inside the crafting table. */ public boolean containsItem(Class itemClass) { //Cycle through all of the item cells contained in the table. for(int i = 0; i < NUM_ITEMS; i++) { //If any of the item cells contain the given item if(itemCells.get(i).getItemClass() == itemClass) //Return true, since the item is contained in one of the item cells in the crafting table. return true; } //If this statement is reached, the given item is not contained in the crafting table. Thus, return false. return false; } /** Returns true if the crafting table cannot take any more items. */ public boolean isFull() { //Cycle through all of the item cells in the table. for(int i = 0; i < NUM_ITEMS; i++) { //If any of the item cells are empty, the table is not full if(itemCells.get(i).getItemClass() == null) //Thus, return false; return false; } //If this statement is reached, the table is full. Thus, return true. return true; } /** Returns true if the actor corresponds to the itemCell. That is, if the actor is contained inside the itemCell's button, the actor is technically equal to the itemCell. * Used to verify whether the actor which delegated a button click corresponds to a certain ItemCell. */ private boolean buttonEquals(Actor actor, ItemCell itemCell) { //Returns true if the actor corresponds to any of the widgets in the given ItemCell. if(actor.equals(itemCell.getButton()) || actor.equals(itemCell.itemImage) || actor.equals(itemCell.itemBoxImage) || actor.equals(itemCell.getButton().getLabel())) return true; //Else, return false if no widgets in the ItemCell correspond to the given actor. return false; } }