1 package de
.cwde
.freeshisen
;
4 import java
.util
.Locale
;
5 import java
.util
.Timer
;
6 import java
.util
.TimerTask
;
8 import android
.app
.Activity
;
9 import android
.content
.Context
;
10 import android
.graphics
.Bitmap
;
11 import android
.graphics
.BitmapFactory
;
12 import android
.graphics
.Canvas
;
13 import android
.graphics
.Color
;
14 import android
.graphics
.Matrix
;
15 import android
.graphics
.Paint
;
16 import android
.graphics
.Paint
.Align
;
17 import android
.graphics
.Paint
.Cap
;
18 import android
.graphics
.Paint
.Join
;
19 import android
.graphics
.Paint
.Style
;
20 import android
.graphics
.Rect
;
21 import android
.graphics
.Typeface
;
22 import android
.os
.Handler
;
23 import android
.os
.Message
;
24 import android
.view
.MenuItem
;
25 import android
.view
.MotionEvent
;
26 import android
.view
.SurfaceHolder
;
27 import android
.view
.SurfaceView
;
29 class ShisenShoView
extends SurfaceView
implements SurfaceHolder
.Callback
{
31 private static final String COLOR_TEXT
= "#FFFFFF";
32 private static final String COLOR_TEXT_SHADOW
= "#000000";
33 private static final String COLOR_HINT
= "#F0C000";
34 private static final String COLOR_SELECTED
= "#FF0000";
36 private enum StatePlay
{ UNINITIALIZED
, IDLE
, SELECTED1
, SELECTED2
, GAMEOVER
};
37 private enum StatePaint
{ BOARD
, SELECTED1
, SELECTED2
, MATCHED
, WIN
, LOSE
, HINT
, TIME
};
39 private int screenWidth
;
40 private int screenHeight
;
41 private int tilesetRows
;
42 private int tilesetCols
;
43 private int tileHeight
;
44 private int tileWidth
;
46 private Bitmap tile
[];
47 private int[] selection1
=new int[2];
48 private int[] selection2
=new int[2];
49 private List
<Point
> path
=null;
50 private List
<Line
> pairs
=null;
51 private long startTime
;
52 private long playTime
;
53 private long baseTime
;
55 private static Handler timerHandler
;
57 private boolean timerRegistered
=false;
58 private ShisenSho app
;
59 private StatePlay cstate
;
60 private StatePaint pstate
;
61 private Canvas canvas
= null;
62 private SurfaceHolder surfaceHolder
= null;
63 public ShisenShoView(ShisenSho shishenSho
) {
64 super((Context
)shishenSho
);
65 this.app
= shishenSho
;
66 cstate
= StatePlay
.UNINITIALIZED
;
67 surfaceHolder
= getHolder();
68 surfaceHolder
.addCallback(this);
71 public ShisenShoView(Context ctx
) {
76 private void paint(StatePaint pstate
) {
81 private void control(StatePlay cstate
) {
85 private void loadTileset() {
86 BitmapFactory
.Options ops
= new BitmapFactory
.Options();
88 Bitmap tileset
= BitmapFactory
.decodeResource(getResources(), R
.drawable
.tileset
, ops
);
89 tileset
.setDensity(Bitmap
.DENSITY_NONE
);
91 // The tile set has 4 rows x 9 columns
94 int loadedtileWidth
= tileset
.getWidth()/tilesetCols
;
95 int loadedtileHeight
= tileset
.getHeight()/tilesetRows
;
96 tile
= new Bitmap
[tilesetRows
*tilesetCols
];
99 // "large" is 16x6, and we want to have a nice border, so we use 17x7 and
100 // choose the lowest scale so everything fits
101 float scalex
= ((float) screenWidth
/17) / loadedtileWidth
;
102 float scaley
= ((float) screenHeight
/7) / loadedtileHeight
;
103 if (scaley
< scalex
) {
108 Matrix matrix
= new Matrix();
109 matrix
.setScale(scalex
, scaley
);
112 for (int i
=0; i
<tilesetRows
; i
++) {
113 for (int j
=0; j
<tilesetCols
; j
++) {
114 tile
[k
] = Bitmap
.createBitmap(tileset
, j
*loadedtileWidth
, i
*loadedtileHeight
,
115 loadedtileWidth
, loadedtileHeight
, matrix
, false);
116 tile
[k
].setDensity(Bitmap
.DENSITY_NONE
);
120 tileWidth
= tile
[0].getWidth();
121 tileHeight
= tile
[0].getHeight();
124 private void loadBackground() {
125 BitmapFactory
.Options ops
= new BitmapFactory
.Options();
126 ops
.inScaled
= false;
127 bg
= BitmapFactory
.decodeResource(getResources(), R
.drawable
.kshisen_bgnd
, ops
);
128 bg
.setDensity(Bitmap
.DENSITY_NONE
);
131 private void registerTimer() {
132 if (timer
!=null) return; // Already registered
133 timerHandler
= new Handler() {
134 public void handleMessage(Message msg
) {
139 timer
.scheduleAtFixedRate(new TimerTask() {
141 timerHandler
.sendEmptyMessage(Activity
.RESULT_OK
);
144 timerRegistered
=true;
147 private void unregisterTimer() {
148 if (timer
==null) return; // Already unregistered
152 timerRegistered
=false;
155 public void pauseTime() {
158 startTime
= System
.currentTimeMillis();
162 public void resumeTime() {
163 startTime
= System
.currentTimeMillis();
167 private void updateTime() {
168 if (cstate
!=StatePlay
.GAMEOVER
) {
169 playTime
= (System
.currentTimeMillis()-startTime
)/1000+baseTime
;
173 private void initializeGame() {
175 screenWidth
=getWidth();
176 screenHeight
=getHeight();
178 //undo.sensitive=false;
179 pstate
=StatePaint
.BOARD
;
181 control(StatePlay
.IDLE
);
182 startTime
=System
.currentTimeMillis();
185 if (app
.timeCounter
&& !timerRegistered
) {
188 pairs
=app
.board
.getPairs(1);
191 public boolean onOptionsItemSelected(MenuItem item
) {
192 // Handle item selection
193 switch (item
.getItemId()) {
195 this.postDelayed(new Runnable() { public void run() { onHintActivate(); } }, 100);
198 this.postDelayed(new Runnable() { public void run() { onUndoActivate(); } }, 100);
201 this.postDelayed(new Runnable() { public void run() { reset(); } }, 100);
212 public void reset() {
213 control(StatePlay
.UNINITIALIZED
);
214 paint(StatePaint
.BOARD
);
217 private void onHintActivate() {
218 if (cstate
!=StatePlay
.GAMEOVER
) {
219 pairs
=app
.board
.getPairs(1);
220 paint(StatePaint
.HINT
);
222 paint(StatePaint
.BOARD
);
223 control(StatePlay
.IDLE
);
227 private void onUndoActivate() {
228 if (app
.board
.getCanUndo()) {
229 if (cstate
==StatePlay
.GAMEOVER
&& app
.timeCounter
&& !timerRegistered
) {
230 // Reprogram the time update that had been
231 // deactivated with the game over status
235 paint(StatePaint
.BOARD
);
236 //undo.sensitive=app.board.getCanUndo();
237 control(StatePlay
.IDLE
);
241 public void onTimeCounterActivate() {
242 if (app
.timeCounter
&& cstate
!=StatePlay
.GAMEOVER
&& !timerRegistered
) {
243 // Reprogram the time update that had been
244 // deactivated with the time_counter=false
249 private void onUpdateTime() {
251 if (!(app
.timeCounter
&& cstate
!=StatePlay
.GAMEOVER
)) {
256 @SuppressWarnings("deprecation")
257 public static void drawMessage(Canvas canvas
, int x
, int y
,
258 boolean centered
, String message
, float textSize
) {
259 Paint paint
= new Paint();
260 paint
.setLinearText(true);
261 paint
.setAntiAlias(true);
262 paint
.setTextAlign(centered ? Align
.CENTER
: Align
.LEFT
);
263 paint
.setTypeface(Typeface
.SANS_SERIF
);
264 paint
.setFakeBoldText(true);
265 paint
.setTextSize(textSize
);
266 paint
.setColor(Color
.parseColor(COLOR_TEXT_SHADOW
));
267 canvas
.drawText(message
, x
+ 1, y
+ 1, paint
);
268 paint
.setColor(Color
.parseColor(COLOR_TEXT
));
269 canvas
.drawText(message
, x
, y
, paint
);
272 public void repaint() {
273 if (surfaceHolder
== null) return;
275 if (canvas
== null) canvas
= surfaceHolder
.lockCanvas(null);
276 if (canvas
== null) return;
277 if (cstate
==StatePlay
.UNINITIALIZED
) initializeGame();
278 synchronized (surfaceHolder
) {
282 if (canvas
!= null) {
283 surfaceHolder
.unlockCanvasAndPost(canvas
);
289 protected void doDraw(Canvas canvas
) {
292 // Bitmap buffer = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
293 //Canvas cbuffer = new Canvas(buffer);
294 Canvas cbuffer
= canvas
;
295 if (canvas
== null) return;
297 //super.onDraw(canvas);
299 // Board upper left corner on screen
303 if (app
!=null && app
.board
!=null) {
304 x0
=(screenWidth
-app
.board
.boardSize
[1]*tileWidth
)/2;
305 y0
=(screenHeight
-app
.board
.boardSize
[0]*tileHeight
)/2;
308 int selectcolor
= Color
.parseColor(COLOR_SELECTED
);
309 int hintcolor
= Color
.parseColor(COLOR_HINT
);
310 Paint paint
= new Paint();
311 paint
.setFlags(Paint
.ANTI_ALIAS_FLAG
);
313 // Background & board painting
323 // Background painting
324 int bgWidth
= bg
.getWidth();
325 int bgHeight
= bg
.getHeight();
326 for (int i
=0; i
<screenHeight
/bgHeight
+1; i
++) {
327 for (int j
=0; j
<screenWidth
/bgWidth
+1; j
++) {
328 cbuffer
.drawBitmap(bg
, j
*bgWidth
, i
*bgHeight
, paint
);
333 // Max visible size: 7x17
334 if (app
!=null && app
.board
!=null) {
335 for (int i
=0;i
<app
.board
.boardSize
[0];i
++) {
336 for (int j
=0;j
<app
.board
.boardSize
[1];j
++) {
337 // Tiles are 56px height, 40px width each
338 char piece
=app
.board
.board
[i
][j
];
340 cbuffer
.drawBitmap(tile
[piece
], x0
+j
*tileWidth
, y0
+i
*tileHeight
, paint
);
348 // Red rectangle for selection 1
353 paint
.setColor(selectcolor
);
354 paint
.setStyle(Style
.STROKE
);
355 paint
.setStrokeCap(Cap
.ROUND
);
356 paint
.setStrokeJoin(Join
.ROUND
);
357 paint
.setStrokeWidth(3);
358 cbuffer
.drawRect(new Rect(
359 x0
+selection1
[1]*tileWidth
-2,
360 y0
+selection1
[0]*tileHeight
-2,
361 x0
+selection1
[1]*tileWidth
-2+tileWidth
+2*2,
362 y0
+selection1
[0]*tileHeight
-2+tileHeight
+2*2),
367 // Red rectangle for selection 2
371 paint
.setColor(selectcolor
);
372 paint
.setStyle(Style
.STROKE
);
373 paint
.setStrokeCap(Cap
.ROUND
);
374 paint
.setStrokeJoin(Join
.ROUND
);
375 paint
.setStrokeWidth(3);
376 cbuffer
.drawRect(new Rect(
377 x0
+selection2
[1]*tileWidth
-2,
378 y0
+selection2
[0]*tileHeight
-2,
379 x0
+selection2
[1]*tileWidth
-2+tileWidth
+2*2,
380 y0
+selection2
[0]*tileHeight
-2+tileHeight
+2*2),
388 paint
.setColor(selectcolor
);
389 paint
.setStyle(Style
.STROKE
);
390 paint
.setStrokeCap(Cap
.ROUND
);
391 paint
.setStrokeJoin(Join
.ROUND
);
392 paint
.setStrokeWidth(3);
396 for (Point p1
: path
) {
399 x0
+p0
.j
*tileWidth
-2+(tileWidth
/2),
400 y0
+p0
.i
*tileHeight
-2+(tileHeight
/2),
401 x0
+p1
.j
*tileWidth
-2+(tileWidth
/2),
402 y0
+p1
.i
*tileHeight
-2+(tileHeight
/2),
411 // Orange hint rectangles
414 if (pairs
!=null && pairs
.size()>0) {
415 Line pair
=pairs
.get(0);
418 path
=app
.board
.getPath(a
,b
);
419 paint
.setColor(hintcolor
);
420 paint
.setStyle(Style
.STROKE
);
421 paint
.setStrokeCap(Cap
.ROUND
);
422 paint
.setStrokeJoin(Join
.ROUND
);
423 paint
.setStrokeWidth(3);
425 cbuffer
.drawRect(new Rect(
428 x0
+a
.j
*tileWidth
-2+tileWidth
+2*2,
429 y0
+a
.i
*tileHeight
-2+tileHeight
+2*2),
434 for (Point p1
: path
) {
437 x0
+p0
.j
*tileWidth
-2+(tileWidth
/2),
438 y0
+p0
.i
*tileHeight
-2+(tileHeight
/2),
439 x0
+p1
.j
*tileWidth
-2+(tileWidth
/2),
440 y0
+p1
.i
*tileHeight
-2+(tileHeight
/2),
448 cbuffer
.drawRect(new Rect(
451 x0
+b
.j
*tileWidth
-2+tileWidth
+2*2,
452 y0
+b
.i
*tileHeight
-2+tileHeight
+2*2),
458 // Win & loose notifications
461 drawMessage(cbuffer
, screenWidth
/ 2, screenHeight
/ 2, true,
465 drawMessage(cbuffer
, screenWidth
/ 2, screenHeight
/ 2, true,
470 if (app
.timeCounter
) switch (pstate
) {
480 int hours
= (int) (playTime
/ (60 * 60));
481 int minutes
= (int) ((playTime
/ 60) % 60);
482 int seconds
= (int) (playTime
% 60);
483 String time
= String
.format(Locale
.US
, "%01d:%02d:%02d",
484 hours
, minutes
, seconds
);
486 int timePosX
=screenWidth
-120;
487 int timePosY
=screenHeight
-10;
489 drawMessage(cbuffer
, timePosX
, timePosY
, false, time
, 30);
495 debugMessage="StatePlay: "+cstate+"\n"+"StatePaint: "+pstate;
496 if (debugMessage!=null && debugMessage.length()>0) {
498 String lines[] = debugMessage.split("\n");
499 for (int i=0; i<lines.length; i++) {
500 drawMessage(cbuffer,1,l,false,lines[i],"#FFFF00",30);
506 // Double buffer dumping
507 // canvas.drawBitmap(buffer, 0, 0, null);
509 } catch (Exception e
) {
516 public boolean onTouchEvent(MotionEvent event
) {
517 if (event
.getAction()==MotionEvent
.ACTION_DOWN
) {
518 onClick(Math
.round(event
.getX()),Math
.round(event
.getY()));
520 return super.onTouchEvent(event
);
523 private void onClick(int x
, int y
) {
525 int i
=(y
-(screenHeight
-app
.board
.boardSize
[0]*tileHeight
)/2)/tileHeight
;
526 int j
=(x
-(screenWidth
-app
.board
.boardSize
[1]*tileWidth
)/2)/tileWidth
;
531 i
<app
.board
.boardSize
[0] &&
532 j
>=0 && j
<app
.board
.boardSize
[1] &&
533 app
.board
.board
[i
][j
]!=0) {
536 paint(StatePaint
.SELECTED1
);
537 control(StatePlay
.SELECTED1
);
541 if (i
>=0 && i
<app
.board
.boardSize
[0] &&
542 j
>=0 && j
<app
.board
.boardSize
[1] &&
543 app
.board
.board
[i
][j
]!=0) {
544 if (i
==selection1
[0] && j
==selection1
[1]) {
545 paint(StatePaint
.BOARD
);
546 control(StatePlay
.IDLE
);
550 paint(StatePaint
.SELECTED2
);
552 Point a
=new Point(selection1
[0],selection1
[1]);
553 Point b
=new Point(selection2
[0],selection2
[1]);
554 path
=app
.board
.getPath(a
,b
);
555 paint(StatePaint
.MATCHED
);
557 paint(StatePaint
.BOARD
);
562 paint(StatePaint
.BOARD
);
564 pairs
=app
.board
.getPairs(1);
565 if (pairs
.size()==0) {
566 if (app
.board
.getNumPieces()==0) {
567 paint(StatePaint
.WIN
);
569 paint(StatePaint
.LOSE
);
571 control(StatePlay
.GAMEOVER
);
573 control(StatePlay
.IDLE
);
575 //undo.sensitive=app.board.getCanUndo();
581 paint(StatePaint
.BOARD
);
584 } catch (Exception e
) {
589 public void surfaceChanged(SurfaceHolder holder
, int format
, int width
,
591 surfaceHolder
= holder
;
592 if (cstate
!=StatePlay
.GAMEOVER
&& app
.timeCounter
&& !timerRegistered
) {
598 public void surfaceCreated(SurfaceHolder holder
) {
599 surfaceHolder
= holder
;
603 public void surfaceDestroyed(SurfaceHolder holder
) {
604 surfaceHolder
= null;
605 if (timerRegistered
) {
612 protected void onDraw(Canvas canvas) {
613 super.onDraw(canvas);
615 if (!initialized) initialize();
617 long currTime = System.currentTimeMillis();
619 a = (float)(currTime - startTime) / (float)duration;
620 if (a > (float)1.0) a = (float)1.0;
622 x = Math.round(nextx*a + prevx*(1-a));
623 y = Math.round(nexty*a + prevy*(1-a));
625 if (a == (float)1.0) computeNextTarget();
627 int bgWidth = bg.getWidth();
628 int bgHeight = bg.getHeight();
629 for (int i=0; i<height/bgHeight+1; i++) {
630 for (int j=0; j<width/bgWidth+1; j++) {
631 canvas.drawBitmap(bg, j*bgWidth, i*bgHeight, paint);
635 canvas.drawBitmap(tile[randomtile], x, y, paint);
641 public boolean onTouchEvent(MotionEvent event) {
642 if (event.getActionMasked()==MotionEvent.ACTION_DOWN) {
643 //computeNextTarget();
644 //nextx=Math.round(event.getX());
645 //nexty=Math.round(event.getY());
647 return super.onTouchEvent(event);
650 private void initialize() {
652 height = getHeight();
654 bg = BitmapFactory.decodeResource(getResources(), R.drawable.kshisen_bgnd);
655 Bitmap tileset = BitmapFactory.decodeResource(getResources(), R.drawable.tileset);
657 // The tile set has 4 rows x 9 columns
660 twidth = tileset.getWidth()/tscols;
661 theight = tileset.getHeight()/tsrows;
662 tile = new Bitmap[tsrows*tscols];
664 for (int i=0; i<tsrows; i++) {
665 for (int j=0; j<tscols; j++) {
666 tile[k] = Bitmap.createBitmap(tileset, j*twidth, i*theight, twidth, theight, null, false);
679 private void computeNextTarget() {
680 startTime = System.currentTimeMillis();
683 nextx = (int) Math.floor(Math.random() * width);
684 nexty = (int) Math.floor(Math.random() * height);
685 randomtile = (int) Math.floor(Math.random() * tile.length);
688 paint.setColor(Color.parseColor("#006666"));
689 paint.setFlags(Paint.ANTI_ALIAS_FLAG);