public enum BattleEvent {
    OPPONENT_ADDED,
    OPPONENT_HIT_DAMAGE,
    OPPONENT_DEFEATED,
    OPPONENT_TURN_DONE,
    PLAYER_HIT_DAMAGE,
    PLAYER_RUNNING,
    PLAYER_TURN_DONE,
    PLAYER_TURN_START,
    PLAYER_USED_MAGIC,
    NONE
} public class WorldController extends InputAdapter {

    private static final String TAG = WorldController.class.getName();
    /**
     * main camera
     **/
    public OrthographicCamera camera = null;

    public OrthographicCamera hudCamera = null;
    private InputMultiplexer multiplexer;
    public Skill skill;
    /**
     * head-up-display
     */
    public HUDScreen hudScreen;

    public MapsManager mapMgr;
    public Role player;
    private boolean isGameOver = false;
    //A path
    private Array<MyNode> path = new Array<MyNode>(true, 10);
    public Aim aim;

    public WorldController() {
        init();
    }

    public void resetGame() {
        init();
    }

    private void init() {
        camera = new OrthographicCamera(Constants.VIEWPORT_WIDTH, Constants.VIEWPORT_HEIGHT);
        camera.position.set(0, 0, 0);
        camera.update();

        isGameOver = false;
        player = new Role("lante");
        mapMgr = new MapsManager(player);

        hudCamera = new OrthographicCamera();
        hudCamera.setToOrtho(false, Constants.VIEWPORT_WIDTH, Constants.VIEWPORT_HEIGHT);//physical world
        hudCamera.update();
        hudScreen = new HUDScreen(this, player);
        multiplexer = new InputMultiplexer();
        multiplexer.addProcessor(hudScreen.getStage());
        multiplexer.addProcessor(this);
        input.setInputProcessor(multiplexer);
    }

    public void update(final float deltaTime) {
        if (player.isArrived()) {
            aim = null;
        }
        if (aim != null) {
            aim.update(deltaTime);
        }
        int cx = MathUtils.floor(player.getX() / 32);
        int cy = MathUtils.floor(player.getY() / 32);
        if (isCollisionWithTrap(cx, cy)) {
            //
            Gdx.app.debug(TAG, "Trap");
        }
        if (isCollisionWithEnemy(mapMgr)) {
//            Gdx.app.debug(TAG,"Let's Fight!");
        }
        player.update(deltaTime);
        for (Npc npc : mapMgr.npcs) {
            npc.update(deltaTime);
//            npc.randomMove(this);
        }
        for (Enemy e : mapMgr.enemies) {
            e.update(deltaTime);
//            e.randomMove(this);
        }
        if (skill != null) skill.update(deltaTime);
        if (skill != null && skill.isEnded()) skill = null;
        //scan fight
        //1.for player: player distance 1 has enemy,fight it
        if (fighting == 0 && player.getState() == Role.State.IDLE) {
            final Enemy enemy = scanTarget();
            if (enemy != null) {
                fighting = 1;
                //stop walk
                path.clear();
                aim = null;
//                player.clearPathAndStop();
                //player attack enemy
                player.setState(Role.State.ATTACK);
                Timer.schedule(new Timer.Task() {
                    @Override
                    public void run() {
                        int damage = MathUtils.random(15, 25);
                        if (enemy.getHealthPoint() > 0) {
                            enemy.setHealthPoint(enemy.getHealthPoint() - damage);
                            Gdx.app.debug(TAG,"you hit "+enemy.getEntityId()+damage+" points,it left:"+(enemy.getHealthPoint() - damage));
                        }
                        //if dead
                        if(enemy.getHealthPoint()<=0){
                            //remove from enemy
                            mapMgr.enemies.remove(enemy);
                        }
                        fighting = 0;
                        if (player.getState() == Role.State.ATTACK) {
                            player.setState(Role.State.IDLE);
                        }
                    }
                }, 0.5f);
            }
        }
        //2. for Enemy,
        for (final Enemy e : mapMgr.enemies) {
            Vector2 from = player.getPosInMap();
            Vector2 to = e.getPosInMap();
            float distance = (from.x - to.x) * (from.x - to.x) + (from.y - to.y) * (from.y - to.y);
            if (distance <= 2) {
                if (cycleTime == 0) {
                    e.setState(Role.State.ATTACK);
                    cycleTime = 1;
                    Timer.schedule(new Timer.Task() {
                        @Override
                        public void run() {
                            int damage = MathUtils.random(10, 15);
                            Gdx.app.debug(TAG,e.getEntityId()+" hit you "+damage+" points,you left:"+(player.getHealthPoint() - damage));
                            if (player.getHealthPoint() > 0) {
                                player.setHealthPoint(player.getHealthPoint() - damage);
                            }
                            //if dead
                            if(player.getHealthPoint()<=0){
                                //game over
                                isGameOver = true;
                            }
                            cycleTime = 0;
                        }
                    }, 0.5f);
                }
            }else {
                if (e.getState() == Role.State.ATTACK) {
                    e.setState(Role.State.IDLE);
                    cycleTime = 0;
                }
            }
        }

        //forbidden keyboard
//        handleDebugInput(deltaTime);
        //follow the Player
        float x = player.getX();
        float y = player.getY();
        camera.position.set(x, y, 0f);
        //make sure camera in map
        offsetCamera(mapMgr.width, mapMgr.height, camera);
        camera.update();
    }

    private int fighting = 0;
    private float cycleTime = 0;

    private Enemy scanTarget() {
        List<Enemy> temp = new ArrayList<Enemy>();
        for (Enemy e : mapMgr.enemies) {
            Vector2 from = player.getPosInMap();
            Vector2 to = e.getPosInMap();
            float distance = (from.x - to.x) * (from.x - to.x) + (from.y - to.y) * (from.y - to.y);
            if (distance <= 2) {
                temp.add(e);
            }
        }
        if (temp.size() <= 0) {
            return null;
        }
        if (temp.size() == 1) {
            return temp.get(0);
        }
        Collections.sort(temp, new Comparator<Role>() {
            @Override
            public int compare(Role lhs, Role rhs) {
                // -1 - less than, 1 - greater than, 0 - equal, all inversed for descending
                return lhs.getHealthPoint() > rhs.getHealthPoint() ? -1 : (lhs.getHealthPoint() < rhs.getHealthPoint()) ? 1 : 0;
            }
        });
        return temp.get(0);
    }

    private void offsetCamera(int mapWidth, int mapHeight, Camera cam) {
        // These values likely need to be scaled according to your world coordinates.
        // The left boundary of the map (x)
        int mapLeft = 0;
        // The right boundary of the map (x + width)
        int mapRight = 0 + mapWidth;
        // The bottom boundary of the map (y)
        int mapBottom = 0;
        // The top boundary of the map (y + height)
        int mapTop = 0 + mapHeight;
        // The camera dimensions, halved
        float cameraHalfWidth = cam.viewportWidth * .5f;
        float cameraHalfHeight = cam.viewportHeight * .5f;

        // Move camera after player as normal

        float cameraLeft = cam.position.x - cameraHalfWidth;
        float cameraRight = cam.position.x + cameraHalfWidth;
        float cameraBottom = cam.position.y - cameraHalfHeight;
        float cameraTop = cam.position.y + cameraHalfHeight;

        // Horizontal axis
        if (mapWidth < cam.viewportWidth) {
            cam.position.x = mapRight / 2;
        } else if (cameraLeft <= mapLeft) {
            cam.position.x = mapLeft + cameraHalfWidth;
        } else if (cameraRight >= mapRight) {
            cam.position.x = mapRight - cameraHalfWidth;
        }

        // Vertical axis
        if (mapHeight < cam.viewportHeight) {
            cam.position.y = mapTop / 2;
        } else if (cameraBottom <= mapBottom) {
            cam.position.y = mapBottom + cameraHalfHeight;
        } else if (cameraTop >= mapTop) {
            cam.position.y = mapTop - cameraHalfHeight;
        }
    }

    private void handleDebugInput(float delta) {
        if (Gdx.app.getType() != ApplicationType.Desktop) return;

        // Selected Sprite Controls When Idle
        if (player.getState() == Role.State.IDLE) {
            int x = MathUtils.floor(player.getX() / 32);
            int y = MathUtils.floor(player.getY() / 32);
            //triggle
            if (isCollisionWithTrap(x, y)) {
                return;
            }
            //Keyboard input
            if (input.isKeyPressed(Keys.A) || input.isKeyPressed(Keys.LEFT)) {
                //Gdx.app.debug(TAG, "LEFT key");
                //Collision Test
                if (!isCollisionWithBlock(x - 1, y)) {
                    player.moveTo(x - 1, y);
                } else {
                    player.setCurrentDir(Role.Direction.LEFT);
                }
            } else if (input.isKeyPressed(Keys.D) || input.isKeyPressed(Keys.RIGHT)) {
                //Gdx.app.debug(TAG, "RIGHT key");
                if (!isCollisionWithBlock(x + 1, y)) {
                    player.moveTo(x + 1, y);
                } else {
                    player.setCurrentDir(Role.Direction.RIGHT);
                }
            } else if (input.isKeyPressed(Keys.W) || input.isKeyPressed(Keys.UP)) {
                //Gdx.app.debug(TAG, "UP key");
                if (!isCollisionWithBlock(x, y + 1)) {
                    player.moveTo(x, y + 1);
                } else {
                    player.setCurrentDir(Role.Direction.UP);
                }
            } else if (input.isKeyPressed(Keys.S) || input.isKeyPressed(Keys.DOWN)) {
                //Gdx.app.debug(TAG, "DOWN key");
                if (!isCollisionWithBlock(x, y - 1)) {
                    player.moveTo(x, y - 1);
                } else {
                    player.setCurrentDir(Role.Direction.DOWN);
                }
            } else if (input.isKeyPressed(Keys.Q)) {
                Gdx.app.exit();
            }
        }
    }

    @Override
    public boolean keyUp(int keycode) {
        // Reset game world
        if (keycode == Keys.R) {
            init();
            Gdx.app.debug(TAG, "Game world reset");
        }
        return false;
    }

    /**
     * 轨迹点
     */
    List<Vector2> points = new ArrayList<Vector2>();
    Vector2 startPoint = new Vector2(), endPoint = new Vector2();

    @Override
    public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        Vector3 input = new Vector3(screenX, screenY, 0);
        camera.unproject(input);
        float x = input.x;
        float y = input.y;
        Gdx.app.debug(TAG, "pointer END:" + pointer + "=>(" + x + "," + y + ")");
        endPoint.set(x, y);
        //如果起点和终点一样，我们认为是点击
        if (MathUtils.floor(startPoint.x / 32) == MathUtils.floor(endPoint.x / 32) &&
                MathUtils.floor(startPoint.y / 32) == MathUtils.floor(endPoint.y / 32)) {
            //移动到该位置
            touchToMove(screenX, screenY);
            points.clear();
            return false;
        }
        //判断是否施法成功
        if (!Trace.symbol(this, points, player)) {
            //TODO:自动物理攻击
        }
        ;
        points.clear();
        return false;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {
        Vector3 input = new Vector3(screenX, screenY, 0);
        camera.unproject(input);
        float x = input.x;
        float y = input.y;
//		Gdx.app.debug(TAG, "pointer:"+pointer+"=>("+x+","+y+")=>Cursor:("+cursor.getX()+","+cursor.getY()+")");
        points.add(new Vector2(x, y));
        return false;
    }

    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        Vector3 input = new Vector3(screenX, screenY, 0);
        camera.unproject(input);
        float x = input.x;
        float y = input.y;
        startPoint.set(x, y);
        Gdx.app.debug(TAG, "pointer START:" + pointer + "=>(" + x + "," + y + ")");
        return false;
    }

    public void touchToMove(int screenX, int screenY) {
        Vector3 input = new Vector3(screenX, screenY, 0);
        camera.unproject(input);
        int x = MathUtils.floor(input.x / 32);
        int y = MathUtils.floor(input.y / 32);
        Gdx.app.debug(TAG, "clicked # (x:" + x + ",y:" + y + " )");
        //we click not npc or block,set aim to move
        if (!isCollisionWithNpc(x, y) && !isCollisionWithBlock(x, y)) {
            //A* path finding
            path.clear();
            Vector2 start = new Vector2(MathUtils.round(player.getX() / 32), MathUtils.round(player.getY() / 32));
            //we need set exactly start position
            Vector2 end = new Vector2(x, y);
            int numCols = mapMgr.cols;
            int numRows = mapMgr.rows;
            Gdx.app.debug(TAG, "From:" + start + " to " + end + "|numCols:" + numCols + "|numRows:" + numRows);
            int s = (int) start.x + ((int) start.y) * numCols;
            int t = (int) end.x + ((int) (end.y)) * numCols;
            List<Sprite> temp = new ArrayList<Sprite>();
            temp.addAll(mapMgr.npcs);
            temp.addAll(mapMgr.enemies);
//            temp.addAll(mapMgr.events);
            final MyGraph graph = GraphGenerator.generateGraph(mapMgr.getBlockLayer(), temp, numCols, numRows, 32, 32, start);
            final IndexedAStarPathFinder<MyNode> pathfinder = new IndexedAStarPathFinder<MyNode>(graph);
            final GraphPath<MyNode> outPath = new DefaultGraphPath<MyNode>();
            final boolean searchResult = pathfinder.searchNodePath(graph.getNodes().get(s), graph.getNodes().get(t), new ManhattanDistance(), outPath);
            MyPathSmoother pathSmoother = new MyPathSmoother(new MyRaycastCollisionDetector(graph));
            pathSmoother.smoothPath(outPath);
            StringBuilder sb = new StringBuilder();
            for (int i = outPath.getCount() - 1; i >= 0; i--) {
                sb.append("(" + outPath.get(i).getX() + "," + outPath.get(i).getY() + ")|");
                path.add(outPath.get(i));
            }
            if (searchResult) {
                Gdx.app.debug(TAG, "Start Follow Path:" + sb.toString());
                player.followPath(path);
                aim = new Aim(x, y);
            }
        } else {
            aim = null;
        }
    }

    public void closeSpeechWithNpc() {
        Npc npc = getCurrentSelectedNpc();
        if (npc != null) {
            npc.setSelected(false);
            npc.setCurrentDir(npc.getDefaultDir());
            npc.setState(npc.getDefaultState());
        }
    }

    /**
     * select a npc
     *
     * @param x
     * @param y
     * @return
     */
    private boolean isCollisionWithNpc(int x, int y) {
        for (Npc npc : this.mapMgr.npcs) {
            int npcX = MathUtils.floor(npc.getX() / 32);
            int npcY = MathUtils.floor(npc.getY() / 32);
            //this block is none
            if (npc.isSelected()) {
                npc.setSelected(false);
                npc.setCurrentDir(npc.getDefaultDir());
                npc.setState(npc.getDefaultState());
            }
            if (npcX == x && npcY == y) {
                int x0 = MathUtils.floor(player.getX() / 32);
                int y0 = MathUtils.floor(player.getY() / 32);
                float distance = (x - x0) * (x - x0) + (y - y0) * (y - y0);
                //I click you
                if (distance < 10f) {
                    Gdx.app.debug(TAG, "distance:" + distance + " you clicked " + npc.getEntityId());
                    //1.face to face
                    int gapX = x0 - npcX;
                    int gapY = y0 - npcY;
                    if (Math.abs(gapX) < Math.abs(gapY)) {
                        if (gapY < 0) {
                            player.setCurrentDir(Role.Direction.UP);
                            npc.setCurrentDir(Role.Direction.DOWN);
                        } else {
                            player.setCurrentDir(Role.Direction.DOWN);
                            npc.setCurrentDir(Role.Direction.UP);
                        }
                    } else {
                        if (gapX < 0) {
                            player.setCurrentDir(Role.Direction.RIGHT);
                            npc.setCurrentDir(Role.Direction.LEFT);
                        } else {
                            player.setCurrentDir(Role.Direction.LEFT);
                            npc.setCurrentDir(Role.Direction.RIGHT);
                        }
                    }
                    //2.talk with npc,npc must be fixed when talk
                    npc.setSelected(true);
                    npc.setState(Role.State.FIXED);
                    //3. show talk dialog
                    hudScreen.loadSpeech(npc);
                }
                return true;
            }
        }
        return false;
    }

    public boolean isCollisionWithTrap(int x, int y) {
        for (Trap eo : this.mapMgr.traps) {
            int eoX = MathUtils.floor(eo.getX() / 32);
            int eoY = MathUtils.floor(eo.getY() / 32);
            if (eoX == x && eoY == y) {
                int x0 = MathUtils.floor(player.getX() / 32);
                int y0 = MathUtils.floor(player.getY() / 32);
                float distance = (x - x0) * (x - x0) + (y - y0) * (y - y0);
                //I trap you
                if (distance <= 0f) {
                    Gdx.app.debug(TAG, "trigger event:" + eo.getName() + "|toggle:" + eo.isToggled());
                    if (!eo.isToggled()) {
                        //1.face to item
                        int gapX = x0 - eoX;
                        int gapY = y0 - eoY;
                        if (Math.abs(gapX) < Math.abs(gapY)) {
                            if (gapY < 0) {
                                player.setCurrentDir(Role.Direction.UP);
                            } else {
                                player.setCurrentDir(Role.Direction.DOWN);
                            }
                        } else {
                            if (gapX < 0) {
                                player.setCurrentDir(Role.Direction.RIGHT);
                            } else {
                                player.setCurrentDir(Role.Direction.LEFT);
                            }
                        }
                        //stop walk
                        player.clearPathAndStop();
                        //2.start to execute
                        executeCommand(eo.getCommand());
                        setToggleOnGroup(eo.getName());
                    }
                }
                return true;
            }
        }
        return false;
    }

    /**
     * toggle all the same name tiles
     *
     * @param name
     */
    private void setToggleOnGroup(String name) {
        for (Trap eo : this.mapMgr.traps) {
            if (name.equals(eo.getName())) {
                eo.setToggled(true);
            }
        }
    }

    public boolean isGameOver() {
        return isGameOver;
    }

    public void setGameOver(boolean gameOver) {
        isGameOver = gameOver;
    }

    public boolean isCollisionWithBlock(int x, int y) {
        TiledMapTileLayer mapCollisionLayer = mapMgr.getBlockLayer();

        if (mapCollisionLayer == null) {
            return false;
        }

        if (mapCollisionLayer.getCell(x, y) != null) {
//            Gdx.app.debug(TAG, "Map Collision at = " + x + "," + y);
            return true;
        }
        //Npc's block
        for (Npc npc : mapMgr.npcs) {
            if (MathUtils.floor(npc.getX() / 32) == x && MathUtils.floor(npc.getY() / 32) == y) {
                return true;
            }
        }
        //Enemy's block
        for (Enemy e : mapMgr.enemies) {
            if (MathUtils.floor(e.getX() / 32) == x && MathUtils.floor(e.getY() / 32) == y) {
                return true;
            }
        }

        if (x < 0 || y < 0 || x >= mapMgr.cols || y >= mapMgr.rows) {
            return true;
        }

        return false;
    }

    public boolean isCollisionWithPlayer(int x, int y) {
        if (x == MathUtils.floor(player.getX() / 32) && y == MathUtils.floor(player.getY() / 32)) {
            Gdx.app.debug(TAG, "Player is here");
            return true;
        }
        return false;
    }

    private String _previousEnemySpawn = "0";
    public String _enemySpawnID = "0";

    private boolean isCollisionWithEnemy(MapsManager mapMgr) {
        MapLayer mapEnemySpawnLayer = mapMgr.getEnemySpawnLayer();

        if (mapEnemySpawnLayer == null) {
            return false;
        }

        Rectangle rectangle = null;

        for (MapObject object : mapEnemySpawnLayer.getObjects()) {
            if (object instanceof RectangleMapObject) {
                rectangle = ((RectangleMapObject) object).getRectangle();
                Rectangle _boundingBox = player.getBoundingRectangle();
                if (_boundingBox.overlaps(rectangle)) {
                    String enemySpawnID = object.getName();

                    if (enemySpawnID == null) {
                        return false;
                    }

                    if (_previousEnemySpawn.equalsIgnoreCase(enemySpawnID)) {
                        //Gdx.app.debug(TAG, "Enemy Spawn Area already activated " + enemySpawnID);
                        return true;
                    } else {
                        Gdx.app.debug(TAG, "Enemy Spawn Area " + enemySpawnID + " Activated with previous Spawn value: " + _previousEnemySpawn);
                        _previousEnemySpawn = enemySpawnID;
                    }
                    _enemySpawnID = enemySpawnID;
                    player.setEntryBattle(true);
//                    notify(enemySpawnID, ComponentObserver.ComponentEvent.ENEMY_SPAWN_LOCATION_CHANGED);
                    return true;
                }
            }
        }

        //If no collision, reset the value
        if (!_previousEnemySpawn.equalsIgnoreCase(String.valueOf(0))) {
            Gdx.app.debug(TAG, "Enemy Spawn Area RESET with previous value " + _previousEnemySpawn);
            _previousEnemySpawn = String.valueOf(0);
            _enemySpawnID = _previousEnemySpawn;
            player.setEntryBattle(false);
            player.battleZoneSteps = 0;
//            notify(_previousEnemySpawn, ComponentObserver.ComponentEvent.ENEMY_SPAWN_LOCATION_CHANGED);
        }

        return false;
    }

    public Npc getCurrentSelectedNpc() {
        for (Npc npc : this.mapMgr.npcs) {
            if (npc.isSelected()) {
                return npc;
            }
        }
        return null;
    }

    /*********************************************
     * COMMAND
     ********************************************************/
    public void executeCommand(String cmd) {
        Gdx.app.debug(TAG, "preform cmd:" + cmd);
        String[] list = cmd.split(",");
        if ("MapTo".equals(list[0])) {
            Transfer(list[1], new Vector2(Integer.valueOf(list[2]), Integer.valueOf(list[3])), Role.Direction.valueOf(list[4]));

        }
        if ("openBox".equals(list[0])) {
            OpenBox();
        }
    }

    /********************************************
     * Triggle Event
     *************************************/
    public void Transfer(final String mapName, final Vector2 pos, final Role.Direction dir) {
        float delay = 0.1000f; // seconds
        player.setVisible(false);
        Timer.schedule(new Timer.Task() {
            @Override
            public void run() {
                mapMgr.loadMap(mapName);
                player.setCurrentDir(dir);
                player.setPosInMap(pos);
                player.setVisible(true);
                fighting = 0;
                cycleTime = 0;
            }
        }, delay);
    }

    public void OpenBox() {
        //add money
    }
    /*******************************************************************************************/
} public enum ConversationCommandEvent {
    LOAD_STORE_INVENTORY,
    EXIT_CONVERSATION,
    ACCEPT_QUEST,
    INN_SLEEP,
    ADD_ENTITY_TO_INVENTORY,
    RETURN_QUEST,
    NONE
} public class ConversationGraph {
    private Hashtable<String, Conversation> conversations;
    private Hashtable<String, ArrayList<ConversationChoice>> associatedChoices;
    private String currentConversationID = null;

    public ConversationGraph(){

    }

    public ConversationGraph(Hashtable<String, Conversation> conversations, String rootID){
        setConversations(conversations);
        setCurrentConversation(rootID);
    }

    public void setConversations(Hashtable<String, Conversation> conversations) {
        if( conversations.size() < 0 ){
            throw new IllegalArgumentException("Can't have a negative amount of conversations");
        }

        this.conversations = conversations;
        this.associatedChoices = new Hashtable<String, ArrayList<ConversationChoice>>(conversations.size());

        for( Conversation conversation: conversations.values() ){
            associatedChoices.put(conversation.getId(), new ArrayList<ConversationChoice>());
        }
    }

    public ArrayList<ConversationChoice> getCurrentChoices(){
        return associatedChoices.get(currentConversationID);
    }

    public String getCurrentConversationID(){
        return this.currentConversationID;
    }

    public void setCurrentConversation(String id){
        Conversation conversation = getConversationByID(id);
        if( conversation == null ) return;
        //Can we reach the new conversation from the current one?

        //Make sure we check case where the current node is checked against itself
        if(     currentConversationID == null ||
                currentConversationID.equalsIgnoreCase(id) ||
                isReachable(currentConversationID, id) ){
            currentConversationID = id;
        }else{
            //System.out.println("New conversation node [" + id +"] is not reachable from current node [" + currentConversationID + "]");
        }
    }

    public boolean isValid(String conversationID){
        Conversation conversation = conversations.get(conversationID);
        if( conversation == null ) return false;
        return true;
    }

    public boolean isReachable(String sourceID, String sinkID){
        if( !isValid(sourceID) || !isValid(sinkID) ) return false;
        if( conversations.get(sourceID) == null ) return false;

        //First get edges/choices from the source
        ArrayList<ConversationChoice> list = associatedChoices.get(sourceID);
        if( list == null ) return false;
        for(ConversationChoice choice: list){
            if(     choice.getSourceId().equalsIgnoreCase(sourceID) &&
                    choice.getDestinationId().equalsIgnoreCase(sinkID) ){
                return true;
            }
        }
        return false;
    }

    public Conversation getConversationByID(String id){
        if( !isValid(id) ){
            //System.out.println("Id " + id + " is not valid!");
            return null;
        }
        return conversations.get(id);
    }

    public String displayCurrentConversation(){
        return conversations.get(currentConversationID).getDialog();
    }

    public void addChoice(ConversationChoice conversationChoice){

        ArrayList<ConversationChoice> list = associatedChoices.get(conversationChoice.getSourceId());
        if( list == null) return;

        list.add(conversationChoice);
    }

    public String toString(){
        StringBuilder outputString = new StringBuilder();
        int numberTotalChoices = 0;

        Set<String> keys = associatedChoices.keySet();
        for( String id: keys){
            outputString.append(String.format("[%s]: ", id));

            for( ConversationChoice choice: associatedChoices.get(id)){
                numberTotalChoices++;
                outputString.append(String.format("%s ", choice.getDestinationId()));
            }

            outputString.append(System.getProperty("line.separator"));
        }

        outputString.append(String.format("Number conversations: %d", conversations.size()));
        outputString.append(String.format(", Number of choices: %d", numberTotalChoices));
        outputString.append(System.getProperty("line.separator"));

        return outputString.toString();
    }

    public String toJson(){
        Json json = new Json();
        return json.prettyPrint(this);
    }

} public class Monster extends Role {
    private static final String TAG = Monster.class.getSimpleName();

    public Monster(String entityId,int x,int y) {
        super(entityId);
        this.setCurrentDir(Direction.RIGHT);
        this.idleRightAnimation =loadAnimation(x,y);
        currentFrame =idleRightAnimation.getKeyFrame(0);
        // Define sprite size to be 1m x 1m in game world
        this.setSize(currentFrame.getRegionWidth(), currentFrame.getRegionHeight());
        // Set origin to sprite's center
        this.setOrigin(this.getWidth() / 2.0f, 0);
        //inventory
    }
    //Specific to two frame animations where each frame is stored in a separate texture
    protected Animation loadAnimation(int x,int y){

        TextureRegion[][] texture1Frames = TextureRegion.split(Assets.instance.MONSTERS0, 16, 16);
        TextureRegion[][] texture2Frames = TextureRegion.split(Assets.instance.MONSTERS1, 16, 16);

        Array<TextureRegion> animationKeyFrames = new Array<TextureRegion>(2);
        TextureRegion t1 = texture1Frames[x][y];
        t1.flip(true,false);
        TextureRegion t2 = texture2Frames[x][y];
        t2.flip(true,false);
        animationKeyFrames.add(t1);
        animationKeyFrames.add(t2);

        return new Animation(0.5f, animationKeyFrames, Animation.PlayMode.LOOP);
    }

    private int xpReward = 100;
    private int gpReward = 100;

    public int getXpReward() {
        return xpReward;
    }

    public void setXpReward(int xpReward) {
        this.xpReward = xpReward;
    }

    public int getGpReward() {
        return gpReward;
    }

    public void setGpReward(int gpReward) {
        this.gpReward = gpReward;
    }

} public class Npc extends Role {
    private static final String TAG = Npc.class.getSimpleName();

    private State defaultState;

    private Direction defaultDir;

    public void setDefaultDir(Direction defaultDir) {
        this.defaultDir = defaultDir;
    }

    public Direction getDefaultDir() {
        return defaultDir;
    }

    public State getDefaultState() {
        return defaultState;
    }

    public void setDefaultState(State defaultState) {
        this.defaultState = defaultState;
    }
    public Npc(String entityId) {
        super(entityId);
        this.setCurrentDir(Direction.UP);
        this.setState(State.IDLE);
    }

    public void randomMove(WorldController worldController){
        if(this.getState()==State.FIXED)return;
        int x = MathUtils.floor(getX()/32);
        int y = MathUtils.floor(getY()/32);
        if(this.getState()==State.IDLE){
            int randInt = MathUtils.random(128);
            Gdx.app.debug(TAG,"randInt:"+randInt);
            if(randInt<4){
                switch (randInt){
                    case 0:
                        moveTo(x+randInt, y);
                        break;
                    case 1:
                        moveTo(x-randInt, y);
                        break;
                    case 2:
                        moveTo(x, y+randInt);
                        break;
                    case 3:
                        moveTo(x, y-randInt);
                        break;
                }
            }else{
                setState(State.IDLE);
            }
        }
        if(this.getState()==State.WALKING){
            if((getCurrentDir()==Direction.RIGHT && (worldController.isCollisionWithBlock(x+1,y)||worldController.isCollisionWithPlayer(x+1,y)))
                    ||(getCurrentDir()==Direction.LEFT && (worldController.isCollisionWithBlock(x-1,y)||worldController.isCollisionWithPlayer(x-1,y)))
                    ||(getCurrentDir()==Direction.UP && (worldController.isCollisionWithBlock(x,y+1)||worldController.isCollisionWithPlayer(x,y+1)))
                    ||(getCurrentDir()==Direction.DOWN && (worldController.isCollisionWithBlock(x,y-1)||worldController.isCollisionWithPlayer(x,y-1)))
                    ){
                setState(State.IDLE);
            }
        }
    }
} public class Role extends Sprite{
	private static final String TAG = Role.class.getSimpleName();
	private static Json json = new Json();
//	private Vector2 velocity;
	private float speed = 4*32f;
	private String entityId;
	/** path is arrived **/
	private boolean isArrived = false;
	private boolean isSelected = false;
	private boolean isEntryBattle = false;
	public int battleZoneSteps = 0;
	private boolean isVisible = true;
	/** Animations **/
	public Animation idleLeftAnimation;
	public Animation idleRightAnimation;
	public Animation idleUpAnimation;
	public Animation idleDownAnimation;
	private Animation walkLeftAnimation;
	private Animation walkRightAnimation;
	private Animation walkUpAnimation;
	private Animation walkDownAnimation;
	private Animation attackLeftAnimation;
	private Animation attackRightAnimation;
	private Animation attackUpAnimation;
	private Animation attackDownAnimation;
	/**temp position**/
	protected Vector2 nextPosition;
	private State currentState = State.IDLE;
	private Direction currentDir = Direction.LEFT;
	protected float frameTime = 0f;
	/**just for draw */
	public TextureRegion currentFrame = null;
	public Array<MyNode> path = new Array<MyNode>(true,10);
	// move to aim
	private Vector2 movingTarget;
	//InventoryItem
	private String questConfigPath;
	private String currentQuestID;
	private String itemTypeID = "NONE";
	/** battle **/
	private int healthPoint = 100;
	private int magicPoint = 100;
	private int attackPoint = 100;
	private int defensePoint = 100;
	private int hitDamageTotal = 100;
	private int goldPoint = 100;
	private int level = 1;

	// role inventory
	private Array<InventoryItem.ItemTypeID> inventory = new Array<InventoryItem.ItemTypeID>();

	public Animation getAnimation(State animationType) {
		if(animationType==State.IDLE || animationType==State.FIXED){
			switch (currentDir) {
				case UP:
					return idleUpAnimation;
				case RIGHT:
					return idleRightAnimation;
				case DOWN:
					return idleDownAnimation;
				case LEFT:
					return idleLeftAnimation;
				default:
					break;
			}
		}
		if(animationType==State.WALKING){
			switch (currentDir) {
				case UP:
					return walkUpAnimation;
				case RIGHT:
					return walkRightAnimation;
				case DOWN:
					return walkDownAnimation;
				case LEFT:
					return walkLeftAnimation;
				default:
					break;
			}
		}
		return null;
	}

	public enum State {
		FIXED,IDLE, WALKING,ATTACK
	}

	public enum Direction {
		UP,RIGHT,DOWN,LEFT;
	}

	public Role(String entityId){
		init(entityId);
	}

	public void init(String entityId){
		this.entityId = entityId;
		this.nextPosition = new Vector2();
		this.movingTarget = new Vector2();
		Assets.AssetRole assetRole = Assets.instance.assetRoles.get(entityId);
		if(assetRole!=null){
			this.idleLeftAnimation = assetRole.idleLeftAnimation;
			this.idleRightAnimation = assetRole.idleRightAnimation;
			this.idleUpAnimation = assetRole.idleUpAnimation;
			this.idleDownAnimation = assetRole.idleDownAnimation;
			this.walkLeftAnimation = assetRole.walkLeftAnimation;
			this.walkRightAnimation = assetRole.walkRightAnimation;
			this.walkUpAnimation = assetRole.walkUpAnimation;
			this.walkDownAnimation = assetRole.walkDownAnimation;
			this.attackLeftAnimation = assetRole.attackLeftAnimation;
			this.attackRightAnimation = assetRole.attackRightAnimation;
			this.attackUpAnimation = assetRole.attackUpAnimation;
			this.attackDownAnimation = assetRole.attackDownAnimation;
			currentFrame =idleRightAnimation.getKeyFrame(0);
			// Define sprite size to be 1m x 1m in game world
			this.setSize(currentFrame.getRegionWidth(), currentFrame.getRegionHeight());
			// Set origin to sprite's center
			this.setOrigin(this.getWidth() / 2.0f, 0);
			//inventory
			initInventory("items/"+entityId+".json");
		}
		Gdx.app.debug(TAG, "Construction :"+entityId );
	}

	/**
	 * load from json config
	 */
	public void initInventory(String itemsPath){
		if(Gdx.files.internal(itemsPath).exists()) {
			Array<InventoryItem.ItemTypeID> list = json.fromJson(Array.class, Gdx.files.internal(itemsPath));
			//inventory
			for (int i = 0; i < list.size; i++) {
				inventory.add(list.get(i));
			}
		}
	}
	/**
	 * if walking,just move to center
	 * @param delta
     */
	public void update(float delta) {
		if(this.currentState==State.WALKING){
			calcNextPosition(delta);
			if(isEntryBattle){
				battleZoneSteps++;
			}
			if(Math.abs(this.nextPosition.x-this.movingTarget.x*32f)<speed*delta && Math.abs(this.nextPosition.y-this.movingTarget.y*32f)<speed*delta){
				this.setPosInMap(movingTarget);
				if(path!=null&&path.size>0){
					MyNode nextPoint = path.pop();
					moveTo(nextPoint.getX(),nextPoint.getY());
				}else{
					isArrived = true;
					this.currentState= State.IDLE;
				}
			}
		}
		frameTime = (frameTime + delta) % 4; // Want to avoid overflow
		updateCurrentFrame();
	}

	/**
	 * get correct frame from state and direction
	 */
	public void updateCurrentFrame() {
		// Look into the appropriate variable when changing position
		if(currentState==State.IDLE || currentState==State.FIXED){
			switch (currentDir) {
				case UP:
					currentFrame = idleUpAnimation.getKeyFrame(0);
					break;
				case RIGHT:
					currentFrame = idleRightAnimation.getKeyFrame(0);
					break;
				case DOWN:
					currentFrame = idleDownAnimation.getKeyFrame(0);
					break;
				case LEFT:
					currentFrame = idleLeftAnimation.getKeyFrame(0);
					break;
				default:
					break;
			}
		}
		if(currentState==State.WALKING){
			switch (currentDir) {
				case UP:
					currentFrame = walkUpAnimation.getKeyFrame(frameTime);
					break;
				case RIGHT:
					currentFrame = walkRightAnimation.getKeyFrame(frameTime);
					break;
				case DOWN:
					currentFrame = walkDownAnimation.getKeyFrame(frameTime);
					break;
				case LEFT:
					currentFrame = walkLeftAnimation.getKeyFrame(frameTime);
					break;
				default:
					break;
			}
		}
		if(currentState==State.ATTACK){
			switch (currentDir) {
				case UP:
					currentFrame = attackUpAnimation.getKeyFrame(frameTime);
					break;
				case RIGHT:
					currentFrame = attackRightAnimation.getKeyFrame(frameTime);
					break;
				case DOWN:
					currentFrame = attackDownAnimation.getKeyFrame(frameTime);
					break;
				case LEFT:
					currentFrame = attackLeftAnimation.getKeyFrame(frameTime);
					break;
				default:
					break;
			}
		}
	}

	@Override
	public void draw(Batch batch) {
		if(!isVisible){
			return;
		}
		if(isSelected){
			//Draw selected
			batch.draw(Assets.instance.selected,getX(),getY()-2);
		}else{
			batch.draw(Assets.instance.shadow,getX(),getY()-2);
		}
		/*if(currentState==State.ATTACK){
			// Draw image
			batch.draw(currentFrame.getTexture(), getX()-32, getY(),getOriginX(), getOriginY(), getWidth()*2,getHeight(), getScaleX(), getScaleY(),
					getRotation(), currentFrame.getRegionX(), currentFrame.getRegionY(), currentFrame.getRegionWidth(), currentFrame.getRegionHeight(),false, false);
		}else{*/
			// Draw image
			batch.draw(currentFrame.getTexture(), getX(), getY(),getOriginX(), getOriginY(), getWidth(),getHeight(), getScaleX(), getScaleY(),
					getRotation(), currentFrame.getRegionX(), currentFrame.getRegionY(), currentFrame.getRegionWidth(), currentFrame.getRegionHeight(),false, false);
		/*}*/

		// Reset color to white
		batch.setColor(1, 1, 1, 1);
	}

	/**
	 * move every delta time on speed
	 * @param deltaTime
     */
	public void calcNextPosition(float deltaTime) {
		float testX = this.getX();
		float testY = this.getY();
		speed *= (deltaTime);
		Vector2 v = new Vector2(movingTarget.x*32f-testX,movingTarget.y*32f-testY).nor();
		nextPosition.x = testX + v.x*speed;
		nextPosition.y = testY + v.y*speed;
//		Gdx.app.debug(TAG, "nextPosition:"+nextPosition);
		// velocity
		speed *=(1 / deltaTime);
		setPosition(nextPosition.x, nextPosition.y);
	}
	public  Vector2 getPosInMap(){
		return new Vector2(MathUtils.floor(getX() / 32),MathUtils.floor(getY() / 32));
	}
	public void setPosInMap(Vector2 point){
		this.setPosition(point.x*32f,point.y*32f);
		movingTarget = point.cpy();
	}

	public String getEntityId() {
		return entityId;
	}
	public boolean isArrived() {
		return isArrived;
	}
	public void setArrived(boolean arrived) {
		isArrived = arrived;
	}
	public String getItemTypeID() {
		return itemTypeID;
	}

	public void setItemTypeID(String itemTypeID) {
		this.itemTypeID = itemTypeID;
	}

	public Array<InventoryItem.ItemTypeID> getInventory() {
		return inventory;
	}

	public void setInventory(Array<InventoryItem.ItemTypeID> inventory) {
		this.inventory = inventory;
	}
	public boolean isSelected() {
		return isSelected;
	}

	public void setSelected(boolean selected) {
		isSelected = selected;
	}
	public boolean isVisible() {
		return isVisible;
	}

	public void setVisible(boolean visible) {
		isVisible = visible;
	}
	/**
	* move Role to x,y
	* @param x
	* @param y
	*/
	public void moveTo(int x,int y){
		int gapX = x - MathUtils.floor(getX()/32);
		int gapY = y - MathUtils.floor(getY()/32);
		if(Math.abs(gapX)<Math.abs(gapY)){
			if(gapY<0){
				this.currentDir = Direction.DOWN;
			}else{
				this.currentDir = Direction.UP;
			}
		}else{
			if(gapX<0){
				this.currentDir = Direction.LEFT;
			}else{
				this.currentDir = Direction.RIGHT;
			}
		}
		this.currentState = State.WALKING;
		movingTarget = new Vector2(x,y);
		isArrived = false;
	}
	public void clearPathAndStop(){
//		this.setPosInMap(new Vector2(x,y));
		if(path!=null&&path.size>0){
			path.clear();
		}
		isArrived = true;
		this.currentState= State.IDLE;
	}
	public void followPath(Array<MyNode> newPath){
		if(this.path.size<=0){
			//remove current point
			newPath.pop();
		}
		this.path.clear();
		for(MyNode node:newPath){
			this.path.add(node);
		}
		if(this.path.size>0) {
			MyNode nextPoint = this.path.pop();
			moveTo(nextPoint.getX(), nextPoint.getY());
		}
	}

	public State getState() {
		return currentState;
	}

	public void setState(State currentState) {
		this.currentState = currentState;
	}

	public Direction getCurrentDir() {
		return currentDir;
	}

	public void setCurrentDir(Direction currentDir) {
		this.currentDir = currentDir;
	}
	public int getDefensePoint() {
		return defensePoint;
	}

	public void setDefensePoint(int defensePoint) {
		this.defensePoint = defensePoint;
	}

	public int getAttackPoint() {
		return attackPoint;
	}

	public void setAttackPoint(int attackPoint) {
		this.attackPoint = attackPoint;
	}

	public int getHealthPoint() {
		return healthPoint;
	}

	public void setHealthPoint(int healthPoint) {
		this.healthPoint = healthPoint;
	}

	public int getMagicPoint() {
		return magicPoint;
	}

	public void setMagicPoint(int magicPoint) {
		this.magicPoint = magicPoint;
	}
	public int getHitDamageTotal() {
		return hitDamageTotal;
	}

	public void setHitDamageTotal(int hitDamageTotal) {
		this.hitDamageTotal = hitDamageTotal;
	}

	public int getLevel() {
		return level;
	}

	public void setLevel(int level) {
		this.level = level;
	}

	public int getGoldPoint() {
		return goldPoint;
	}

	public void setGoldPoint(int goldPoint) {
		this.goldPoint = goldPoint;
	}


	public boolean isEntryBattle() {
		return isEntryBattle;
	}

	public void setEntryBattle(boolean entryBattle) {
		isEntryBattle = entryBattle;
	}

} public class BattleUI extends Window{
    private static final String TAG = BattleUI.class.getSimpleName();

    private AnimatedImage _image;
    public HUDScreen hudScreen;
    public Role player;

    private final int _enemyWidth = 96;
    private final int _enemyHeight = 96;

    private BattleState battleState = null;
    private TextButton _attackButton = null;
    private TextButton _runButton = null;
    private Label damageValLabel = null;

    private float battleTimer = 0;
    private final float _checkTimer = 1;

    private ShakeCamera battleShakeCam = null;
    private Array<ParticleEffect> _effects;

    private float _origDamageValLabelY = 0;
    private Vector2 _currentImagePosition;

    public BattleUI(HUDScreen parent,final Role player){
        super("战斗", Assets.instance.STATUSUI_SKIN);
        hudScreen = parent;
        this.player = player;
        battleTimer = 0;
        battleState = new BattleState(this);
//        battleState.addObserver(this);

        _effects = new Array<ParticleEffect>();
        _currentImagePosition = new Vector2(0,0);

        damageValLabel = new Label("0", Assets.instance.STATUSUI_SKIN);
        damageValLabel.setVisible(false);

     // + Background
        Image imgBackground = new Image(new Texture(Gdx.files.internal("battle/background.png")));
        imgBackground.setSize(Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
        imgBackground.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                Gdx.app.debug(TAG,"you are in battle!");
            }
        });
        this.addActor(imgBackground);
        
        _image = new AnimatedImage();
        _image.setTouchable(Touchable.disabled);

        Table table = new Table();
        _attackButton = new TextButton("攻击", Assets.instance.STATUSUI_SKIN, "inventory");
        _runButton = new TextButton("逃跑", Assets.instance.STATUSUI_SKIN, "inventory");
        table.add(_attackButton).pad(20, 20, 20, 20);
        table.row();
        table.add(_runButton).pad(20, 20, 20, 20);

        //layout
        this.setFillParent(true);
        this.add(damageValLabel).align(Align.left).padLeft(_enemyWidth / 2).row();
        this.add(_image).size(_enemyWidth, _enemyHeight).pad(10, 10, 10, _enemyWidth / 2);
        this.add(table);

        this.pack();

        _origDamageValLabelY = damageValLabel.getY()+_enemyHeight;
        this.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                Gdx.app.debug(TAG,"you are in battle!");
            }
        });
        _attackButton.addListener(
                new ClickListener() {
                    @Override
                    public void clicked(InputEvent event, float x, float y) {
                        battleState.playerAttacks(player);
                    }
                }
        );
        _runButton.addListener(
                new ClickListener() {
                    @Override
                    public void clicked(InputEvent event, float x, float y) {
                        battleState.playerRuns();
                    }
                }
        );
    }

    public void battleZoneTriggered(int battleZoneValue){
        battleState.setCurrentZoneLevel(battleZoneValue);
    }

    public boolean isBattleReady(){
        if( battleTimer > _checkTimer ){
            battleTimer = 0;
            return battleState.isOpponentReady();
        }else{
            return false;
        }
    }

    public BattleState getCurrentState(){
        return battleState;
    }
