diff --git a/libs/android-support-v4.jar b/libs/android-support-v4.jar new file mode 100644 index 0000000..65ebaf8 Binary files /dev/null and b/libs/android-support-v4.jar differ diff --git a/res/drawable-hdpi/icon.png b/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..ea81d62 Binary files /dev/null and b/res/drawable-hdpi/icon.png differ diff --git a/res/drawable-ldpi/icon.png b/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..a42dbdf Binary files /dev/null and b/res/drawable-ldpi/icon.png differ diff --git a/res/drawable-mdpi/icon.png b/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..854f4b6 Binary files /dev/null and b/res/drawable-mdpi/icon.png differ diff --git a/res/drawable-xhdpi/icon.png b/res/drawable-xhdpi/icon.png new file mode 100644 index 0000000..4e215cc Binary files /dev/null and b/res/drawable-xhdpi/icon.png differ diff --git a/res/drawable/bingo.png b/res/drawable/bingo.png new file mode 100644 index 0000000..217b28e Binary files /dev/null and b/res/drawable/bingo.png differ diff --git a/res/drawable/box.png b/res/drawable/box.png new file mode 100644 index 0000000..7ba273e Binary files /dev/null and b/res/drawable/box.png differ diff --git a/res/drawable/floor.png b/res/drawable/floor.png new file mode 100644 index 0000000..ec357be Binary files /dev/null and b/res/drawable/floor.png differ diff --git a/res/drawable/goal.png b/res/drawable/goal.png new file mode 100644 index 0000000..beb7c84 Binary files /dev/null and b/res/drawable/goal.png differ diff --git a/res/drawable/lock.png b/res/drawable/lock.png new file mode 100644 index 0000000..956f144 Binary files /dev/null and b/res/drawable/lock.png differ diff --git a/res/drawable/unlock.png b/res/drawable/unlock.png new file mode 100644 index 0000000..61f5729 Binary files /dev/null and b/res/drawable/unlock.png differ diff --git a/res/drawable/wall.png b/res/drawable/wall.png new file mode 100644 index 0000000..5b583e0 Binary files /dev/null and b/res/drawable/wall.png differ diff --git a/res/drawable/worker.png b/res/drawable/worker.png new file mode 100644 index 0000000..1468487 Binary files /dev/null and b/res/drawable/worker.png differ diff --git a/res/drawable/worker_select.png b/res/drawable/worker_select.png new file mode 100644 index 0000000..fe47688 Binary files /dev/null and b/res/drawable/worker_select.png differ diff --git a/src/org/dyndns/vahagn/sokoban/SokobanMenu.java b/src/org/dyndns/vahagn/sokoban/MainMenu.java similarity index 100% rename from src/org/dyndns/vahagn/sokoban/SokobanMenu.java rename to src/org/dyndns/vahagn/sokoban/MainMenu.java diff --git a/src/org/dyndns/vahagn/sokoban/PlayActivity.java b/src/org/dyndns/vahagn/sokoban/PlayActivity.java deleted file mode 100644 index 63f4110..0000000 --- a/src/org/dyndns/vahagn/sokoban/PlayActivity.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.dyndns.vahagn.sokoban; - -import android.app.Activity; -import android.os.Bundle; -import android.util.Log; -import android.view.View; -import android.view.View.OnClickListener; -//import android.widget.TextView; - -//import org.dyndns.vahagn.sokoban.R; -import static org.dyndns.vahagn.sokoban.App.theApp; - -public class PlayActivity extends Activity - implements View.OnClickListener - -{ - protected final String TAG = "PlayActivity"; - protected Puzzle puzzle = null; - public PlayView board; - protected int level; - - @Override - public void onCreate(Bundle savedInstanceState) - { - Log.d(TAG, "onCreate: " + savedInstanceState ); - super.onCreate(savedInstanceState); - - board = new PlayView( this ); - setContentView( board ); - board.setOnClickListener(this); - - if (savedInstanceState == null) - { - level = 0; - setPuzzle( level ); - // We were just launched -- set up a new game -// mSnakeView.setMode(SnakeView.READY); - } - else - { - // We are being restored -// Bundle map = savedInstanceState.getBundle(ICICLE_KEY); -// if (map != null) { -// mSnakeView.restoreState(map); -// } else { -// mSnakeView.setMode(SnakeView.PAUSE); -// } - } -// -// -// -// super.onCreate(savedInstanceState); -// setContentView(R.layout.menu); -// -// try { -// theApp().getPuzzleContainer().getPuzzle( 0 ); -// } catch (Exception e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } - } - - - @Override - public void onClick( View v ) - { - Log.d(TAG, "onClick: " ); - ++level; - if ( level >= theApp().getPuzzleContainer().getCount() ) - level = 0; - setPuzzle( level ); - board.invalidate(); - } - - private void setPuzzle( int level ) - { - String title = "Sokoban: level " + new Integer(level+1).toString(); - setTitle(title); - puzzle = theApp().getPuzzleContainer().getPuzzle( level ); - board.setPuzzle(puzzle); - } -} diff --git a/src/org/dyndns/vahagn/sokoban/PlayView.java b/src/org/dyndns/vahagn/sokoban/PlayView.java deleted file mode 100644 index bd8a757..0000000 --- a/src/org/dyndns/vahagn/sokoban/PlayView.java +++ /dev/null @@ -1,231 +0,0 @@ -package org.dyndns.vahagn.sokoban; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.util.Log; -import android.graphics.*; -import android.view.*; -import static org.dyndns.vahagn.sokoban.Puzzle.symbole.*; - -/** - */ -public class PlayView extends View -{ - private final static String TAG = "PlayView"; - private Puzzle puzzle; - - private Matrix col2scr = new Matrix(); - private Matrix scr2col = new Matrix(); - private Bitmap[] tile; - private int tile_x_size; - private int tile_y_size; - - private int offset_x; - private int offset_y; - - private final Paint mPaint = new Paint(); - - public PlayView(Context context) - { - super(context); - - setFocusable(true); -// -// Resources r = this.getContext().getResources(); - -// TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView); -// -// mTileSize = a.getInt(R.styleable.TileView_tileSize, 12); -// -// a.recycle(); - } - - public void setPuzzle( Puzzle p ) - { - puzzle = p; - initTiles(); - } - - /** - * Set up tiles and matrices based on puzzle and view sizes. - */ - private void initTiles() - { - // - // Get the sizes of view. If it is zero then simplly do nothing. - // Otherwise, make sure that the width is bigger the height. - // - int w = getWidth(); - int h = getHeight(); - if ( w==0 || h==0 ) - return; - else if ( w < h ) - { - col2scr.reset(); - col2scr.setRotate(90, w/2, w/2); - col2scr.invert(scr2col); - - int t = w; - w = h; - h = t; - } - else - { - col2scr.reset(); - } - // - // Calculate the tile sizes. - // NOTE: since puzzle width is always bigger the height then we - // rotate the puzzle. - // - tile_x_size = w / puzzle.getColumnCount(); - tile_y_size = h / puzzle.getRowCount(); - if ( tile_x_size < tile_y_size ) - tile_y_size = tile_x_size; - else - tile_x_size = tile_y_size; - // - // Calculate the offset of puzzle. - // - offset_x = (w-tile_x_size*puzzle.getColumnCount())/2; - offset_y = (h-tile_y_size*puzzle.getRowCount())/2; - // - // Set up inverse matrix to get col,row from screen pixel. - // - col2scr.invert(scr2col); - scr2col.postTranslate(-offset_x, -offset_y); - scr2col.postScale((float)1./tile_x_size, (float)1./tile_y_size); - // - // Create tile bitmaps for given width and height. - // - tile = new Bitmap[Puzzle.getSymCount()]; - tile[FLOOR.ordinal()] = createFloorTile(); - tile[WALL.ordinal()] = createWallTile(); - tile[BOX.ordinal()] = createBoxTile(); - tile[HOLE.ordinal()] = createHoleTile(); - tile[BOX_IN_HOLE.ordinal()] = createBingoTile(); - tile[WORKER.ordinal()] = createWorkerTile(); - } - - /** - * Apply screen to board mapping and if the points are in the - * column,row range then return it. Otherwise return null. - */ - public Point getTile( float scr_x, float scr_y ) - { - float [] scr_p = { scr_x, scr_y }; - scr2col.mapPoints( scr_p ); - - if ( 0 <= scr_p[0] && scr_p[0] < puzzle.getColumnCount() - && 0 <= scr_p[1] && scr_p[1] < puzzle.getRowCount()) - return new Point( (int)scr_p[0], (int)scr_p[1] ); - else - return null; - } - - private Bitmap createFloorTile() - { - Bitmap bitmap = Bitmap.createBitmap(tile_x_size, tile_y_size, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - canvas.drawARGB(0xff,0x40,0x40,0x40); - return bitmap; - } - private Bitmap createWallTile() - { - Bitmap bitmap = Bitmap.createBitmap(tile_x_size, tile_y_size, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - canvas.drawARGB(0xff,0xF0,0x40,0x40); - return bitmap; - } - private Bitmap createBoxTile() - { - Bitmap bitmap = Bitmap.createBitmap(tile_x_size, tile_y_size, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - canvas.drawARGB(0xff,0x40,0xF0,0x40); - return bitmap; - } - private Bitmap createHoleTile() - { - Bitmap bitmap = Bitmap.createBitmap(tile_x_size, tile_y_size, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - canvas.drawARGB(0xff,0x40,0x40,0xF0); - return bitmap; - } - private Bitmap createBingoTile() - { - Bitmap bitmap = Bitmap.createBitmap(tile_x_size, tile_y_size, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - canvas.drawARGB(0xff,0x40,0xF0,0xF0); - return bitmap; - } - private Bitmap createWorkerTile() - { - Bitmap bitmap = createFloorTile(); - Canvas canvas = new Canvas(bitmap); - Paint paint = new Paint(); - paint.setARGB(0xff,0xF0,0x40,0xF0); - canvas.drawOval(new RectF(1,1,tile_x_size-2,tile_y_size-2), paint); - - Paint p = new Paint(); - p.setColor( Color.YELLOW ); - canvas.drawLine(tile_x_size/2, tile_y_size/2, tile_x_size/2, 0, p ); - return bitmap; - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) - { - initTiles(); - } - - @Override - public boolean onTouchEvent (MotionEvent event) - { - int a = event.getAction(); - int ps = event.getPointerCount(); - int hs = event.getHistorySize(); - for ( int p = 0; p < ps; ++p ) - { - String msg = String.format("Action %d Pointer %d History %d", a, event.getPointerId(p), hs); - // for ( int h = 0; h < hs; ++h ) - // msg += String.format("(%f,%f)", event.getHistoricalX(p,h), event.getHistoricalY(p,h)); - msg += String.format("(%f,%f)", event.getX(p), event.getY(p)); - Log.d("Sokoban", msg); - } - Point p = getTile(event.getX(),event.getY()); - if ( p == null ) - { - Log.d(TAG, "onTouchEvent: outside."); - return super.onTouchEvent(event); - } - -// Log.d(TAG, "onTouchEvent: " + p.x + " " + p.y); - return true; //super.onTouchEvent(event); - } - - @Override - public void onDraw(Canvas canvas) - { - super.onDraw(canvas); - if ( puzzle != null ) - { - int cols = puzzle.getColumnCount(); - int rows = puzzle.getRowCount(); - - canvas.concat(col2scr); - for (int i = 0; i < rows; ++i) - { - for (int j = 0; j < cols; ++j) - { - canvas.drawBitmap(tile[puzzle.getSym(i,j).ordinal()], - offset_x + j * tile_x_size, - offset_y + i * tile_y_size, - mPaint); - } - } - } - } -} \ No newline at end of file diff --git a/src/org/dyndns/vahagn/sokoban/play/Animator.java b/src/org/dyndns/vahagn/sokoban/play/Animator.java new file mode 100644 index 0000000..d5c7b30 --- /dev/null +++ b/src/org/dyndns/vahagn/sokoban/play/Animator.java @@ -0,0 +1,209 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.dyndns.vahagn.sokoban.play; + +import org.dyndns.vahagn.sokoban.play.PuzzleView; +import android.support.v4.view.ViewCompat; +import java.util.LinkedList; + +/** + * + * @author vahagnk + */ +public class Animator implements Runnable +{ + protected PuzzleView view; + protected LinkedList actions = new LinkedList(); + protected boolean stop; + protected int step; + protected Action currentAction; + protected AnimationLister theLister; + + public interface AnimationLister + { + public void onAnimationEnd(); + } + + public Animator( PuzzleView v) + { + view = v; + } + + public void setAnimationLister( AnimationLister lister ) + { + theLister = lister; + } + + public void queue( Action a ) + { + actions.addLast(a); + } + + public void play() + { + // + // If no actions exist then nothing to do. + // + if ( actions.size() == 0 ) + return; + // + // Get the first action an play it. + // + stop = false; + step = 0; + currentAction = actions.removeFirst(); + ViewCompat.postOnAnimation(view, this); + } + + public void stop() + { + stop = true; + view.removeCallbacks(this); + if ( theLister != null ) + theLister.onAnimationEnd(); + } + + public void clear() + { + actions.clear(); + currentAction = null; + } + + public void run() + { + if ( currentAction == null ) + return; + else if ( currentAction.steps > step ) + { + currentAction.intermediate(this,step); + step++; + ViewCompat.postOnAnimation(view, this); + } + else + { + currentAction.last(this); + step = 0; + currentAction = null; + if ( !stop && actions.size() > 0 ) + { + currentAction = actions.removeFirst(); + step = 0; + ViewCompat.postOnAnimation(view, this); + } + else + { + if ( theLister != null ) + theLister.onAnimationEnd(); + } + } + } + + public static abstract class Action + { + public int steps; + + public Action() + { + steps = 0; + } + + public abstract void intermediate( Animator ap, int step ); + public abstract void last( Animator ap ); + } + + protected static abstract class XYAction extends Action + { + int x; + int y; + + public XYAction( int _x, int _y ) + { + x = _x; + y = _y; + } + } + + public static class Move extends XYAction + { + public Move( int x, int y ) + { + super(x,y); + } + + public void intermediate( Animator ap, int step ) + {} + + public void last( Animator ap ) + { + ap.view.getPuzzle().walk(x,y); + ap.view.invalidate(); + } + } + + public static class Push extends XYAction + { + public Push( int x, int y ) + { + super(x,y); + } + + public void intermediate( Animator ap, int step ) + { + } + + public void last( Animator ap ) + { + ap.view.getPuzzle().push(x,y); + ap.view.invalidate(); + } + } + + public static class Select extends XYAction + { + public Select(int x, int y ) + { + super(x,y); + } + + public void intermediate( Animator ap, int step ) + { + } + + public void last( Animator ap ) + { + ap.view.getPuzzle().select(x,y); + ap.view.invalidate(); + } + } + + public static class Unselect extends Action + { + public Unselect() + {} + + public void intermediate( Animator ap, int step ) + {} + + public void last( Animator ap ) + { + ap.view.getPuzzle().unselect(); + ap.view.invalidate(); + } + } + + public static class NoMove extends XYAction + { + public NoMove( int x, int y ) + { + super(x,y); + } + + public void intermediate( Animator ap, int step ) + {} + + public void last( Animator ap ) + {} + } +} diff --git a/src/org/dyndns/vahagn/sokoban/play/PlayActivity.java b/src/org/dyndns/vahagn/sokoban/play/PlayActivity.java new file mode 100644 index 0000000..fb6447c --- /dev/null +++ b/src/org/dyndns/vahagn/sokoban/play/PlayActivity.java @@ -0,0 +1,190 @@ +package org.dyndns.vahagn.sokoban.play; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import static org.dyndns.vahagn.sokoban.App.TAG; +import static org.dyndns.vahagn.sokoban.App.theApp; +import org.dyndns.vahagn.sokoban.Puzzle; +import org.dyndns.vahagn.sokoban.MainMenu; + +public class PlayActivity extends Activity + implements PuzzleControl.PuzzleControlLister +{ + protected final static int RESET_ITEM = 1; + protected final static int UNDO_ITEM = 2; + + public Puzzle puzzle = null; + public PuzzleControl view; + public View title; + + @Override + public void onCreate(Bundle savedInstanceState) + { + Log.d(TAG, "onCreate: " + savedInstanceState ); + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_NO_TITLE); + // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + // WindowManager.LayoutParams.FLAG_FULLSCREEN); + //requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); + //requestWindowFeature(Window.FEATURE_OPTIONS_PANEL); + //getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, RESULT_OK); + //getWindow().getWindowStyle(). + //getWindow(). + + + + + + + + + // + // Create and set up the view. + // + view = new PuzzleControl( this ); + setContentView( view ); + view.setPuzzleControlLister( this ); + registerForContextMenu(view); + // + // Load the puzzle. + // + loadCurrentPuzzle(); + + if (savedInstanceState == null) + { + + + // We were just launched -- set up a new game +// mSnakeView.setMode(SnakeView.READY); + } + else + { + // We are being restored +// Bundle map = savedInstanceState.getBundle(ICICLE_KEY); +// if (map != null) { +// mSnakeView.restoreState(map); +// } else { +// mSnakeView.setMode(SnakeView.PAUSE); +// } + } +// +// +// +// super.onCreate(savedInstanceState); +// setContentView(R.layout.menu); +// +// try { +// theApp().getPuzzleContainer().getPuzzle( 0 ); +// } catch (Exception e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } + } + + public void onSolved() + { + // + // Advance current level and achieved level. + // + theApp().advanceCurrentLevel(); + // + // Bring a dialog for user to choose to continue or stop. + // + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(""); + builder.setMessage( "Congratulations! You Won!" ); + builder.setNegativeButton("Menu", + new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface d, int w) + { + Intent intent = new Intent(PlayActivity.this, MainMenu.class); + startActivity( intent ); + } + }); + builder.setPositiveButton("Next", + new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface d, int w) + { + loadCurrentPuzzle(); + view.invalidate(); + } + }); + AlertDialog dlg = builder.create(); + dlg.show(); + } + + public void onLongPress() + { + openContextMenu(view); + } + + public boolean onReset() + { + loadCurrentPuzzle(); + view.invalidate(); + return true; + } + + public boolean onUndo() + { + if ( puzzle.isUndoable() ) + { + puzzle.restore(); + view.invalidate(); + return true; + } + else + return false; + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) + { + super.onCreateContextMenu(menu, v, menuInfo); + // + // Undo item. + // + if ( puzzle.isUndoable() ) + { + menu.add("Undo").setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener(){ + public boolean onMenuItemClick(MenuItem mi){ + return onUndo(); + } + }); + } + // + // Reset item. + // + menu.add("Reset").setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener(){ + public boolean onMenuItemClick(MenuItem mi){ + return onReset(); + } + }); + } + + private void loadCurrentPuzzle() + { + puzzle = theApp().getCurrentPuzzle(); + view.setPuzzle(puzzle); + + String title = "Sokoban: level " + new Integer(theApp().getCurrentLevel()).toString(); + setTitle(title); + } +} diff --git a/src/org/dyndns/vahagn/sokoban/play/PuzzleControl.java b/src/org/dyndns/vahagn/sokoban/play/PuzzleControl.java new file mode 100644 index 0000000..0fa042f --- /dev/null +++ b/src/org/dyndns/vahagn/sokoban/play/PuzzleControl.java @@ -0,0 +1,186 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.dyndns.vahagn.sokoban.play; + +import org.dyndns.vahagn.sokoban.play.Animator; +import org.dyndns.vahagn.sokoban.play.PuzzleView; +import org.dyndns.vahagn.sokoban.play.PuzzleLogic; +import android.content.Context; +import android.graphics.Point; +import android.util.Log; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import org.dyndns.vahagn.sokoban.Puzzle; +import static org.dyndns.vahagn.sokoban.App.TAG; + +//import android.support.v4.view.GestureDetectorCompat; + +/** + * + * @author vahagnk + */ +public class PuzzleControl extends PuzzleView + implements GestureDetector.OnGestureListener + , GestureDetector.OnDoubleTapListener + , ScaleGestureDetector.OnScaleGestureListener + , Animator.AnimationLister +{ + protected GestureDetector simpled; + protected ScaleGestureDetector scaled; + protected PuzzleLogic logic; + protected Animator animator; + protected PuzzleControlLister lister; + + protected float lastSpan; + protected Point singleTapTile = new Point(); + + public interface PuzzleControlLister + { + public void onSolved(); + public void onLongPress(); + } + + public PuzzleControl(Context c) + { + super(c); + logic = new PuzzleLogic(); + animator = new Animator( this ); + animator.setAnimationLister(this); + + simpled = new GestureDetector( c, this ); + simpled.setOnDoubleTapListener(this); + scaled = new ScaleGestureDetector( c, this ); + } + + public void setPuzzle( Puzzle p ) + { + super.setPuzzle(p); + logic.setPuzzle(p); + } + + public void setPuzzleControlLister( PuzzleControlLister l ) + { + lister = l; + } + + @Override + public boolean onTouchEvent(MotionEvent event) + { + // + // Stop any pending animations. + // + animator.stop(); + animator.clear(); + // + // Pass this event to scale and gesture detectors. + // + boolean r = scaled.onTouchEvent(event); + r = simpled.onTouchEvent(event) || r; + //r = super.onTouchEvent(event) || r; + return r; + } + + public boolean onDown(MotionEvent e) + { + Log.d(TAG, "onDown"); + return true; + } + public void onLongPress(MotionEvent e) + { + Log.d(TAG, "onLongPress"); + if ( lister != null ) + lister.onLongPress(); + } + public void onShowPress(MotionEvent e) + { + Log.d(TAG, "onShowPress"); + } + public boolean onSingleTapUp(MotionEvent e) + { + Log.d(TAG, "onSingleTapUp"); + return true; + } + public boolean onDoubleTap(MotionEvent e) + { + Log.d(TAG, "onDoubleTap"); + return true; + } + public boolean onDoubleTapEvent(MotionEvent e) + { + Log.d(TAG, "onDoubleTapEvent"); + return true; + } + + public boolean onSingleTapConfirmed(MotionEvent e) + { + // + // Translate the tap point into tile column and row. + // + getTile( e.getX(), e.getY(), singleTapTile ); + // + // If the tap is not on a valid tile then there is not point to + // continue. + // + if ( !puzzle.isValid(singleTapTile.x, singleTapTile.y) ) + return true; + // + // Create sequence of steps and then animate it. + // + if ( logic.createSteps(animator, singleTapTile.x, singleTapTile.y) ) + { + puzzle.save(); + animator.play(); + } + // + // This event is processed. + // + return true; + } + + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) + { + Log.d(TAG, "onFling"); + return true; + } + + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) + { + Log.d(TAG, "onScroll"); + scrollViewport( distanceX, distanceY ); + return true; + } + + public boolean onScaleBegin(ScaleGestureDetector detector) + { + Log.d(TAG, "onScaleBegin"); + lastSpan = scaled.getCurrentSpan(); + return true; + } + + public boolean onScale(ScaleGestureDetector detector) + { + Log.d(TAG, "onScale"); + float span = scaled.getCurrentSpan(); + float focusX = scaled.getFocusX(); + float focusY = scaled.getFocusY(); + scaleViewport(focusX,focusY,lastSpan/span); + lastSpan = span; + return true; + } + + public void onScaleEnd(ScaleGestureDetector detector) + { + Log.d(TAG, "onScaleEnd"); + scaleViewportDone(); + } + + public void onAnimationEnd() + { + if ( puzzle.isDone() ) + if ( lister != null ) + lister.onSolved(); + } +} diff --git a/src/org/dyndns/vahagn/sokoban/play/PuzzleLogic.java b/src/org/dyndns/vahagn/sokoban/play/PuzzleLogic.java new file mode 100644 index 0000000..bf2f334 --- /dev/null +++ b/src/org/dyndns/vahagn/sokoban/play/PuzzleLogic.java @@ -0,0 +1,380 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.dyndns.vahagn.sokoban.play; + +import static java.lang.Math.*; +import java.util.Arrays; +import org.dyndns.vahagn.sokoban.Puzzle; + +/** + * + * @author vahagnk + */ +public class PuzzleLogic +{ + //protected PlayActivity activity; + protected Puzzle puzzle = null; + protected int [] moves; + protected int [] setOfCells; + + public PuzzleLogic() + {} + + public void setPuzzle( Puzzle p ) + { + puzzle = p; + moves = new int[puzzle.getColumnCount()*puzzle.getRowCount()]; + setOfCells = new int[2*puzzle.getColumnCount()*puzzle.getRowCount()]; + } + + public boolean createSteps(Animator animator, int x, int y ) + { + // + // Check that the x,y are valid. + // + if ( !puzzle.isValid(x, y) ) + return false; + // + // Now check what tile was tapped. + // If the tapped is a floor then ... + // + int tile = puzzle.getSym( x, y); + if ( Puzzle.isEmpty(tile) ) + { + // + // Check if worker selected a box and can push it? + // + if ( puzzle.isSelected() ) + { + // + // Get directions to push and act accordingly. + // + int pushes[] = getPushDirections(x, y); + if ( pushes != null ) + { + for ( int i = 0; i < pushes.length; i+=2 ) + animator.queue( new Animator.Push(pushes[i],pushes[i+1]) ); + return true; + } + } + // + // Either nothing was selected or the box cannot be moved to + // tapped location. Try move worker alone. + // + calcMoves(); + if ( isAccessible(x,y) ) + { + // + // First unselect box. + // + if ( puzzle.isSelected() ) + animator.queue( new Animator.Unselect() ); + // + // Get directions and queue moves accordingly. + // + int dirs[] = getDirections(x, y); + for ( int i = 0; i < dirs.length; i+=2 ) + animator.queue( new Animator.Move(dirs[i],dirs[i+1]) ); + } + else + { + // + // Show that action is not allowed. + // + animator.queue( new Animator.NoMove(x,y) ); + } + } + // + // The tapped is the worker. Unselect if selected. + // + else if ( Puzzle.hasWorker(tile) ) + { + if ( puzzle.isSelected() ) + { + animator.queue( new Animator.Unselect() ); + } + } + // + // The tapped is a box. + // + else if ( Puzzle.hasBox(tile) ) + { + calcMoves(); + int north = puzzle.getSym(x,y-1); + int south = puzzle.getSym(x,y+1); + int west = puzzle.getSym(x-1,y); + int east = puzzle.getSym(x+1,y); + // + // First check if there is a worker in a nighbour cell. If + // yes then simplly select the box. + // + if ( Puzzle.hasWorker( west ) + || Puzzle.hasWorker( east ) + || Puzzle.hasWorker( north ) + || Puzzle.hasWorker( south ) ) + { + if ( !puzzle.isSelected(x,y) ) + { + if ( puzzle.isSelected() ) + animator.queue( new Animator.Unselect() ); + animator.queue( new Animator.Select(x,y) ); + } + } + // + // Otherwise, check which is of the cells is in closes walking + // distance and move worker to that cell, then select. + // + else + { + int pref_x = -1; + int pref_y = -1; + int shortest = Integer.MAX_VALUE; + if ( Puzzle.isEmpty( north ) + && shortest > stepsAway( x, y-1) ) + { + shortest = stepsAway( x, y-1); + pref_x = x; + pref_y = y-1; + } + if ( Puzzle.isEmpty( south ) + && shortest > stepsAway( x, y+1) ) + { + shortest = stepsAway( x, y+1); + pref_x = x; + pref_y = y+1; + } + if ( Puzzle.isEmpty( west ) + && shortest > stepsAway( x-1, y) ) + { + shortest = stepsAway( x-1, y); + pref_x = x-1; + pref_y = y; + } + if ( Puzzle.isEmpty( east ) + && shortest > stepsAway( x+1, y) ) + { + shortest = stepsAway( x+1, y); + pref_x = x+1; + pref_y = y; + } + // + // Looks there is place to approach to box. + // + if ( shortest < Integer.MAX_VALUE ) + { + if ( puzzle.isSelected() ) + animator.queue( new Animator.Unselect() ); + int dirs[] = getDirections(pref_x, pref_y); + for ( int i = 0; i < dirs.length; i+=2 ) + animator.queue( new Animator.Move(dirs[i],dirs[i+1]) ); + //animator.queue( new Animator.Select(x,y) ); + } + else + { + // + // Show that action is not allowed. + // + animator.queue( new Animator.NoMove(x,y) ); + } + } + } + return true; + } + + + ////////////////////////////////////////////////////////////////////////// + // + // Routes to accessible cells from where worker stands. + // + + protected final int getMoves( int x, int y ) + { + return moves[puzzle.getIndex(x,y)]; + } + protected void setMoves( int x, int y, int v ) + { + moves[puzzle.getIndex(x,y)]=v; + } + private boolean setMovesIfGreater( int x, int y, int l ) + { + // + // If out of borders then nothing to do. + // + if ( y < 0 || y >= puzzle.getRowCount() + || x < 0 || x >= puzzle.getColumnCount() ) + return false; + // + // Check if the cell is a floor or goal. If yes then set the l + // if current value is greater than l. + // + if ( getMoves(x,y) > l && puzzle.isEmpty(puzzle.getSym(x,y))) + { + setMoves(x,y,l); + return true; + } + else + return false; + } + + public void calcMoves() + { + // + // Erase moves array. + // + Arrays.fill(moves,Integer.MAX_VALUE); + // + // For the beginning there is no cell in the list. + // + int front = 0; + int last = 0; + // + // Set the seed. + // + setMoves(puzzle.getWorkerX(),puzzle.getWorkerY(),0); + setOfCells[last++] = puzzle.getWorkerX(); + setOfCells[last++] = puzzle.getWorkerY(); + // + // Now on each loop pop one cell from the list and calculate the + // distance of cell around that cell. + // + while ( front < last ) + { + // + // Pop the cell. + // + int x = setOfCells[front++]; + int y = setOfCells[front++]; + // + // Increase the length of cells all around given cell and push + // them into the list. + // + int l = getMoves(x,y)+1; + if ( setMovesIfGreater(x-1,y,l) ) + { + setOfCells[last++] = x-1; + setOfCells[last++] = y; + } + if ( setMovesIfGreater(x+1,y,l) ) + { + setOfCells[last++] = x+1; + setOfCells[last++] = y; + } + if ( setMovesIfGreater(x,y-1,l) ) + { + setOfCells[last++] = x; + setOfCells[last++] = y-1; + } + if ( setMovesIfGreater(x,y+1,l) ) + { + setOfCells[last++] = x; + setOfCells[last++] = y+1; + } + } + } + + public final int stepsAway( int x, int y ) + { + return getMoves(x,y); + } + + public final boolean isAccessible( int x, int y ) + { + return stepsAway(x, y) != Integer.MAX_VALUE; + } + + public int [] getDirections( int x, int y ) + { + int away = stepsAway(x,y); + if (away == Integer.MAX_VALUE) + return null; + // + // Ok looks there is a routh to given cell. Now create an array + // and fill in the step to get to the cell. + // + int [] steps = new int[2*away]; + int i = steps.length; + steps[--i] = y; + steps[--i] = x; + while ( i > 0 ) + { + x = steps[i]; + y = steps[i+1]; + int j = stepsAway(x,y); + if ( stepsAway(x-1,y) < j ) + { + steps[--i] = y; + steps[--i] = x-1; + } + else if ( stepsAway(x+1,y) < j ) + { + steps[--i] = y; + steps[--i] = x+1; + } + else if ( stepsAway(x,y-1) < j ) + { + steps[--i] = y-1; + steps[--i] = x; + } + else if ( stepsAway(x,y+1) < j ) + { + steps[--i] = y+1; + steps[--i] = x; + } + } + return steps; + } + + public int [] getPushDirections( int x, int y ) + { + // + // The selected box can be moved only orthogonally. + // Check that worker is on opposite side of the box. + // Statement: + // If scaliar product of selected->x,y and worker->selected is equal + // to manhatten distance of x,y from the selected box then the worker + // push direction is directed to x,y and is not opposit. In other words + // selected->x,y and worker->selected are codirectional. + // + int selx = puzzle.getSelectedX(); + int sely = puzzle.getSelectedY(); + int xx = x-selx; + int yy = y-sely; + int sx = selx-puzzle.getWorkerX(); + int sy = sely-puzzle.getWorkerY(); + double scaliar = (double)(xx)*(double)(sx) + +(double)(yy)*(double)(sy); + int len = abs(xx)+abs(yy); + if ( scaliar != (double)len ) + return null; + // + // Now check that all cell till x,y are free. + // + int ix = selx; + int iy = sely; + while ( x != ix || y != iy ) + { + ix+=sx; + iy+=sy; + if ( !puzzle.isEmpty(puzzle.getSym(ix,iy)) ) + return null; + } + // + // Looks we could move the box till x,y. Create the steps array + // and fill it with push steps. + // + int steps[] = new int [2*len]; + int i = 0; + ix = selx; + iy = sely; + while ( x != ix || y != iy ) + { + steps[i++] = ix; + steps[i++] = iy; + ix+=sx; + iy+=sy; + } + return steps; + } +} diff --git a/src/org/dyndns/vahagn/sokoban/play/PuzzleView.java b/src/org/dyndns/vahagn/sokoban/play/PuzzleView.java new file mode 100644 index 0000000..baf3d50 --- /dev/null +++ b/src/org/dyndns/vahagn/sokoban/play/PuzzleView.java @@ -0,0 +1,444 @@ +package org.dyndns.vahagn.sokoban.play; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.RectF; +import android.support.v4.view.ViewCompat; +import android.util.Log; +import android.view.View; +import static java.lang.Math.*; +import static org.dyndns.vahagn.sokoban.App.TAG; +import org.dyndns.vahagn.sokoban.Puzzle; +import static org.dyndns.vahagn.sokoban.Puzzle.*; +import org.dyndns.vahagn.sokoban.R; + +/** + */ +public class PuzzleView extends View +{ + protected Puzzle puzzle; + + private RectF screen = new RectF(0,0,0,0); + private RectF viewport = new RectF(); + private RectF board = new RectF(); + + private Matrix col2scr = new Matrix(); + private Matrix scr2col = new Matrix(); + private Bitmap[] tile = new Bitmap[Puzzle.getSymCount()]; + private Point tile_size = new Point(0,0); + private int rotated; + + private final Paint mPaint = new Paint(); + private Paint mSelectPaint = new Paint(); + private Rect tile_src = new Rect(); + private RectF tile_dest = new RectF(); + + public PuzzleView(Context context) + { + super(context); + setFocusable(true); + +// +// Resources r = this.getContext().getResources(); + +// TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView); +// +// mTileSize = a.getInt(R.styleable.TileView_tileSize, 12); +// +// a.recycle(); + } + + public void setPuzzle( Puzzle p ) + { + puzzle = p; + board.set(0,0,puzzle.getColumnCount(),puzzle.getRowCount()); + viewport.set(board); + tile_size.set(0,0); + calcTransforms(); + initTiles(); + } + + public Puzzle getPuzzle() + { + return puzzle; + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) + { + screen.set(0, 0, w, h); + tile_size.set(0,0); + calcTransforms(); + initTiles(); + } + + + public void scaleViewport( float focusX, float focusY, float scale ) + { + float left = scale*(focusX - screen.left); + float right = scale*(screen.right - focusX); + float top = scale*(focusY - screen.top); + float bottom = scale*(screen.bottom - focusY); + viewport.set( focusX-left, focusY-top, focusX+right, focusY+bottom ); + scr2col.mapRect(viewport); + constrainViewport(); + calcTransforms(); + ViewCompat.postInvalidateOnAnimation(this); + } + + public void scaleViewportDone() + { + //initTiles(); + ViewCompat.postInvalidateOnAnimation(this); + } + + public void scrollViewport( float distanceX, float distanceY ) + { + viewport.set( screen.left+distanceX, + screen.top+distanceY, + screen.right+distanceX, + screen.bottom+distanceY ); + scr2col.mapRect(viewport); + constrainViewport(); + calcTransforms(); + ViewCompat.postInvalidateOnAnimation(this); + } + + private void constrainViewport() + { + if ( !board.contains(viewport) ) + { + if ( board.width() < viewport.width() ) + { + viewport.left = board.left; + viewport.right = board.right; + } + if ( viewport.left < board.left ) + { + viewport.right += board.left - viewport.left; + viewport.left = board.left; + } + if ( board.right < viewport.right ) + { + viewport.left -= viewport.right - board.right; + viewport.right = board.right; + } + + if ( board.height() < viewport.height() ) + { + viewport.top = board.top; + viewport.bottom = board.bottom; + } + if ( viewport.top < board.top ) + { + viewport.bottom += board.top - viewport.top; + viewport.top = board.top; + } + if ( viewport.bottom > board.bottom ) + { + viewport.top -= viewport.bottom - board.bottom; + viewport.bottom = board.bottom; + } + } + if ( viewport.width() < 4 ) + { + float center = (viewport.left + viewport.right)/2; + viewport.left = center-2; + viewport.right = center+2; + } + if ( viewport.height() < 4 ) + { + float center = (viewport.top + viewport.bottom)/2; + viewport.top = center-2; + viewport.bottom = center+2; + } + } + + /** + * Enlarge viewport to meet aspect ratio and calculate transforms. + */ + private void calcTransforms() + { + // + // Get the sizes of screen. If it is zero then simplly do nothing. + // Otherwise, make sure that the width is bigger the height. + // + double sw = (double)screen.width(); + double sh = (double)screen.height(); + if ( sw <= 0 || sh <= 0 || puzzle == null ) + return; + else if ( sw < sh ) + { + double t = sw; + sw = sh; + sh = t; + rotated = 90; + } + // + // Enlarge the view to have the same aspect ratio as the screen. + // + double wscale = sw/(double)viewport.width(); + double hscale = sh/(double)viewport.height(); +// double scale = floor(min(wscale,hscale)); + double scale = min(wscale,hscale); + float enlarge_vert = (float)(sh / scale - viewport.height())/2; + float enlarge_horiz = (float)(sw / scale - viewport.width())/2; + viewport.top -= enlarge_vert; + viewport.bottom += enlarge_vert; + viewport.left -= enlarge_horiz; + viewport.right += enlarge_horiz; + // + // Calculate the tile sizes which is the scale. + // + tile_size.set((int)scale,(int)scale); + // + // Set transformations from and to screen. + // + col2scr.reset(); + col2scr.postTranslate(-viewport.left, -viewport.top); + col2scr.postScale((float)scale,(float)scale); + col2scr.postTranslate(screen.left, screen.top); + if ( screen.width() < screen.height() ) + { + float around = screen.width()/2; + col2scr.postRotate(90, around, around); + } + col2scr.invert(scr2col); + } + + /** + * Set up tiles and matrices based on puzzle and view sizes. + */ + private void initTiles() + { + // + // Prevent this from being called if tile_size is not set. + // + if ( tile_size.x <= 0 && tile_size.y <= 0 || puzzle == null ) + return; + // + // Selection rect. + // + mSelectPaint.setARGB(0xff,0x40,0x40,0xf0); + mSelectPaint.setStrokeWidth((float)3.0); + // + // Create tile bitmaps for given width and height. + // + tile[FLOOR] = createFloorTile(); + tile[GOAL] = createGoalTile(); + tile[WALL] = createWallTile(); + tile[BOX] = createBoxTile(); + tile[BOX|SELECTED] = createBoxSelTile(); + tile[BINGO] = createBingoTile(); + tile[BINGO|SELECTED] = createBingoSelTile(); + tile[WORKER] = createWorkerTile(); + tile[WORKER|SELECTED] = createWorkerSelTile(); + } + + /** + * Apply screen to board mapping and if the points are in the + * column,row range then return it. Otherwise return null. + */ + public void getTile( float scr_x, float scr_y, Point /*out*/ tile ) + { + float [] scr_p = { scr_x, scr_y }; + scr2col.mapPoints( scr_p ); + + if ( 0 <= scr_p[0] && scr_p[0] < puzzle.getColumnCount() + && 0 <= scr_p[1] && scr_p[1] < puzzle.getRowCount()) + tile.set((int)scr_p[0], (int)scr_p[1] ); + else + tile.set(-1, -1); + } + + private Bitmap createFloorTile() + { + Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.floor ); + Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + //canvas.drawARGB(0xff,0x40,0x40,0x40); + canvas.drawBitmap(tmp, + new Rect(0,0,tmp.getWidth()-1,tmp.getHeight()-1), + new Rect(0,0,tile_size.x, tile_size.y), + mPaint); + return bitmap; + } + private Bitmap createGoalTile() + { + Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.goal ); + Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); +// canvas.drawARGB(0xff,0x40,0x40,0xF0); + canvas.drawBitmap(tmp, + new Rect(0,0,tmp.getWidth()-1,tmp.getHeight()-1), + new Rect(0,0,tile_size.x, tile_size.y), + mPaint); + return bitmap; + } + private Bitmap createWallTile() + { + Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.wall ); + Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + //canvas.drawARGB(0xff,0xF0,0x40,0x40); + canvas.drawBitmap(tmp, + new Rect(1,1,tmp.getWidth()-2,tmp.getHeight()-2), + new Rect(0,0,tile_size.x, tile_size.y), + mPaint); + return bitmap; + } + private Bitmap createBoxTile() + { + Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.box ); + Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); +// canvas.drawARGB(0xff,0x40,0xF0,0x40); + canvas.drawBitmap(tmp, + new Rect(0,0,tmp.getWidth()-1,tmp.getHeight()-1), + new Rect(0,0,tile_size.x, tile_size.y), + mPaint); + return bitmap; + } + private Bitmap createBoxSelTile() + { + Bitmap bitmap = createBoxTile(); + Canvas canvas = new Canvas(bitmap); + canvas.drawLine(0,0,tile_size.x-1, 0, mSelectPaint); + canvas.drawLine(tile_size.x-1, 0,tile_size.x-1, tile_size.y-1, mSelectPaint); + canvas.drawLine(tile_size.x-1, tile_size.y-1, 0, tile_size.y-1, mSelectPaint); + canvas.drawLine(0, tile_size.y-1, 0, 0, mSelectPaint); + return bitmap; + } + private Bitmap createBingoTile() + { + Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.bingo ); + Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); +// canvas.drawARGB(0xff,0x40,0xF0,0xF0); + canvas.drawBitmap(tmp, + new Rect(0,0,tmp.getWidth()-1,tmp.getHeight()-1), + new Rect(0,0,tile_size.x, tile_size.y), + mPaint); + return bitmap; + } + private Bitmap createBingoSelTile() + { + Bitmap bitmap = createBingoTile(); + Canvas canvas = new Canvas(bitmap); + canvas.drawLine(0,0,tile_size.x-1, 0, mSelectPaint); + canvas.drawLine(tile_size.x-1, 0,tile_size.x-1, tile_size.y-1, mSelectPaint); + canvas.drawLine(tile_size.x-1, tile_size.y-1, 0, tile_size.y-1, mSelectPaint); + canvas.drawLine(0, tile_size.y-1, 0, 0, mSelectPaint); + return bitmap; + } + private Bitmap createWorkerTile() + { + Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.worker ); + Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(tmp, + new Rect(0,0,tmp.getWidth()-1,tmp.getHeight()-1), + new Rect(0,0,tile_size.x, tile_size.y), + mPaint); + + return bitmap; + } + private Bitmap createWorkerSelTile() + { + Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.worker_select ); + Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(tmp, + new Rect(0,0,tmp.getWidth()-1,tmp.getHeight()-1), + new Rect(0,0,tile_size.x, tile_size.y), + mPaint); + + return bitmap; + } + + @Override + public void onDraw(Canvas canvas) + { + Log.d(TAG, "onDraw"); + super.onDraw(canvas); + if ( puzzle != null ) + { + //int cols = puzzle.getColumnCount(); + //int rows = puzzle.getRowCount(); +// canvas.concat(col2scr); + int xbgn = Math.max((int)viewport.left,(int)board.left); + int xend = Math.min((int)viewport.right,(int)board.right-1); + int ybgn = Math.max((int)viewport.top,(int)board.top); + int yend = Math.min((int)viewport.bottom,(int)board.bottom-1); + tile_src.set(0,0,tile_size.x,tile_size.y); + for (int y = ybgn; y <= yend; ++y) + { + for (int x = xbgn; x <= xend; ++x) + { + tile_dest.set(x,y,x+1,y+1); + col2scr.mapRect(tile_dest); + //tile_hlpr.roundOut(tile_hlpr); + //tile_hlpr.right -=1; + //tile_hlpr.bottom -=1; + int sym = puzzle.getSym(x,y); + if ( !Puzzle.hasWorker(sym) ) + { + canvas.drawBitmap(tile[sym], + tile_src,tile_dest, + mPaint); + } + else + { + // + // Draw floor. + // + canvas.drawBitmap(tile[sym&~(WORKER_MASK|SELECTED)], + tile_src,tile_dest, + mPaint); + // + // Draw worker. + // + canvas.save(); + Bitmap normal_worker = ((sym&SELECTED)!=0) ? + tile[WORKER|SELECTED] : tile[WORKER]; + int direction = sym & WORKER_MASK; + if ( direction == WORKER_NORTH ) + { + canvas.translate(tile_dest.centerX(), tile_dest.centerY()); + canvas.rotate(180+rotated); + canvas.translate(-tile_dest.centerX(), -tile_dest.centerY()); + } + else if ( direction == WORKER_SOUTH ) + { + canvas.translate(tile_dest.centerX(), tile_dest.centerY()); + canvas.rotate(0+rotated); + canvas.translate(-tile_dest.centerX(), -tile_dest.centerY()); + } + else if ( direction == WORKER_WEST ) + { + canvas.translate(tile_dest.centerX(), tile_dest.centerY()); + canvas.rotate(90+rotated); + canvas.translate(-tile_dest.centerX(), -tile_dest.centerY()); + } + else if ( direction == WORKER_EAST ) + { + canvas.translate(tile_dest.centerX(), tile_dest.centerY()); + canvas.rotate(-90+rotated); + canvas.translate(-tile_dest.centerX(), -tile_dest.centerY()); + } + canvas.drawBitmap(normal_worker, + tile_src,tile_dest, + mPaint); + canvas.restore(); + } + } + } + } + } +} \ No newline at end of file