DialogueBox.java

package com.skloch.game;

import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.Window;
import com.badlogic.gdx.utils.Array;

/**
 * A class to display a dialogue box for text and options on the screen.
 *
 */
public class DialogueBox {
    private Window dialogueWindow;
    private Table dialogueTable;
    private Label textLabel;
    private Skin skin;
    private final int MAXCHARS;
    private SelectBox selectBox;
    private Array<String> textLines;
    private int linePointer = 0;
    private String eventKey = null;
    private float textCounter = 0;
    private boolean scrollingText = false;



    public DialogueBox (Skin skin) {
        // Define some key values
        int WIDTH = 800;
        int HEIGHT = 200;
        MAXCHARS = 35;
        this.skin = skin;

        // Create the window for the dialogue box
        dialogueWindow = new Window("", skin);

        // Create the table for the text in the dialogue box
        dialogueTable = new Table();
        dialogueWindow.addActor(dialogueTable);
        dialogueTable.setFillParent(true);

        textLabel = new Label("Are you sure you want to sleep at the Piazza? This will cost you 10 energy", skin, "dialogue");
        dialogueTable.add(textLabel).expand().width(WIDTH - 80).top().padTop(40);
        textLabel.setWrap(false);


        dialogueWindow.setWidth(WIDTH);
        dialogueWindow.setHeight(HEIGHT);

        // Create selection box to allow user to make choices when interacting with objects (class defined below)
        this.selectBox = new SelectBox();
        selectBox.setOptions(new String[]{"Yes", "No"}, new String[]{"piazza", "close"});

        setText("Are you sure you want to sleep at the Piazza? This will cost you 10 energy");

    }

    /**
     * A class displaying a little selction box to the user when an input is needed in dialog
     */
    class SelectBox {
        private Window selectWindow;
        private Table selectTable;
        private int choiceIndex = 0;
        private String[] options;
        private String[] events;
        private Array<Label> optionPointers = new Array<Label>();
        public SelectBox () {
            selectWindow = new Window("", skin);
            selectTable = new Table();
            selectWindow.add(selectTable);



            selectWindow.setPosition(
                    dialogueWindow.getX() + dialogueWindow.getWidth() - selectWindow.getWidth(),
                    dialogueWindow.getY() + dialogueWindow.getHeight()-24
            );



        }

        /**
         * Sets the options visible to the player when asking for a choice.
         * Also sets which events to call from each option.
         * Event strings are translated into events in EventManager
         * @see EventManager
         *
         * @param options The options available to the player e.g. "Yes" and "No"
         * @param events The events called to the option of the same index E.g. "piazza" and "closeDialogue"
         */
        public void setOptions (String[] options, String[] events) {
            selectTable.clearChildren();

            this.options = options;
            this.events = events;
            optionPointers.clear();

            for (String option : options) {
                // Add each pointer to an array so it can be shown/hidden later without searching the table
                Label pointer = new Label(">", skin, "dialogue");
                optionPointers.add(pointer);
                selectTable.add(pointer).padRight(10).padLeft(10);
                pointer.setVisible(false);

                selectTable.add(new Label(option, skin, "dialogue")).left().padRight(10);
                selectTable.row();
            }

            selectTable.pack();
            selectWindow.setWidth(selectTable.getWidth()+70);
            selectWindow.setHeight(selectTable.getHeight()+70);

            // selectWindow.add(selectTable);

            // Recenter
            selectWindow.setPosition(
                    dialogueWindow.getX() + dialogueWindow.getWidth() - selectWindow.getWidth(),
                    dialogueWindow.getY() + dialogueWindow.getHeight()-24
            );

            // Show first pointer
            setChoice(0);
            show();


        }

        /**
         * Moves the player's choice up one selection
         * Also hides the pointer at the old index, and shows the pointer at the new index
         */
        public void choiceUp () {
            optionPointers.get(choiceIndex).setVisible(false);
            choiceIndex -= 1;
            // If statement to prevent the user from choosing outside the options range
            if (choiceIndex < 0) {
                choiceIndex = 0;
            }
            optionPointers.get(choiceIndex).setVisible(true);

        }

        /**
         * The same as choiceUp but in the opposite direction
         */
        public void choiceDown () {
            optionPointers.get(choiceIndex).setVisible(false);
            choiceIndex += 1;
            // If statement to prevent the user from choosing outside the options range
            if (choiceIndex >= options.length) {
                choiceIndex = options.length - 1;
            }
            optionPointers.get(choiceIndex).setVisible(true);
        }

        /**
         * Returns the event string associated with the selected choice
         * Call hide() afterwards to close the menu
         *
         * @return An event string to be passed to EventManager
         */

        public String getChoice () {
            return events[choiceIndex];
        }

        /**
         * Gets the window of the select box
         *
         * @return The window of the select box
         */
        public Window getWindow() {
            return selectWindow;
        }

        /**
         * Hides the selection widget
         */
        public void hide() {
            selectWindow.setVisible(false);
        }

        /**
         * Shows the selection widget
         */
        public void show() {
            selectWindow.setVisible(true);
        }

        /**
         * Returns whether the selection box is visible or not
         *
         * @return true if the selection box is visible
         */
        public boolean isVisible() {
            return selectWindow.isVisible();
        }

        /**
         * Sets the player's choice to a specific value, used to default to "No" for most options
         *
         * @param index The new choice index
         */
        public void setChoice(int index) {
            if (choiceIndex < options.length) {
                // Don't try and set option 4 to invisible if we only have 2 options
                optionPointers.get(choiceIndex).setVisible(false);
            }
            choiceIndex = index;
            optionPointers.get(choiceIndex).setVisible(true);
        }
    }