//    @Override
    public void updateEvent(Monster entity, BattleEvent event) {
        hudScreen.onNotify(entity,event);
        switch(event){
            case PLAYER_TURN_START:
                _runButton.setDisabled(true);
                _runButton.setTouchable(Touchable.disabled);
                _attackButton.setDisabled(true);
                _attackButton.setTouchable(Touchable.disabled);
                break;
            case OPPONENT_ADDED:
                _image.setEntity(entity);
                _image.setCurrentAnimation(Role.State.FIXED);
                _image.setSize(_enemyWidth, _enemyHeight);

                _currentImagePosition.set(_image.getX(),_image.getY());
                if( battleShakeCam == null ){
                    battleShakeCam = new ShakeCamera(_currentImagePosition.x, _currentImagePosition.y, 30.0f);
                }
                //set title todo
                this.setName("Level " + battleState.getCurrentZoneLevel() + " " + entity.getEntityId());
                break;
            case OPPONENT_HIT_DAMAGE:
//                int damage = Integer.parseInt(entity.getEntityConfig().getPropertyValue(EntityConfig.EntityProperties.ENTITY_HIT_DAMAGE_TOTAL.toString()));
                int damage = entity.getHitDamageTotal();
                damageValLabel.setText(String.valueOf(damage));
                damageValLabel.setY(_origDamageValLabelY);
                battleShakeCam.startShaking();
                damageValLabel.setVisible(true);
                break;
            case OPPONENT_DEFEATED:
                damageValLabel.setVisible(false);
                damageValLabel.setY(_origDamageValLabelY);
                break;
            case OPPONENT_TURN_DONE:
                 _attackButton.setDisabled(false);
                 _attackButton.setTouchable(Touchable.enabled);
                _runButton.setDisabled(false);
                _runButton.setTouchable(Touchable.enabled);
                break;
            case PLAYER_TURN_DONE:
                battleState.opponentAttacks();
                break;
            case PLAYER_USED_MAGIC:
                float x = _currentImagePosition.x + (_enemyWidth/2);
                float y = _currentImagePosition.y + (_enemyHeight/2);
//                _effects.add(ParticleEffectFactory.getParticleEffect(ParticleEffectFactory.ParticleEffectType.WAND_ATTACK, x,y));
                break;
            default:
                break;
        }
    }

    public void resetDefaults(){
        battleTimer = 0;
        battleState.resetDefaults();
    }

    @Override
    public void draw(Batch batch, float parentAlpha){
        super.draw(batch, parentAlpha);

        //Draw the particles last
        for( int i = 0; i < _effects.size; i++){
            ParticleEffect effect = _effects.get(i);
            if( effect == null ) continue;
            effect.draw(batch);
        }
    }

    @Override
    public void act(float delta){
        battleTimer = (battleTimer + delta)%60;
        if( damageValLabel.isVisible() && damageValLabel.getY() < this.getHeight()){
            damageValLabel.setY(damageValLabel.getY()+5);
        }

        if( battleShakeCam != null && battleShakeCam.isCameraShaking() ){
            Vector2 shakeCoords = battleShakeCam.getNewShakePosition();
            _image.setPosition(shakeCoords.x, shakeCoords.y);
        }

        for( int i = 0; i < _effects.size; i++){
            ParticleEffect effect = _effects.get(i);
            if( effect == null ) continue;
            if( effect.isComplete() ){
                _effects.removeIndex(i);
                effect.dispose();
            }else{
                effect.update(delta);
            }
        }

        super.act(delta);
    }
} public class ButtonUI extends Table {

    private ImageButton inventoryButton;
    private ImageButton questButton;

    public ButtonUI(){
        super(Assets.instance.STATUSUI_SKIN);
        //buttons
        inventoryButton= new ImageButton(Assets.instance.STATUSUI_SKIN, "inventory-button");
        inventoryButton.getImageCell().size(32, 32);

        questButton = new ImageButton(Assets.instance.STATUSUI_SKIN, "quest-button");
        questButton.getImageCell().size(32,32);

        //Add to layout
        defaults().expand().fill();

        //UI
        Table topTable = new Table();
        topTable.add(questButton);
        topTable.add(inventoryButton);
        topTable.row();

        this.add(topTable);

//        this.debug();
        this.pack();
    }

    public ImageButton getInventoryButton() {
        return inventoryButton;
    }

    public ImageButton getQuestButton() {
        return questButton;
    }

} public class DialogUI extends Window {
    private static final String TAG = DialogUI.class.getSimpleName();

    private Label talkText;
    private List items;
    private ConversationGraph graph;
    private HUDScreen hudScreen;
    private TextButton closeButton;

    private Json json;

    public DialogUI(HUDScreen parent) {
        super("对话", Assets.instance.STATUSUI_SKIN, "solidbackground");
        hudScreen = parent;
        json = new Json();
        graph = new ConversationGraph();

        //create
        talkText = new Label("", Assets.instance.STATUSUI_SKIN);
        talkText.setWrap(true);
        talkText.setAlignment(Align.left);
        items = new List<ConversationChoice>(Assets.instance.STATUSUI_SKIN);

        closeButton = new TextButton("X", Assets.instance.STATUSUI_SKIN);

        ScrollPane scrollPane = new ScrollPane(items);
        scrollPane.setOverscroll(false, false);
        scrollPane.setFadeScrollBars(false);
        scrollPane.setScrollingDisabled(true, false);
        scrollPane.setForceScroll(true, false);
        scrollPane.setScrollBarPositions(false, true);

        //layout
        this.add();
        this.add(closeButton);
        this.row();

        this.defaults().expand().fill();
        this.add(talkText).pad(10, 10, 10, 10);
        this.row();
        this.add(scrollPane).pad(10,10,10,10);

        //this.debug();
        this.pack();

        //Listeners
        items.addListener(new ClickListener() {
            @Override
            public void clicked (InputEvent event, float x, float y) {
                ConversationChoice choice = (ConversationChoice)items.getSelected();
                if( choice == null ) return;
                //TODO: pickup,sleep event etc
//                graph.notify(graph, choice.getConversationCommandEvent());
                hudScreen.executeCommandEvent(graph, choice.getConversationCommandEvent());
                populateConversationDialog(choice.getDestinationId());
            }
        });
    }

    public TextButton getCloseButton(){
        return closeButton;
    }

    public void loadConversation(Role role){
        String fullFilenamePath = "dialog/"+role.getEntityId()+".json";
        this.setName("");

        clearDialog();

        if( fullFilenamePath == null||"".equals(fullFilenamePath.trim()) || !Gdx.files.internal(fullFilenamePath).exists() ){
            Gdx.app.debug(TAG, "dialog file does not exist!");
            return;
        }

        this.setName(role.getEntityId());

        ConversationGraph graph = json.fromJson(ConversationGraph.class, Gdx.files.internal(fullFilenamePath));
        setConversationGraph(graph);
    }

    public void setConversationGraph(ConversationGraph graph){
        this.graph = graph;
        populateConversationDialog(graph.getCurrentConversationID());
    }

    public ConversationGraph getCurrentConversationGraph(){
        return this.graph;
    }

    private void populateConversationDialog(String conversationID){
        clearDialog();

        Conversation conversation = graph.getConversationByID(conversationID);
        if( conversation == null ) return;
        graph.setCurrentConversation(conversationID);
        talkText.setText(conversation.getDialog());
        ArrayList<ConversationChoice> choices =  graph.getCurrentChoices();
        if( choices == null ) {
            this.setHeight(160);
            return;
        }
        this.setHeight(140 + choices.size()*30);
        items.setItems(choices.toArray());
        items.setSelectedIndex(-1);
    }

    private void clearDialog(){
        talkText.setText("...");
        items.clearItems();
    }

} public class InventoryUI extends Window{
    public static final String TAG = InventoryUI.class.getName();
    public static final String PLAYER_INVENTORY = "Player_Inventory";
    public static final String STORE_INVENTORY = "Store_Inventory";
    private HUDScreen hudScreen;
    private BattleState battleState;
    private final TextButton closeButton;
    //num of slots
    public final static int _numSlots = 36;
    private int slotRows = 9;
    private Table inventorySlotTable;
    private Table playerSlotsTable;
    private Table equipSlots;
    private DragAndDrop dragAndDrop;
    private Array<Actor> inventoryActors;

    private Label DPValLabel;
    private int DPVal = 0;
    private Label APValLabel;
    private int APVal = 0;

    private final int _slotWidth = 50;
    private final int _slotHeight = 50;


    private InventorySlotTooltip inventorySlotTooltip;

    public InventoryUI(HUDScreen parent){
        super("道具", Assets.instance.STATUSUI_SKIN, "solidbackground");
        hudScreen = parent;
        battleState = parent.battleUI.getCurrentState();
        closeButton = new TextButton("X", Assets.instance.STATUSUI_SKIN);
        dragAndDrop = new DragAndDrop();
        inventoryActors = new Array<Actor>();

        //create
        inventorySlotTable = new Table();
        inventorySlotTable.setName("Inventory_Slot_Table");

        playerSlotsTable = new Table();
        equipSlots = new Table();
        equipSlots.setName("Equipment_Slot_Table");

        equipSlots.defaults().space(10);
        inventorySlotTooltip = new InventorySlotTooltip(Assets.instance.STATUSUI_SKIN);

        Label DPLabel = new Label("Defense: ", Assets.instance.STATUSUI_SKIN);
        DPValLabel = new Label(String.valueOf(DPVal), Assets.instance.STATUSUI_SKIN);

        Label APLabel = new Label("Attack : ", Assets.instance.STATUSUI_SKIN);
        APValLabel = new Label(String.valueOf(APVal), Assets.instance.STATUSUI_SKIN);

        Table labelTable = new Table();
        labelTable.add(DPLabel).align(Align.left);
        labelTable.add(DPValLabel).align(Align.center);
        labelTable.row();
        labelTable.row();
        labelTable.add(APLabel).align(Align.left);
        labelTable.add(APValLabel).align(Align.center);

        InventorySlot headSlot = new InventorySlot(this,
                InventoryItem.ItemUseType.ARMOR_HELMET.getValue(),
                new Image(Assets.instance.ITEMS_TEXTUREATLAS.findRegion("inv_helmet")));

        InventorySlot leftArmSlot = new InventorySlot(this,
                InventoryItem.ItemUseType.WEAPON_ONEHAND.getValue() |
                InventoryItem.ItemUseType.WEAPON_TWOHAND.getValue() |
                InventoryItem.ItemUseType.ARMOR_SHIELD.getValue() |
                InventoryItem.ItemUseType.WAND_ONEHAND.getValue() |
                InventoryItem.ItemUseType.WAND_TWOHAND.getValue(),
                new Image(Assets.instance.ITEMS_TEXTUREATLAS.findRegion("inv_weapon"))
        );

        InventorySlot rightArmSlot = new InventorySlot(this,
                InventoryItem.ItemUseType.WEAPON_ONEHAND.getValue() |
                InventoryItem.ItemUseType.WEAPON_TWOHAND.getValue() |
                InventoryItem.ItemUseType.ARMOR_SHIELD.getValue() |
                InventoryItem.ItemUseType.WAND_ONEHAND.getValue() |
                InventoryItem.ItemUseType.WAND_TWOHAND.getValue(),
                new Image(Assets.instance.ITEMS_TEXTUREATLAS.findRegion("inv_shield"))
        );

        InventorySlot chestSlot = new InventorySlot(this,
                InventoryItem.ItemUseType.ARMOR_CHEST.getValue(),
                new Image(Assets.instance.ITEMS_TEXTUREATLAS.findRegion("inv_chest")));

        InventorySlot legsSlot = new InventorySlot(this,
                InventoryItem.ItemUseType.ARMOR_FEET.getValue(),
                new Image(Assets.instance.ITEMS_TEXTUREATLAS.findRegion("inv_boot")));

        headSlot.addListener(new InventorySlotTooltipListener(inventorySlotTooltip));
        leftArmSlot.addListener(new InventorySlotTooltipListener(inventorySlotTooltip));
        rightArmSlot.addListener(new InventorySlotTooltipListener(inventorySlotTooltip));
        chestSlot.addListener(new InventorySlotTooltipListener(inventorySlotTooltip));
        legsSlot.addListener(new InventorySlotTooltipListener(inventorySlotTooltip));

        /*headSlot.addObserver(this);
        leftArmSlot.addObserver(this);
        rightArmSlot.addObserver(this);
        chestSlot.addObserver(this);
        legsSlot.addObserver(this);*/

        dragAndDrop.addTarget(new InventorySlotTarget(headSlot));
        dragAndDrop.addTarget(new InventorySlotTarget(leftArmSlot));
        dragAndDrop.addTarget(new InventorySlotTarget(chestSlot));
        dragAndDrop.addTarget(new InventorySlotTarget(rightArmSlot));
        dragAndDrop.addTarget(new InventorySlotTarget(legsSlot));

        playerSlotsTable.setBackground(new Image(Assets.instance.STATUSUI_SKIN,"cell").getDrawable());

        //layout
        for(int i = 1; i <= _numSlots; i++){
            InventorySlot inventorySlot = new InventorySlot();
            inventorySlot.addListener(new InventorySlotTooltipListener(inventorySlotTooltip));
            dragAndDrop.addTarget(new InventorySlotTarget(inventorySlot));

            inventorySlotTable.add(inventorySlot).size(_slotWidth, _slotHeight);

            inventorySlot.addListener(new ClickListener() {
                                         @Override
                                         public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
                                             super.touchUp(event, x, y, pointer, button);
                                             if( getTapCount() == 2 ){
                                                 InventorySlot slot = (InventorySlot)event.getListenerActor();
                                                 if( slot.hasItem() ){
                                                     InventoryItem item = slot.getTopInventoryItem();
                                                     if( item.isConsumable() ){
//                                                         String itemInfo = item.getItemUseType() + Component.MESSAGE_TOKEN + item.getItemUseTypeValue();
                                                         hudScreen.consumeItem(item.getItemUseType(),item.getItemUseTypeValue());
                                                         slot.removeActor(item);
                                                         slot.remove(item);
                                                     }
                                                 }
                                             }
                                         }


                                      }
            );


            if(i % slotRows == 0){
                inventorySlotTable.row();
            }
        }

        equipSlots.add();
        equipSlots.add(headSlot).size(_slotWidth, _slotHeight);
        equipSlots.row();

        equipSlots.add(leftArmSlot).size(_slotWidth, _slotHeight);
        equipSlots.add(chestSlot).size(_slotWidth, _slotHeight);
        equipSlots.add(rightArmSlot).size(_slotWidth, _slotHeight);
        equipSlots.row();

        equipSlots.add();
        equipSlots.right().add(legsSlot).size(_slotWidth, _slotHeight);

        playerSlotsTable.add(equipSlots);
        inventoryActors.add(inventorySlotTooltip);

        //layout
        this.row();
        this.add(playerSlotsTable).padBottom(20);
        this.add(labelTable);
        this.add(closeButton).align(Align.topRight);
        this.row();
        this.add(inventorySlotTable).colspan(3);
        this.row();
//        this.debug();
        this.pack();
    }
    public TextButton getCloseButton(){
        return closeButton;
    }
    public DragAndDrop getDragAndDrop(){
        return dragAndDrop;
    }

    public Table getInventorySlotTable() {
        return inventorySlotTable;
    }

    public Table getEquipSlotTable() {
        return equipSlots;
    }

    public void resetEquipSlots(){
        DPVal = 0;
        APVal = 0;

        DPValLabel.setText(String.valueOf(DPVal));
        battleState.updateForFight(String.valueOf(DPVal), InventoryEvent.UPDATED_DP);

        APValLabel.setText(String.valueOf(APVal));
        battleState.updateForFight(String.valueOf(APVal), InventoryEvent.UPDATED_AP);
    }

    public static void clearInventoryItems(Table targetTable){
        Array<Cell> cells = targetTable.getCells();
        for( int i = 0; i < cells.size; i++){
            InventorySlot inventorySlot = (InventorySlot)cells.get(i).getActor();
            if( inventorySlot == null ) continue;
            inventorySlot.clearAllInventoryItems(false);
        }
    }

    public static Array<InventoryItemLocation> removeInventoryItems(String name, Table inventoryTable){
        Array<Cell> cells = inventoryTable.getCells();
        Array<InventoryItemLocation> items = new Array<InventoryItemLocation>();
        for(int i = 0; i < cells.size; i++){
            InventorySlot inventorySlot =  ((InventorySlot)cells.get(i).getActor());
            if( inventorySlot == null ) continue;
            inventorySlot.removeAllInventoryItemsWithName(name);
        }
        return items;
    }

    public static void populateInventory(Table targetTable, Array<InventoryItemLocation> inventoryItems, DragAndDrop draganddrop, String defaultName, boolean disableNonDefaultItems){
        clearInventoryItems(targetTable);

        Array<Cell> cells = targetTable.getCells();
        for(int i = 0; i < inventoryItems.size; i++){
            InventoryItemLocation itemLocation = inventoryItems.get(i);
            InventoryItem.ItemTypeID itemTypeID = InventoryItem.ItemTypeID.valueOf(itemLocation.getItemTypeAtLocation());
            InventorySlot inventorySlot =  ((InventorySlot)cells.get(itemLocation.getLocationIndex()).getActor());

            for( int index = 0; index < itemLocation.getNumberItemsAtLocation(); index++ ){
                InventoryItem item = InventoryItemFactory.getInstance().getInventoryItem(itemTypeID);
                String itemName =  itemLocation.getItemNameProperty();
                if( itemName == null || "".equals(itemName.trim())){
                    item.setName(defaultName);
                }else{
                    item.setName(itemName);
                }

                inventorySlot.add(item);
                if( item.getName().equalsIgnoreCase(defaultName) ){
                    draganddrop.addSource(new InventorySlotSource(inventorySlot, draganddrop));
                }else if( disableNonDefaultItems == false ){
                    draganddrop.addSource(new InventorySlotSource(inventorySlot, draganddrop));
                }
            }
        }
    }

    public static Array<InventoryItemLocation> getInventory(Table targetTable){
        Array<Cell> cells = targetTable.getCells();
        Array<InventoryItemLocation> items = new Array<InventoryItemLocation>();
        for(int i = 0; i < cells.size; i++){
            InventorySlot inventorySlot =  ((InventorySlot)cells.get(i).getActor());
            if( inventorySlot == null ) continue;
            int numItems = inventorySlot.getNumItems();
            if( numItems > 0 ){
                items.add(new InventoryItemLocation(
                        i,
                        inventorySlot.getTopInventoryItem().getItemTypeID().toString(),
                        numItems,
                        inventorySlot.getTopInventoryItem().getName()));
            }
        }
        return items;
    }

    public static Array<InventoryItemLocation> getInventoryFiltered(Table targetTable, String filterOutName){
        Array<Cell> cells = targetTable.getCells();
        Array<InventoryItemLocation> items = new Array<InventoryItemLocation>();
        for(int i = 0; i < cells.size; i++){
            InventorySlot inventorySlot =  ((InventorySlot)cells.get(i).getActor());
            if( inventorySlot == null ) continue;
            int numItems = inventorySlot.getNumItems();
            if( numItems > 0 ){
                String topItemName = inventorySlot.getTopInventoryItem().getName();
                if( topItemName.equalsIgnoreCase(filterOutName)) continue;
                //System.out.println("[i] " + i + " itemtype: " + inventorySlot.getTopInventoryItem().getItemTypeID().toString() + " numItems " + numItems);
                items.add(new InventoryItemLocation(
                        i,
                        inventorySlot.getTopInventoryItem().getItemTypeID().toString(),
                        numItems,
                        inventorySlot.getTopInventoryItem().getName()));
            }
        }
        return items;
    }

    public static Array<InventoryItemLocation> getInventory(Table targetTable, String name){
        Array<Cell> cells = targetTable.getCells();
        Array<InventoryItemLocation> items = new Array<InventoryItemLocation>();
        for(int i = 0; i < cells.size; i++){
            InventorySlot inventorySlot =  ((InventorySlot)cells.get(i).getActor());
            if( inventorySlot == null ) continue;
            int numItems = inventorySlot.getNumItems(name);
            if( numItems > 0 ){
                //System.out.println("[i] " + i + " itemtype: " + inventorySlot.getTopInventoryItem().getItemTypeID().toString() + " numItems " + numItems);
                items.add(new InventoryItemLocation(
                        i,
                        inventorySlot.getTopInventoryItem().getItemTypeID().toString(),
                        numItems,
                        name));
            }
        }
        return items;
    }

    public static Array<InventoryItemLocation> getInventoryFiltered(Table sourceTable, Table targetTable, String filterOutName){
        Array<InventoryItemLocation> items = getInventoryFiltered(targetTable, filterOutName);
        Array<Cell> sourceCells = sourceTable.getCells();
        int index = 0;
        for( InventoryItemLocation item : items ) {
            for (; index < sourceCells.size; index++) {
                InventorySlot inventorySlot = ((InventorySlot) sourceCells.get(index).getActor());
                if (inventorySlot == null) continue;
                int numItems = inventorySlot.getNumItems();
                if (numItems == 0) {
                    item.setLocationIndex(index);
                    Gdx.app.debug(TAG,"[index] " + index + " itemtype: " + item.getItemTypeAtLocation() + " numItems " + numItems);
                    index++;
                    break;
                }
            }
            if( index == sourceCells.size ){
                Gdx.app.debug(TAG,"[index] " + index + " itemtype: " + item.getItemTypeAtLocation() + " numItems " + item.getNumberItemsAtLocation());
                item.setLocationIndex(index-1);
            }
        }
        return items;
    }


    public static void setInventoryItemNames(Table targetTable, String name){
        Array<Cell> cells = targetTable.getCells();
        for(int i = 0; i < cells.size; i++){
            InventorySlot inventorySlot =  ((InventorySlot)cells.get(i).getActor());
            if( inventorySlot == null ) continue;
            inventorySlot.updateAllInventoryItemNames(name);
        }
    }

    public boolean doesInventoryHaveSpace(){
        Array<Cell> sourceCells = inventorySlotTable.getCells();
        int index = 0;

        for (; index < sourceCells.size; index++) {
            InventorySlot inventorySlot = ((InventorySlot) sourceCells.get(index).getActor());
            if (inventorySlot == null) continue;
            int numItems = inventorySlot.getNumItems();
            if (numItems == 0) {
                return true;
            }else{
                index++;
            }
        }
        return false;
    }

    public void addEntityToInventory(Role entity, String itemName){
        Array<Cell> sourceCells = inventorySlotTable.getCells();
        int index = 0;

            for (; index < sourceCells.size; index++) {
                InventorySlot inventorySlot = ((InventorySlot) sourceCells.get(index).getActor());
                if (inventorySlot == null) continue;
                int numItems = inventorySlot.getNumItems();
                if (numItems == 0) {
                    InventoryItem inventoryItem = InventoryItemFactory.getInstance().getInventoryItem(InventoryItem.ItemTypeID.valueOf(entity.getItemTypeID()));
                    inventoryItem.setName(itemName);
                    inventorySlot.add(inventoryItem);
                    dragAndDrop.addSource(new InventorySlotSource(inventorySlot, dragAndDrop));
                    break;
                }
            }
    }

    public void removeQuestItemFromInventory(String questID){
        Array<Cell> sourceCells = inventorySlotTable.getCells();
        for (int index = 0; index < sourceCells.size; index++) {
            InventorySlot inventorySlot = ((InventorySlot) sourceCells.get(index).getActor());
            if (inventorySlot == null) continue;
            InventoryItem item = inventorySlot.getTopInventoryItem();
            if( item == null ) continue;
            String inventoryItemName = item.getName();
            if (inventoryItemName != null && inventoryItemName.equals(questID) ) {
                inventorySlot.clearAllInventoryItems(false);
            }
        }
    }

    public Array<Actor> getInventoryActors(){
        return inventoryActors;
    }

    public void addedItem(InventorySlot slot){
        InventoryItem addItem = slot.getTopInventoryItem();
        if( addItem == null ) return;
        if( addItem.isInventoryItemOffensive() ){
            APVal += addItem.getItemUseTypeValue();
            APValLabel.setText(String.valueOf(APVal));
            battleState.updateForFight(String.valueOf(APVal), InventoryEvent.UPDATED_AP);

            if( addItem.isInventoryItemOffensiveWand() ){
                battleState.updateForFight(String.valueOf(addItem.getItemUseTypeValue()), InventoryEvent.ADD_WAND_AP);
            }

        }else if( addItem.isInventoryItemDefensive() ){
            DPVal += addItem.getItemUseTypeValue();
            DPValLabel.setText(String.valueOf(DPVal));
            battleState.updateForFight(String.valueOf(DPVal), InventoryEvent.UPDATED_DP);
        }
    }
    public void removedItem(InventorySlot slot){
        InventoryItem removeItem = slot.getTopInventoryItem();
        if( removeItem == null ) return;
        if( removeItem.isInventoryItemOffensive() ){
            APVal -= removeItem.getItemUseTypeValue();
            APValLabel.setText(String.valueOf(APVal));
            battleState.updateForFight(String.valueOf(APVal), InventoryEvent.UPDATED_AP);

            if( removeItem.isInventoryItemOffensiveWand() ){
                battleState.updateForFight(String.valueOf(removeItem.getItemUseTypeValue()), InventoryEvent.REMOVE_WAND_AP);
            }

        }else if( removeItem.isInventoryItemDefensive() ){
            DPVal -= removeItem.getItemUseTypeValue();
            DPValLabel.setText(String.valueOf(DPVal));
            battleState.updateForFight(String.valueOf(DPVal), InventoryEvent.UPDATED_DP);
        }
    }
} public class QuestUI extends Window {
    private static final String TAG = QuestUI.class.getSimpleName();

    public static final String RETURN_QUEST = "quests/quest_return.json";
    public static final String FINISHED_QUEST = "quests/quest_finished.json";

    private List listQuests;
    private List listTasks;
    private Json json;
    private Array<QuestGraph> quests;
    private Label questLabel;
    private Label tasksLabel;
    public TextButton closeButton;

    public QuestUI() {
        super("任务列表", Assets.instance.STATUSUI_SKIN);

        json = new Json();
        quests = new Array<QuestGraph>();
        closeButton = new TextButton("X", Assets.instance.STATUSUI_SKIN);
        //create
        questLabel = new Label("任务:", Assets.instance.STATUSUI_SKIN);
        tasksLabel = new Label("步骤:", Assets.instance.STATUSUI_SKIN);

        listQuests = new List<QuestGraph>(Assets.instance.STATUSUI_SKIN);

        ScrollPane scrollPane = new ScrollPane (listQuests);
        scrollPane.setOverscroll(false, false);
        scrollPane.setFadeScrollBars(false);
        scrollPane.setForceScroll(true, false);

        listTasks = new List<QuestTask>(Assets.instance.STATUSUI_SKIN);

        ScrollPane scrollPaneTasks = new ScrollPane (listTasks);
        scrollPaneTasks.setOverscroll(false, false);
        scrollPaneTasks.setFadeScrollBars(false);
        scrollPaneTasks.setForceScroll(true, false);

        //layout
        this.add (questLabel).align(Align.left);
        this.add (tasksLabel).align(Align.left);
        this.add(closeButton).align(Align.right);
        this.row();
        this.defaults().expand().fill();
        this.add(scrollPane).padRight(15);
        this.add(scrollPaneTasks).padLeft(5).colspan(2);

        //this.debug();
        this.pack();

        //Listeners
        listQuests.addListener(new ClickListener() {
                                   @Override
                                   public void clicked(InputEvent event, float x, float y) {
                                       QuestGraph quest = (QuestGraph) listQuests.getSelected();
                                       if (quest == null) return;
                                       populateQuestTaskDialog(quest);
                                   }
                               }
        );
    }
    public TextButton getCloseButton(){
        return closeButton;
    }
    public void questTaskComplete(String questID, String questTaskID){
        for( QuestGraph questGraph: quests ){
            if( questGraph.getQuestID().equalsIgnoreCase(questID)){
                if( questGraph.isQuestTaskAvailable(questTaskID) ){
                    questGraph.setQuestTaskComplete(questTaskID);
                }else{
                    return;
                }
            }
        }
    }

    public QuestGraph loadQuest(String questConfigPath){
        if( questConfigPath==null|| "".equals(questConfigPath.trim()) || !Gdx.files.internal(questConfigPath).exists() ){
            Gdx.app.debug(TAG, "Quest file does not exist!");
            return null;
        }

        QuestGraph graph = json.fromJson(QuestGraph.class, Gdx.files.internal(questConfigPath));
        if( doesQuestExist(graph.getQuestID()) ){
            return null;
        }

        clearDialog();
        quests.add(graph);
        updateQuestItemList();
        return graph;
    }

    public boolean isQuestReadyForReturn(String questID){
        if( questID==null||"".equals(questID.trim())){
            Gdx.app.debug(TAG, "Quest ID not valid");
            return false;
        }

        if( !doesQuestExist(questID) ) return false;

        QuestGraph graph = getQuestByID(questID);
        if( graph == null ) return false;

        if( graph.updateQuestForReturn() ){
            graph.setQuestComplete(true);
        }else{
            return false;
        }
        return true;
    }

    public QuestGraph getQuestByID(String questGraphID){
        for( QuestGraph questGraph: quests ){
            if( questGraph.getQuestID().equalsIgnoreCase(questGraphID)){
                return questGraph;
            }
        }
        return null;
    }

    public boolean doesQuestExist(String questGraphID){
        for( QuestGraph questGraph: quests ){
            if( questGraph.getQuestID().equalsIgnoreCase(questGraphID)){
                return true;
            }
        }
        return false;
    }


    public Array<QuestGraph> getQuests() {
        return quests;
    }

    public void setQuests(Array<QuestGraph> quests) {
        this.quests = quests;
        updateQuestItemList();
    }

    public void updateQuestItemList(){
        clearDialog();

        listQuests.setItems (quests);
        listQuests.setSelectedIndex(-1);
    }

    private void clearDialog(){
        listQuests.clearItems();
        listTasks.clearItems();
    }

    private void populateQuestTaskDialog(QuestGraph graph){
        listTasks.clearItems();

        ArrayList<QuestTask> tasks =  graph.getAllQuestTasks();
        if( tasks == null ) return;

        listTasks.setItems(tasks.toArray());
        listTasks.setSelectedIndex(-1);
    }

    public void initQuests(MapsManager mapMgr){
//        mapMgr.clearAllMapQuestEntities();

        //populate items if quests have them
        for( QuestGraph quest : quests ){
            if( !quest.isQuestComplete() ){
                quest.init(mapMgr);
            }
        }
//        ProfileManager.getInstance().setProperty("playerQuests", quests);
    }

    public void updateQuests(MapsManager mapMgr){
        for( QuestGraph quest : quests ){
            if( !quest.isQuestComplete() ){
                quest.update(mapMgr);
            }
        }
//        ProfileManager.getInstance().setProperty("playerQuests", quests);
    }

} public class StatusUI extends Table {
    public static final String TAG = StatusUI.class.getName();
    private HUDScreen hudScreen;

    private Image hpBar;
    private Image mpBar;
    private Image xpBar;

    private Array<LevelTable> levelTables;
    private static final String LEVEL_TABLE_CONFIG = "data/level_tables.json";

    //Attributes
    private int levelVal = 1;
    private int goldVal = 10;
    private int hpVal = 80;
    private int mpVal = 80;
    private int xpVal = 10;

    private int xpCurrentMax = 100;
    private int hpCurrentMax = 100;
    private int mpCurrentMax = 100;

    private Label hpValLabel;
    private Label mpValLabel;
    private Label xpValLabel;
    private Label levelValLabel;
    private Label goldValLabel;

    private float _barHeight = 0;

    public StatusUI(HUDScreen parent){
        super(Assets.instance.STATUSUI_SKIN);
        hudScreen = parent;
        levelTables = LevelTable.getLevelTables(LEVEL_TABLE_CONFIG);

        //groups
        WidgetGroup group = new WidgetGroup();
        WidgetGroup group2 = new WidgetGroup();
        WidgetGroup group3 = new WidgetGroup();

        //images
        hpBar = new Image(new Texture(Gdx.files.internal("hud/hp_bar.png")));
        Image bar = new Image(new Texture(Gdx.files.internal("hud/bar.png")));
        mpBar = new Image(new Texture(Gdx.files.internal("hud/mp_bar.png")));
        Image bar2 = new Image(new Texture(Gdx.files.internal("hud/bar.png")));
        xpBar = new Image(new Texture(Gdx.files.internal("hud/xp_bar.png")));
        Image bar3 = new Image(new Texture(Gdx.files.internal("hud/bar.png")));

        Image border = new Image(new Texture(Gdx.files.internal("hud/border.png")));
        Image header = new Image(new Texture(Gdx.files.internal("hud/lante.png")));
        header.setPosition(3,3);

        _barHeight = hpBar.getHeight();
        //labels
        Label hpLabel = new Label(" 血量: ", Assets.instance.STATUSUI_SKIN);
        hpValLabel = new Label(String.valueOf(hpVal), Assets.instance.STATUSUI_SKIN);
        Label mpLabel = new Label(" 魔力: ", Assets.instance.STATUSUI_SKIN);
        mpValLabel = new Label(String.valueOf(mpVal), Assets.instance.STATUSUI_SKIN);
        Label xpLabel = new Label(" 经验: ", Assets.instance.STATUSUI_SKIN);
        xpValLabel = new Label(String.valueOf(xpVal), Assets.instance.STATUSUI_SKIN);
        Label levelLabel = new Label("等级:", Assets.instance.STATUSUI_SKIN);
        levelValLabel = new Label(String.valueOf(levelVal), Assets.instance.STATUSUI_SKIN);
        Label goldLabel = new Label(" 金币: ", Assets.instance.STATUSUI_SKIN);
        goldValLabel = new Label(String.valueOf(goldVal), Assets.instance.STATUSUI_SKIN);


        //Align images
        hpBar.setWidth(100 * (hpVal*1.0f/hpCurrentMax));
        mpBar.setWidth(100 * (mpVal*1.0f/mpCurrentMax));
        xpBar.setWidth(100 * (xpVal*1.0f/xpCurrentMax));

        //add to widget groups
        group.addActor(hpBar);
        group.addActor(bar);
        group2.addActor(mpBar);
        group2.addActor(bar2);
        group3.addActor(xpBar);
        group3.addActor(bar3);

        //Add to layout
        defaults().expand().fill();

        //top layout
        WidgetGroup headGroup = new WidgetGroup();
        headGroup.addActor(header);
        headGroup.addActor(border);
        Table leftTable = new Table();
        leftTable.add(headGroup).size(border.getWidth(), border.getHeight());
        leftTable.row();


        Table rightTable = new Table();
        rightTable.add(levelLabel).align(Align.left);
        rightTable.add(levelValLabel).align(Align.left);
//        rightTable.add(goldLabel);
//        rightTable.add(goldValLabel).align(Align.left);
        rightTable.row();

        rightTable.add(group).size(bar.getWidth(), bar.getHeight()).colspan(2);
//        rightTable.add(hpLabel);
//        rightTable.add(hpValLabel).align(Align.left);
        rightTable.row();

        rightTable.add(group2).size(bar2.getWidth(), bar2.getHeight()).colspan(2);
//        rightTable.add(mpLabel);
//        rightTable.add(mpValLabel).align(Align.left);
        rightTable.row();

        rightTable.add(group3).size(bar3.getWidth(), bar3.getHeight()).colspan(2);
//        rightTable.add(xpLabel);
//        rightTable.add(xpValLabel).align(Align.left).padRight(20);
        rightTable.row();

        this.add(leftTable);
        this.add(rightTable);

//        this.debug();
        this.pack();
        headGroup.addListener(new ClickListener() {
            public void clicked(InputEvent event, float x, float y) {
                //nothing to do ,only catch the click
            }
        });
    }

    public int getLevelValue(){
        return levelVal;
    }
    public void setLevelValue(int levelValue){
        this.levelVal = levelValue;
        levelValLabel.setText(String.valueOf(levelVal));
        hudScreen.updateLevel(levelValue);
//        notify(levelVal, StatusObserver.StatusEvent.UPDATED_LEVEL);
    }

    public int getGoldValue(){
        return goldVal;
    }
    public void setGoldValue(int goldValue){
        this.goldVal = goldValue;
        goldValLabel.setText(String.valueOf(goldVal));
        hudScreen.updateGP(goldValue);
//        notify(goldVal, StatusObserver.StatusEvent.UPDATED_GP);
    }

    public void addGoldValue(int goldValue){
        this.goldVal += goldValue;
        goldValLabel.setText(String.valueOf(goldVal));
        hudScreen.updateGP(goldValue);
//        notify(goldVal, StatusObserver.StatusEvent.UPDATED_GP);
    }

    public int getXPValue(){
        return xpVal;
    }

    public void addXPValue(int xpValue){
        this.xpVal += xpValue;
        if( xpVal > xpCurrentMax ){
            updateToNewLevel();
        }
        xpValLabel.setText(String.valueOf(xpVal));
        updateBar(xpBar, xpVal, xpCurrentMax);
        hudScreen.updateXP(xpVal);
//        notify(xpVal, StatusObserver.StatusEvent.UPDATED_XP);
    }

    public void setXPValue(int xpValue){
        this.xpVal = xpValue;
        if( xpVal > xpCurrentMax ){
            updateToNewLevel();
        }
        xpValLabel.setText(String.valueOf(xpVal));
        updateBar(xpBar, xpVal, xpCurrentMax);
        hudScreen.updateXP(xpVal);
//        notify(xpVal, StatusObserver.StatusEvent.UPDATED_XP);
    }

    public void setXPValueMax(int maxXPValue){
        this.xpCurrentMax = maxXPValue;
    }

    public void setStatusForLevel(int level){
        for( LevelTable table: levelTables ){
            if( Integer.parseInt(table.getLevelID()) == level ){
                setXPValueMax(table.getXpMax());
                setXPValue(0);

                setHPValueMax(table.getHpMax());
                setHPValue(table.getHpMax());

                setMPValueMax(table.getMpMax());
                setMPValue(table.getMpMax());

                setLevelValue(Integer.parseInt(table.getLevelID()));
                return;
            }
        }
    }

    public void updateToNewLevel(){
        for( LevelTable table: levelTables ){
            //System.out.println("XPVAL " + xpVal + " table XPMAX " + table.getXpMax() );
            if( xpVal > table.getXpMax() ){
                continue;
            }else{
                setXPValueMax(table.getXpMax());

                setHPValueMax(table.getHpMax());
                setHPValue(table.getHpMax());

                setMPValueMax(table.getMpMax());
                setMPValue(table.getMpMax());

                setLevelValue(Integer.parseInt(table.getLevelID()));
                hudScreen.levelUp();
//                notify(levelVal, StatusObserver.StatusEvent.LEVELED_UP);
                return;
            }
        }
    }

    public int getXPValueMax(){
        return xpCurrentMax;
    }

    //HP
    public int getHPValue(){
        return hpVal;
    }

    public void removeHPValue(int hpValue){
        hpVal = MathUtils.clamp(hpVal - hpValue, 0, hpCurrentMax);
        hpValLabel.setText(String.valueOf(hpVal));
        updateBar(hpBar, hpVal, hpCurrentMax);
        hudScreen.updateHP(hpVal);
//        notify(hpVal, StatusObserver.StatusEvent.UPDATED_HP);
    }

    public void addHPValue(int hpValue){
        hpVal = MathUtils.clamp(hpVal + hpValue, 0, hpCurrentMax);
        hpValLabel.setText(String.valueOf(hpVal));
        updateBar(hpBar, hpVal, hpCurrentMax);
        hudScreen.updateHP(hpVal);
//        notify(hpVal, StatusObserver.StatusEvent.UPDATED_HP);
    }

    public void setHPValue(int hpValue){
        this.hpVal = hpValue;
        hpValLabel.setText(String.valueOf(hpVal));
        updateBar(hpBar, hpVal, hpCurrentMax);
        hudScreen.updateHP(hpVal);
//        notify(hpVal, StatusObserver.StatusEvent.UPDATED_HP);
    }

    public void setHPValueMax(int maxHPValue){
        this.hpCurrentMax = maxHPValue;
    }

    public int getHPValueMax(){
        return hpCurrentMax;
    }

    //MP
    public int getMPValue(){
        return mpVal;
    }

    public void removeMPValue(int mpValue){
        mpVal = MathUtils.clamp(mpVal - mpValue, 0, mpCurrentMax);
        mpValLabel.setText(String.valueOf(mpVal));
        updateBar(mpBar, mpVal, mpCurrentMax);
        hudScreen.updateMP(mpVal);
//        notify(mpVal, StatusObserver.StatusEvent.UPDATED_MP);
    }

    public void addMPValue(int mpValue){
        mpVal = MathUtils.clamp(mpVal + mpValue, 0, mpCurrentMax);
        mpValLabel.setText(String.valueOf(mpVal));
        updateBar(mpBar, mpVal, mpCurrentMax);
        hudScreen.updateMP(mpVal);
//        notify(mpVal, StatusObserver.StatusEvent.UPDATED_MP);
    }

    public void setMPValue(int mpValue){
        this.mpVal = mpValue;
        mpValLabel.setText(String.valueOf(mpVal));
        updateBar(mpBar, mpVal, mpCurrentMax);
        hudScreen.updateMP(mpVal);
//        notify(mpVal, StatusObserver.StatusEvent.UPDATED_MP);
    }

    public void setMPValueMax(int maxMPValue){
        this.mpCurrentMax = maxMPValue;
    }

    public int getMPValueMax(){
        return mpCurrentMax;
    }

    public void updateBar(Image bar, int currentVal, int maxVal){
        int val = MathUtils.clamp(currentVal, 0, maxVal);
        float tempPercent = (float) val / (float) maxVal;
        float percentage = MathUtils.clamp(tempPercent, 0, 100);
        bar.setSize(100*percentage, _barHeight);
    }

} public class StoreInventoryUI extends Window{
    /** parent */
    private HUDScreen hudScreen;
    private int numStoreInventorySlots = 36;
    private int lengthSlotRow = 12;
    private Table inventorySlotTable;
    private Table playerInventorySlotTable;
    private DragAndDrop dragAndDrop;
    private Array<Actor> inventoryActors;

    private final int slotWidth = 46;
    private final int slotHeight = 46;

    private InventorySlotTooltip inventorySlotTooltip;

    private Label sellTotalLabel;
    private Label buyTotalLabel;
    private Label playerTotalGP;

    private int tradeInVal = 0;
    private int fullValue = 0;
    private int playerTotal = 0;

    private Button sellButton;
    private Button buyButton;
    public TextButton closeButton;

    private Table buttons;
    private Table totalLabels;

    private Json json;

    private static String SELL = "卖出";
    private static String BUY = "买进";
    private static String GP = " 金币";
    private static String PLAYER_TOTAL = "玩家合计";

    public StoreInventoryUI(HUDScreen parent){
        super("店铺存货", Assets.instance.STATUSUI_SKIN, "solidbackground");
        this.hudScreen = parent;
        json = new Json();

        this.setFillParent(true);

        //create
        dragAndDrop = new DragAndDrop();
        inventoryActors = new Array<Actor>();
        inventorySlotTable = new Table();
        inventorySlotTable.setName(InventoryUI.STORE_INVENTORY);

        playerInventorySlotTable = new Table();
        playerInventorySlotTable.setName(InventoryUI.PLAYER_INVENTORY);
        inventorySlotTooltip = new InventorySlotTooltip(Assets.instance.STATUSUI_SKIN);

        sellButton = new TextButton(SELL, Assets.instance.STATUSUI_SKIN, "inventory");
        disableButton (sellButton, true);

        sellTotalLabel = new Label(SELL + " : " + tradeInVal + GP, Assets.instance.STATUSUI_SKIN);
        sellTotalLabel.setAlignment(Align.center);
        buyTotalLabel = new Label(BUY + " : " + fullValue + GP, Assets.instance.STATUSUI_SKIN);
        buyTotalLabel.setAlignment(Align.center);

        playerTotalGP = new Label(PLAYER_TOTAL + " : " + playerTotal +  GP, Assets.instance.STATUSUI_SKIN);

        buyButton = new TextButton(BUY, Assets.instance.STATUSUI_SKIN, "inventory");
        disableButton (buyButton, true);

        closeButton = new TextButton("X", Assets.instance.STATUSUI_SKIN);

        buttons = new Table();
        buttons.defaults().expand().fill();
        buttons.add (sellButton).padLeft(10).padRight(10);
        buttons.add (buyButton).padLeft(10).padRight(10);

        totalLabels = new Table();
        totalLabels.defaults().expand().fill();
        totalLabels.add (sellTotalLabel).padLeft(40);
        totalLabels.add();
        totalLabels.add (buyTotalLabel).padRight(40);

        //layout
        for(int i = 1; i <= numStoreInventorySlots; i++){
            InventorySlot inventorySlot = new InventorySlot(this);
            inventorySlot.addListener(new InventorySlotTooltipListener(inventorySlotTooltip));
//            inventorySlot.addObserver(this);
            inventorySlot.setName(InventoryUI.STORE_INVENTORY);

            dragAndDrop.addTarget(new InventorySlotTarget(inventorySlot));

            inventorySlotTable.add(inventorySlot).size (slotWidth, slotHeight);

            if(i % lengthSlotRow == 0){
                inventorySlotTable.row();
            }
        }

        for(int i = 1; i <= InventoryUI._numSlots; i++){
            InventorySlot inventorySlot = new InventorySlot(this);
            inventorySlot.addListener(new InventorySlotTooltipListener(inventorySlotTooltip));
//            inventorySlot.addObserver(this);
            inventorySlot.setName(InventoryUI.PLAYER_INVENTORY);

            dragAndDrop.addTarget(new InventorySlotTarget(inventorySlot));

            playerInventorySlotTable.add(inventorySlot).size (slotWidth, slotHeight);

            if(i % lengthSlotRow == 0){
                playerInventorySlotTable.row();
            }
        }

        inventoryActors.add(inventorySlotTooltip);

        this.add();
        this.add (closeButton);
        this.row();

        //this.debugAll();
        this.defaults().expand().fill();
        this.add(inventorySlotTable).pad(10, 10, 10, 10).row();
        this.add (buttons).row();
        this.add (totalLabels).row();
        this.add (playerInventorySlotTable).pad(10, 10, 10, 10).row();
        this.add (playerTotalGP);
        this.pack();

        //Listeners
        buyButton.addListener(new ClickListener() {
                                               @Override
                                               public void clicked(InputEvent event, float x, float y) {
                                                   if( fullValue > 0 && playerTotal >= fullValue) {
                                                       playerTotal -= fullValue;
//                                                       StoreInventoryUI.this.notify(Integer.toString (playerTotal), StoreInventoryEvent.PLAYER_GP_TOTAL_UPDATED);
                                                       hudScreen.updateTotalGoldPoint (playerTotal);
                                                       fullValue = 0;
                                                       buyTotalLabel.setText(BUY  + " : " +  fullValue + GP);

                                                       checkButtonStates();

                                                       //Make sure we update the owner of the items
                                                       InventoryUI.setInventoryItemNames (playerInventorySlotTable, InventoryUI.PLAYER_INVENTORY);
                                                       savePlayerInventory();
                                                   }
                                               }
                                           }
        );

        sellButton.addListener(new ClickListener() {
                                   @Override
                                   public void clicked(InputEvent event, float x, float y) {
                                       if( tradeInVal > 0 ) {
                                           playerTotal += tradeInVal;
//                                           StoreInventoryUI.this.notify(Integer.toString (playerTotal), StoreInventoryEvent.PLAYER_GP_TOTAL_UPDATED);
                                           hudScreen.updateTotalGoldPoint (playerTotal);
                                           tradeInVal = 0;
                                           sellTotalLabel.setText(SELL  + " : " +  tradeInVal + GP);

                                           checkButtonStates();

                                           //Remove sold items
                                           Array<Cell> cells = inventorySlotTable.getCells();
                                           for( int i = 0; i < cells.size; i++){
                                               InventorySlot inventorySlot = (InventorySlot)cells.get(i).getActor();
                                               if( inventorySlot == null ) continue;
                                               if( inventorySlot.hasItem() &&
                                                       inventorySlot.getTopInventoryItem().getName().equalsIgnoreCase(InventoryUI.PLAYER_INVENTORY)){
                                                   inventorySlot.clearAllInventoryItems(false);
                                               }
                                           }
                                           savePlayerInventory();
                                       }
                                   }
                                }
        );
    }

    public TextButton getCloseButton(){
        return closeButton;
    }

    public Table getInventorySlotTable() {
        return inventorySlotTable;
    }

    public Array<Actor> getInventoryActors(){
        return inventoryActors;
    }

    public void loadPlayerInventory(Array<InventoryItemLocation> playerInventoryItems){
        InventoryUI.populateInventory (playerInventorySlotTable, playerInventoryItems, dragAndDrop, InventoryUI.PLAYER_INVENTORY, true);
    }

    public void loadStoreInventory(Array<InventoryItemLocation> storeInventoryItems){
        InventoryUI.populateInventory(inventorySlotTable, storeInventoryItems, dragAndDrop, InventoryUI.STORE_INVENTORY, false);
    }

    public void savePlayerInventory(){
        Array<InventoryItemLocation> playerItemsInPlayerInventory = InventoryUI.getInventoryFiltered (playerInventorySlotTable, InventoryUI.STORE_INVENTORY);
        Array<InventoryItemLocation> playerItemsInStoreInventory = InventoryUI.getInventoryFiltered (playerInventorySlotTable, inventorySlotTable, InventoryUI.STORE_INVENTORY);
        playerItemsInPlayerInventory.addAll(playerItemsInStoreInventory);
//        StoreInventoryUI.this.notify(json.toJson(playerItemsInPlayerInventory), StoreInventoryEvent.PLAYER_INVENTORY_UPDATED);
        hudScreen.updateInventory(playerItemsInPlayerInventory);
    }

    public void cleanupStoreInventory(){
        InventoryUI.removeInventoryItems(InventoryUI.STORE_INVENTORY, playerInventorySlotTable);
        InventoryUI.removeInventoryItems(InventoryUI.PLAYER_INVENTORY, inventorySlotTable);
    }

    public void removedItem(InventorySlot slot) {
        if( slot.getTopInventoryItem().getName().equalsIgnoreCase(InventoryUI.PLAYER_INVENTORY) &&
                slot.getName().equalsIgnoreCase(InventoryUI.STORE_INVENTORY) ) {
            tradeInVal -= slot.getTopInventoryItem().getTradeValue();
            sellTotalLabel.setText(SELL + " : " + tradeInVal + GP);
        }
        if( slot.getTopInventoryItem().getName().equalsIgnoreCase(InventoryUI.STORE_INVENTORY) &&
                slot.getName().equalsIgnoreCase(InventoryUI.PLAYER_INVENTORY) ) {
            fullValue -= slot.getTopInventoryItem().getItemValue();
            buyTotalLabel.setText(BUY + " : " + fullValue + GP);
        }
        checkButtonStates();
    }

    public void addedItem(InventorySlot slot) {
        //moving from player inventory to store inventory to sell
        if( slot.getTopInventoryItem().getName().equalsIgnoreCase(InventoryUI.PLAYER_INVENTORY) &&
                slot.getName().equalsIgnoreCase(InventoryUI.STORE_INVENTORY) ) {
            tradeInVal += slot.getTopInventoryItem().getTradeValue();
            sellTotalLabel.setText(SELL + " : " + tradeInVal + GP);
        }
        //moving from store inventory to player inventory to buy
        if( slot.getTopInventoryItem().getName().equalsIgnoreCase(InventoryUI.STORE_INVENTORY) &&
                slot.getName().equalsIgnoreCase(InventoryUI.PLAYER_INVENTORY) ) {
            fullValue += slot.getTopInventoryItem().getItemValue();
            buyTotalLabel.setText(BUY + " : " +  fullValue + GP);
        }
        checkButtonStates();
    }

//    @Override
   /* public void onNotify(InventorySlot slot, SlotEvent event) {
        switch(event)
        {
            case ADDED_ITEM:
                //moving from player inventory to store inventory to sell
                if( slot.getTopInventoryItem().getName().equalsIgnoreCase(InventoryUI.PLAYER_INVENTORY) &&
                        slot.getName().equalsIgnoreCase(InventoryUI.STORE_INVENTORY) ) {
                    tradeInVal += slot.getTopInventoryItem().getTradeValue();
                    sellTotalLabel.setText(SELL + " : " + tradeInVal + GP);
                }
                //moving from store inventory to player inventory to buy
                if( slot.getTopInventoryItem().getName().equalsIgnoreCase(InventoryUI.STORE_INVENTORY) &&
                        slot.getName().equalsIgnoreCase(InventoryUI.PLAYER_INVENTORY) ) {
                    fullValue += slot.getTopInventoryItem().getItemValue();
                    buyTotalLabel.setText(BUY + " : " +  fullValue + GP);
                }
                break;
            case REMOVED_ITEM:
                if( slot.getTopInventoryItem().getName().equalsIgnoreCase(InventoryUI.PLAYER_INVENTORY) &&
                        slot.getName().equalsIgnoreCase(InventoryUI.STORE_INVENTORY) ) {
                    tradeInVal -= slot.getTopInventoryItem().getTradeValue();
                    sellTotalLabel.setText(SELL + " : " + tradeInVal + GP);
                }
                if( slot.getTopInventoryItem().getName().equalsIgnoreCase(InventoryUI.STORE_INVENTORY) &&
                        slot.getName().equalsIgnoreCase(InventoryUI.PLAYER_INVENTORY) ) {
                    fullValue -= slot.getTopInventoryItem().getItemValue();
                    buyTotalLabel.setText(BUY + " : " + fullValue + GP);
                }
                break;
        }
        checkButtonStates();
    }*/

    public void checkButtonStates(){
        if( tradeInVal <= 0 ) {
            disableButton (sellButton, true);
        }else{
            disableButton (sellButton, false);
        }

        if( fullValue <= 0 || playerTotal < fullValue) {
            disableButton (buyButton, true);
        }else{
            disableButton (buyButton, false);
        }
    }

    public void setPlayerGP(int value){
        playerTotal = value;
        playerTotalGP.setText(PLAYER_TOTAL + " : " + playerTotal +  GP);
    }

    private void disableButton(Button button, boolean disable){
        if( disable ){
            button.setDisabled(true);
            button.setTouchable(Touchable.disabled);
        }else{
            button.setDisabled(false);
            button.setTouchable(Touchable.enabled);
        }
    }

} public class InventoryItem extends Image {

    public enum ItemAttribute{
        CONSUMABLE(1),
        EQUIPPABLE(2),
        STACKABLE(4);

        private int attribute;

        ItemAttribute(int attribute){
            this.attribute = attribute;
        }

        public int getValue(){
            return attribute;
        }

    }

    public enum ItemUseType{
        ITEM_RESTORE_HEALTH(1),
        ITEM_RESTORE_MP(2),
        ITEM_DAMAGE(4),
        WEAPON_ONEHAND(8),
        WEAPON_TWOHAND(16),
        WAND_ONEHAND(32),
        WAND_TWOHAND(64),
        ARMOR_SHIELD(128),
        ARMOR_HELMET(256),
        ARMOR_CHEST(512),
        ARMOR_FEET(1024),
        QUEST_ITEM(2048);

        private int itemUseType;

        ItemUseType(int itemUseType){
            this.itemUseType = itemUseType;
        }

        public int getValue(){
            return itemUseType;
        }
    }

    public enum ItemTypeID {
        ARMOR01,ARMOR02,ARMOR03,ARMOR04,ARMOR05,
        BOOTS01,BOOTS02,BOOTS03,BOOTS04,BOOTS05,
        HELMET01,HELMET02,HELMET03,HELMET04,HELMET05,
        SHIELD01,SHIELD02,SHIELD03,SHIELD04,SHIELD05,
        WANDS01,WANDS02,WANDS03,WANDS04,WANDS05,
        WEAPON01,WEAPON02,WEAPON03,WEAPON04,WEAPON05,
        POTIONS01,POTIONS02,POTIONS03,
        SCROLL01,SCROLL02,SCROLL03,
        HERB001,BABY001,HORNS001,FUR001,
        NONE
        ;
    }

    private int itemAttributes;
    private int itemUseType;
    private int itemUseTypeValue;
    private ItemTypeID itemTypeID;
    private String itemShortDescription;
    private int itemValue;

    public InventoryItem(TextureRegion textureRegion, int itemAttributes, ItemTypeID itemTypeID, int itemUseType, int itemUseTypeValue, int itemValue){
        super(textureRegion);

        this.itemTypeID = itemTypeID;
        this.itemAttributes = itemAttributes;
        this.itemUseType = itemUseType;
        this.itemUseTypeValue = itemUseTypeValue;
        this.itemValue = itemValue;
    }

    public InventoryItem(){
        super();
    }

    public InventoryItem(InventoryItem inventoryItem){
        super();
        this.itemTypeID = inventoryItem.getItemTypeID();
        this.itemAttributes = inventoryItem.getItemAttributes();
        this.itemUseType = inventoryItem.getItemUseType();
        this.itemUseTypeValue = inventoryItem.getItemUseTypeValue();
        this.itemShortDescription = inventoryItem.getItemShortDescription();
        this.itemValue = inventoryItem.getItemValue();
    }

    public int getItemUseTypeValue() {
        return itemUseTypeValue;
    }

    public void setItemUseTypeValue(int itemUseTypeValue) {
        this.itemUseTypeValue = itemUseTypeValue;
    }

    public int getItemValue() {
        return itemValue;
    }

    public void setItemValue(int itemValue) {
        this.itemValue = itemValue;
    }

    public ItemTypeID getItemTypeID() {
        return itemTypeID;
    }

    public void setItemTypeID(ItemTypeID itemTypeID) {
        this.itemTypeID = itemTypeID;
    }

    public int getItemAttributes() {
        return itemAttributes;
    }

    public void setItemAttributes(int itemAttributes) {
        this.itemAttributes = itemAttributes;
    }

    public int getItemUseType() {
        return itemUseType;
    }

    public void setItemUseType(int itemUseType) {
        this.itemUseType = itemUseType;
    }

    public String getItemShortDescription() {
        return itemShortDescription;
    }

    public void setItemShortDescription(String itemShortDescription) {
        this.itemShortDescription = itemShortDescription;
    }

    public boolean isStackable(){
        return ((itemAttributes & ItemAttribute.STACKABLE.getValue()) == ItemAttribute.STACKABLE.getValue());
    }

    public boolean isConsumable(){
        return ((itemAttributes & ItemAttribute.CONSUMABLE.getValue()) == ItemAttribute.CONSUMABLE.getValue());
    }

    public boolean isSameItemType(InventoryItem candidateInventoryItem){
        return itemTypeID == candidateInventoryItem.getItemTypeID();
    }

    public static boolean doesRestoreHP(int itemUseType){
        return ((itemUseType & ItemUseType.ITEM_RESTORE_HEALTH.getValue()) == ItemUseType.ITEM_RESTORE_HEALTH.getValue());
    }

    public static boolean doesRestoreMP(int itemUseType){
        return ((itemUseType & ItemUseType.ITEM_RESTORE_MP.getValue()) == ItemUseType.ITEM_RESTORE_MP.getValue());
    }

    public int getTradeValue(){
        //For now, we will set the trade in value of items at about one third their original value
        if( itemValue >= 0 ) {
            return MathUtils.floor(itemValue * .33f) + 2;
        }else{
            return 0;
        }
    }

    public boolean isInventoryItemOffensiveWand(){
        if(     (itemUseType & ItemUseType.WAND_ONEHAND.getValue() ) == ItemUseType.WAND_ONEHAND.getValue() ||
                (itemUseType & ItemUseType.WAND_TWOHAND.getValue() ) == ItemUseType.WAND_TWOHAND.getValue()
                ){
            return true;
        }else{
            return false;
        }
    }


    public boolean isInventoryItemOffensive(){
        if(     (itemUseType & ItemUseType.WEAPON_ONEHAND.getValue() ) == ItemUseType.WEAPON_ONEHAND.getValue() ||
                (itemUseType & ItemUseType.WEAPON_TWOHAND.getValue() ) == ItemUseType.WEAPON_TWOHAND.getValue() ||
                (itemUseType & ItemUseType.WAND_ONEHAND.getValue() ) == ItemUseType.WAND_ONEHAND.getValue() ||
                (itemUseType & ItemUseType.WAND_TWOHAND.getValue() ) == ItemUseType.WAND_TWOHAND.getValue()
                ){
            return true;
        }else{
            return false;
        }
    }

    public boolean isInventoryItemDefensive(){
        if(     (itemUseType & ItemUseType.ARMOR_CHEST.getValue() ) == ItemUseType.ARMOR_CHEST.getValue() ||
                (itemUseType & ItemUseType.ARMOR_HELMET.getValue() ) == ItemUseType.ARMOR_HELMET.getValue() ||
                (itemUseType & ItemUseType.ARMOR_FEET.getValue() ) == ItemUseType.ARMOR_FEET.getValue() ||
                (itemUseType & ItemUseType.ARMOR_SHIELD.getValue() ) == ItemUseType.ARMOR_SHIELD.getValue()
                ){
            return true;
        }else{
            return false;
        }
    }
} public class InventoryItemLocation {
    private int locationIndex;
    private String itemTypeAtLocation;
    private int numberItemsAtLocation;
    private String itemNameProperty;

    public InventoryItemLocation(){
    }

    public InventoryItemLocation( int locationIndex, String itemTypeAtLocation, int numberItemsAtLocation, String itemNameProperty){
        this.locationIndex = locationIndex;
        this.itemTypeAtLocation = itemTypeAtLocation;
        this.numberItemsAtLocation = numberItemsAtLocation;
        this.itemNameProperty = itemNameProperty;
    }

    public String getItemNameProperty() {
        return itemNameProperty;
    }

    public void setItemNameProperty(String itemNameProperty) {
        this.itemNameProperty = itemNameProperty;
    }

    public String getItemTypeAtLocation() {
        return itemTypeAtLocation;
    }

    public void setItemTypeAtLocation(String itemTypeAtLocation) {
        this.itemTypeAtLocation = itemTypeAtLocation;
    }

    public int getLocationIndex() {
        return locationIndex;
    }

    public void setLocationIndex(int locationIndex) {
        this.locationIndex = locationIndex;
    }

    public int getNumberItemsAtLocation() {
        return numberItemsAtLocation;
    }

    public void setNumberItemsAtLocation(int numberItemsAtLocation) {
        this.numberItemsAtLocation = numberItemsAtLocation;
    }
} public class QuestGraph {
    private static final String TAG = QuestGraph.class.getSimpleName();

    private Hashtable<String, QuestTask> questTasks;
    private Hashtable<String, ArrayList<QuestTaskDependency>> questTaskDependencies;
    private String questTitle;
    private String questID;
    private boolean isQuestComplete = false;
    private int goldReward;
    private int xpReward;

    public int getGoldReward() {
        return goldReward;
    }

    public void setGoldReward(int goldReward) {
        this.goldReward = goldReward;
    }

    public int getXpReward() {
        return xpReward;
    }

    public void setXpReward(int xpReward) {
        this.xpReward = xpReward;
    }

    public boolean isQuestComplete() {
        return isQuestComplete;
    }

    public void setQuestComplete(boolean isQuestComplete) {
        this.isQuestComplete = isQuestComplete;
    }

    public String getQuestID() {
        return questID;
    }

    public void setQuestID(String questID) {
        this.questID = questID;
    }

    public String getQuestTitle() {
        return questTitle;
    }

    public void setQuestTitle(String questTitle) {
        this.questTitle = questTitle;
    }

    public boolean areAllTasksComplete(){
        ArrayList<QuestTask> tasks = getAllQuestTasks();
        for( QuestTask task: tasks ){
            if( !task.isTaskComplete() ){
                return false;
            }
        }
        return true;
    }

    public void setTasks(Hashtable<String, QuestTask> questTasks) {
        if( questTasks.size() < 0 ){
            throw new IllegalArgumentException("Can't have a negative amount of conversations");
        }

        this.questTasks = questTasks;
        this.questTaskDependencies = new Hashtable<String, ArrayList<QuestTaskDependency>>(questTasks.size());

        for( QuestTask questTask: questTasks.values() ){
            questTaskDependencies.put(questTask.getId(), new ArrayList<QuestTaskDependency>());
        }
    }

    public ArrayList<QuestTask> getAllQuestTasks(){
        Enumeration<QuestTask> enumeration = questTasks.elements();
        return Collections.list(enumeration);
    }

    public void clear(){
        questTasks.clear();
        questTaskDependencies.clear();
    }

    public boolean isValid(String taskID){
        QuestTask questTask = questTasks.get(taskID);
        if( questTask == null ) return false;
        return true;
    }

    public boolean isReachable(String sourceID, String sinkID){
        if( !isValid(sourceID) || !isValid(sinkID) ) return false;
        if( questTasks.get(sourceID) == null ) return false;

        ArrayList<QuestTaskDependency> list = questTaskDependencies.get(sourceID);
        if( list == null ) return false;
        for(QuestTaskDependency dependency: list){
            if(     dependency.getSourceId().equalsIgnoreCase(sourceID) &&
                    dependency.getDestinationId().equalsIgnoreCase(sinkID) ){
                return true;
            }
        }
        return false;
    }

    public QuestTask getQuestTaskByID(String id){
        if( !isValid(id) ){
            //System.out.println("Id " + id + " is not valid!");
            return null;
        }
        return questTasks.get(id);
    }

    public void addDependency(QuestTaskDependency questTaskDependency){
        ArrayList<QuestTaskDependency> list = questTaskDependencies.get(questTaskDependency.getSourceId());
        if( list == null) return;

        //Will not add if creates cycles
        if( doesCycleExist(questTaskDependency) ){
            //System.out.println("Cycle exists! Not adding");
            return;
        }

        list.add(questTaskDependency);
    }

    public boolean doesCycleExist(QuestTaskDependency questTaskDep){
        Set<String> keys = questTasks.keySet();
        for( String id: keys ){
            if( doesQuestTaskHaveDependencies(id) &&
                    questTaskDep.getDestinationId().equalsIgnoreCase(id)){
                    //System.out.println("ID: " + id + " destID: " + questTaskDep.getDestinationId());
                    return true;
                }
            }
        return false;
    }

    public boolean doesQuestTaskHaveDependencies(String id){
        QuestTask task = getQuestTaskByID(id);
        if( task == null) return false;
        ArrayList<QuestTaskDependency> list = questTaskDependencies.get(id);

        if( list.isEmpty() || list.size() == 0){
            return false;
        }else{
            return true;
        }
    }

    public boolean updateQuestForReturn(){
        ArrayList<QuestTask> tasks = getAllQuestTasks();
        QuestTask readyTask = null;

        //First, see if all tasks are available, meaning no blocking dependencies
        for( QuestTask task : tasks){
            if( !isQuestTaskAvailable(task.getId())){
                return false;
            }
            if( !task.isTaskComplete() ){
                if( task.getQuestType().equals(QuestTask.QuestType.RETURN) ){
                    readyTask = task;
                }else{
                    return false;
                }
            }
        }
        if( readyTask == null ) return false;
        readyTask.setTaskComplete();
        return true;
    }

    public boolean isQuestTaskAvailable(String id){
        QuestTask task = getQuestTaskByID(id);
        if( task == null) return false;
        ArrayList<QuestTaskDependency> list = questTaskDependencies.get(id);

        for(QuestTaskDependency dep: list){
            QuestTask depTask = getQuestTaskByID(dep.getDestinationId());
            if( depTask == null || depTask.isTaskComplete() ) continue;
            if( dep.getSourceId().equalsIgnoreCase(id) ){
                return false;
            }
        }
        return true;
    }

    public void setQuestTaskComplete(String id){
        QuestTask task = getQuestTaskByID(id);
        if( task == null) return;
        task.setTaskComplete();
    }

    public void update(MapsManager mapMgr){
        ArrayList<QuestTask> allQuestTasks = getAllQuestTasks();
        for( QuestTask questTask: allQuestTasks ) {

            if( questTask.isTaskComplete() ) continue;

            //We first want to make sure the task is available and is relevant to current location
            if (!isQuestTaskAvailable(questTask.getId())) continue;

            String taskLocation = questTask.getPropertyValue(QuestTask.QuestTaskPropertyType.TARGET_LOCATION.toString());
            if (taskLocation == null ||
                    "".equals(taskLocation) ||
                    !taskLocation.equalsIgnoreCase(mapMgr.getMapName())) continue;

            switch (questTask.getQuestType()) {
                case FETCH:
                    String taskConfig = questTask.getPropertyValue(QuestTask.QuestTaskPropertyType.TARGET_TYPE.toString());
                    if( taskConfig == null || "".equals(taskConfig.trim()) ) break;
                    //获取这个人的任务道具,如果当前任务的任务道具收集够了，任务完成
//                    EntityConfig config = Entity.getEntityConfig(taskConfig);

//                    Array<Vector2> questItemPositions = ProfileManager.getInstance().getProperty(config.getEntityID(), Array.class);
//                    if( questItemPositions == null ) break;

                    //Case where all the items have been picked up
                    /*if( questItemPositions.size == 0 ){
                        questTask.setTaskComplete();
                        Gdx.app.debug(TAG, "TASK : " + questTask.getId() + " is complete of Quest: " + questID);
                        Gdx.app.debug(TAG, "INFO : " + QuestTask.QuestTaskPropertyType.TARGET_TYPE.toString());
                    }*/
                    break;
                case KILL:
                    break;
                case DELIVERY:
                    break;
                case GUARD:
                    break;
                case ESCORT:
                    break;
                case RETURN:
                    break;
                case DISCOVER:
                    break;
            }
        }
    }

    public void init(MapsManager mapMgr){
        ArrayList<QuestTask> allQuestTasks = getAllQuestTasks();
        for( QuestTask questTask: allQuestTasks ) {

            if( questTask.isTaskComplete() ) continue;

            //We first want to make sure the task is available and is relevant to current location
            if (!isQuestTaskAvailable(questTask.getId())) continue;

            String taskLocation = questTask.getPropertyValue(QuestTask.QuestTaskPropertyType.TARGET_LOCATION.toString());
            if (     taskLocation == null ||
                     "".equals(taskLocation.trim()) ||
                    !taskLocation.equalsIgnoreCase(mapMgr.getMapName())) continue;

            switch (questTask.getQuestType()) {
                case FETCH:
                   /* Array<Entity> questEntities = new Array<Entity>();
                    Array<Vector2> positions = mapMgr.getQuestItemSpawnPositions(questID, questTask.getId());
                    String taskConfig = questTask.getPropertyValue(QuestTask.QuestTaskPropertyType.TARGET_TYPE.toString());
                    if( taskConfig == null || "".equals(taskConfig.trim()) ) break;
                    EntityConfig config = Entity.getEntityConfig(taskConfig);

                    Array<Vector2> questItemPositions = ProfileManager.getInstance().getProperty(config.getEntityID(), Array.class);

                    if( questItemPositions == null ){
                        questItemPositions = new Array<Vector2>();
                        for( Vector2 position: positions ){
                            questItemPositions.add(position);
                            Entity entity = Entity.initEntity(config, position);
                            entity.getEntityConfig().setCurrentQuestID(questID);
                            questEntities.add(entity);
                        }
                    }else{
                        for( Vector2 questItemPosition: questItemPositions ){
                            Entity entity = Entity.initEntity(config, questItemPosition);
                            entity.getEntityConfig().setCurrentQuestID(questID);
                            questEntities.add(entity);
                        }
                    }

                    mapMgr.addMapQuestEntities(questEntities);*/
//                    ProfileManager.getInstance().setProperty(config.getEntityID(), questItemPositions);
                    break;
                case KILL:
                    break;
                case DELIVERY:
                    break;
                case GUARD:
                    break;
                case ESCORT:
                    break;
                case RETURN:
                    break;
                case DISCOVER:
                    break;
            }
        }
    }

    public String toString(){
        return questTitle;
    }

    public String toJson(){
        Json json = new Json();
        return json.prettyPrint(this);
    }

} public class ScreenTransitionAction extends Action {
    public static enum ScreenTransitionType{
        FADE_IN,
        FADE_OUT,
        NONE
    }

    private ScreenTransitionType _transitionType = ScreenTransitionType.NONE;
    private float _transitionDuration = 3;

    public ScreenTransitionAction(){
    }

    public ScreenTransitionAction(ScreenTransitionType type, float duration){
        this._transitionType = type;
        this._transitionDuration = duration;
    }

    @Override
    public boolean act(float delta) {
        Actor actor = getTarget();
        if ( actor == null ) return false;
        switch(_transitionType){
            case FADE_IN:
                SequenceAction fadeIn = Actions.sequence(
                        Actions.alpha(1),
                        Actions.fadeOut(_transitionDuration));
                actor.addAction(fadeIn);
                break;
            case FADE_OUT:
                SequenceAction fadeOut = Actions.sequence(
                        Actions.alpha(0),
                        Actions.fadeIn(_transitionDuration));
                actor.addAction(fadeOut);
                break;
            case NONE:
                break;
            default:
                break;
        }
        return true;
    }

    public static ScreenTransitionAction transition (ScreenTransitionType type, float duration) {
        ScreenTransitionAction action = Actions.action(ScreenTransitionAction.class);
        action.setTransitionType(type);
        action.setTransitionDuration(duration);
        return action;
    }

    public ScreenTransitionType getTransitionType() {
        return _transitionType;
    }

    public void setTransitionType(ScreenTransitionType transitionType) {
        this._transitionType = transitionType;
    }

    public float getTransitionDuration() {
        return _transitionDuration;
    }

    public void setTransitionDuration(float transitionDuration) {
        this._transitionDuration = transitionDuration;
    }
} public class ScreenTransitionActor extends Image {
    private Color _transitionColor = Color.BLACK;

    public ScreenTransitionActor(){
        init();
    }

    public ScreenTransitionActor(Color color){
        this._transitionColor = color;

        init();
    }

    private void init(){
        toFront();
        setFillParent(true);

        Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
        pixmap.setColor(_transitionColor);
        pixmap.fill();
        setDrawable(new TextureRegionDrawable(new TextureRegion(new Texture(pixmap))));
        clearListeners();
        setTouchable(Touchable.disabled);
    }

    public Color getTransitionColor() {
        return _transitionColor;
    }

    public void setTransitionColor(Color transitionColor) {
        this._transitionColor = transitionColor;
    }
} public class ShakeCamera {
	private static final String TAG = ShakeCamera.class.getSimpleName();
	
	private boolean _isShaking = false;
	private float _origShakeRadius = 30.0f;
	private float _shakeRadius;
	private float _randomAngle;
	private Vector2 _offset;
	private Vector2 _currentPosition;
	private Vector2 _origPosition;

	
	public ShakeCamera(float x, float y, float shakeRadius){
		this._origPosition = new Vector2(x,y);
		this._shakeRadius = shakeRadius;
		this._origShakeRadius = shakeRadius;
		this._offset = new Vector2();
		this._currentPosition = new Vector2();
		reset();
	}
	
	public boolean isCameraShaking(){
		return _isShaking;
	}
	
	public void startShaking(){
		_isShaking = true;
	}
	
	private void seedRandomAngle(){
		_randomAngle = MathUtils.random(1, 360);
	}
	
	private void computeCameraOffset(){
		float sine = MathUtils.sinDeg(_randomAngle);
		float cosine = MathUtils.cosDeg(_randomAngle);

		//Gdx.app.debug(TAG, "Sine of " + _randomAngle + " is: " + sine);
		//Gdx.app.debug(TAG, "Cosine of " + _randomAngle + " is: " + cosine);

		_offset.x =  cosine * _shakeRadius;
		_offset.y =  sine * _shakeRadius;

		//Gdx.app.debug(TAG, "Offset is x:" + _offset.x + " , y: " + _offset.y );
	}
	
	private void computeCurrentPosition(){
		_currentPosition.x = _origPosition.x + _offset.x;
		_currentPosition.y = _origPosition.y + _offset.y;

		//Gdx.app.debug(TAG, "Current position is x:" + _currentPosition.x + " , y: " + _currentPosition.y );
	}
	
	private void diminishShake(){
		//Gdx.app.debug(TAG, "Current shakeRadius is: " + _shakeRadius + " randomAngle is: " + _randomAngle);

		if( _shakeRadius < 2.0 ){
			//Gdx.app.debug(TAG, "Done shaking");
			reset();
			return;
		}
		
		_isShaking = true;
		_shakeRadius *= .9f;
		//Gdx.app.debug(TAG, "New shakeRadius is: " + _shakeRadius);


		_randomAngle = MathUtils.random(1, 360);
		//Gdx.app.debug(TAG, "New random angle: " + _randomAngle);
	}

	public void reset(){
		_shakeRadius = _origShakeRadius;
		_isShaking = false;
		seedRandomAngle();
		_currentPosition.x = _origPosition.x;
		_currentPosition.y = _origPosition.y;
	}
	
	public Vector2 getNewShakePosition(){
		computeCameraOffset();
		computeCurrentPosition();
		diminishShake();
		return _currentPosition;
	}
} public class Assets implements Disposable, AssetErrorListener {

	public static final String TAG = Assets.class.getName();

	public static final Assets instance = new Assets();
	/** libGDX 's asset manager*/
	private static AssetManager assetManager;
	private static InternalFileHandleResolver filePathResolver =  new InternalFileHandleResolver();
	/** load all characters */
	public Map<String,AssetRole> assetRoles = new HashMap<String, AssetRole>();
	/** load all tiled map */
	public Map<String,AssetTiledMap> assetTiledMaps = new HashMap<String, AssetTiledMap>();
	public AssetTouch touch;
	public Texture shadow;
	public Texture selected;
	public Texture hpBar,bar;

	public static TextureRegion[] water;
	public static TextureRegion[] earth;
	public static TextureRegion[] fire;
	public static TextureRegion[] thunder;

	public TextureAtlas STATUSUI_TEXTUREATLAS ;
	public TextureAtlas ITEMS_TEXTUREATLAS ;
	public Skin STATUSUI_SKIN ;
	public Texture MONSTERS0;
	public Texture MONSTERS1;
	// singleton: prevent instantiation from other classes
	private Assets() {
	}

	public class AssetRole {
		public final Animation idleDownAnimation;
		public final Animation idleLeftAnimation;
		public final Animation idleRightAnimation;
		public final Animation idleUpAnimation;
		public final Animation walkDownAnimation;
		public final Animation walkLeftAnimation;
		public final Animation walkRightAnimation;
		public final Animation walkUpAnimation;
		public  Animation attackDownAnimation;
		public  Animation attackLeftAnimation;
		public  Animation attackRightAnimation;
		public  Animation attackUpAnimation;

		public AssetRole(Texture atlas,Texture attack) {
			//role's animation png is 0-idle,1-walk,2-walk
			TextureRegion[][] textureFrames = TextureRegion.split(atlas, 32, 48);
			Array<TextureRegion> idleDownFrames = new Array<TextureRegion>(1);
			Array<TextureRegion> idleLeftFrames = new Array<TextureRegion>(1);
			Array<TextureRegion> idleRightFrames = new Array<TextureRegion>(1);
			Array<TextureRegion> idleUpFrames = new Array<TextureRegion>(1);
			Array<TextureRegion> walkDownFrames = new Array<TextureRegion>(2);
			Array<TextureRegion> walkLeftFrames = new Array<TextureRegion>(2);
			Array<TextureRegion> walkRightFrames = new Array<TextureRegion>(2);
			Array<TextureRegion> walkUpFrames = new Array<TextureRegion>(2);
			for (int i = 0; i < 4; i++) {
				for (int j = 0; j < 3; j++) {
					TextureRegion region = textureFrames[i][j];
					if( region == null ){
						Gdx.app.debug(TAG, "Got null animation frame " + i + "," + j);
					}
					if(j==0){
						switch(i)
						{
							case 0:
								idleDownFrames.insert(0, region);
								break;
							case 1:
								idleLeftFrames.insert(0, region);
								break;
							case 2:
								idleUpFrames.insert(0, region);
								break;
							case 3:
								idleRightFrames.insert(0, region);
								break;
						}
					}else{
						switch(i)
						{
							case 0:
								walkDownFrames.insert(j-1, region);
								break;
							case 1:
								walkLeftFrames.insert(j-1, region);
								break;
							case 2:
								walkUpFrames.insert(j-1, region);
								break;
							case 3:
								walkRightFrames.insert(j-1, region);
								break;
						}
					}
				}
			}
			idleDownAnimation = new Animation(0.25f, idleDownFrames, Animation.PlayMode.LOOP);
			idleLeftAnimation = new Animation(0.25f, idleLeftFrames, Animation.PlayMode.LOOP);
			idleRightAnimation = new Animation(0.25f, idleRightFrames, Animation.PlayMode.LOOP);
			idleUpAnimation = new Animation(0.25f, idleUpFrames, Animation.PlayMode.LOOP);
			walkDownAnimation = new Animation(0.25f, walkDownFrames, Animation.PlayMode.LOOP);
			walkLeftAnimation = new Animation(0.25f, walkLeftFrames, Animation.PlayMode.LOOP);
			walkRightAnimation = new Animation(0.25f, walkRightFrames, Animation.PlayMode.LOOP);
			walkUpAnimation = new Animation(0.25f, walkUpFrames, Animation.PlayMode.LOOP);
			attackDownAnimation = null;
			attackLeftAnimation = null;
			attackRightAnimation = null;
			attackUpAnimation = null;
			if(attack!=null){
				TextureRegion[][] attackFrames = TextureRegion.split(attack, 32, 48);
				Array<TextureRegion> attackDownFrames = new Array<TextureRegion>(3);
				Array<TextureRegion> attackLeftFrames = new Array<TextureRegion>(3);
				Array<TextureRegion> attackRightFrames = new Array<TextureRegion>(3);
				Array<TextureRegion> attackUpFrames = new Array<TextureRegion>(3);
				for (int i = 0; i < 4; i++) {
					for (int j = 0; j < 3; j++) {
						TextureRegion region = attackFrames[i][j];
						if (region == null) {
							Gdx.app.debug(TAG, "Got null animation frame " + i + "," + j);
						}
						switch (i) {
							case 0:
								attackDownFrames.insert(j, region);
								break;
							case 1:
								attackLeftFrames.insert(j, region);
								break;
							case 2:
								attackUpFrames.insert(j, region);
								break;
							case 3:
								attackRightFrames.insert(j, region);
								break;
						}
					}
				}
				attackDownAnimation = new Animation(0.25f, attackDownFrames, Animation.PlayMode.LOOP);
				attackLeftAnimation = new Animation(0.25f, attackLeftFrames, Animation.PlayMode.LOOP);
				attackRightAnimation = new Animation(0.25f, attackRightFrames, Animation.PlayMode.LOOP);
				attackUpAnimation = new Animation(0.25f, attackUpFrames, Animation.PlayMode.LOOP);
			}
		}
	}

	public class AssetTiledMap{
		public final TiledMap tiledMap;
		public final String mapName;
		public final int mapTileWidth;
		public final int mapTileHeight;
		public final int tileWidth;
		public final int tileHeight;
		public final int mapPixelWidth;
		public final int mapPixelHeight;
		public AssetTiledMap(TiledMap currentTileMap){
			tiledMap = currentTileMap;
			MapProperties props =  currentTileMap.getProperties();
			mapName = props.get("mapName",String.class);
			mapTileWidth = props.get("width",Integer.class);
			mapTileHeight = props.get("height",Integer.class);
			tileWidth = props.get("tilewidth",Integer.class);
			tileHeight = props.get("tileheight",Integer.class);
			mapPixelWidth = mapTileWidth * tileWidth;
			mapPixelHeight = mapTileHeight * tileHeight;
		}
	}

	public class AssetTouch{
		public final Animation touchPointAnimation;
		public AssetTouch(Texture atlas){
			TextureRegion[][] textureFrames = TextureRegion.split(atlas, 32, 32);
			Array<TextureRegion> touchPointFrames = new Array<TextureRegion>(3);
			for( int i=0;i<2;i++){
				touchPointFrames.insert(i,textureFrames[0][i]);
			}
			touchPointAnimation = new Animation(0.25f, touchPointFrames, Animation.PlayMode.LOOP);
		}
	}
	private static TextureRegion[] loads(String pre, String tail, int mnt,
										 int width, int height) {
		TextureRegion[] res = new TextureRegion[mnt];
		for(int i=0;i<mnt;i++){
			res[i] = load(pre+(i+1)+tail,width,height);
		}
		return res;
	}
	/**
	 * 从文件加载Texture
	 *
	 * @param name
	 * @param width
	 * @param height
	 * @return
	 */
	public static TextureRegion load (String name, int width, int height) {
		Texture texture = new Texture(Gdx.files.internal(name));
		TextureRegion region = new TextureRegion(texture, 0, 0, width, height);
//		region.flip(false, true);
		return region;
	}

	public void init (AssetManager assetManager) {
		this.assetManager = assetManager;
		// set asset manager error handler
		this.assetManager.setErrorListener(this);
		this.assetManager.setLoader(TiledMap.class, new TmxMapLoader(new InternalFileHandleResolver()));
		// load texture atlas
		water = loads("skill/storm/storm (",").png",35, 495, 331);
		earth = loads("skill/sand/sand (",").png",26, 428, 369);
		fire = loads("skill/fire/fir (",").png",28, 464, 273);
		thunder = loads("skill/thunder/benlei (",").png",35, 450, 450);
		//load all hero's pack
		for (Map.Entry<String, String> entry : Resource.instance.players.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue().toString();
			assetManager.load(value, Texture.class);
			String attackFilePath = Resource.instance.fighters.get(key);
			if(attackFilePath!=null){
				assetManager.load(attackFilePath, Texture.class);
			}
		}
		for (Map.Entry<String, String> entry : Resource.instance.npcs.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue().toString();
			assetManager.load(value, Texture.class);
		}
		//TODO:load all map's file
		for (Map.Entry<String, String> entry : Resource.instance.maps.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue().toString();
			assetManager.load(value, TiledMap.class);
		}
		assetManager.load(Resource.MONSTERS0,Texture.class);
		assetManager.load(Resource.MONSTERS1,Texture.class);
		assetManager.load(Resource.TOUCH,Texture.class);
		assetManager.load(Resource.SHADOW,Texture.class);
		assetManager.load(Resource.SELECTED,Texture.class);
		assetManager.load(Resource.HP_BAR,Texture.class);
		assetManager.load(Resource.BAR,Texture.class);

		assetManager.load(Resource.STATUSUI_TEXTURE_ATLAS_PATH,TextureAtlas.class);
		assetManager.load(Resource.ITEMS_TEXTURE_ATLAS_PATH,TextureAtlas.class);
		// start loading assets and wait until finished
		assetManager.finishLoading();

		Gdx.app.debug(TAG, "# of assets loaded: " + assetManager.getAssetNames().size);
		for (String a : assetManager.getAssetNames()) {
			Gdx.app.debug(TAG, "asset: " + a);
		}
		//store into map
		for (Map.Entry<String, String> entry : Resource.instance.players.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue().toString();
			Texture atlas = assetManager.get(value);
			String attackFilePath = Resource.instance.fighters.get(key);
			Texture attackAtlas = null;
			if(attackFilePath!=null){
				attackAtlas = assetManager.get(attackFilePath);
			}
			// create game resource objects
			assetRoles.put(key,new AssetRole(atlas,attackAtlas));
		}
		for (Map.Entry<String, String> entry : Resource.instance.npcs.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue().toString();
			Texture atlas = assetManager.get(value);
			// create game resource objects
			assetRoles.put(key,new AssetRole(atlas,null));
		}
		for (Map.Entry<String, String> entry : Resource.instance.maps.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue().toString();
			TiledMap map = assetManager.get(value);
			// create game resource objects
			assetTiledMaps.put(key,new AssetTiledMap(map));
		}
		touch = new AssetTouch(assetManager.get(Resource.TOUCH,Texture.class));
		shadow = assetManager.get(Resource.SHADOW,Texture.class);
		selected = assetManager.get(Resource.SELECTED,Texture.class);
		hpBar = assetManager.get(Resource.HP_BAR,Texture.class);
		bar = assetManager.get(Resource.BAR,Texture.class);
		STATUSUI_TEXTUREATLAS = assetManager.get(STATUSUI_TEXTURE_ATLAS_PATH,TextureAtlas.class);
		ITEMS_TEXTUREATLAS = assetManager.get(ITEMS_TEXTURE_ATLAS_PATH,TextureAtlas.class);
		STATUSUI_SKIN = new Skin(Gdx.files.internal(Resource.STATUSUI_SKIN_PATH), STATUSUI_TEXTUREATLAS);
		MONSTERS0 = assetManager.get(Resource.MONSTERS0,Texture.class);
		MONSTERS1 = assetManager.get(Resource.MONSTERS1,Texture.class);
	}
	public static void loadSoundAsset(String soundFilenamePath){
		if( soundFilenamePath == null || "".equals(soundFilenamePath.trim()) ){
			return;
		}
		if( assetManager.isLoaded(soundFilenamePath) ){
			return;
		}
		//load asset
		if( filePathResolver.resolve(soundFilenamePath).exists() ){
			assetManager.setLoader(Sound.class, new SoundLoader(filePathResolver));
			assetManager.load(soundFilenamePath, Sound.class);
			//Until we add loading screen, just block until we load the map
			assetManager.finishLoadingAsset(soundFilenamePath);
			Gdx.app.debug(TAG, "Sound loaded!: " + soundFilenamePath);
		}
		else{
			Gdx.app.debug(TAG, "Sound doesn't exist!: " + soundFilenamePath );
		}
	}


	public static Sound getSoundAsset(String soundFilenamePath){
		Sound sound = null;

		// once the asset manager is done loading
		if( assetManager.isLoaded(soundFilenamePath) ){
			sound = assetManager.get(soundFilenamePath,Sound.class);
		} else {
			Gdx.app.debug(TAG, "Sound is not loaded: " + soundFilenamePath );
		}

		return sound;
	}

	public static void loadMusicAsset(String musicFilenamePath){
		if( musicFilenamePath == null || "".equals(musicFilenamePath.trim()) ){
			return;
		}

		if( assetManager.isLoaded(musicFilenamePath) ){
			return;
		}

		//load asset
		if( filePathResolver.resolve(musicFilenamePath).exists() ){
			assetManager.setLoader(Music.class, new MusicLoader(filePathResolver));
			assetManager.load(musicFilenamePath, Music.class);
			//Until we add loading screen, just block until we load the map
			assetManager.finishLoadingAsset(musicFilenamePath);
			Gdx.app.debug(TAG, "Music loaded!: " + musicFilenamePath);
		}
		else{
			Gdx.app.debug(TAG, "Music doesn't exist!: " + musicFilenamePath );
		}
	}


	public static Music getMusicAsset(String musicFilenamePath){
		Music music = null;

		// once the asset manager is done loading
		if( assetManager.isLoaded(musicFilenamePath) ){
			music = assetManager.get(musicFilenamePath,Music.class);
		} else {
			Gdx.app.debug(TAG, "Music is not loaded: " + musicFilenamePath );
		}

		return music;
	}
	public static boolean isAssetLoaded(String fileName){
		return assetManager.isLoaded(fileName);
	}

	@Override
	public void dispose () {
		assetManager.dispose();
	}

	@Override
	public void error(AssetDescriptor assetDesc, Throwable throwable) {
		Gdx.app.error(TAG, "Couldn't load asset '" + assetDesc.fileName + "'", throwable);
	}

	private Pixmap createProceduralPixmap(int width, int height) {
		Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
		// Fill square with red color at 50% opacity
		pixmap.setColor(1, 0, 0, 0.5f);
		pixmap.fill();
		// Draw a yellow-colored X shape on square
		pixmap.setColor(1, 1, 0, 1);
		pixmap.drawLine(0, 0, width, height);
		pixmap.drawLine(width, 0, 0, height);
		// Draw a cyan-colored border around square
		pixmap.setColor(0, 1, 1, 1);
		pixmap.fillRectangle(0, 0, width, height);
		return pixmap;
	}

} public class Constants {
	// Visible game world is 5 meters wide
//	public static final float VIEWPORT_WIDTH = 854f;//20f;//640.0f/32;
	// Visible game world is 5 meters tall
//	public static final float VIEWPORT_HEIGHT = 480f;//15f;//480.0f/32;
	// Visible game world is 5 meters wide
	public static final float VIEWPORT_WIDTH = 640f;//20f;//640.0f/32;
	// Visible game world is 5 meters tall
	public static final float VIEWPORT_HEIGHT = 480f;//15f;//480.0f/32;

}
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.viewport.ExtendViewport;
import com.v5ent.game.battle.BattleEvent;
import com.v5ent.game.core.WorldController;
import com.v5ent.game.dialog.ConversationCommandEvent;
import com.v5ent.game.dialog.ConversationGraph;
import com.v5ent.game.entities.Monster;
import com.v5ent.game.entities.Npc;
import com.v5ent.game.entities.Role;
import com.v5ent.game.hud.BattleUI;
import com.v5ent.game.hud.ButtonUI;
import com.v5ent.game.hud.DialogUI;
import com.v5ent.game.hud.InventoryUI;
import com.v5ent.game.hud.QuestUI;
import com.v5ent.game.hud.StatusUI;
import com.v5ent.game.hud.StoreInventoryUI;
import com.v5ent.game.inventory.InventoryItem;
import com.v5ent.game.inventory.InventoryItemLocation;
import com.v5ent.game.quest.QuestGraph;
import com.v5ent.game.sfx.ScreenTransitionAction;
import com.v5ent.game.sfx.ScreenTransitionActor;
import com.v5ent.game.sfx.ShakeCamera;
import com.v5ent.game.utils.Assets;
import com.v5ent.game.utils.Constants;
package com.v5ent.game.screens;


/**
 * Created by Mignet on 2016/11/26.
 */
public class HUDScreen implements Screen {
    private Stage stage;
    private WorldController worldController;
    private ShakeCamera shakeCam;
    private StatusUI statusUI;
    private ButtonUI buttonUI;
    /** speech */
    private DialogUI dialogUI;
    private InventoryUI inventoryUI;
    private StoreInventoryUI storeInventoryUI;