com.prisonbreak.game.MapControlRenderer.java Source code

Java tutorial

Introduction

Here is the source code for com.prisonbreak.game.MapControlRenderer.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.prisonbreak.game;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.MapObject;
import com.badlogic.gdx.maps.MapObjects;
import com.badlogic.gdx.maps.objects.RectangleMapObject;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapTile;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.List;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.TextField;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Array;
import com.prisonbreak.game.entities.Guard;
import com.prisonbreak.game.entities.Item;
import com.prisonbreak.game.entities.PatrolGuard;
import com.prisonbreak.game.entities.Player;
import com.prisonbreak.game.entities.StationGuard;

/**
 *
 * @author krankai
 */
public class MapControlRenderer extends OrthogonalTiledMapRenderer implements InputProcessor {

    public static final int WORLD_WIDTH = 100 * 32;
    public static final int WORLD_HEIGHT = 100 * 32;

    public enum STATE {
        ONGOING, WIN, LOSE, PAUSE, END
    }

    private STATE state;

    public enum TYPE_INTERACTION {
        NO_INTERACTION, SEARCH_INTERACTION, OPEN_LOCK_INTERACTION, READ_INTERACTION, PASS_UNLOCK_INTERACTION, BREAK_INTERACTION
    }

    private TYPE_INTERACTION typeInteraction = TYPE_INTERACTION.NO_INTERACTION;

    private final Player player;
    private final OrthographicCamera camera;
    private final ShapeRenderer shapeRenderer;
    private final int drawSpritesAfterLayer = this.map.getLayers().getIndex("Walls") + 1;
    private final int drawDetectionAreaAfterLayer = this.map.getLayers().getIndex("Tiles") + 1;
    private Array<Array<Item>> listItems; // array holds the overall list of list items of all objects
    private Array<Array<String>> messageTree; // array holds the message tree (for all objects)
    private Array<Item> objectItems; // current list of items for one object
    private Item neededKey; // the required key to unlock an object (door)
    private Array<Guard> guards;

    private int indexForMessageTree = 0;
    private int indexRemainingItems = 1; // for safe locker (pass_unlock_interaction)
    private String latestObjectName = ""; // name of the lastest object in interaction with Player
    private boolean interactHappen = false; // indicate whether interaction happens
    private boolean latestDoorLocked = false; // indicate whether the latest door in interaction
                                              // with Player is locked or not
    private boolean doorHidden = false; // indicate whether the current is hidden

    private int[][] blockedWallUnitGrid;
    private final MapObjects interactionObjects;
    private final MapObjects staticObjects;
    private final MapObjects doorObjects;
    private final MapObject winningLocation;
    private RectangleMapObject currentDoor; // current door in contact, for hiding/showing unlocked door

    private TiledMapTile tile1; // tiles that, together, represent a door object
    private TiledMapTile tile2; // tile1 -> lower left cell ; tile2 -> lower right cell
    private TiledMapTile tile3; // tile3 -> upper left cell ; tile4 -> upper right cell
    private TiledMapTile tile4;
    //    private Array<TiledMapTile> tiles;

    public Stage stage;
    public Label dialogBoxLabel;
    public Label descLabel; // description label, for inventory and read_interaction
    public Label titleLabel;
    public List itemList;
    public List inventory;
    public TextField passField; // to enter password -> unlock objects
    public TextButton txtButton;

    public MapControlRenderer(TiledMap map) {
        super(map);
        state = STATE.ONGOING;

        // ui components for "interaction conversation"
        stage = new Stage();

        // create new player
        player = new Player();
        player.setMapControlRenderer(this); // add map to player
        setPlayerToSpawm(); // add Player at spawming point of the map

        // extract a grid identifying position of walls -> for detecting collision
        extractBlockedWalls(); // stored in blockedWallUnitGrid

        // get list of static (blocked) objects
        staticObjects = map.getLayers().get("ObjectCollision").getObjects();

        // get list of door objects
        doorObjects = map.getLayers().get("DoorCollision").getObjects();

        // get list of interaction objects
        interactionObjects = map.getLayers().get("ObjectInteraction").getObjects();

        // extract winning location
        winningLocation = map.getLayers().get("SpecialLocations").getObjects().get("winningPoint");

        // initialize list of items, and message tree
        initializeListItems();
        initializeMessageTree();

        // initialize tiles
        //        tiles = new Array<TiledMapTile>();

        // initialize list of guards in the map
        initializeGuards();

        // create camera
        camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        camera.translate(player.x, player.y);

        // create shape renderer
        shapeRenderer = new ShapeRenderer();
    }

    public STATE getCurrentState() {
        return state;
    }

    public void setCurrentState(STATE state) {
        this.state = state;
    }

    public Player getPlayer() {
        return player;
    }

    public Array<Guard> getListGuards() {
        return guards;
    }

    public OrthographicCamera getCamera() {
        return camera;
    }

    public int[][] getBlockedWallsGrid() {
        return blockedWallUnitGrid;
    }

    public MapObjects getInteractionObjects() {
        return interactionObjects;
    }

    public MapObjects getStaticObjects() {
        return staticObjects;
    }

    public MapObjects getDoorObjects() {
        return doorObjects;
    }

    public MapObject getWinningLocation() {
        return winningLocation;
    }

    // allow camera to move along with player
    public void moveCamera() {
        camera.position.x = MathUtils.clamp(player.x, camera.viewportWidth / 2f,
                WORLD_WIDTH - camera.viewportWidth / 2f);
        camera.position.y = MathUtils.clamp(player.y, camera.viewportHeight / 2,
                WORLD_HEIGHT - camera.viewportHeight / 2);

        camera.update();
        this.setView(camera);
    }

    // initialize objectItems of items in the map
    private void initializeListItems() {
        Item item;
        Array<Item> list;

        // the overall objectItems
        listItems = new Array<Array<Item>>();

        // add an old key + trash into the garbageBin1 ; listItemID = 0
        list = new Array<Item>();
        item = new Item("Old Key", 1);
        item.setDescription("An old key. Maybe it can be used to open a cell ...");
        list.add(item);
        item = new Item("Trash", 2);
        item.setDescription("Filthy trash.");
        list.add(item);
        listItems.add(list);

        // add a key into wardrobe1 ; listItemID = 1
        list = new Array<Item>();
        item = new Item("Uniform", 3);
        item.setDescription(
                "Karvick's uniform. There is an identity card in his pocket. Maybe you can make use of it ...");
        list.add(item);
        listItems.add(list);

        // add a modern key below the pillow ; listItemID = 2
        list = new Array<Item>();
        item = new Item("Modern Key", 4);
        item.setDescription("A large key. It seems that this key is used to open a big door.");
        list.add(item);
        listItems.add(list);

        // add an ice cream + frozen meat + a bottle into the fride ; listItemID = 3
        list = new Array<Item>();
        item = new Item("Ice cream", 5);
        item.setDescription("A chocolate ice cream. Very yummy.");
        list.add(item);
        item = new Item("Frozen meat", 6);
        item.setDescription("Ordered meat for Henry Karvick.");
        list.add(item);
        item = new Item("Bottle of drink", 7);
        item.setDescription("A bottle full of cold water. Best for hot days.");
        list.add(item);
        listItems.add(list);

        // add a crowbar into the racks ; listItemID = 4
        list = new Array<Item>();
        item = new Item("Crowbar", 8);
        item.setDescription("A steel crowbar. Very strong and sturdy.");
        list.add(item);
        listItems.add(list);

        // add a crinkle letter into the draining holes (in bathroom) ; listItemID = 5
        list = new Array<Item>();
        item = new Item("Crinkle Letter", 9);
        item.setDescription("The letter is in bad condition. "
                + "You can hardly recognize any words at all, except a number '6'");
        list.add(item);
        listItems.add(list);

        // add the final key + an airplane ticket into the safe locker ; listItemID = 6
        list = new Array<Item>();
        item = new Item("Final Key", 10);
        item.setDescription("THIS IS THE KEY TO FREEDOM. Now, you only need to find the door to use it in.");
        list.add(item);
        item = new Item("Airplane Ticket", 11);
        item.setDescription(
                "XXX Airlines.\nDATE: 12 Mar, 2025\nFLIGHT HOUR: 16:30\nFROM: Lanbagana, Homoca\nTO: Malag, Facas");
        list.add(item);
        listItems.add(list);
    }

