/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* 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 . *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
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 ArrayList list(X ... xs){
return new ArrayList(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 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 chipLeft = State.list(
new FromFile("ram-1.png"), new FromFile("ram-2.png"),
new FromFile("ram-3.png"), new FromFile("ram-4.png"));
static ArrayList chipRight = State.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;
}
}