/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * JavaWorld Library, Copyright 2011 Bryan Chadwick * * * * FILE: ./world/test/StateExamples.java * * * * This file is part of JavaWorld. * * * * JavaWorld is free software: you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation, either version * * 3 of the License, or (at your option) any later version. * * * * JavaWorld is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with JavaWorld. If not, see <http://www.gnu.org/licenses/>. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ package world.test; import java.util.*; import world.*; import image.*; /** World example with more complex States (Menus, Options, Etc.) */ public class StateExamples extends VoidWorld{ State state; StateExamples(){ this.state = new Loading(); } /** Draw the current State of the world */ public Scene onDraw(){ return this.state.onDraw(); } /** Tick the current State of the world */ public void onTick(){ this.state = this.state.onTick(); } /** React to a Key in the current State of the world */ public void onKey(String key){ this.state = this.state.onKey(key); } /** React to a KeyRelease in the current State of the world */ public void onRelease(String key){ this.state = this.state.onRelease(key); } /** React to a Mouse Event in the current State of the world */ public void onMouse(int x, int y, String me){ this.state = this.state.onMouse(x, y, me); } /** Run the application as an Example */ public static void main(String[] args){ new StateExamples().bigBang(); } } /** Abstract State: represents a state in the application sequence */ abstract class State{ int WIDTH = 500, HEIGHT = 400; /** Do nothing */ State onTick(){ return this; } /** Do nothing */ State onKey(String key){ return this; } /** Do nothing */ State onRelease(String key){ return this; } /** Do nothing */ State onMouse(int x, int y, String me){ return this; } /** Return a blank background */ Scene backGround(){ return new EmptyScene(this.WIDTH, this.HEIGHT, "black"); } /** Draw just the background (we can change the "background" later)*/ Scene onDraw(){ return this.backGround(); } /** Create an ArrayList of arbitrary things */ static <X> ArrayList<X> list(X ... xs){ return new ArrayList<X>(Arrays.asList(xs)); } /** Place the given image left-aligned to the given X */ Scene placeLeft(Image img, int x, int y, Scene scn){ return scn.placeImage(img, x+img.width()/2.0, y); } } /** Fake Loading state... */ class Loading extends State{ String START = "LOADING"; String msg = START; int countdown = 80; /** Count down, the switch to the Menu State */ State onTick(){ this.countdown--; if(this.countdown <= 0) return new Menu(); if(this.countdown % 10 == 0){ if(this.msg.length() < this.START.length()+4) this.msg = this.msg + "."; else this.msg = this.START; } return this; } /** Draw the "Loading" Message */ Scene onDraw(){ return placeLeft(new Text(this.msg, 20, "red"), this.WIDTH/2-50, this.HEIGHT/2, this.backGround()); } } /** Menu Screen: Allow the user to select a "next" state */ class Menu extends State{ int selected = 0; ArrayList<String> options = list("High Scores","Game Options","Play It!"); /** Draw the menu, with the selected item highlighted */ Scene onDraw(){ Scene scn = this.backGround().placeImage(new Text("Menu Screen", 30, "red"), this.WIDTH/2, 60); for(int i = 0; i < options.size(); i++){ scn = placeLeft(new Text(options.get(i), 20, color(i)), 120, 120+i*40, scn); } return scn; } /** React by selecting a new Option, or moving to the selected State */ State onKey(String key){ if(key.equals("up")){ selected = (selected + options.size()-1) % options.size(); }else if(key.equals("down")){ selected = (selected + 1) % options.size(); }else if(key.equals("\n")){ return getOption(); } return this; } /** Which Option (State) was selected? */ State getOption(){ if(selected == 0){ return new HighScores(); }else if(selected == 1){ return new Options(); }else if(selected == 2){ return new PlayGame(); } // Should never happen!! return this; } /** Selected Color */ String color(int i){ if(i == selected)return "blue"; return "white"; } } /** Fake Options Screen... Escape to go back */ class Options extends State{ /** Override this for a different Message */ String what(){ return "Sorry no Options"; } /** Draw the Message on the Background */ Scene onDraw(){ return this.backGround() .placeImage(new Text(what(), 30, "red"), this.WIDTH/2, 60) .placeImage(new Text("Hit ESC to go the Menu", 20, "red"), this.WIDTH/2, 100); } /** Only Escape Matters */ State onKey(String key){ if(key.equals("escape")) return new Menu(); return this; } } /** Fake HighScore Screen */ class HighScores extends Options{ /** */ String what(){ return "Sorry no HighScores"; } } /** Chip Died :( Show where he was... and Message over the top */ class Dead extends Options{ Scene last; Dead(Scene last){ this.last = last; } /** Yup... tis-true */ String what(){ return "Sorry You Died!!"; } /** Swap in the last background Scene */ Scene backGround(){ return this.last; } } /** Represents Chip the Cheap Sheep */ class Chip{ // Number of Images static int NUM_TICKS = 4; // Constants static double SPEED = 6, GRAVITY = 1, JUMP = 12; // Images... to the left and the right static ArrayList<Image> chipLeft = State.<Image>list( new FromFile("ram-1.png"), new FromFile("ram-2.png"), new FromFile("ram-3.png"), new FromFile("ram-4.png")); static ArrayList<Image> chipRight = State.<Image>list( new FromFile("ram-1.png").flipHorizontal(), new FromFile("ram-2.png").flipHorizontal(), new FromFile("ram-3.png").flipHorizontal(), new FromFile("ram-4.png").flipHorizontal()); static int WIDTH_BY2 = chipRight.get(0).width()/2; static int HEIGHT_BY2 = chipRight.get(0).height()/2; // Is chip going to the right? boolean right = true; // Which Image are we on? How many Ticks? int ticks = 0, img = 0; // Where is Chip? double x, y; // Is he falling? double vy; // Is he Moving? boolean moving = false; Chip(double x, double y, double vy){ this.x = x; this.y = y; this.vy = vy; } /** Tick chip... Move him, increase gravity, and change his image. */ void tick(int width, int base){ if(moving){ ticks++; if(ticks > NUM_TICKS){ ticks = 0; img = (img+1) % Chip.chipRight.size(); } if(right && x <= width-WIDTH_BY2) x += SPEED; if(!right && x >= WIDTH_BY2) x -= SPEED; }else{ img = 0; } // Make sure we don't fall off the screen if(y+vy >= base){ y = base; vy = 0; }else{ y += vy; vy += GRAVITY; } } /** Jump Chip, Jump!! */ void jump(int base){ if(y == base && vy == 0) vy -= JUMP; } /** Now he's moving... or is He? */ void setMoving(boolean m){ this.moving = m; } /** Face left buddy */ void pointLeft(){ this.right = false; } /** Face right buddy */ void pointRight(){ this.right = true; } /** Draw the current Image of Chip in the right spot */ Scene draw(Scene scn){ Image which = Chip.chipLeft.get(this.img); if(this.right) which = Chip.chipRight.get(this.img); return scn.placeImage(which, this.x, this.y-HEIGHT_BY2); } } /** The Playing State (the real deal) */ class PlayGame extends State{ // How many "arrow presses" have we had? int moving = 0; // Ata'boy Chip Chip chip = new Chip(this.WIDTH/2, this.HEIGHT/2, 0); /** Chip is the only thing we have to draw */ Scene onDraw(){ return this.chip.draw(this.backGround()); } /** And he's the only thing we have to tick */ State onTick(){ chip.tick(this.WIDTH, this.HEIGHT); return this; } /** Escape kills him, left/right to move, space to jump */ State onKey(String key){ if(key.equals("escape")) return new Dead(this.onDraw()); // Moving is ++ if(key.equals("left")){ chip.pointLeft(); this.moving++; if(this.moving > 0)this.chip.setMoving(true); }else if(key.equals("right")){ chip.pointRight(); this.moving++; if(this.moving > 0)this.chip.setMoving(true); }else if(key.equals(" ")){ chip.jump(this.HEIGHT); } return this; } /** */ State onRelease(String key){ if(key.equals("left") || key.equals("right")){ // No longer moving... but only when moving == 0 this.moving--; if(this.moving == 0) this.chip.setMoving(false); } return this; } /** Nothing to do... included for full generality */ State onMouse(int x, int y, String me){ return this; } }