    // initialize message tree
    private void initializeMessageTree() {
        messageTree = new Array<Array<String>>();

        // for search_interaction
        // message for the first case - index = 0 -> Found nothing
        Array<String> messages = new Array<String>();
        messages.add("There seems to be nothing here ...");
        messageTree.add(messages);

        // message for the second case - index = 1 -> There are (many) items here
        messages = new Array<String>();
        messages.add("You found something. Which will you pick up?");
        messages.add("Obtained 1 x ");
        messageTree.add(messages);

        // for open_lock_interaction
        // message for the third case - index = 2 -> The door is locked
        messages = new Array<String>();
        messages.add("The door is locked.");
        messages.add("You can unlock it using");
        messages.add("Will you unlock the door?");
        messages.add("The door is unlocked.");
        messageTree.add(messages);

        // message for the fourth case - index = 3 -> The door is unlocked
        messages = new Array<String>();
        messages.add("The door is unlocked.");
        messages.add("You can lock it using");
        messages.add("Will you lock the door?");
        messages.add("The door is locked.");
        messageTree.add(messages);

        // for read interaction
        // message for the fifth case - index = 4 -> Any readable objects
        messages = new Array<String>();
        messages.add("Something is written on the ");
        messages.add("...");
        messageTree.add(messages);

        // for pass-unlock interaction (use password to unlock objects)
        // message for the sixth case - index = 5 -> Door with password lock
        messages = new Array<String>();
        messages.add("The door is locked.");
        messages.add("There is a card holder at the handle. Use the card obtained with the uniform?");
        messages.add("A voice prompts for your identity ...");
        messages.add("The door is unlocked.");
        messageTree.add(messages);

        // message for the seventh case - index = 6 -> Safe locker with passwork lock
        messages = new Array<String>();
        messages.add("Enter the correct password to open.");
        messages.add("The safe locker has been unlocked");
        messages.add("Obtain 1 x ");
        messageTree.add(messages);

        // for break interaction (with door)
        // message for the eigth case - index = 7 -> Break the (office) door
        messages = new Array<String>();
        messages.add("The door is locked.");
        messages.add("You can break it using ");
        messages.add("Will you break the door?");
        messages.add("The door has been cracked open.");
        messageTree.add(messages);
    }

    // initialize list of guards
    private void initializeGuards() {
        String type, sheetName, direction;
        guards = new Array<Guard>();

        // extract all guard objects in map
        MapObjects guardObjects = map.getLayers().get("Guards").getObjects();

        // for each object
        for (MapObject guard : guardObjects) {
            RectangleMapObject rectGuard = (RectangleMapObject) guard;

            // if current guard is "not visible" -> ignore
            if (!rectGuard.isVisible()) {
                continue;
            }

            // extract values of attributes of 'guard' object
            type = rectGuard.getProperties().get("type", "none", String.class);
            sheetName = rectGuard.getProperties().get("sheetName", "", String.class);
            if (type.equalsIgnoreCase("none")) {
                Gdx.app.log("Error: ", "'type' attribute not found");
                return;
            }

            // if stationary guard
            if (type.equalsIgnoreCase("stationary")) {
                direction = rectGuard.getProperties().get("direction", "none", String.class);
                if (direction.equalsIgnoreCase("none")) {
                    Gdx.app.log("Error: ", "invalid value for direction attribute");
                    return;
                }

                // create the specified guard
                Guard specifiedGuard = new StationGuard(sheetName, direction, rectGuard.getRectangle().getX(),
                        rectGuard.getRectangle().getY());
                specifiedGuard.setMapControlRenderer(this);

                // add the corresponding guard to the array
                guards.add(specifiedGuard);
            }
            // if patrol guard
            else if (type.equalsIgnoreCase("patrol")) {
                float secondDelay = rectGuard.getProperties().get("delay", Float.class);

                // create the specified guard (without setting mark points)
                Guard specifiedGuard = new PatrolGuard(sheetName, rectGuard.getRectangle().getX(),
                        rectGuard.getRectangle().getY(), secondDelay);
                specifiedGuard.setMapControlRenderer(this);

                // extract list of mark points from the map
                String listPoints = rectGuard.getProperties().get("markPoints", String.class);
                String[] tileIndices = listPoints.split(",");
                for (int i = 0, indexX, indexY; i < tileIndices.length; i += 2) {
                    indexX = Integer.parseInt(tileIndices[i]);
                    indexY = Integer.parseInt(tileIndices[i + 1]);

                    //                    Gdx.app.log("x: ", "" + indexX);
                    //                    Gdx.app.log("y: ", "" + indexY);

                    if (!((PatrolGuard) specifiedGuard).addMarkPoint(new Vector2(indexX * 32f, indexY * 32f))) {
                        Gdx.app.log("Cannot add mark point ", "(indeX, indexY)");
                    }
                }

                //                Gdx.app.log("Mark point 1: ", ((PatrolGuard) specifiedGuard).listMarkPoints.get(0).toString());
                //                Gdx.app.log("Mark point 2: ", ((PatrolGuard) specifiedGuard).listMarkPoints.get(1).toString());

                // add the corresponding guard to the array
                guards.add(specifiedGuard);
            }
        }
    }

    // trigger the interaction with the next-to object (if possible)
    // Note: flip the interactHappen flag if Player is in appropriate position
    // for search_interaction: return the list of items in the object (if any)
    //     open_lock_interaction: return the list contain (one) the key to open (if can be opened)
    private Array<Item> triggerInteraction() {
        for (MapObject object : interactionObjects) {
            // all interaction objects are guaranteed to be rectangle
            RectangleMapObject rectObject = (RectangleMapObject) object;
            //            Gdx.app.log("object: ", rectObject.getRectangle().toString());
            //            Gdx.app.log("player: ", player.getSprite().getBoundingRectangle().toString());

            // search and find for the object in interaction with Player
            if (rectObject.getRectangle().overlaps(player.getSprite().getBoundingRectangle())) {
                String directionCheck = rectObject.getProperties().get("contactDirection", "none", String.class);
                //                Gdx.app.log("Contact direction: ", directionCheck);
                //                Gdx.app.log("Player direction: ", player.getCurrentDirection());

                // check if user faces object in correct direction
                if (directionCheck.equalsIgnoreCase("all")
                        || (!player.getCurrentDirection().equalsIgnoreCase("none")
                                && player.getCurrentDirection().equalsIgnoreCase(directionCheck))) {

                    // retrieve type of interaction
                    String type = rectObject.getProperties().get("type", "", String.class);

                    // if searchable objects
                    if (type.equalsIgnoreCase("search_interaction")) {
                        interactHappen = true;

                        // set the flag for type of interaction
                        typeInteraction = TYPE_INTERACTION.SEARCH_INTERACTION;

                        // extract attributes' values
                        boolean haveItems = rectObject.getProperties().get("haveItems", false, Boolean.class);
                        int listID = rectObject.getProperties().get("listItemID", -1, Integer.class);

                        // if object does contain items
                        //      -> return list of those items
                        // otherwise -> return null
                        if (haveItems) {
                            // get the item objectItems ID
                            if (listID == -1)
                                break;
                            else
                                return listItems.get(listID);
                        }
                    }

                    // if door object (open_lock_interaction)
                    else if (type.equalsIgnoreCase("open_lock_interaction")) {
                        // extract attributes' values
                        boolean locked = rectObject.getProperties().get("locked", false, Boolean.class);
                        int keyID = rectObject.getProperties().get("keyID", -1, Integer.class);
                        Array<Item> needKey = new Array<Item>();
                        Item key = new Item("Needed key", keyID);
                        needKey.add(key);

                        // extract the name of current door in interaction with Player
                        latestObjectName = rectObject.getName();

                        // set the status of current door in interaction with Player
                        latestDoorLocked = locked;
                        //                        Gdx.app.log("locked: ", latestDoorLocked + "");

                        // if the door is not locked
                        //      check the position of Player before invoking interaction
                        //      -> return the required key to lock it
                        if (!latestDoorLocked) {
                            MapObject o = doorObjects.get(latestObjectName);
                            RectangleMapObject r = (RectangleMapObject) o;

                            if (!r.getRectangle().overlaps(player.getSprite().getBoundingRectangle())) {
                                interactHappen = true;

                                // set the flag for type of interaction
                                typeInteraction = TYPE_INTERACTION.OPEN_LOCK_INTERACTION;

                                return needKey;
                            }
                        }
                        // if the door is locked
                        //      -> return the required key to unlock it, or
                        //                the key with keyID = -1 ; indicate
                        //                   that the door CANNOT be opened
                        else {
                            interactHappen = true;

                            // set the flag for type of interaction
                            typeInteraction = TYPE_INTERACTION.OPEN_LOCK_INTERACTION;

                            return needKey;
                        }
                    }

                    // if readable objects
                    else if (type.equalsIgnoreCase("read_interaction")) {
                        interactHappen = true;

                        // set the flag for type of interaction
                        typeInteraction = TYPE_INTERACTION.READ_INTERACTION;

                        // extract attributes' values
                        String message = rectObject.getProperties().get("message", "", String.class);

                        // set the name of the object
                        latestObjectName = rectObject.getProperties().get("name", "Object", String.class);

                        // return a list with one "item" contain the message; id for item = -2
                        Array<Item> l = new Array<Item>();
                        Item mess = new Item("Written Message", -2);
                        mess.setDescription(message);
                        l.add(mess);

                        return l;
                    }

                    // if password locking objects
                    else if (type.equalsIgnoreCase("pass_unlock_interaction")) {
                        boolean locked = rectObject.getProperties().get("locked", false, Boolean.class);

                        // if the object has already been unlocked
                        //      -> return null; no interaction happens
                        if (!locked) {
                            return null;
                        }
                        // otherwise
                        //      -> return a list with first "item" containing the password,
                        //          and all the remaining items are ones that are inside
                        //          the object (in case of safe locker)
                        else {
                            interactHappen = true;

                            // set the flag indicating the type of interaction
                            typeInteraction = TYPE_INTERACTION.PASS_UNLOCK_INTERACTION;

                            // extract the name of object in interaction
                            latestObjectName = rectObject.getName();

                            // extract attributes
                            String password = rectObject.getProperties().get("pass", "", String.class);
                            Item passItem = new Item("Password", -3); // item contains password
                            passItem.setDescription(password);

                            // create the list
                            Array<Item> list = new Array<Item>();
                            list.add(passItem);

                            // if safe locker, add all the contained items into the list
                            int listID = rectObject.getProperties().get("listItemID", -1, Integer.class);
                            if (listID == -1) {
                                Gdx.app.log("Error: ", "listItemID attribute not found");
                            } else {
                                list.addAll(listItems.get(listID));
                            }

                            return list;
                        }
                    }

                    // if breakable object (here, break the office door)
                    else if (type.equalsIgnoreCase("break_interaction")) {
                        boolean broken = rectObject.getProperties().get("broken", true, Boolean.class);

                        // if object has been cracked open (broken = true)
                        //      -> do nothing, no interaction happens, return null
                        if (broken) {
                            return null;
                        }
                        // otherwise
                        //      -> return the required item to break the object (door), which is a crowbar in this game
                        else {
                            interactHappen = true;
                            typeInteraction = TYPE_INTERACTION.BREAK_INTERACTION;

                            // extract the name of object in interaction
                            latestObjectName = rectObject.getName();

                            int id = rectObject.getProperties().get("breakItemID", Integer.class);
                            Item item = new Item("Required Item", id);
                            Array<Item> l = new Array<Item>();
                            l.add(item);

                            return l;
                        }
                    }
                }
            }
        }

        return null;
    }

