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 INVALID_TIME
= "9:99:99";
32 private static final String COLOR_TEXT
= "#FFFFFF";
33 private static final String COLOR_TEXT_SHADOW
= "#000000";
34 private static final String COLOR_HINT
= "#F0C000";
35 private static final String COLOR_SELECTED
= "#FF0000";
37 private enum StatePlay
{ UNINITIALIZED
, IDLE
, SELECTED1
, SELECTED2
, GAMEOVER
};
38 private enum StatePaint
{ BOARD
, SELECTED1
, SELECTED2
, MATCHED
, WIN
, LOSE
, HINT
, TIME
};
40 private int screenWidth
;
41 private int screenHeight
;
42 private int tilesetRows
;
43 private int tilesetCols
;
44 private int tileHeight
;
45 private int tileWidth
;
47 private Bitmap tile
[];
48 private Point selection1
= new Point(0,0);
49 private Point selection2
= new Point(0,0);
50 private List
<Point
> path
=null;
51 private List
<Line
> pairs
=null;
52 private long startTime
;
53 private long playTime
;
54 private long baseTime
;
56 private static Handler timerHandler
;
58 private boolean timerRegistered
=false;
59 private ShisenSho app
;
60 private StatePlay cstate
;
61 private StatePaint pstate
;
62 private Canvas canvas
= null;
63 private SurfaceHolder surfaceHolder
= null;
64 private String time
= INVALID_TIME
;
66 public ShisenShoView(ShisenSho shishenSho
) {
67 super((Context
) shishenSho
);
68 this.app
= shishenSho
;
69 cstate
= StatePlay
.UNINITIALIZED
;
70 surfaceHolder
= getHolder();
71 surfaceHolder
.addCallback(this);
74 public ShisenShoView(Context ctx
) {
79 private void paint(StatePaint pstate
) {
84 private void control(StatePlay cstate
) {
88 private void loadTileset() {
89 BitmapFactory
.Options ops
= new BitmapFactory
.Options();
91 Bitmap tileset
= BitmapFactory
.decodeResource(getResources(), R
.drawable
.tileset
, ops
);
92 tileset
.setDensity(Bitmap
.DENSITY_NONE
);
94 // The tile set has 4 rows x 9 columns
97 int loadedtileWidth
= tileset
.getWidth()/tilesetCols
;
98 int loadedtileHeight
= tileset
.getHeight()/tilesetRows
;
99 tile
= new Bitmap
[tilesetRows
*tilesetCols
];
102 // "large" is 16x6, and we want to have a nice border, so we use 17x7 and
103 // choose the lowest scale so everything fits
104 float scalex
= ((float) screenWidth
/17) / loadedtileWidth
;
105 float scaley
= ((float) screenHeight
/7) / loadedtileHeight
;
106 if (scaley
< scalex
) {
111 Matrix matrix
= new Matrix();
112 matrix
.setScale(scalex
, scaley
);
115 for (int i
=0; i
<tilesetRows
; i
++) {
116 for (int j
=0; j
<tilesetCols
; j
++) {
117 tile
[k
] = Bitmap
.createBitmap(tileset
, j
*loadedtileWidth
, i
*loadedtileHeight
,
118 loadedtileWidth
, loadedtileHeight
, matrix
, false);
119 tile
[k
].setDensity(Bitmap
.DENSITY_NONE
);
123 tileWidth
= tile
[0].getWidth();
124 tileHeight
= tile
[0].getHeight();
127 private void loadBackground() {
128 BitmapFactory
.Options ops
= new BitmapFactory
.Options();
129 ops
.inScaled
= false;
130 bg
= BitmapFactory
.decodeResource(getResources(), R
.drawable
.kshisen_bgnd
, ops
);
131 bg
.setDensity(Bitmap
.DENSITY_NONE
);
134 private void registerTimer() {
135 if (timer
!=null) return; // Already registered
136 timerHandler
= new Handler() {
137 public void handleMessage(Message msg
) {
142 timer
.scheduleAtFixedRate(new TimerTask() {
144 timerHandler
.sendEmptyMessage(Activity
.RESULT_OK
);
147 timerRegistered
=true;
150 private void unregisterTimer() {
151 if (timer
==null) return; // Already unregistered
155 timerRegistered
=false;
158 public void pauseTime() {
161 startTime
= System
.currentTimeMillis();
165 public void resumeTime() {
166 startTime
= System
.currentTimeMillis();
170 private void updateTime() {
171 if (cstate
!=StatePlay
.GAMEOVER
) {
172 playTime
= (System
.currentTimeMillis()-startTime
)/1000+baseTime
;
176 private void initializeGame() {
178 screenWidth
=getWidth();
179 screenHeight
=getHeight();
181 //undo.sensitive=false;
182 pstate
=StatePaint
.BOARD
;
184 control(StatePlay
.IDLE
);
185 startTime
=System
.currentTimeMillis();
188 if (app
.timeCounter
&& !timerRegistered
) {
191 pairs
=app
.board
.getPairs(1);
194 public boolean onOptionsItemSelected(MenuItem item
) {
195 // Handle item selection
196 switch (item
.getItemId()) {
198 this.postDelayed(new Runnable() { public void run() { onHintActivate(); } }, 100);
201 this.postDelayed(new Runnable() { public void run() { onUndoActivate(); } }, 100);
204 this.postDelayed(new Runnable() { public void run() { reset(); } }, 100);
215 public void reset() {
216 control(StatePlay
.UNINITIALIZED
);
217 paint(StatePaint
.BOARD
);
220 private void onHintActivate() {
221 if (cstate
!=StatePlay
.GAMEOVER
) {
222 pairs
=app
.board
.getPairs(1);
223 paint(StatePaint
.HINT
);
225 paint(StatePaint
.BOARD
);
226 control(StatePlay
.IDLE
);
230 private void onUndoActivate() {
231 if (app
.board
.getCanUndo()) {
232 if (cstate
==StatePlay
.GAMEOVER
&& app
.timeCounter
&& !timerRegistered
) {
233 // Reprogram the time update that had been
234 // deactivated with the game over status
238 paint(StatePaint
.BOARD
);
239 //undo.sensitive=app.board.getCanUndo();
240 control(StatePlay
.IDLE
);
244 public void onTimeCounterActivate() {
245 if (app
.timeCounter
&& cstate
!=StatePlay
.GAMEOVER
&& !timerRegistered
) {
246 // Reprogram the time update that had been
247 // deactivated with the time_counter=false
252 private void onUpdateTime() {
254 if (!(app
.timeCounter
&& cstate
!=StatePlay
.GAMEOVER
)) {
259 @SuppressWarnings("deprecation")
260 public static void drawMessage(Canvas canvas
, int x
, int y
,
261 boolean centered
, String message
, float textSize
) {
262 Paint paint
= new Paint();
263 paint
.setLinearText(true);
264 paint
.setAntiAlias(true);
265 paint
.setTextAlign(centered ? Align
.CENTER
: Align
.LEFT
);
266 paint
.setTypeface(Typeface
.SANS_SERIF
);
267 paint
.setFakeBoldText(true);
268 paint
.setTextSize(textSize
);
269 paint
.setColor(Color
.parseColor(COLOR_TEXT_SHADOW
));
270 canvas
.drawText(message
, x
+ 1, y
+ 1, paint
);
271 paint
.setColor(Color
.parseColor(COLOR_TEXT
));
272 canvas
.drawText(message
, x
, y
, paint
);
275 public void repaint() {
276 if (surfaceHolder
== null) return;
278 if (canvas
== null) canvas
= surfaceHolder
.lockCanvas(null);
279 if (canvas
== null) return;
280 if (cstate
==StatePlay
.UNINITIALIZED
) initializeGame();
281 synchronized (surfaceHolder
) {
285 if (canvas
!= null) {
286 surfaceHolder
.unlockCanvasAndPost(canvas
);
292 protected void doDraw(Canvas canvas
) {
294 if (canvas
== null) return;
296 // Board upper left corner on screen
300 if (app
!=null && app
.board
!=null) {
301 x0
=(screenWidth
-app
.board
.boardSize
[1]*tileWidth
)/2;
302 y0
=(screenHeight
-app
.board
.boardSize
[0]*tileHeight
)/2;
305 int selectcolor
= Color
.parseColor(COLOR_SELECTED
);
306 int hintcolor
= Color
.parseColor(COLOR_HINT
);
307 Paint paint
= new Paint();
308 paint
.setFlags(Paint
.ANTI_ALIAS_FLAG
);
310 // Background & board painting
320 // Background painting
321 int bgWidth
= bg
.getWidth();
322 int bgHeight
= bg
.getHeight();
323 for (int i
=0; i
<screenHeight
/bgHeight
+1; i
++) {
324 for (int j
=0; j
<screenWidth
/bgWidth
+1; j
++) {
325 canvas
.drawBitmap(bg
, j
*bgWidth
, i
*bgHeight
, paint
);
330 // Max visible size: 7x17
331 if (app
!=null && app
.board
!=null) {
332 for (int i
=0;i
<app
.board
.boardSize
[0];i
++) {
333 for (int j
=0;j
<app
.board
.boardSize
[1];j
++) {
334 // Tiles are 56px height, 40px width each
335 char piece
=app
.board
.board
[i
][j
];
337 canvas
.drawBitmap(tile
[piece
], x0
+j
*tileWidth
, y0
+i
*tileHeight
, paint
);
345 // rectangle for selection 1
350 highlightTile(canvas
, x0
, y0
, selection1
, selectcolor
);
354 // rectangle for selection 2
358 highlightTile(canvas
, x0
, y0
, selection2
, selectcolor
);
365 paint
.setColor(selectcolor
);
366 paint
.setStyle(Style
.STROKE
);
367 paint
.setStrokeCap(Cap
.ROUND
);
368 paint
.setStrokeJoin(Join
.ROUND
);
369 paint
.setStrokeWidth(3);
373 for (Point p1
: path
) {
375 drawLine(canvas
, x0
, y0
, p0
, p1
, paint
);
386 if (pairs
!= null && pairs
.size() > 0) {
387 Line pair
= pairs
.get(0);
390 path
= app
.board
.getPath(a
, b
);
391 paint
.setColor(hintcolor
);
392 paint
.setStyle(Style
.STROKE
);
393 paint
.setStrokeCap(Cap
.ROUND
);
394 paint
.setStrokeJoin(Join
.ROUND
);
395 paint
.setStrokeWidth(3);
397 highlightTile(canvas
, x0
, y0
, a
, hintcolor
);
401 for (Point p1
: path
) {
403 drawLine(canvas
, x0
, y0
, p0
, p1
, paint
);
410 highlightTile(canvas
, x0
, y0
, b
, hintcolor
);
415 // Win & loose notifications
418 drawMessage(canvas
, screenWidth
/ 2, screenHeight
/ 2, true,
422 drawMessage(canvas
, screenWidth
/ 2, screenHeight
/ 2, true,
437 int hours
= (int) (playTime
/ (60 * 60));
438 int minutes
= (int) ((playTime
/ 60) % 60);
439 int seconds
= (int) (playTime
% 60);
441 time
= String
.format(Locale
.US
, "%01d:%02d:%02d",
442 hours
, minutes
, seconds
);
447 int timePosX
=screenWidth
-120;
448 int timePosY
=screenHeight
-10;
450 if (app
.timeCounter
) {
451 drawMessage(canvas
, timePosX
, timePosY
, false, time
, 30);
456 } catch (Exception e
) {
462 private void drawLine(Canvas canvas
, int x0
, int y0
, Point p0
, Point p1
,
465 x0
+ p0
.j
* tileWidth
- 2 + (tileWidth
/ 2),
466 y0
+ p0
.i
* tileHeight
- 2 + (tileHeight
/ 2),
467 x0
+ p1
.j
* tileWidth
- 2 + (tileWidth
/ 2),
468 y0
+ p1
.i
* tileHeight
- 2 + (tileHeight
/ 2), paint
);
471 private void highlightTile(Canvas canvas
, int x0
, int y0
, Point p
, int color
) {
472 Paint paint
= new Paint();
473 paint
.setFlags(Paint
.ANTI_ALIAS_FLAG
);
474 paint
.setColor(color
);
475 paint
.setStyle(Style
.STROKE
);
476 paint
.setStrokeCap(Cap
.ROUND
);
477 paint
.setStrokeJoin(Join
.ROUND
);
478 paint
.setStrokeWidth(3);
480 x0
+ p
.j
* tileWidth
- 2,
481 y0
+ p
.i
* tileHeight
- 2,
482 x0
+ p
.j
* tileWidth
+ tileWidth
+ 2,
483 y0
+ p
.i
* tileHeight
+ tileHeight
+ 2);
484 canvas
.drawRect(r
, paint
);
488 public boolean onTouchEvent(MotionEvent event
) {
489 if (event
.getAction()==MotionEvent
.ACTION_DOWN
) {
490 onClick(Math
.round(event
.getX()),Math
.round(event
.getY()));
492 return super.onTouchEvent(event
);
495 private void onClick(int x
, int y
) {
497 int i
=(y
-(screenHeight
-app
.board
.boardSize
[0]*tileHeight
)/2)/tileHeight
;
498 int j
=(x
-(screenWidth
-app
.board
.boardSize
[1]*tileWidth
)/2)/tileWidth
;
502 if (i
>= 0 && i
< app
.board
.boardSize
[0] && j
>= 0
503 && j
< app
.board
.boardSize
[1]
504 && app
.board
.board
[i
][j
] != 0) {
505 selection1
.set(i
, j
);
506 paint(StatePaint
.SELECTED1
);
507 control(StatePlay
.SELECTED1
);
511 if (i
>= 0 && i
< app
.board
.boardSize
[0] && j
>= 0
512 && j
< app
.board
.boardSize
[1]
513 && app
.board
.board
[i
][j
] != 0) {
514 if (selection1
.equals(i
, j
)) {
515 paint(StatePaint
.BOARD
);
516 control(StatePlay
.IDLE
);
518 selection2
.set(i
, j
);
519 paint(StatePaint
.SELECTED2
);
521 Point a
= selection1
.copy();
522 Point b
= selection2
.copy();
523 path
= app
.board
.getPath(a
, b
);
524 paint(StatePaint
.MATCHED
);
526 paint(StatePaint
.BOARD
);
527 if (path
.size() > 0) {
528 app
.board
.play(a
, b
);
531 paint(StatePaint
.BOARD
);
533 pairs
= app
.board
.getPairs(1);
534 if (pairs
.size() == 0) {
535 if (app
.board
.getNumPieces() == 0) {
536 paint(StatePaint
.WIN
);
538 paint(StatePaint
.LOSE
);
540 control(StatePlay
.GAMEOVER
);
542 control(StatePlay
.IDLE
);
544 //undo.sensitive=app.board.getCanUndo();
550 paint(StatePaint
.BOARD
);
553 } catch (Exception e
) {
558 public void surfaceChanged(SurfaceHolder holder
, int format
, int width
,
560 surfaceHolder
= holder
;
561 if (cstate
!=StatePlay
.GAMEOVER
&& app
.timeCounter
&& !timerRegistered
) {
567 public void surfaceCreated(SurfaceHolder holder
) {
568 surfaceHolder
= holder
;
572 public void surfaceDestroyed(SurfaceHolder holder
) {
573 surfaceHolder
= null;
574 if (timerRegistered
) {