package com.touchspin.td; import java.io.IOException; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.scenes.scene2d.ui.Image; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.ScreenUtils; import com.badlogic.gdx.utils.XmlReader; import com.badlogic.gdx.utils.XmlReader.Element; /* ====================================================================================== * File: GameDialog.java * Authors: Brian Adams - b.adams5736@edmail.edcc.edu * Russell Brendel - russell.brendel.2925@edmail.edcc.edu * Damian Forrester - dforrester777@gmail.com * Wendi Tang - w.tang2404@myedmail.edcc.edu * * Organization: Edmonds Community College * Term: Spring 2014 * Class: CS 185 - Game Project Developement * Instructor: Tim Hunt - thunt@edcc.edu * * Project: Ollie * -------------------------------------------------------------------------------------- * * Holds GameDialog class which is used to display all in-game dialog as well as * the control instructions * * ====================================================================================== */ public class GameDialog extends GameObject { //---Variables---// //used to parse XML file private XmlReader xml; private FileHandle script; private Element root; private Array<Element> items; //used for display purposes private SpriteBatch batch; private BitmapFont font; // info from XML file for a single snippet private String[] textArray; private String currentText; private String speakerName; private char position; private String backGroundPath; private String lastPath; private short dialogCount; //text strip to read from in the current dialog snippet private short snippetCount;//dialog snippet being used private short charCount;// used to create substrings for display text one at a time. private int h;//holds screen height private int w;//holds screen width private final long secondsPerChar;//how many seconds past between each char print private long nextPrintTime;//next time that a char will be printed private Texture background; private float textX = 0; private int textY = 0; private boolean finishedWithSnippet; private int skipTextPauseTime; private long finalPauseForSkip; private boolean skipable; private int textXPadding; private int textYPadding; private int commandLength; // length of embedded commands in xml script files private boolean canBreakText; private boolean masterSkip; private boolean usingScreenShot; private Image screenShot; /** * Constructor * Sets defaults. * Calls : nextSnippet() * @param game * @param scriptPath */ public GameDialog(MainGame game, String scriptPath) { Element languageRoot = null; g.i().sound.sfxMute(true); g.i().leAnonymizer.resetAll(); //parse XML file xml = new XmlReader(); script = Gdx.files.internal(scriptPath); try { root = xml.parse(script);//get parent } catch(IOException e) {} // get dialog based on current language settings languageRoot = root.getChildByName("dialog_" + g.i().language); //get list of dialog snippet items = languageRoot.getChildrenByName("Snippet"); //check if this dialog has its own music to play if(!root.getAttribute("music").equals("")) g.i().sound.BGMusic(root.getAttribute("music")); //initialize variables font = new BitmapFont(g.i().font); font.scale(Gdx.graphics.getDensity()); w = Gdx.graphics.getWidth(); h = Gdx.graphics.getHeight(); currentText = ""; secondsPerChar = 150; finalPauseForSkip = 0; nextPrintTime = 0; backGroundPath = null; snippetCount = 0; charCount = 0; batch = new SpriteBatch(); lastPath = null; finishedWithSnippet = false; skipTextPauseTime = 2000; skipable = true; canBreakText = true; textXPadding = 50; textYPadding = 70; masterSkip = false; screenShot = null; commandLength = 3; usingScreenShot = false; nextSnippet(); //check for actions in the parent of this dialog and perform it if(!root.getAttribute("command").equals("")) performAction(root.getAttribute("command"), "null", false); }// end of constructor @Override public void render(float delta) { update(); draw(); } @Override public void draw() { Gdx.graphics.getGL20().glClearColor( 0, 0, 0, 0 ); Gdx.graphics.getGL20().glClear( GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT ); batch.begin(); //draw background if(!usingScreenShot) { if(!backGroundPath.equals("black")) { if(!backGroundPath.equals(lastPath)) { lastPath = backGroundPath; background = new Texture(Gdx.files.internal(backGroundPath)); } batch.draw(background, w/2 - (background.getWidth()/2), h/2 - (background.getHeight()/2)); } } else screenShot.draw(batch, 1); // draw text font.setColor(Color.BLACK); font.drawMultiLine(batch, currentText, textX - 2, textY - 2); font.setColor(Color.WHITE); font.drawMultiLine(batch, currentText, textX, textY); batch.end(); } /** * Display all the text in this snippet at once. */ private void displayAllText() { currentText = speakerName + '\n'; for(int count = 0; count <textArray.length; count++) { if(textArray[count].equals("C_")) { performAction(textArray[count + 1],textArray[count + (commandLength - 1)],true); count += commandLength - 1; } else for(int innerCount = 0; innerCount < textArray[count].length(); innerCount++) { currentText += textArray[count].charAt(innerCount); if(font.getBounds(currentText).width + textXPadding > w && textArray[count].charAt(innerCount) != ' '&& canBreakText) breakText(); } } nextPrintTime += finalPauseForSkip + skipTextPauseTime; finishedWithSnippet = true; } /** * inserts a new line right before the last word in a text. */ private void breakText() { //only in these languages does it matter where we break the text..because //only in these languages that we are using the words need to be kept together if(g.i().language == "en" || g.i().language == "es" || g.i().language == "fr") { int count = 0; String lastWord = ""; for(count = currentText.length() - 1; currentText.charAt(count) != ' '; count--) lastWord += currentText.charAt(count); currentText = currentText.substring(0, count) + '\n'; for(int counter = lastWord.length() - 1; counter >= 0; counter--) currentText += lastWord.charAt(counter); }//end of if English statement else { currentText += '\n'; } canBreakText = false; }// end of breakText() @Override public void update() { //this if statement is for if the user can skip the current text at this time // even though it is not print time. //This is done to prevent the user from rapidly clicking skip at the first // snippet and then have all those clicks register for the next snippets to come. if(masterSkip) { if(g.i().leAnonymizer.pausePressed) { g.i().leAnonymizer.pausePressed = false; performAction("end", "null", false); } if(g.i().leAnonymizer.click) { g.i().leAnonymizer.click = false; if(skipable != false) nextSnippet(); masterSkip = false; nextPrintTime = 0; } } //if can print right now //this is done to allow pauses and to print character by character if(System.currentTimeMillis() >= nextPrintTime) { nextPrintTime = System.currentTimeMillis(); if(g.i().leAnonymizer.pausePressed) { g.i().leAnonymizer.pausePressed = false; performAction("end", "null", false); } if(finishedWithSnippet) { nextSnippet(); finishedWithSnippet = false; } else { if(g.i().leAnonymizer.click && skipable) { g.i().leAnonymizer.click = false; skipable = false; masterSkip = true; displayAllText(); } else nextChar(); } }//end of outer if statement }//end of update method /** * Get the next snippet in this dialog sequence. */ private void nextSnippet() { canBreakText = true; try { speakerName = items.get(snippetCount).getAttribute("name") + ':'; textArray = items.get(snippetCount).getAttribute("text").split(":"); position = items.get(snippetCount).getAttribute("position").charAt(0); backGroundPath = items.get(snippetCount).getAttribute("background"); currentText = speakerName + '\n'; finalPauseForSkip = Integer.parseInt(items.get(snippetCount).getAttribute("finalPause")) * 100; } catch(IndexOutOfBoundsException e) { performAction("end", "null",false); } charCount = 0; dialogCount = 0; snippetCount++; //set text position switch(position) { case'L': textX = textXPadding; textY = textYPadding + (int)(font.getCapHeight() * 2); break; case 'T': textX = textXPadding; textY = h - (int)(font.getCapHeight() * 2); break; default: case 'C': textX = textXPadding; textY = h/2; break; } }// end of next snippet method /** * Adds the next char for displaying text */ private void nextChar() { skipable = true; g.i().leAnonymizer.click = false; try { if(textArray[dialogCount].equals("C_"))// check if current text is for a command { dialogCount += commandLength; performAction(textArray[dialogCount - 2], textArray[dialogCount - 1], false); } else { currentText += textArray[dialogCount].charAt(charCount++); if(textArray[dialogCount].charAt(charCount - 1) == ' ') currentText += textArray[dialogCount].charAt(charCount++); //check if current text goes off screen if(font.getBounds(currentText).width + textXPadding> w && textArray[dialogCount].charAt(charCount) != ' ' && canBreakText) breakText(); } } catch(Exception e) { charCount = 0; if(dialogCount > textArray.length) { nextPrintTime += finalPauseForSkip; finishedWithSnippet = true; } else dialogCount++; } nextPrintTime += secondsPerChar; } /** * Performs action commands that are embedded within the snippet text. * @param command * @param value * @param skipPauses */ private void performAction(String command, String value, boolean skipPauses) { switch(command) { case "pause": if(!skipPauses) nextPrintTime += Integer.parseInt(value) * 100; break; case "changeBackground": backGroundPath = value; break; case "end": g.i().t.action(root.get("End")); break; case "instaPrint": if(!skipPauses) currentText += textArray[dialogCount++]; break; case "masterSkip": if(value.equals("true")) masterSkip = true; else masterSkip = false; break; case "screenShot": screenShot = new Image(ScreenUtils.getFrameBufferTexture()); usingScreenShot = true; break; default : } } @Override public void resize(int width, int height) { // TODO Auto-generated method stub } @Override public void show() { // TODO Auto-generated method stub } @Override public void hide() { // TODO Auto-generated method stub } @Override public void pause() { // TODO Auto-generated method stub } @Override public void resume() { // TODO Auto-generated method stub } @Override public void dispose() { //backGround.dispose(); } }