    private void extractBlockedWalls() {
        // get map objects of ObjectLayer "Collision"
        MapLayer layer = map.getLayers().get("WallCollision");
        MapObjects objects = layer.getObjects();

        // fill in blockedWallUnitGrid with position of static blocked map objects
        // 1 -> blocked     0 -> not blocked
        blockedWallUnitGrid = new int[100][100];
        boolean isBlocked = false;
        for (int indexX = 0; indexX < 100; ++indexX) {
            for (int indexY = 0; indexY < 100; ++indexY) {
                // convert to position in world map
                Vector2 worldPosition = new Vector2(indexX * 32f + 16f, indexY * 32f + 16f); // consider center

                // check whether that position is "blocked" or not
                for (MapObject o : objects) {
                    // retrieve RectangleMapObject objects that are blocked
                    if (o.isVisible() && o instanceof RectangleMapObject && o.getProperties().get("blocked") != null
                            && o.getProperties().get("blocked", Boolean.class)) {
                        RectangleMapObject rectObject = (RectangleMapObject) o;

                        if (rectObject.getRectangle().contains(worldPosition)) {
                            isBlocked = true;
                            break;
                        }
                    }
                }

                // fill 1 / 0 into the corrsponding tile index
                if (isBlocked) {
                    blockedWallUnitGrid[indexX][indexY] = 1;
                } else {
                    blockedWallUnitGrid[indexX][indexY] = 0;
                }

                // reset flag
                isBlocked = false;
            }
        }

        //        Gdx.app.log("Test: ", " " + blockedWallUnitGrid[13][38] + " " + blockedWallUnitGrid[13][39] +
        //                " " + blockedWallUnitGrid[12][38] + " " + blockedWallUnitGrid[12][39]);
    }

    // set Player position to spawming location
    private void setPlayerToSpawm() {
        MapObject spawmPoint = map.getLayers().get("SpecialLocations").getObjects().get("spawmPoint");
        RectangleMapObject rectSpawmPoint = (RectangleMapObject) spawmPoint;

        player.x = rectSpawmPoint.getRectangle().getX();
        player.y = rectSpawmPoint.getRectangle().getY();
        //        player.x = 50 * 32;
        //        player.y = 79 * 32;
    }

    // check winning condition
    private boolean winGame() {
        RectangleMapObject rectLoc = (RectangleMapObject) winningLocation;

        return state != STATE.END && rectLoc.getRectangle().contains(player.getSprite().getBoundingRectangle());
    }

    // perform actions in case PLayer loses the game
    private void loseGame() {
        // set state to PAUSE
        state = STATE.PAUSE;

        // notify Player
        dialogBoxLabel = new Label("Ouch!!! You have been found.", PrisonBreakGame.gameSkin, "custom");
        dialogBoxLabel.setPosition(0, 0);
        dialogBoxLabel.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight() / 5);
        dialogBoxLabel.setAlignment(Align.topLeft);
        dialogBoxLabel.addListener(new InputListener() {
            @Override
            public boolean keyDown(InputEvent event, int keycode) {
                if (keycode == Keys.F) {
                    state = STATE.LOSE;
                    dialogBoxLabel.remove();
                    resetInputProcessor();
                }
                return true;
            }
        });
        stage.addActor(dialogBoxLabel);
        stage.setKeyboardFocus(dialogBoxLabel);

