package com.jonathan.survivor.hud;
import java.util.HashMap;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Sprite;
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.ScrollPane;
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.StringBuilder;
import com.jonathan.survivor.Assets;
import com.jonathan.survivor.inventory.Inventory;
import com.jonathan.survivor.managers.ItemManager;
public class InventoryList
{
/** Stores the width of the list in pixels for the target (480x320) resolution. */
public static final float LIST_WIDTH = 210;
/** Holds the distance between the left of the button and the left starting point of the text. */
private static final float BUTTON_TEXT_DISTANCE = 42;
/** Holds the distance between the left of the button and the center of each item image. */
private static final float BUTTON_IMAGE_DISTANCE = 20;
/** Stores the size of the item box which acts as a background behind each item image. */
private static final float ITEM_BOX_WIDTH = 32;
private static final float ITEM_BOX_HEIGHT = 32;
/** 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 name of each item. */
private static final Color TEXT_COLOR = new Color(0.2f, 0.2f, 0.2f, 1);
/** Stores the color of the name of each item when the item is pressed. */
private static final Color TEXT_DOWN_COLOR = new Color(0.0f, 0.4f, 0.8f, 1);
/** Holds the inventory from which the list of player items is retrieved. */
private Inventory inventory;
/** Holds the ItemManager instance from which the Sprites for each item is retrieved. */
private ItemManager itemManager;
/** 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 ScrollPane which allows the item list stored inside the itemTable to be scrollable. */
private ScrollPane scrollPane;
/** Stores the table where all item buttons are placed. */
private Table buttonTable;
/** Holds the table where the scroll pane is contained. This is the high-level container for the list. */
private Table scrollPaneTable;
/** Holds the height of the inventory list. */
private float listHeight;
/** Maps an Item subclass with a button displaying this item. */
private HashMap<Class, ImageTextButton> 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 InventoryList(ItemManager itemManager, Inventory inventory, ClickListener buttonListener, float height)
{
//Stores the parameters in their respective member variables
this.inventory = inventory;
this.itemManager = itemManager;
this.buttonListener = buttonListener;
this.listHeight = height;
//Populates the table with all the item buttons corresponding to the items in the player's inventory.
generateList();
}
/** Populates the list with buttons corresponding to all the items in the player's inventory. */
public void generateList()
{
//Stores the itemMap, which pairs each Item subclass with the quantity of that item in the player's inventory.
HashMap<Class, Integer> itemMap = inventory.getItemMap();
//Creates the button table which holds all of the item buttons, and that will be later placed into the ScrollPane.
Table buttonTable = new Table();
//Creates the HashMap linking each item subclass to a button corresponding to the item in the player's inventory.
buttonMap = new HashMap<Class, ImageTextButton>();
//Cycles through each item stored inside the player's inventory.
for(Class key : inventory.getItemMap().keySet())
{
//Creates a button for the given item subclass, passing in the quantity of that item (the value of the key) as a second argument.
createItemButton(key, itemMap.get(key));
//Adds the created button into the button table to be displayed in the list.
buttonTable.add(buttonMap.get(key)).width(LIST_WIDTH).padLeft(3);
//Skips a row for the next item button.
buttonTable.row();
}
//Places the subTable containing all of the buttons into a ScrollPane for scrolling functionality.
scrollPane = new ScrollPane(buttonTable, assets.inventoryScrollPaneStyle);
//Modifies the overscroll of the scroll pane. Args: maxOverscrollDistance, minVelocity, maxVelocity
scrollPane.setupOverscroll(30, 100, 200);
//Disables scrolling in the x-direction.
scrollPane.setScrollingDisabled(true, false);
//Always make the scroll bar appear.
scrollPane.setFadeScrollBars(false);
//Creates the high-level table which acts as a container for the scrollPane.
scrollPaneTable = new Table();
//Resizes the table to fit its parent (the stage)
scrollPaneTable.setFillParent(true);
//Adds the scrollPane to the buttonTable. The buttonTable is the high-level container for the button list. Sets the height of the list to
//the listHeight passed in as a constructor argument.
scrollPaneTable.add(scrollPane).width(LIST_WIDTH).height(listHeight);
}
/** Called when the contents of the inventory list must be updated. Updates the buttons inside the list, along with their quantities. */
public void updateList()
{
//Stores the HashMap linking each item in the inventory with the amount the player has.
HashMap<Class, Integer> inventoryMap = inventory.getItemMap();
//Cycles through each item which has a button inside the list.
for(Class key : buttonMap.keySet())
{
//If the player still has this item inside his inventory, update the quantity displayed in the list.
if(inventoryMap.containsKey(key))
{
//Updates the button for the given item to display the correct quantity.
updateItemButton(key, inventoryMap.get(key));
}
//Else, if the button represents an item that is no longer in the inventory, remove the button from the table.
else
{
//Removes the item from the inventory list
removeItemButton(key);
}
}
//Cycles through all of the item classes inside the inventory. Creates buttons for the items if they aren't already in the inventory list.
for(Class key : inventoryMap.keySet())
{
//If the HashMap of buttons doesn't have a button for the item, create the button.
if(!buttonMap.containsKey(key))
{
System.out.println("InventoryList.updateList(): Add the button to the list for item: " + key);
//Create a button for the item using createItemButton(itemClass, quantity):Button. Passes created button into 'addToList()' to be added in the inventory list.
addToList(createItemButton(key, inventory.getQuantity(key)));
}
}
}
/** Adds the given amount of items to the inventory. If a button already exists for the item, the number shown is updated. If the quantity is negative
* and makes the amount in the inventory zero, the button is deleted. */
public void addItem(Class itemClass, int quantity)
{
//Adds the given quantity to the amount already in the inventory.
int newQuantity = quantity + inventory.getQuantity(itemClass);
//If the item has been removed from the inventory
if(newQuantity <= 0)
{
//Delete the button corresponding to the item, and remove it from the inventory list.
removeItemButton(itemClass);
}
//If the item was empty in the inventory before calling this method, create a new button to display the item in the inventory list.
else if(inventory.getQuantity(itemClass) == 0)
{
//Create a new itemButton corresponding to the itemClass. Second argument is the quantity displayed on the button. The created button is then added to the list.
addToList(createItemButton(itemClass, newQuantity));
}
//Else, if the item already exists in the inventory, update the quantity displayed next to the item
else
{
//Update the corresponding itemButton to display the given quantity of items inside the inventory.
updateItemButton(itemClass, newQuantity);
}
}
/** Creates a button for the given item, with the given quantity specified in the button's text. Stores it inside the buttonMap HashMap. */
private ImageTextButton createItemButton(Class itemClass, int quantity)
{
//Creates a new ImageTextButtonStyle to define the look of the item button.
ImageTextButtonStyle buttonStyle = new ImageTextButtonStyle();
//Stores the sprite displaying the image of the item for the button. Accessed through the ItemManager using the item's class.
Sprite itemSprite = itemManager.getSprite(itemClass);
//Sets the properties which defines the look of the item button.
buttonStyle.font = assets.moonFlowerBold_38;
buttonStyle.fontColor = TEXT_COLOR;
buttonStyle.downFontColor = TEXT_DOWN_COLOR;
buttonStyle.imageDown = buttonStyle.imageUp = new SpriteDrawable(itemSprite);
buttonStyle.pressedOffsetX = 1;
buttonStyle.pressedOffsetY = -1.5f;
//Stores the text for the item button, which consists of the item's name, follows by the quantity of the item.
String buttonText = itemManager.obtainItem(itemClass).getName() + " (" + quantity + ")";
//Creates the button displaying the item passed as arguments to this method.
ImageTextButton itemButton = new ImageTextButton(buttonText, buttonStyle);
//Registers the buttonListener member variable as the button's listener. Since this listener belongs to the CraftingHud, that class will receive the click event.
itemButton.addListener(buttonListener);
//Aligns all the elements in the button to the left so that the buttons look left-aligned in the list.
itemButton.left();
//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.
Image itemBoxImage = new Image(assets.hudSkin.getDrawable("ItemBox"));
//Sets the properties of the itemBox's image
itemBoxImage.setColor(ITEM_BOX_COLOR);
itemBoxImage.setSize(ITEM_BOX_WIDTH, ITEM_BOX_HEIGHT);
//Places the item box at the center of the item image to act as an appropriate background.
itemBoxImage.setPosition(BUTTON_IMAGE_DISTANCE - itemBoxImage.getWidth()/2, itemButton.getHeight()/2 - ITEM_BOX_HEIGHT/2);
//Adds the itemBoxImage before the button's image. Since the button's image is an item's sprite, the box acts as a background to the item.
itemButton.addActorBefore(itemButton.getImage(), itemBoxImage);
//Pads the item image to the left so that its center is at x=BUTTON_IMAGE_DISTANCE relative to the left of the button.
itemButton.getImageCell().padLeft(BUTTON_IMAGE_DISTANCE - itemSprite.getWidth()/2);
//Pads the item image to the right so that the text for each button starts at the same x position (x = BUTTON_TEXT_DISTANCE).
itemButton.getImageCell().padRight(BUTTON_TEXT_DISTANCE - BUTTON_IMAGE_DISTANCE - itemSprite.getWidth()/2);
//Adds the ImageTextButton to the corresponding key inside the button HashMap.
buttonMap.put(itemClass, itemButton);
//Returns the created button.
return itemButton;
}
/** Updates the quantity displayed on the item button for the given class. */
private void updateItemButton(Class itemClass, int quantity)
{
//Retrieves the button corresponding to the item in the player's inventory.
ImageTextButton itemButton = buttonMap.get(itemClass);
//Retrieves the button's current text.
StringBuilder buttonText = new StringBuilder(itemButton.getText());
//Shaves off the number from the button's text.
buttonText.delete(buttonText.indexOf("("), buttonText.length());
//Adds the quantity of the item to the button's text.
buttonText.append("(" + quantity + ")");
//Updates the button's text to display the new quantity of the item inside the inventory.
itemButton.setText(buttonText.toString());
}
/** Adds the given button to the ScrollPane. Note that this method must be called AFTER generateTable() is called. */
private void addToList(ImageTextButton itemButton)
{
//Adds the button into the ScrollPane's widget. Its widget is the buttonTable, where buttons are stored. Pads to the left so as to add spacing between the left
//of the button and the left of the list.
((Table)scrollPane.getWidget()).add(itemButton).width(LIST_WIDTH).padLeft(3);
//Skips a row for the next item button.
((Table)scrollPane.getWidget()).row();
}
/** Removes the item button from the inventory list. Note that items are refered to by their corresponding class. */
private void removeItemButton(Class itemClass)
{
//Removes the button from the table so that it is no longer displayed, since the item corresponding to the button is no longer in the inventory.
((Table)scrollPane.getWidget()).removeActor(buttonMap.get(itemClass));
//Clears the button of all its functionality. Good practice before disposing of the button.
buttonMap.get(itemClass).clear();
//Removes the key corresponding to the given itemClass, since the button was removed for the class. Disposes of the reference to the button, since it is no longer needed.
buttonMap.remove(itemClass);
}
//Returns the class of the item which corresponds to the given button in the inventory list.
public Class getButtonClass(Actor actor)
{
//Cycle through each Class key inside the buttonMap. Effectively cycles through each button inside the inventoryList according to its corresponding item class.
for(Class key : buttonMap.keySet())
{
//If the button inside the buttonMap is null, skip to the next key in the buttonMap.
if(buttonMap.get(key) == null)
continue;
//If the actor is one of the widgets inside a button in the buttonMap
if(buttonEquals(actor, buttonMap.get(key)))
{
//Return the key, as it holds the item subclass corresponding to the given actor.
return key;
}
}
//If this statement is reached, the button does not exist in the inventory list. Thus, no item class corresponds to it. So, return null
return null;
}
//Returns true if the given actor is a button contained inside the inventory. Used by CraftingHud to dictate if a button from the inventory was pressed.
public boolean contains(Actor actor)
{
//Cycle through each Class key inside the buttonMap. Effectively cycles through each button inside the inventoryList according to its corresponding item class.
for(Class key : buttonMap.keySet())
{
//If the button inside the buttonMap is null, skip to the next key in the buttonMap.
if(buttonMap.get(key) == null)
continue;
//If the button belonging to the key is the same as the given actor
if(buttonEquals(actor, buttonMap.get(key)))
{
//Return true, since the given actor is inside the inventory list.
return true;
}
}
//Returns true if the button is contained inside the buttonMap, which links each item class to its button in the inventory.
return buttonMap.containsValue(actor);
}
/** Returns true if the actor corresponds to the button. That is, if the actor is a widget contained inside the button, the actor is considered to be equal to the given button.
* Used to verify whether the actor which delegated a button click corresponds to a certain button in the inventory. */
private boolean buttonEquals(Actor actor, ImageTextButton button)
{
//Returns true if the actor corresponds to any of the widgets in the given ItemCell.
if(actor.equals(button) || actor.equals(button.getLabel()) || actor.equals(button.getImage()))
return true;
//Else, return false if no widgets in the ItemCell correspond to the given actor.
return false;
}
/** Returns the table containing all of the buttons in the inventory list. */
public Table getTable()
{
//Returns the table containing all of the item buttons.
return scrollPaneTable;
}
}