    /**
     * Sets the dialogue box and all its elements to a position onscreen
     *
     * @param x The x coordinate of the bottom left corner
     * @param y The y coordinate
     */
    public void setPos(float x, float y) {
        dialogueWindow.setPosition(x, y);

        selectBox.selectWindow.setPosition(
                x + dialogueWindow.getWidth() - selectBox.selectWindow.getWidth(),
                y + dialogueWindow.getHeight()-24
        );
    }

    /**
     * Sets the text to be displayed on the dialogue box, automatically wraps it correctly
     * @param text
     */
    public void setText(String text) {
        initialiseLabelText(text);
        scrollingText = true;
        textCounter = 0;
    }

    /**
     * Sets the text to be displayed on the dialogue box, automatically wraps it correctly
     * Additionally, schedules an event to be called after the text is done displaying
     * @param text THe text to display
     * @param eventKey The event key to be triggered
     */
    public void setText(String text, String eventKey) {
        initialiseLabelText(text);
        this.eventKey = eventKey;
        scrollingText = true;
        textCounter = 0;

    }

    public void scrollText(float speed) {
        if (scrollingText) {
            textCounter += speed;
            if (Math.round(textCounter) >= textLines.get(linePointer).length()) {
                scrollingText = false;
                textLabel.setText(textLines.get(linePointer));
            }
            textLabel.setText(textLines.get(linePointer).substring(0, Math.round(textCounter)));
        }
    }

    /**
     * Formats the text to be displayed on a label widget. Adds a newline character every MAXCHARS num of characters
     * accounts for any occuring linebreaks to take use of the size of the most space possible.
     * Stores the formatted text in 3 chunks, which are then queued up to be pushed to the label whenever the user
     * presses e.
     *
     * @param text The text to format and push to the label
     */
    public void initialiseLabelText(String text) {
        // Add a newline every 36 chars
        String newString = "";
        int lastSpace = 0;
        int index = 0;
        int totalIndex = 0;

        // Add newline characters where the length of a section between two linebreaks is greater than MAXCHARS
        for (char c : text.toCharArray()) {
            // Account for any occuring linebreaks
            if (c == '\n') {
                index = 0;
            }

            if (index >= MAXCHARS) {
                // If the current line is a space, just add a newline instead of a space
                if (c == ' ') {
                    newString = newString + "\n";
                    totalIndex += 1;
                    index = 0;
                } else {
                    // If not, Replace the last space with a linebreak and add the char
                    // If the last linebreak is 0 or greater than MAXCHARS away, just add a break now
                    if (lastSpace == 0 || (totalIndex - lastSpace) >= MAXCHARS) {
                        newString = newString + "\n";
                        index = 0;
                    } else {
                        newString = newString.substring(0, lastSpace) + "\n" + newString.substring(lastSpace+1);
                        newString = newString + c;
                        index = totalIndex - lastSpace;
                        totalIndex += 1;
                    }
                }
            } else {
                newString = newString + c;
                if (c == ' ') {
                    lastSpace = totalIndex;
                }

                index += 1;
                totalIndex += 1;
            }
        }

        // Split the newString into chunks with 3 linebreaks
        textLines = new Array<String>();
        int numBreaks = 0;
        String subString = "";

        for (String s: newString.split("\n")) {
            if (numBreaks == 2) {
                subString += s;
                textLines.add(subString);
                subString = "";
                numBreaks = 0;
            } else {
                subString += s + "\n";
                numBreaks += 1;
            }
        }
        if (subString != "") {
            textLines.add(subString);
        }

        textLabel.setText(textLines.get(0));
        linePointer = 0;
    }

    /**
     * Makes the dialogue box visible, along with any elements that need to be shown
     */
    public void show() {
        dialogueWindow.setVisible(true);
    }

    /**
     * Hides the dialogue box and all of its elements
     */
    public void hide() {
        dialogueWindow.setVisible(false);
        selectBox.hide();
    }

    /**
     * Pressing 'confirm' on the dialogue box
     * Either selects the choice if the selectbox is open, or advances text if not
     */
    public void enter(EventManager eventManager) {
        if (selectBox.isVisible()) {
            selectBox.hide();
            eventManager.event(selectBox.getChoice());
        } else {
            advanceText(eventManager);
        }
    }

    /**
     * Continues on to the next bit of text, or closes the window if the end is reached
     */
    private void advanceText(EventManager eventManager) {
        if (scrollingText) {
            scrollingText = false;
            textCounter = 0;
            textLabel.setText(textLines.get(linePointer));

        } else {
            linePointer += 1;
            if (linePointer >= textLines.size) {
                hide();
                scrollingText = false;
                textCounter = 0;
                if (eventKey != null) {
                    eventManager.event(eventKey);
                    eventKey = null;
                }
            } else {
                textCounter = 0;
                scrollingText = true;
//            textLabel.setText(textLines.get(linePointer));
            }
        }
    }

    /**
     * Hides just the selectbox window
     */
    public void hideSelectBox() {
        selectBox.hide();
    }

    /**
     * Checks if the main dialogue box is visible
     * @return true if it is visible, false otherwise
     */
    public boolean isVisible() {
        return dialogueWindow.isVisible();
    }

    /**
     * Gets the window widget of the dialogue box
     *
     * @return A window widget
     */
    public Window getWindow() {
        return dialogueWindow;
    }

    /**
     * Returns the width of the main dialogue screen widget
     * @return The width
     */

    public float getWidth() {
        return dialogueWindow.getWidth();
    }

    /**
     * Returns the height of the main dialogue screen widget
     * @return The height
     */
    public float getHeight() {
        return dialogueWindow.getHeight();
    }

    /**
     * Returns the created selectbox class
     * @return A SelectBox class
     */
    public SelectBox getSelectBox() {
        return selectBox;
    }




}