        Gdx.input.setInputProcessor(stage);
    }

    // reset the InputProcessor to this (MapControlRenderer) (from stage)
    private void resetInputProcessor() {
        Gdx.input.setInputProcessor(this);
    }

    // show/hide the Player's inventory based on its current status
    private void showHideInventory() {
        // create an array of names of Player's items
        Array<String> names = new Array<String>();
        if (player.getInventory().size == 0) {
            names.add("Nothing");
        } else {
            for (Item item : player.getInventory()) {
                names.add(item.getItemName());
            }
        }

        // create the List panel to display the inventory
        inventory = new List(PrisonBreakGame.gameSkin, "dimmed");
        inventory.setItems(names);
        inventory.setSelectedIndex(0);
        inventory.setSize(Gdx.graphics.getWidth() / 6, Gdx.graphics.getHeight() / 2);
        inventory.setPosition(Gdx.graphics.getWidth() * 3 / 4 - inventory.getWidth() / 2,
                Gdx.graphics.getHeight() / 2 - inventory.getHeight() / 2);

        // create the description Label to display description of each item
        descLabel = new Label("", PrisonBreakGame.gameSkin, "custom-small");
        descLabel.setSize(Gdx.graphics.getWidth() / 6, Gdx.graphics.getHeight() / 4);
        descLabel.setPosition(inventory.getX() + inventory.getWidth(),
                inventory.getY() + inventory.getHeight() - descLabel.getHeight());
        descLabel.setAlignment(Align.topLeft);
        descLabel.setWrap(true);
        if (player.getInventory().size > 0) {
            descLabel.setText(player.getInventory().get(inventory.getSelectedIndex()).getDescription());
        }
        stage.addActor(descLabel);

        // create title: Inventory
        titleLabel = new Label("INVENTORY", PrisonBreakGame.gameSkin, "subtitle");
        titleLabel.setSize(inventory.getWidth(), titleLabel.getHeight() * 3 / 2);
        titleLabel.setPosition(inventory.getX(), inventory.getY() + inventory.getHeight());
        titleLabel.setAlignment(Align.center);
        stage.addActor(titleLabel);

        // add InputListener to inventory
        inventory.addListener(new InputListener() {
            @Override
            public boolean keyDown(InputEvent event, int keycode) {
                int currentIndex = inventory.getSelectedIndex();
                int upIndex = currentIndex - 1;
                int downIndex = currentIndex + 1;
                String desc = "";

                if (upIndex < 0)
                    upIndex = inventory.getItems().size - 1;
                if (downIndex >= inventory.getItems().size)
                    downIndex = 0;

                switch (keycode) {
                case Keys.UP:
                    // scroll up the item objectItems
                    inventory.setSelectedIndex(upIndex);

                    // display the corresponding description in descLabel
                    if (player.getInventory().size > 0)
                        desc = player.getInventory().get(upIndex).getDescription();
                    descLabel.setText(desc);

                    break;
                case Keys.DOWN:
                    // scroll down the item objectItems
                    inventory.setSelectedIndex(downIndex);

                    // display the corresponding description in descLabel
                    if (player.getInventory().size > 0)
                        desc = player.getInventory().get(downIndex).getDescription();
                    descLabel.setText(desc);

                    break;
                case Keys.I:
                    // if the inventory is currently opened, press I again
                    // to close

                    descLabel.remove(); // remove actors
                    inventory.remove();
                    titleLabel.remove();

                    state = STATE.ONGOING; // unpause the game

                    resetInputProcessor(); // reset InputProcessor to this MapControlRenderer
                    break;
                default:
                    break;
                }

                return true;
            }
        });
        stage.addActor(inventory);
        stage.setKeyboardFocus(inventory);

        Gdx.input.setInputProcessor(stage); // change InputProcessor to stage
    }

    // carry out search interaction
    private void searchInteraction() {
        // if no items
        if (objectItems == null) {
            if (indexForMessageTree == messageTree.get(0).size) {
                state = STATE.ONGOING; // unpause the game
                interactHappen = false; // flip the flag again
                typeInteraction = TYPE_INTERACTION.NO_INTERACTION; // reset type of interaction to
                                                                   // NO_INTERACTION
                indexForMessageTree = 0;
                dialogBoxLabel.remove();
                return;
            }

            // create conversation box
            // for the first message 
            if (indexForMessageTree == 0) {
                dialogBoxLabel = new Label(messageTree.get(0).get(0), PrisonBreakGame.gameSkin, "custom");
                dialogBoxLabel.setPosition(0, 0);
                dialogBoxLabel.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight() / 5);
                dialogBoxLabel.setAlignment(Align.topLeft);
                //                dialogBoxLabel.setFontScale(2f);
                stage.addActor(dialogBoxLabel);
            }

            // for every next message
            else {
                dialogBoxLabel.setText(messageTree.get(0).get(indexForMessageTree));
            }

            ++indexForMessageTree; // increment index

        }

        // if it does contain items
        else {
            //            Gdx.app.log("class: ", "" + objectItems.getClass());

            if (indexForMessageTree == messageTree.get(1).size) {
                state = STATE.ONGOING; // unpause the game
                interactHappen = false; // flip the flag again
                typeInteraction = TYPE_INTERACTION.NO_INTERACTION; // reset type of interaction to
                                                                   // NO_INTERACTION
                indexForMessageTree = 0;
                dialogBoxLabel.remove();
                return;
            }

            // in case Player choose "Nothing" -> does not pick anything
            if (indexForMessageTree > messageTree.get(1).size) {
                indexForMessageTree = messageTree.get(1).size;
                return;
            }

            // for the first message 
            if (indexForMessageTree == 0) {
                // create a String array of items' names
                Array<String> itemNames = new Array<String>();
                for (Item item : objectItems) {
                    itemNames.add(item.getItemName());
                }
                itemNames.add("Nothing"); // add additional value - for not choosing any items

                // create conversation box
                dialogBoxLabel = new Label(messageTree.get(1).get(0), PrisonBreakGame.gameSkin, "custom");
                dialogBoxLabel.setPosition(0, 0);
                dialogBoxLabel.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight() / 5);
                dialogBoxLabel.setAlignment(Align.topLeft);
                //                dialogBoxLabel.setFontScale(2f);
                stage.addActor(dialogBoxLabel);

                // create objectItems of items
                itemList = new List(PrisonBreakGame.gameSkin, "custom");
                itemList.setItems(itemNames);
                itemList.setSelectedIndex(0);
                itemList.setSize(Gdx.graphics.getWidth() / 4, Gdx.graphics.getHeight() / 3);
                itemList.setPosition(Gdx.graphics.getWidth() - itemList.getWidth(), dialogBoxLabel.getHeight());
                itemList.addListener(new InputListener() {
                    @Override
                    public boolean keyDown(InputEvent event, int keycode) {
                        int currentIndex = itemList.getSelectedIndex();
                        int upIndex = currentIndex - 1;
                        int downIndex = currentIndex + 1;

                        if (upIndex < 0)
                            upIndex = itemList.getItems().size - 1;
                        if (downIndex >= itemList.getItems().size)
                            downIndex = 0;

                        //                        Gdx.app.log("currentIndex: ", "" + currentIndex);
                        //                        Gdx.app.log("upIndex: ", "" + upIndex);
                        //                        Gdx.app.log("downIndex: ", "" + downIndex);

                        switch (keycode) {
                        case Keys.UP:
                            // scroll up the item list
                            itemList.setSelectedIndex(upIndex);
                            break;
                        case Keys.DOWN:
                            // scroll down the item list
                            itemList.setSelectedIndex(downIndex);
                            break;
                        case Keys.F:
                            int index = itemList.getSelectedIndex();

                            // if Player chooses "Nothing"
                            if ("Nothing".equals((String) itemList.getSelected())) {
                                // remove list of items from stage
                                itemList.remove();

                                // confirm the selection
                                dialogBoxLabel.setText("You choose nothing.");

                                // end the "conversation"
                                indexForMessageTree = messageTree.get(1).size + 1;
                                resetInputProcessor();
                                break;
                            }

                            // otherwise;
                            // add the chosen item into Player's objectItems of items
                            player.addItem(objectItems.get(index));

                            // remove that item from the overall listItems
                            objectItems.removeIndex(index);

                            // remove list of items from stage
                            itemList.remove();

                            // reconfirm message
                            dialogBoxLabel.setText(messageTree.get(1).get(indexForMessageTree));
                            resetInputProcessor();
                            break;
                        default:
                            break;
                        }

                        return true;
                    }
                });
                stage.addActor(itemList);
                stage.setKeyboardFocus(itemList);

                // set InputProcessor to stage
                Gdx.input.setInputProcessor(stage);
            }

            // for every next message
            else {
                dialogBoxLabel.setText(messageTree.get(1).get(indexForMessageTree)
                        + player.getInventory().get(player.getInventory().size - 1).getItemName() + ".");
            }

            ++indexForMessageTree; // increment index

        }
    }

    // carry out open/lock interaction
    private void openLockInteraction() {
        if (objectItems == null) {
            Gdx.app.log("Error in locked door: ", "objectItems == null");
            return;
        }

        // if the door is locked
        if (latestDoorLocked) {
            // end of "conversation" -> reset 
            if (indexForMessageTree == messageTree.get(2).size) {
                state = STATE.ONGOING;
                interactHappen = false;
                typeInteraction = TYPE_INTERACTION.NO_INTERACTION;
                indexForMessageTree = 0;

                neededKey = null;
                latestDoorLocked = false;
                //                latestObjectName = "";

                dialogBoxLabel.remove();
                return;
            }

            // first
            if (indexForMessageTree == 0) {
                // create conversation box
                dialogBoxLabel = new Label(messageTree.get(2).get(0), PrisonBreakGame.gameSkin, "custom");
                dialogBoxLabel.setPosition(0, 0);
                dialogBoxLabel.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight() / 5);
                dialogBoxLabel.setAlignment(Align.topLeft);
                stage.addActor(dialogBoxLabel);

                // check if Player have the needed "key" to unlock
                boolean haveIt = false;
                for (Item item : player.getInventory()) {
                    if (item.getItemID() == objectItems.get(0).getItemID()) {
                        haveIt = true;
                        neededKey = item;
                        break;
                    }
                }
                //                Gdx.app.log("haveIt: ", "" + haveIt);

                // if Player DOEST NOT have the neccessary key -> end the "conversation" dialog
                // otherwise; let it go on
                if (!haveIt) {
                    indexForMessageTree = messageTree.get(2).size - 1;
                }
            }
            // second
            else if (indexForMessageTree == 1) {
                dialogBoxLabel.setText(messageTree.get(2).get(1) + " " + neededKey.getItemName() + ".");
            }
            // third
            else if (indexForMessageTree == 2) {
                dialogBoxLabel.setText(messageTree.get(2).get(2));

                // create a List contain Yes/No options
                Array<String> options = new Array<String>();
                options.add("Yes");
                options.add("No");
                itemList = new List(PrisonBreakGame.gameSkin, "custom");
                itemList.setItems(options);
                itemList.setSelectedIndex(0);
                itemList.setSize(Gdx.graphics.getWidth() / 4, Gdx.graphics.getHeight() / 3);
                itemList.setPosition(Gdx.graphics.getWidth() - itemList.getWidth(), dialogBoxLabel.getHeight());
                itemList.addListener(new InputListener() {
                    @Override
                    public boolean keyDown(InputEvent event, int keycode) {
                        int currentIndex = itemList.getSelectedIndex();
                        int upIndex = currentIndex - 1;
                        int downIndex = currentIndex + 1;

                        if (upIndex < 0)
                            upIndex = itemList.getItems().size - 1;
                        if (downIndex >= itemList.getItems().size)
                            downIndex = 0;

                        switch (keycode) {
                        case Keys.UP:
                            // scroll up the option list
                            itemList.setSelectedIndex(upIndex);
                            break;
                        case Keys.DOWN:
                            // scroll down the option list
                            itemList.setSelectedIndex(downIndex);
                            break;
                        case Keys.F:
                            // if Player choose "Yes" -> unlock the door
                            if (itemList.getSelectedIndex() == 0) {
                                // set "locked" to false in object of DoorCollision
                                MapObject object = doorObjects.get(latestObjectName);
                                object.getProperties().put("locked", false);

                                // set "locked" to false in object of ObjectInteration
                                object = interactionObjects.get(latestObjectName);
                                object.getProperties().put("locked", false);

                                // set "locked" to false in object of ...
                            }
                            // if "No" -> end dialog "conversation"
                            else {
                                indexForMessageTree = messageTree.get(2).size; // end the dialog
                            }

                            // remove list of items from stage
                            itemList.remove();

                            resetInputProcessor();
                            break;
                        default:
                            break;
                        }

                        return true;
                    }
                });
                stage.addActor(itemList);
                stage.setKeyboardFocus(itemList);

                // set InputProcessor to stage
                Gdx.input.setInputProcessor(stage);
            }
            // others
            else {
                dialogBoxLabel.setText(messageTree.get(2).get(indexForMessageTree));
            }

            ++indexForMessageTree;
        }
        // if current door is not locked
        else {
            // end of "conversation" -> reset 
            if (indexForMessageTree == messageTree.get(3).size) {
                state = STATE.ONGOING;
                interactHappen = false;
                typeInteraction = TYPE_INTERACTION.NO_INTERACTION;
                indexForMessageTree = 0;

                neededKey = null;
                latestDoorLocked = false;
                //                latestObjectName = "";

                dialogBoxLabel.remove();
                return;
            }

            // first
            if (indexForMessageTree == 0) {
                // create conversation box
                dialogBoxLabel = new Label(messageTree.get(3).get(0), PrisonBreakGame.gameSkin, "custom");
                dialogBoxLabel.setPosition(0, 0);
                dialogBoxLabel.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight() / 5);
                dialogBoxLabel.setAlignment(Align.topLeft);
                stage.addActor(dialogBoxLabel);

                // check if Player have the requied key to lock the door
                boolean haveIt = false;
                for (Item item : player.getInventory()) {
                    if (item.getItemID() == objectItems.get(0).getItemID()) {
                        haveIt = true;
                        neededKey = item;
                    }
                }

                // if Player DOEST NOT have the neccessary key -> end the "conversation" dialog
                // otherwise; let it go on
                if (!haveIt) {
                    indexForMessageTree = messageTree.get(3).size - 1;
                }
            }
            // second
            else if (indexForMessageTree == 1) {
                dialogBoxLabel.setText(messageTree.get(3).get(1) + " " + neededKey.getItemName() + ".");
            }
            // third
            else if (indexForMessageTree == 2) {
                dialogBoxLabel.setText(messageTree.get(3).get(2));

                // create a List contain Yes/No options
                Array<String> options = new Array<String>();
                options.add("Yes");
                options.add("No");
                itemList = new List(PrisonBreakGame.gameSkin, "custom");
                itemList.setItems(options);
                itemList.setSelectedIndex(0);
                itemList.setSize(Gdx.graphics.getWidth() / 4, Gdx.graphics.getHeight() / 3);
                itemList.setPosition(Gdx.graphics.getWidth() - itemList.getWidth(), dialogBoxLabel.getHeight());
                itemList.addListener(new InputListener() {
                    @Override
                    public boolean keyDown(InputEvent event, int keycode) {
                        int currentIndex = itemList.getSelectedIndex();
                        int upIndex = currentIndex - 1;
                        int downIndex = currentIndex + 1;

                        if (upIndex < 0)
                            upIndex = itemList.getItems().size - 1;
                        if (downIndex >= itemList.getItems().size)
                            downIndex = 0;

                        switch (keycode) {
                        case Keys.UP:
                            // scroll up the item objectItems
                            itemList.setSelectedIndex(upIndex);
                            break;
                        case Keys.DOWN:
                            // scroll down the item objectItems
                            itemList.setSelectedIndex(downIndex);
                            break;
                        case Keys.F:
                            // if Player choose "Yes" -> lock the door
                            if (itemList.getSelectedIndex() == 0) {
                                // set "locked" to true in object of DoorCollision
                                MapObject object = doorObjects.get(latestObjectName);
                                object.getProperties().put("locked", true);

                                // set "locked" to true in object of ObjectInteration
                                object = interactionObjects.get(latestObjectName);
                                object.getProperties().put("locked", true);

                                // set "locked" to true in object of ...
                            }
                            // if "No" -> end dialog "conversation"
                            else {
                                indexForMessageTree = messageTree.get(3).size; // end the dialog
                            }

                            // remove list of items from stage
                            itemList.remove();

                            resetInputProcessor();
                            break;
                        default:
                            break;
                        }

                        return true;
                    }
                });
                stage.addActor(itemList);
                stage.setKeyboardFocus(itemList);

                // set InputProcessor to stage
                Gdx.input.setInputProcessor(stage);
            }
            // others
            else {
                dialogBoxLabel.setText(messageTree.get(3).get(indexForMessageTree));
            }

            ++indexForMessageTree;
        }
    }

    // carry out read interaction
    private void readInteraction() {
        if (objectItems == null) {
            Gdx.app.log("Error in read_interaction: ", "objectItems == null");
            return;
        }

        // end of conversation -> reset
        if (indexForMessageTree == messageTree.get(4).size) {
            state = STATE.ONGOING;
            interactHappen = false;
            typeInteraction = TYPE_INTERACTION.NO_INTERACTION;
            indexForMessageTree = 0;

            dialogBoxLabel.remove(); // remove actors from stage
            return;
        }

        // first
        if (indexForMessageTree == 0) {
            // create conversation box
            dialogBoxLabel = new Label(messageTree.get(4).get(0) + latestObjectName + ".", PrisonBreakGame.gameSkin,
                    "custom");
            dialogBoxLabel.setPosition(0, 0);
            dialogBoxLabel.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight() / 5);
            dialogBoxLabel.setAlignment(Align.topLeft);
            stage.addActor(dialogBoxLabel);
        }
        // second -> display along the message written on the object
        else if (indexForMessageTree == 1) {
            // set the test of conversation box
            dialogBoxLabel.setText(messageTree.get(4).get(1));

            // create description/message box to display the written message
            descLabel = new Label("\"" + objectItems.get(0).getDescription() + "\"", PrisonBreakGame.gameSkin,
                    "custom");
            descLabel.setSize(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
            descLabel.setPosition(Gdx.graphics.getWidth() / 2 - descLabel.getWidth() / 2,
                    Gdx.graphics.getHeight() * 3 / 5 - descLabel.getHeight() / 2);
            descLabel.setAlignment(Align.topLeft);
            descLabel.setWrap(true);
            descLabel.addListener(new InputListener() {
                @Override
                public boolean keyDown(InputEvent event, int keycode) {
                    // if user presses F again, "turn off" the displayed message
                    if (keycode == Keys.F) {
                        descLabel.remove(); // remove the message label
                        resetInputProcessor(); // reset the InputProcessor to MapControlRenderer
                        return true;
                    }

                    return false;
                }
            });
            stage.addActor(descLabel);
            stage.setKeyboardFocus(descLabel);

            // set InputProcessor to stage
            Gdx.input.setInputProcessor(stage);
        }
        // others
        else {
            dialogBoxLabel.setText(messageTree.get(4).get(indexForMessageTree));
        }

        ++indexForMessageTree;
    }

    // carry out password-unlock interaction (using password to unlock objects)
    private void passUnlockInteraction() {
        // for door object
        if (objectItems.size == 1) {
            // end of "conversation" -> reset
            if (indexForMessageTree == messageTree.get(5).size) {
                state = STATE.ONGOING;
                interactHappen = false;
                typeInteraction = TYPE_INTERACTION.NO_INTERACTION;
                indexForMessageTree = 0;

                dialogBoxLabel.remove();
                return;
            }

            // first
            if (indexForMessageTree == 0) {
                // create conversation box
                dialogBoxLabel = new Label(messageTree.get(5).get(0), PrisonBreakGame.gameSkin, "custom");
                dialogBoxLabel.setPosition(0, 0);
                dialogBoxLabel.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight() / 5);
                dialogBoxLabel.setAlignment(Align.topLeft);
                stage.addActor(dialogBoxLabel);

                // check if Player have required card to unlock (obtained from the uniform)
                boolean haveIt = false;
                for (Item item : player.getInventory()) {
                    if (item.getItemID() == 3) { // the required card in uniform - id = 3
                        haveIt = true;
                        break;
                    }
                }

                // if not
                if (!haveIt) {
                    indexForMessageTree = messageTree.get(5).size - 1;
                }
            }
            // second
            else if (indexForMessageTree == 1) {
                dialogBoxLabel.setText(messageTree.get(5).get(1));

                // create a List contain Yes/No options
                Array<String> options = new Array<String>();
                options.add("Yes");
                options.add("No");
                itemList = new List(PrisonBreakGame.gameSkin, "custom");
                itemList.setItems(options);
                itemList.setSelectedIndex(0);
                itemList.setSize(Gdx.graphics.getWidth() / 4, Gdx.graphics.getHeight() / 3);
                itemList.setPosition(Gdx.graphics.getWidth() - itemList.getWidth(), dialogBoxLabel.getHeight());
                itemList.addListener(new InputListener() {
                    @Override
                    public boolean keyDown(InputEvent event, int keycode) {
                        int currentIndex = itemList.getSelectedIndex();
                        int upIndex = currentIndex - 1;
                        int downIndex = currentIndex + 1;

                        if (upIndex < 0)
                            upIndex = itemList.getItems().size - 1;
                        if (downIndex >= itemList.getItems().size)
                            downIndex = 0;

                        switch (keycode) {
                        case Keys.UP:
                            // scroll up the option list
                            itemList.setSelectedIndex(upIndex);
                            break;
                        case Keys.DOWN:
                            // scroll down the option list
                            itemList.setSelectedIndex(downIndex);
                            break;
                        case Keys.F:
                            // if Player chooses "No" -> end the dialog
                            // otherwise; let it continue on
                            if (itemList.getSelectedIndex() == 1) {
                                indexForMessageTree = messageTree.get(5).size;
                            }

                            // remove list of items from stage
                            itemList.remove();

                            resetInputProcessor();
                            break;
                        default:
                            break;
                        }

                        return true;
                    }
                });
                stage.addActor(itemList);
                stage.setKeyboardFocus(itemList);

                // set InputProcessor to stage
                Gdx.input.setInputProcessor(stage);
            }
            // third
            else if (indexForMessageTree == 2) {
                dialogBoxLabel.setText(messageTree.get(5).get(2));

                // create a TextField for player to enter password
                passField = new TextField("Full name", PrisonBreakGame.gameSkin, "default");
                passField.setHeight(25);
                passField.setPosition(Gdx.graphics.getWidth() / 2 - passField.getWidth() / 2,
                        Gdx.graphics.getHeight() / 2 - passField.getHeight() / 2);
                stage.addActor(passField);

                // create button, goes with the textfield
                txtButton = new TextButton("Enter", PrisonBreakGame.gameSkin, "round");
                txtButton.setHeight(passField.getHeight());
                txtButton.setPosition(passField.getWidth() + passField.getX(), passField.getY());
                txtButton.addListener(new ClickListener() {
                    @Override
                    public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                        // if player enters correct name of any guards -> unlock the door
                        // note: in this case, we only check for the name 'Henry Karvick'
                        String enterName = passField.getText();
                        if (enterName.equalsIgnoreCase(objectItems.first().getDescription())) {
                            // unlock the door
                            // set "locked" to false in object of DoorCollision
                            MapObject object = doorObjects.get(latestObjectName);
                            object.getProperties().put("locked", false);

                            // set "locked" to false in object of ObjectInteration
                            object = interactionObjects.get(latestObjectName);
                            object.getProperties().put("locked", false);

                            dialogBoxLabel.setText("Valid name.");
                        }
                        // enter wrong name
                        else {
                            dialogBoxLabel.setText("Invalid name.");

                            // end the dialog
                            indexForMessageTree = messageTree.get(5).size;
                        }

                        passField.remove(); // remove actors from stage
                        txtButton.remove();

                        resetInputProcessor(); // reset InputProcessor to MapControlRenderer

                        return true;
                    }
                });
                stage.addActor(txtButton);

                Gdx.input.setInputProcessor(stage);
            }
            // others
            else {
                dialogBoxLabel.setText(messageTree.get(5).get(indexForMessageTree));
            }

            ++indexForMessageTree;
        }

        // for safe locker object
        else {
            // end of "conversation" -> reset
            if (indexForMessageTree == messageTree.get(6).size) {
                state = STATE.ONGOING;
                interactHappen = false;
                typeInteraction = TYPE_INTERACTION.NO_INTERACTION;
                indexForMessageTree = 0;

                dialogBoxLabel.remove();
                return;
            }

            // first
            if (indexForMessageTree == 0) {
                // create conversation box
                dialogBoxLabel = new Label(messageTree.get(6).get(0), PrisonBreakGame.gameSkin, "custom");
                dialogBoxLabel.setPosition(0, 0);
                dialogBoxLabel.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight() / 5);
                dialogBoxLabel.setAlignment(Align.topLeft);
                stage.addActor(dialogBoxLabel);

                // create a TextField + TextButton -> prompt for password to unlock the safe locker
                // textfield
                passField = new TextField("", PrisonBreakGame.gameSkin, "default");
                passField.setHeight(25);
                passField.setPosition(Gdx.graphics.getWidth() / 2 - passField.getWidth() / 2,
                        Gdx.graphics.getHeight() / 2 - passField.getHeight() / 2);
                stage.addActor(passField);

                // textbutton
                txtButton = new TextButton("Enter", PrisonBreakGame.gameSkin, "round");
                txtButton.setHeight(passField.getHeight());
                txtButton.setPosition(passField.getWidth() + passField.getX(), passField.getY());
                txtButton.addListener(new ClickListener() {
                    @Override
                    public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                        // if Player enters the correct password -> unlock the safe locker
                        //      -> Player obtains all items in it
                        // password: 3863 - get from object in the map
                        String enterPass = passField.getText();
                        if (enterPass.equalsIgnoreCase(objectItems.first().getDescription())) {
                            // unlock the safe locker
                            // set "locked" to false in object of ObjectInteraction
                            MapObject object = interactionObjects.get(latestObjectName);
                            object.getProperties().put("locked", false);

                            dialogBoxLabel.setText("*Clicked*");
                        }
                        // enter wrong password
                        else {
                            dialogBoxLabel.setText("Wrong password!");

                            // end the dialog
                            indexForMessageTree = messageTree.get(6).size;
                        }

                        passField.remove(); // remove actors from stage
                        txtButton.remove();

                        resetInputProcessor(); // reset InputProcessor to MapControlRenderer

                        return true;
                    }
                });
                stage.addActor(txtButton);

                Gdx.input.setInputProcessor(stage);
            }
            // last - message when obtain items
            else if (indexForMessageTree == messageTree.get(6).size - 1) {
                dialogBoxLabel.setText(messageTree.get(6).get(indexForMessageTree)
                        + objectItems.get(indexRemainingItems).getItemName());

                // add the corresponding item into Player's inventory
                //                player.getInventory().add(objectItems.get(indexRemainingItems++));
                player.addItem(objectItems.get(indexRemainingItems++));

                if (indexRemainingItems < objectItems.size) {
                    --indexForMessageTree;
                }
            }
            // others
            else {
                dialogBoxLabel.setText(messageTree.get(6).get(indexForMessageTree));
            }

            ++indexForMessageTree;
        }
    }

    // carry out break interaction (crack the door open)
    private void breakInteraction() {
        // end of "conversation" -> reset
        if (indexForMessageTree == messageTree.get(7).size) {
            state = STATE.ONGOING;
            interactHappen = false;
            typeInteraction = TYPE_INTERACTION.NO_INTERACTION;
            indexForMessageTree = 0;

            dialogBoxLabel.remove();
            return;
        }

        // first
        if (indexForMessageTree == 0) {
            // create conversation box
            dialogBoxLabel = new Label(messageTree.get(7).get(0), PrisonBreakGame.gameSkin, "custom");
            dialogBoxLabel.setPosition(0, 0);
            dialogBoxLabel.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight() / 5);
            dialogBoxLabel.setAlignment(Align.topLeft);
            stage.addActor(dialogBoxLabel);

            // check if Player has the required item to break the door
            boolean haveIt = false;
            for (Item item : player.getInventory()) {
                if (item.getItemID() == objectItems.first().getItemID()) {
                    haveIt = true;
                    break;
                }
            }

            // if not -> end the dialog
            if (!haveIt) {
                indexForMessageTree = messageTree.get(7).size - 1;
            }
        }
        // third
        else if (indexForMessageTree == 2) {
            dialogBoxLabel.setText(messageTree.get(7).get(2));

            // create a List contain Yes/No options
            Array<String> options = new Array<String>();
            options.add("Yes");
            options.add("No");
            itemList = new List(PrisonBreakGame.gameSkin, "custom");
            itemList.setItems(options);
            itemList.setSelectedIndex(0);
            itemList.setSize(Gdx.graphics.getWidth() / 4, Gdx.graphics.getHeight() / 3);
            itemList.setPosition(Gdx.graphics.getWidth() - itemList.getWidth(), dialogBoxLabel.getHeight());
            itemList.addListener(new InputListener() {
                @Override
                public boolean keyDown(InputEvent event, int keycode) {
                    int currentIndex = itemList.getSelectedIndex();
                    int upIndex = currentIndex - 1;
                    int downIndex = currentIndex + 1;

                    if (upIndex < 0)
                        upIndex = itemList.getItems().size - 1;
                    if (downIndex >= itemList.getItems().size)
                        downIndex = 0;

                    switch (keycode) {
                    case Keys.UP:
                        // scroll up the option list
                        itemList.setSelectedIndex(upIndex);
                        break;
                    case Keys.DOWN:
                        // scroll down the option list
                        itemList.setSelectedIndex(downIndex);
                        break;
                    case Keys.F:
                        // if Player choose "Yes" -> crack the door open
                        if (itemList.getSelectedIndex() == 0) {
                            // remove the door
                            // extract neccessary attributes
                            MapObject object = doorObjects.get(latestObjectName);
                            String layerName = object.getProperties().get("tileLayerName", "", String.class);
                            TiledMapTileLayer layer = (TiledMapTileLayer) map.getLayers().get(layerName);
                            int x = object.getProperties().get("lowerLeftX", Integer.class);
                            int y = object.getProperties().get("lowerLeftY", Integer.class);

                            // extract the tiles of the door
                            tile1 = layer.getCell(x, y).getTile(); // lower left
                            tile2 = layer.getCell(x + 1, y).getTile(); // lower right
                            tile3 = layer.getCell(x, y + 1).getTile(); // upper left
                            tile4 = layer.getCell(x + 1, y + 1).getTile(); // upper right

                            // "erase" the door
                            layer.getCell(x, y).setTile(null);
                            layer.getCell(x + 1, y).setTile(null);
                            layer.getCell(x, y + 1).setTile(null);
                            layer.getCell(x + 1, y + 1).setTile(null);

                            // set "locked" to false in object of DoorCollision
                            object = doorObjects.get(latestObjectName);
                            object.getProperties().put("locked", false);

                            // set "broken" to true in object of ObjectInteration
                            object = interactionObjects.get(latestObjectName);
                            object.getProperties().put("broken", true);
                        }
                        // if "No" -> end dialog "conversation"
                        else {
                            indexForMessageTree = messageTree.get(7).size; // end the dialog
                        }

                        // remove list of items from stage
                        itemList.remove();

                        resetInputProcessor();
                        break;
                    default:
                        break;
                    }

                    return true;
                }
            });
            stage.addActor(itemList);
            stage.setKeyboardFocus(itemList);

            // set InputProcessor to stage
            Gdx.input.setInputProcessor(stage);
        }
        // others
        else {
            dialogBoxLabel.setText(messageTree.get(7).get(indexForMessageTree));
        }

        ++indexForMessageTree;
    }

    @Override
    public void render() {
        if (state == STATE.ONGOING) {
            player.update();
            moveCamera();

            // check if Player is detected by the Guards
            for (Guard guard : guards) {
                if (guard instanceof PatrolGuard) {
                    ((PatrolGuard) guard).update();
                }

                if (guard.detectPlayer()) {
                    //                    Gdx.app.log("Game over", "");
                    loseGame();
                    break;
                }
            }

            // search for the door in contact
            for (Object o : doorObjects) {
                RectangleMapObject r = null;
                if (o instanceof RectangleMapObject) {
                    r = (RectangleMapObject) o;
                }

                if (r != null && r.getRectangle().overlaps(player.getSprite().getBoundingRectangle())) {
                    currentDoor = r;
                    break;
                }
            }

            // the door "disappears" when Player steps in
            // and "showed" again when Player steps out
            if (currentDoor != null) {
                int x = currentDoor.getProperties().get("lowerLeftX", Integer.class);
                int y = currentDoor.getProperties().get("lowerLeftY", Integer.class);
                String name = currentDoor.getProperties().get("tileLayerName", String.class);
                TiledMapTileLayer layer = (TiledMapTileLayer) map.getLayers().get(name);

                // if the door has not been hidden, and Player steps in -> hide the door
                if (!currentDoor.getProperties().get("locked", Boolean.class) && !doorHidden
                        && currentDoor.getRectangle().overlaps(player.getSprite().getBoundingRectangle())) {
                    // extract the tiles
                    tile1 = layer.getCell(x, y).getTile(); // lower left
                    tile2 = layer.getCell(x + 1, y).getTile(); // lower right
                    tile3 = layer.getCell(x, y + 1).getTile(); // upper left
                    tile4 = layer.getCell(x + 1, y + 1).getTile(); // upper right

                    // "hide" the door when Player steps in
                    layer.getCell(x, y).setTile(null);
                    layer.getCell(x + 1, y).setTile(null);
                    layer.getCell(x, y + 1).setTile(null);
                    layer.getCell(x + 1, y + 1).setTile(null);

                    doorHidden = true; // set the flag
                }

                // if the door is currently hidden, and Player steps out -> show it
                if (!currentDoor.getProperties().get("locked", Boolean.class) && doorHidden
                        && !currentDoor.getRectangle().overlaps(player.getSprite().getBoundingRectangle())) {
                    // "show" the door when Player steps out
                    layer.getCell(x, y).setTile(tile1);
                    layer.getCell(x + 1, y).setTile(tile2);
                    layer.getCell(x, y + 1).setTile(tile3);
                    layer.getCell(x + 1, y + 1).setTile(tile4);

                    doorHidden = false;
                }
            }

            // update winning state
            if (winGame())
                state = STATE.WIN;
        }

        shapeRenderer.setProjectionMatrix(camera.combined);

        beginRender();

        int currentLayer = 0;
        for (MapLayer layer : map.getLayers()) {
            if (layer.isVisible()) {
                // tile layer
                if (layer instanceof TiledMapTileLayer) {
                    renderTileLayer((TiledMapTileLayer) layer);
                    ++currentLayer;

                    // layer to draw characters and guards
                    if (currentLayer == drawSpritesAfterLayer) {
                        // draw player
                        player.getSprite().draw(this.getBatch());

                        // draw all the guards
                        for (Guard guard : guards) {
                            guard.getSprite().draw(this.getBatch());
                        }
                    }
                    // layer to draw guard's detection area
                    // note: even though detection area is RECTANGLE
                    //      , draw a POLYGON instead (POLYGON still inside the RECTANGLE)                            
                    if (currentLayer == drawDetectionAreaAfterLayer) {
                        for (Guard guard : guards) {
                            endRender();

                            // first, determine the four vertices
                            float nearLeftX, nearLeftY, nearRightX, nearRightY, // "nearer" points in Guard's POV
                                    farLeftX, farLeftY, farRightX, farRightY; // "further" points in Guard's POV
                            if (guard.getCurrentDirection().equalsIgnoreCase("up")) {
                                nearLeftX = guard.getDetectArea().x + guard.getDetectArea().width / 4;
                                nearRightX = guard.getDetectArea().x + guard.getDetectArea().width * 3 / 4;
                                nearLeftY = nearRightY = guard.getDetectArea().y;

                                farLeftX = guard.getDetectArea().x;
                                farRightX = guard.getDetectArea().x + guard.getDetectArea().width;
                                farLeftY = farRightY = guard.getDetectArea().y + guard.getDetectArea().height;
                            } else if (guard.getCurrentDirection().equalsIgnoreCase("down")) {
                                nearLeftX = guard.getDetectArea().x + guard.getDetectArea().width * 3 / 4;
                                nearRightX = guard.getDetectArea().x + guard.getDetectArea().width / 4;
                                nearLeftY = nearRightY = guard.getDetectArea().y + guard.getDetectArea().height;

                                farLeftX = guard.getDetectArea().x + guard.getDetectArea().width;
                                farRightX = guard.getDetectArea().x;
                                farLeftY = farRightY = guard.getDetectArea().y;
                            } else if (guard.getCurrentDirection().equalsIgnoreCase("right")) {
                                nearLeftY = guard.getDetectArea().y + guard.getDetectArea().height * 3 / 4;
                                nearRightY = guard.getDetectArea().y + guard.getDetectArea().height / 4;
                                nearLeftX = nearRightX = guard.getDetectArea().x;

                                farLeftY = guard.getDetectArea().y + guard.getDetectArea().height;
                                farRightY = guard.getDetectArea().y;
                                farLeftX = farRightX = guard.getDetectArea().x + guard.getDetectArea().width;
                            } else if (guard.getCurrentDirection().equalsIgnoreCase("left")) {
                                nearLeftY = guard.getDetectArea().y + guard.getDetectArea().height / 4;
                                nearRightY = guard.getDetectArea().y + guard.getDetectArea().height * 3 / 4;
                                nearLeftX = nearRightX = guard.getDetectArea().x + guard.getDetectArea().width;

                                farLeftY = guard.getDetectArea().y;
                                farRightY = guard.getDetectArea().y + guard.getDetectArea().height;
                                farLeftX = farRightX = guard.getDetectArea().x;
                            } else {
                                nearLeftX = nearLeftY = nearRightX = nearRightY = 0;
                                farLeftX = farLeftY = farRightY = farRightX = 0;
                            }

                            // draw detection area
                            Gdx.gl.glEnable(GL20.GL_BLEND);
                            Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
                            shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
                            shapeRenderer.setColor(1, 0, 0, 0.5f);
                            if (guard.getCurrentDirection().equalsIgnoreCase("up")) {
                                // the body (rectangle) of the polygon
                                shapeRenderer.rect(nearLeftX, nearLeftY, nearRightX - nearLeftX,
                                        farLeftY - nearLeftY);

                                // the left side (triangle) of the polygon
                                shapeRenderer.triangle(nearLeftX, nearLeftY, farLeftX, farLeftY, nearLeftX,
                                        farLeftY);

                                // the right side (triangle) of the polygon
                                shapeRenderer.triangle(nearRightX, nearRightY, farRightX, farRightY, nearRightX,
                                        farRightY);
                            } else if (guard.getCurrentDirection().equalsIgnoreCase("down")) {
                                // the body (rectangle) of the polygon
                                shapeRenderer.rect(nearRightX, farRightY, nearLeftX - nearRightX,
                                        nearRightY - farRightY);

                                // the left side (triangle) of the polygon
                                shapeRenderer.triangle(nearLeftX, nearLeftY, farLeftX, farLeftY, nearLeftX,
                                        farLeftY);

                                // the right side (triangle) of the polygon
                                shapeRenderer.triangle(nearRightX, nearRightY, farRightX, farRightY, nearRightX,
                                        farRightY);
                            } else if (guard.getCurrentDirection().equalsIgnoreCase("right")) {
                                // the body (rectangle) of the polygon
                                shapeRenderer.rect(nearRightX, nearRightY, farRightX - nearRightX,
                                        nearLeftY - nearRightY);

                                // the left side (triangle) of the polygon
                                shapeRenderer.triangle(nearLeftX, nearLeftY, farLeftX, farLeftY, farLeftX,
                                        nearLeftY);

                                // the right side (triangle) of the polygon
                                shapeRenderer.triangle(nearRightX, nearRightY, farRightX, farRightY, farRightX,
                                        nearRightY);
                            } else if (guard.getCurrentDirection().equalsIgnoreCase("left")) {
                                // the body (rectangle) of the polygon
                                shapeRenderer.rect(farLeftX, nearLeftY, nearLeftX - farLeftX,
                                        nearRightY - nearLeftY);

                                // the left side (triangle) of the polygon
                                shapeRenderer.triangle(nearLeftX, nearLeftY, farLeftX, farLeftY, farLeftX,
                                        nearLeftY);

                                // the right side (triangle) of the polygon
                                shapeRenderer.triangle(nearRightX, nearRightY, farRightX, farRightY, farRightX,
                                        nearRightY);
                            }

                            shapeRenderer.end();
                            Gdx.gl.glDisable(GL20.GL_BLEND);

                            beginRender();
                        }
                    }
                }
                // object layer
                else {
                    for (MapObject object : layer.getObjects()) {
                        renderObject(object);
                    }
                }
            }
        }

        endRender();

        stage.act();
        stage.draw();
    }

    @Override
    public boolean keyDown(int keycode) {
        switch (keycode) {
        case Input.Keys.LEFT:
        case Input.Keys.A:
            player.setLeftMove(true);
            break;
        case Input.Keys.RIGHT:
        case Input.Keys.D:
            player.setRightMove(true);
            break;
        case Input.Keys.UP:
        case Input.Keys.W:
            player.setUpMove(true);
            break;
        case Input.Keys.DOWN:
        case Input.Keys.S:
            player.setDownMove(true);
            break;
        case Input.Keys.SPACE:
            player.sleepWakeup();
            break;
        case Input.Keys.I:
            Gdx.app.log("Key I: ", "typed");
            if (state != STATE.PAUSE) {
                showHideInventory();
                state = STATE.PAUSE; // pause the game temporarily
            }
            break;
        }

        return true;
    }

    @Override
    public boolean keyUp(int keycode) {
        switch (keycode) {
        case Input.Keys.LEFT:
        case Input.Keys.A:
            player.setLeftMove(false);
            break;
        case Input.Keys.RIGHT:
        case Input.Keys.D:
            player.setRightMove(false);
            break;
        case Input.Keys.UP:
        case Input.Keys.W:
            player.setUpMove(false);
            break;
        case Input.Keys.DOWN:
        case Input.Keys.S:
            player.setDownMove(false);
            break;
        case Input.Keys.ESCAPE:
            break;
        case Input.Keys.F:
            //                Gdx.app.log("Interaction flag: ", "" + interactHappen);
            // if first time
            if (indexForMessageTree == 0) {
                objectItems = triggerInteraction(); // trigger interaction with the object   
                                                    // get list of items (if any) at the current object
            }

            // if no interactions with any objects -> break
            // otherwise; pause the game
            if (!interactHappen) {
                break;
            } else {
                state = STATE.PAUSE; // temporarily "pause" the game -> Player can't move
            }

            // for search_interaction
            if (typeInteraction == TYPE_INTERACTION.SEARCH_INTERACTION) {
                searchInteraction();
            }

            // for open_lock_interaction (with door)
            else if (typeInteraction == TYPE_INTERACTION.OPEN_LOCK_INTERACTION) {
                openLockInteraction();
            }

            // for read_interaction
            else if (typeInteraction == TYPE_INTERACTION.READ_INTERACTION) {
                readInteraction();
            }

            // for pass_unlock_interaction
            else if (typeInteraction == TYPE_INTERACTION.PASS_UNLOCK_INTERACTION) {
                passUnlockInteraction();
            }

            // for break_interaction
            else if (typeInteraction == TYPE_INTERACTION.BREAK_INTERACTION) {
                breakInteraction();
            }

            break;

        default:
            break;
        }

        return true;
    }

    @Override
    public boolean keyTyped(char character) {
        return false;
    }

    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        return false;
    }

    @Override
    public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        return false;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {
        return false;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        return false;
    }

    @Override
    public boolean scrolled(int amount) {
        return false;
    }

    @Override
    public void dispose() {
        player.dispose();
        map.dispose();
        stage.dispose();
        shapeRenderer.dispose();
        for (int i = 0; i < guards.size; ++i) {
            guards.get(i).dispose();
        }
    }

}