/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* 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(); }
}
}