Wandering NPCs now leash to bounded areas correctly.
Wandering NPCs now leash to bounded areas correctly.

Updated bounds information for 3 wandering NPCs in Actors.json
Refactored WanderingPerson.java

--- a/res/Actors.json
+++ b/res/Actors.json
@@ -21,20 +21,20 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":6,
-		"h":6,
-		"rx":4,
-		"ry":4,
-		"con":0,
-		"px":-1,
-		"py":-1,
-		"location":
-			{
-				"class":"com.dryerzinia.pokemon.map.Pose",
-				"x":11.0,
-				"y":10.0,
+		"boundsWidth":17,
+		"boundsHeight":17,
+		"boundsX":4,
+		"boundsY":2,
+		"nFrames":0,
+		"px":-1,
+		"py":-1,
+		"location":
+			{
+				"class":"com.dryerzinia.pokemon.map.Pose",
+				"x":10.0,
+				"y":9.0,
 				"level":2,
-				"facing":"DOWN"
+				"facing":"LEFT"
 			},
 		"imgName":"Lady2",
 		"canBeSteppedOn":false,
@@ -46,18 +46,18 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":7,
-		"h":4,
-		"rx":0,
-		"ry":3,
-		"con":0,
-		"px":-1,
-		"py":-1,
-		"location":
-			{
-				"class":"com.dryerzinia.pokemon.map.Pose",
-				"x":21.0,
-				"y":18.0,
+		"boundsWidth":15,
+		"boundsHeight":10,
+		"boundsX":6,
+		"boundsY":8,
+		"nFrames":0,
+		"px":-1,
+		"py":-1,
+		"location":
+			{
+				"class":"com.dryerzinia.pokemon.map.Pose",
+				"x":16.0,
+				"y":17.0,
 				"level":2,
 				"facing":"LEFT"
 			},
@@ -111,11 +111,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":1,
-		"h":4,
-		"rx":0,
-		"ry":0,
-		"con":0,
+		"boundsWidth":1,
+		"boundsHeight":4,
+		"boundsX":0,
+		"boundsY":0,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -136,11 +136,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":1,
-		"h":1,
-		"rx":0,
-		"ry":0,
-		"con":0,
+		"boundsWidth":1,
+		"boundsHeight":1,
+		"boundsX":0,
+		"boundsY":0,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -161,11 +161,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":1,
-		"h":1,
-		"rx":0,
-		"ry":0,
-		"con":0,
+		"boundsWidth":1,
+		"boundsHeight":1,
+		"boundsX":0,
+		"boundsY":0,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -186,11 +186,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":0,
-		"h":0,
-		"rx":5,
-		"ry":4,
-		"con":0,
+		"boundsWidth":0,
+		"boundsHeight":0,
+		"boundsX":5,
+		"boundsY":4,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -211,18 +211,18 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":0,
-		"h":0,
-		"rx":2,
-		"ry":21,
-		"con":0,
-		"px":-1,
-		"py":-1,
-		"location":
-			{
-				"class":"com.dryerzinia.pokemon.map.Pose",
-				"x":3.0,
-				"y":34.0,
+		"boundsWidth":0,
+		"boundsHeight":2,
+		"boundsX":6,
+		"boundsY":25,
+		"nFrames":0,
+		"px":-1,
+		"py":-1,
+		"location":
+			{
+				"class":"com.dryerzinia.pokemon.map.Pose",
+				"x":6.0,
+				"y":25.0,
 				"level":5,
 				"facing":"DOWN"
 			},
@@ -236,11 +236,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":0,
-		"h":0,
-		"rx":10,
-		"ry":11,
-		"con":0,
+		"boundsWidth":0,
+		"boundsHeight":0,
+		"boundsX":10,
+		"boundsY":11,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -281,11 +281,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":0,
-		"h":0,
-		"rx":5,
-		"ry":16,
-		"con":0,
+		"boundsWidth":0,
+		"boundsHeight":0,
+		"boundsX":5,
+		"boundsY":16,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -306,11 +306,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":0,
-		"h":0,
-		"rx":22,
-		"ry":22,
-		"con":0,
+		"boundsWidth":0,
+		"boundsHeight":0,
+		"boundsX":22,
+		"boundsY":22,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -351,11 +351,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":0,
-		"h":0,
-		"rx":9,
-		"ry":2,
-		"con":0,
+		"boundsWidth":0,
+		"boundsHeight":0,
+		"boundsX":9,
+		"boundsY":2,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -396,11 +396,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":0,
-		"h":0,
-		"rx":6,
-		"ry":1,
-		"con":0,
+		"boundsWidth":0,
+		"boundsHeight":0,
+		"boundsX":6,
+		"boundsY":1,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -481,11 +481,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":0,
-		"h":0,
-		"rx":2,
-		"ry":2,
-		"con":0,
+		"boundsWidth":0,
+		"boundsHeight":0,
+		"boundsX":2,
+		"boundsY":2,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -586,11 +586,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":0,
-		"h":0,
-		"rx":-3,
-		"ry":-1,
-		"con":0,
+		"boundsWidth":0,
+		"boundsHeight":0,
+		"boundsX":-3,
+		"boundsY":-1,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -611,11 +611,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":0,
-		"h":0,
-		"rx":0,
-		"ry":1,
-		"con":0,
+		"boundsWidth":0,
+		"boundsHeight":0,
+		"boundsX":0,
+		"boundsY":1,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -716,11 +716,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":0,
-		"h":0,
-		"rx":-2,
-		"ry":0,
-		"con":0,
+		"boundsWidth":0,
+		"boundsHeight":0,
+		"boundsX":-2,
+		"boundsY":0,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -801,11 +801,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":0,
-		"h":0,
-		"rx":20,
-		"ry":25,
-		"con":0,
+		"boundsWidth":0,
+		"boundsHeight":0,
+		"boundsX":20,
+		"boundsY":25,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":
@@ -1026,11 +1026,11 @@
 	},
 	{
 		"class":"com.dryerzinia.pokemon.obj.tiles.WanderingPerson",
-		"w":0,
-		"h":0,
-		"rx":-1,
-		"ry":-1,
-		"con":0,
+		"boundsWidth":0,
+		"boundsHeight":0,
+		"boundsX":-1,
+		"boundsY":-1,
+		"nFrames":0,
 		"px":-1,
 		"py":-1,
 		"location":

--- a/src/com/dryerzinia/pokemon/obj/tiles/WanderingPerson.java
+++ b/src/com/dryerzinia/pokemon/obj/tiles/WanderingPerson.java
@@ -1,23 +1,28 @@
 package com.dryerzinia.pokemon.obj.tiles;

 

 import java.util.HashMap;

+import java.util.Random;

 

 import com.dryerzinia.pokemon.PokemonServer;

 import com.dryerzinia.pokemon.map.Direction;

 import com.dryerzinia.pokemon.map.Grid;

 import com.dryerzinia.pokemon.map.Pose;

+import com.dryerzinia.pokemon.obj.Actor;

 import com.dryerzinia.pokemon.util.JSONObject;

-import com.dryerzinia.pokemon.obj.Actor;

 

 public class WanderingPerson extends Person implements Actor {

 

     static final long serialVersionUID = -3752479366750590617L;

+    

+    private static final float CHANCE_TURN = 0.20f;

+    private static final float CHANCE_MOVE = 0.30f;

+    private static final int MAGIC_FRAME_COUNT = 6;

 

-    protected int w, h;

-    protected int rx = 0, ry = 0;

-    protected int con = 0;

+    protected int boundsWidth, boundsHeight;

+    protected int boundsX, boundsY;

+    protected int nFrames;

 

-    protected transient Grid g;

+    protected transient Grid grid;

 

     public WanderingPerson() {

     }

@@ -31,137 +36,155 @@
 

         canBeSteppedOn = cbso;

 

-        this.w = w;

-        this.h = h;

+        this.boundsWidth = w;

+        this.boundsHeight = h;

 

-        this.rx = rx;

-        this.ry = ry;

+        this.boundsX = rx;

+        this.boundsY = ry;

 

         this.location = location;

 

-        this.g = g;

+        this.grid = g;

 

         loadImage();

-

     }

-

+    

+    /**

+     * Return true if the given (x, y) location is in the walkable bounds

+     * of this wandering person.

+     * @param x

+     * @param y

+     * @return

+     */

+    private boolean isInBounds(int x, int y) {

+    	boolean xValid = x >= boundsX && x <= boundsX + boundsWidth;

+    	boolean yValid = y >= boundsY && y <= boundsY + boundsHeight;

+    	

+    	return xValid && yValid;

+    }

+    

+    /**

+     * Return true if the given tile is walkable and no player is occupying it

+     * @param tileX

+     * @param tileY

+     * @return

+     */

+    private boolean isTileWanderable(int tileX, int tileY) {

+    	return 	isInBounds(tileX, tileY) &&

+    			grid.canStepOn(tileX, tileY) &&

+    			!PokemonServer.isPlayerAtTile(tileX, tileY, location.getLevel());

+    }

+    

+    /**

+     * Return true if the tile in the given direction is wanderable.

+     * @param direction

+     * @return

+     */

+    private boolean canWanderTowards(Direction direction) {

+    	int x = (int) location.getX();

+    	int y = (int) location.getY();

+    	

+    	switch (direction) {

+    	case UP:

+    		return isTileWanderable(x, y - 1);

+    	case DOWN:

+    		return isTileWanderable(x, y + 1);

+    	case LEFT:

+    		return isTileWanderable(x - 1, y);

+    	case RIGHT:

+    		return isTileWanderable(x + 1, y);

+    	default:

+    		return false;

+    	}

+    }    

+    

     protected boolean wander() {

-

-    	boolean canMove = false;

-        boolean changed = false;

-

-        int pos = (int) (Math.random() * 20);

-

-        if (pos < 4) {

-            if (location.facing().getValue() != pos) {

-                location.changeDirection(Direction.get(pos));

-                changed = true;

-            }

-        }

-

-        Direction dir = location.facing();

-        int level = location.getLevel();

-        int x = (int) location.getX();

-        int y = (int) location.getY();

-

-        pos = (int) (Math.random() * 20);

-

-        if (pos > 2 && con % 4 == 0) {

-

-        	if (dir == Direction.UP) {

-

-        		if (!PokemonServer.isPlayerAtTile(x, y - 1, level)

-                        && g.canStepOn(x, y - 1))

-                    canMove = true;

-

-        	} else if (dir == Direction.DOWN) {

-

-        		if (!PokemonServer.isPlayerAtTile(x, y + 1, level)

-                        && g.canStepOn(x, y + 1))

-                    canMove = true;

-

-        	} else if (dir == Direction.LEFT) {

-

-        		if (!PokemonServer.isPlayerAtTile(x - 1, y, level)

-                        && g.canStepOn(x - 1, y))

-                    canMove = true;

-

-        	} else if (dir == Direction.RIGHT) {

-

-        		if (!PokemonServer.isPlayerAtTile(x + 1, y, level)

-                        && g.canStepOn(x + 1, y))

-                    canMove = true;

-

-        	}

-        }

-

-        if (canMove && con % 7 == 0) {

-

-        	changed = true;

-

-        	if (dir == Direction.UP) {

-

-        		g.move(x, y - 1, x, y, this);

-                ry--;

-                y--;

-

-        	} else if (dir == Direction.DOWN) {

-

-        		g.move(x, y + 1, x, (int)y, this);

-                ry++;

-                y++;

-

-        	} else if (dir == Direction.LEFT) {

-

-        		g.move(x - 1, y, x, y, this);

-                rx--;

-                x--;

-

-        	} else if (dir == Direction.RIGHT) {

-

-        		g.move(x + 1, y, x, y, this);

-                rx++;

-                x++;

-

-        	}

-        }

-

-        con++;

-

-        if(changed){

-        	location.setX(x);

-        	location.setY(y);

-        	location.changeDirection(dir);

-        }

-

+    	

+    	nFrames++;

+    	

+    	boolean changed = false;

+    	Random random = new Random();

+    	

+    	if (random.nextFloat() < CHANCE_TURN) {

+    		

+    		Direction newDirection;

+    		

+    		if (boundsWidth == 0 && boundsHeight != 0) {

+    			// can only move up and down

+    			newDirection = Direction.get(random.nextInt(2));

+    		} else if (boundsWidth != 0 && boundsHeight == 0) {

+    			// can only move left and right

+    			newDirection = Direction.get(random.nextInt(2)+2);

+    		} else {

+    			newDirection = Direction.get(random.nextInt(4));

+    		}

+    		

+    		if (location.facing() != newDirection) {

+    			location.changeDirection(newDirection);

+    			changed = true;

+    		}

+    	}

+    	

+    	

+    	if (nFrames % MAGIC_FRAME_COUNT == 0 

+    			&& random.nextFloat() < CHANCE_MOVE) {

+    		

+    		Direction direction = location.facing();

+    		

+    		if (canWanderTowards(direction)) {

+    			int x = (int) location.getX();

+        		int y = (int) location.getY();

+        		int newX = x; int newY = y;

+        		

+        		switch (direction) {

+        		case UP:

+        			newY -= 1; break;

+        		case DOWN:

+        			newY += 1; break;

+        		case LEFT:

+        			newX -= 1; break;

+        		case RIGHT:

+        			newX += 1; break;

+        		case NONE:

+        			throw new IllegalArgumentException("Invalid direction!");

+        		}        		

+	        	

+        		grid.move(newX, newY, x, y, this);

+	        	location.setX(newX);

+	        	location.setY(newY);    		

+	        	changed = true;

+	        	nFrames = 0;

+        		

+    		}

+    	} 

+    	

         return changed;

-

     }

+    

 

     public boolean act() {

-

     	return wander();

-

     }

 

     public void initializeSecondaryReferences(Grid g) {

-        this.g = g;

+        this.grid = g;

     }

 

     public Object deepCopy() {

         return new WanderingPerson(new String(imgName), canBeSteppedOn,

-                w, h, rx, ry, location, this.g);

+                boundsWidth, boundsHeight, 

+                boundsX, boundsY, location, this.grid);

     }

 

     public void fromJSON(HashMap<String, Object> json){

 

     	super.fromJSON(json);

 

-        w = ((Float) json.get("w")).intValue();

-        h = ((Float) json.get("h")).intValue();;

-        rx = ((Float) json.get("rx")).intValue();

-        ry = ((Float) json.get("ry")).intValue();

-        con = ((Float) json.get("con")).intValue();

+        boundsWidth = ((Float) json.get("boundsWidth")).intValue();

+        boundsHeight = ((Float) json.get("boundsHeight")).intValue();

+        boundsX = ((Float) json.get("boundsX")).intValue();

+        boundsY = ((Float) json.get("boundsY")).intValue();

+        nFrames = ((Float) json.get("nFrames")).intValue();

 

     }