/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * JavaWorld Library, Copyright 2011 Bryan Chadwick * * * * FILE: ./universe/world/World.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 universe.world; import java.io.Serializable; import universe.Package; import universe.world.base.*; import image.Image; import image.Scene; /** * * * A Class representing a World and the related methods for drawing * the world and handling various events connected to a {@link * universe.Universe Universe}. The class is parameterized by the * type of messages (which must extend {@link * java.io.Serializable Serializable}) that are passed throughout the * Universe. * *

In order to implement a functioning World you must * extend this class, and implement an {@link * universe.world.World#onDraw onDraw} method. Other handler methods * ({@link universe.world.World#tickRate tickRate}, {@link * universe.world.World#onTick onTick}, {@link * universe.world.World#onMouse onMouse}, {@link * universe.world.World#onKey onKey}, {@link * universe.world.World#onRelease onRelease}, {@link * universe.world.World#stopWhen stopWhen}, and {@link * universe.world.World#lastScene lastScene}) are optional, and can * be overridden to add new functionality.

* *

Most handler methods return a {@link universe.Package Package}, * which contains the updated World, and a possible message * to the connected Universe. In order to interact with the * Universe and other Worlds, implementations must * override the {@link universe.world.World#onReceive onReceive} * method that handles messages from the Universe.

* *

* See also {@link world.World world.World} for similar documentation on individual methods. *

* *

*

Extending World

*
* Below is a simple example of a World that adds a new point at each mouse click. The world * contains a {@link image.Scene Scene} and a new {@link image.Circle Circle} is placed for each * "button-down" event received. * * *
   
 * import image.*;
 * import universe.Package;
 * import universe.world.*;
 * 
 * // Mouse World/Universe point/click Test
 * public class MousePointsWorld extends World<None>{
 *     public static void main(String[] s){
 *         new MousePointsWorld(new EmptyScene(400,400))
 *               .bigBang();
 *     }
 *     // The inner Scene
 *     Scene scene;
 *     // Create a new World with the given Scene
 *     MousePointsWorld(Scene scene){
 *         this.scene = scene;    
 *     }
 *     // Draw by returning the inner Scene
 *     public Scene onDraw(){ return this.scene; }
 *     // On a mouse click add a circle to the inner Scene
 *     public Package<None> onMouse(int x, int y, String me){
 *         if(!me.equals("button-down"))
 *             return new Package<None>(this);
 *         else
 *             return new Package<None>(
 *                     new MousePointsWorld(
 *                             this.scene.placeImage(new Circle(20, "solid","red")
 *                                       .overlay(new Circle(20, "outline", "blue")), x, y)));
 *     }
 * }
 * 
* * After a few mouse clicks, the window will look something like this:

* * *
*

*/ public abstract class World{ /** Default Tick rate for the world: ~33 frames per second */ public static double DEFAULT_TICK_RATE = BigBang.DEFAULT_TICK_RATE; /** Mouse down (button-down) event String */ public static String MOUSE_DOWN = BigBang.MOUSE_DOWN; /** Mouse up (button-up) event String */ public static String MOUSE_UP = BigBang.MOUSE_UP; /** Mouse window enter (enter) event String */ public static String MOUSE_ENTER = BigBang.MOUSE_ENTER; /** Mouse window leave (leave) event String */ public static String MOUSE_LEAVE = BigBang.MOUSE_LEAVE; /** Mouse motion (move) event String */ public static String MOUSE_MOVE = BigBang.MOUSE_MOVE; /** Mouse down & move (drag) event String */ public static String MOUSE_DRAG = BigBang.MOUSE_DRAG; /** Key arrow-up event String */ public static String KEY_ARROW_UP = BigBang.KEY_ARROW_UP; /** Key arrow-down event String */ public static String KEY_ARROW_DOWN = BigBang.KEY_ARROW_RIGHT; /** Key arrow-left event String */ public static String KEY_ARROW_LEFT = BigBang.KEY_ARROW_LEFT; /** Key arrow-right event String */ public static String KEY_ARROW_RIGHT = BigBang.KEY_ARROW_RIGHT; /** Return a visualization of this World as a {@link image.Scene Scene}. * See {@link image.EmptyScene}, {@link image.Scene#placeImage(Image, int, int)}, and * {@link image.Scene#addLine(int, int, int, int, String)} for documentation on * constructing Scenes */ public abstract Scene onDraw(); /** Return the tick rate for this World in seconds. For example, * 0.5 means two ticks per second. The * rate is only accessed when bigBang() is initially called and the * window is created. */ public double tickRate(){ return DEFAULT_TICK_RATE; } /** Produce a Package, containing a (possibly) new World and a * possible Message, based on the Tick of the clock. This method is * called to get the next world on each clock tick.*/ public Package onTick(){ return new Package(this); } /** Produce a Package, containing a (possibly) new World and a * possible Message, when a mouse event is triggered. x * and y are the location of the event in the window, * and event is a String that describes what * kind of event occurred. * *

* Possible Mouse Events * * * * * * * * * * * * * *
"button-down" : The user presses a mouse button in the World window
"button-up" : The user releases a mouse button in the World window
"move" : The user moves the mouse in the World window
"drag" : The user holds a mouse button and moves the mouse in the World window
"enter" : The user moves the mouse in-to the World window
"leave" : The user moves the mouse out-of the World window
*

*/ public Package onMouse(int x, int y, String me){ return new Package(this); } /** Produce a Package, containing a (possibly) new World and a * possible Message, when a key is pressed. The given * event is a String that describes what key * was pressed. * *

* Special Keys * * * * * * * * * *
"up" : The user presses the up-arrow key
"down" : The user presses the down-arrow key
"left" : The user presses the left-arrow key
"right" : The user presses the right-arrow key
* * Other keys generate a single character String that * represents the key pressed. For example, Pressing the B * key on the keyboard generates "b" as an * event. If the shift key is held while pressing B then * "B" is generated.

*/ public Package onKey(String ke){ return new Package(this); } /** Produce a Package, containing a (possibly) new World and a * possible Message, when a key is released. The given * event is a String that describes which key * was released. * *

* Special Keys * * * * * * * * * *
"up" : The user presses the up-arrow key
"down" : The user presses the down-arrow key
"left" : The user presses the left-arrow key
"right" : The user presses the right-arrow key
* * Other keys generate a single character String that * represents the key released. For example, Pressing then * releasing the B key on the keyboard generates "b" as an onKey event and again as an * onRelease event. If the shift key is held while * pressing/releasing B then "B" is * generated.

*/ public Package onRelease(String ke){ return new Package(this); } /** Produce a Package, containing a (possibly) new World and a * possible Message, when a message from the universe is * received. */ public Package onReceive(Msg msg){ return new Package(this); } /** Determine if the World/interaction/animation should be * stopped. Returning a value of true * discontinues all events (mouse, key, ticks, messages) and * causes {@link universe.world.World#lastScene} to be used to * draw the final Scene. */ public boolean stopWhen(){ return false; } /** Returns the Scene that should be displayed when the * interaction/animation completes ({@link universe.world.World#stopWhen} * returns true). */ public Scene lastScene(){ return this.onDraw(); } /** Kick off the interaction/animation and connect to the given * Universe server URL, under the given client name. This * method returns the final state of the world after the user * closes the World window. */ public World bigBang(String server, String name){ return new BigBang(this) .onDraw(new WorldDraw()) .onTick(new WorldTick(), tickRate()) .onMouse(new WorldMouse()) .onKey(new WorldKey()) .onRelease(new WorldRelease()) .onReceive(new WorldReceive()) .stopWhen(new WorldStop()) .lastScene(new WorldLast()) .register(server) .name(name) .bigBang(); } /** Kick off the interaction/animation without connecting to the * Universe. This method returns the final state of the world * after the user closes the World window. The Universe is not * contacted, but the client is run locally*/ public World bigBang(){ return new BigBang(this) .onDraw(new WorldDraw()) .onTick(new WorldTick(), tickRate()) .onMouse(new WorldMouse()) .onKey(new WorldKey()) .onRelease(new WorldRelease()) .stopWhen(new WorldStop()) .lastScene(new WorldLast()) .bigBang(); } /** Wrapper for OnDraw callback */ private class WorldDraw implements OnDraw{ public Scene apply(World w){ return w.onDraw(); } } /** Wrapper for OnTick callback */ private class WorldTick implements OnTick{ public Package apply(World w){ return w.onTick(); } } /** Wrapper for OnMouse callback */ private class WorldMouse implements OnMouse{ public Package apply(World w, int x, int y, String me) { return w.onMouse(x,y,me); } } /** Wrapper for OnKey callback */ private class WorldKey implements OnKey{ public Package apply(World w, String ke){ return w.onKey(ke); } } /** Wrapper for OnKey callback */ private class WorldRelease implements OnRelease{ public Package apply(World w, String ke){ return w.onRelease(ke); } } /** Wrapper for OnReceive callback */ private class WorldReceive implements OnReceive{ public Package apply(World w, Msg msg){ return w.onReceive(msg); } } /** Wrapper for StopWhen callback */ private class WorldStop implements StopWhen{ public boolean apply(World w){ return w.stopWhen(); } } /** Wrapper for LastScene callback */ private class WorldLast implements LastScene{ public Scene apply(World w){ return w.lastScene(); } } }