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 Point selection1
= new Point(0,0);
48 private Point selection2
= new Point(0,0);
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;
64 public ShisenShoView(ShisenSho shishenSho
) {
65 super((Context
) shishenSho
);
66 this.app
= shishenSho
;
67 cstate
= StatePlay
.UNINITIALIZED
;
68 surfaceHolder
= getHolder();
69 surfaceHolder
.addCallback(this);
72 public ShisenShoView(Context ctx
) {
77 private void paint(StatePaint pstate
) {
82 private void control(StatePlay cstate
) {
86 private void loadTileset() {
87 BitmapFactory
.Options ops
= new BitmapFactory
.Options();
89 Bitmap tileset
= BitmapFactory
.decodeResource(getResources(), R
.drawable
.tileset
, ops
);
90 tileset
.setDensity(Bitmap
.DENSITY_NONE
);
92 // The tile set has 4 rows x 9 columns
95 int loadedtileWidth
= tileset
.getWidth()/tilesetCols
;
96 int loadedtileHeight
= tileset
.getHeight()/tilesetRows
;
97 tile
= new Bitmap
[tilesetRows
*tilesetCols
];
100 // "large" is 16x6, and we want to have a nice border, so we use 17x7 and
101 // choose the lowest scale so everything fits
102 float scalex
= ((float) screenWidth
/17) / loadedtileWidth
;
103 float scaley
= ((float) screenHeight
/7) / loadedtileHeight
;
104 if (scaley
< scalex
) {
109 Matrix matrix
= new Matrix();
110 matrix
.setScale(scalex
, scaley
);
113 for (int i
=0; i
<tilesetRows
; i
++) {
114 for (int j
=0; j
<tilesetCols
; j
++) {
115 tile
[k
] = Bitmap
.createBitmap(tileset
, j
*loadedtileWidth
, i
*loadedtileHeight
,
116 loadedtileWidth
, loadedtileHeight
, matrix
, false);
117 tile
[k
].setDensity(Bitmap
.DENSITY_NONE
);
121 tileWidth
= tile
[0].getWidth();
122 tileHeight
= tile
[0].getHeight();
125 private void loadBackground() {
126 BitmapFactory
.Options ops
= new BitmapFactory
.Options();
127 ops
.inScaled
= false;
128 bg
= BitmapFactory
.decodeResource(getResources(), R
.drawable
.kshisen_bgnd
, ops
);
129 bg
.setDensity(Bitmap
.DENSITY_NONE
);
132 private void registerTimer() {
133 if (timer
!=null) return; // Already registered
134 timerHandler
= new Handler() {
135 public void handleMessage(Message msg
) {
140 timer
.scheduleAtFixedRate(new TimerTask() {
142 timerHandler
.sendEmptyMessage(Activity
.RESULT_OK
);
145 timerRegistered
=true;
148 private void unregisterTimer() {
149 if (timer
==null) return; // Already unregistered
153 timerRegistered
=false;
156 public void pauseTime() {
159 startTime
= System
.currentTimeMillis();
163 public void resumeTime() {
164 startTime
= System
.currentTimeMillis();
168 private void updateTime() {
169 if (cstate
!=StatePlay
.GAMEOVER
) {
170 playTime
= (System
.currentTimeMillis()-startTime
)/1000+baseTime
;
174 private void initializeGame() {
176 screenWidth
=getWidth();
177 screenHeight
=getHeight();
179 //undo.sensitive=false;
180 pstate
=StatePaint
.BOARD
;
182 control(StatePlay
.IDLE
);
183 startTime
=System
.currentTimeMillis();
186 if (app
.timeCounter
&& !timerRegistered
) {
189 pairs
=app
.board
.getPairs(1);
192 public boolean onOptionsItemSelected(MenuItem item
) {
193 // Handle item selection
194 switch (item
.getItemId()) {
196 this.postDelayed(new Runnable() { public void run() { onHintActivate(); } }, 100);
199 this.postDelayed(new Runnable() { public void run() { onUndoActivate(); } }, 100);
202 this.postDelayed(new Runnable() { public void run() { reset(); } }, 100);
213 public void reset() {
214 control(StatePlay
.UNINITIALIZED
);
215 paint(StatePaint
.BOARD
);
218 private void onHintActivate() {
219 if (cstate
!=StatePlay
.GAMEOVER
) {
220 pairs
=app
.board
.getPairs(1);
221 paint(StatePaint
.HINT
);
223 paint(StatePaint
.BOARD
);
224 control(StatePlay
.IDLE
);
228 private void onUndoActivate() {
229 if (app
.board
.getCanUndo()) {
230 if (cstate
==StatePlay
.GAMEOVER
&& app
.timeCounter
&& !timerRegistered
) {
231 // Reprogram the time update that had been
232 // deactivated with the game over status
236 paint(StatePaint
.BOARD
);
237 //undo.sensitive=app.board.getCanUndo();
238 control(StatePlay
.IDLE
);
242 public void onTimeCounterActivate() {
243 if (app
.timeCounter
&& cstate
!=StatePlay
.GAMEOVER
&& !timerRegistered
) {
244 // Reprogram the time update that had been
245 // deactivated with the time_counter=false
250 private void onUpdateTime() {
252 if (!(app
.timeCounter
&& cstate
!=StatePlay
.GAMEOVER
)) {
257 @SuppressWarnings("deprecation")
258 public static void drawMessage(Canvas canvas
, int x
, int y
,
259 boolean centered
, String message
, float textSize
) {
260 Paint paint
= new Paint();
261 paint
.setLinearText(true);
262 paint
.setAntiAlias(true);
263 paint
.setTextAlign(centered ? Align
.CENTER
: Align
.LEFT
);
264 paint
.setTypeface(Typeface
.SANS_SERIF
);
265 paint
.setFakeBoldText(true);
266 paint
.setTextSize(textSize
);
267 paint
.setColor(Color
.parseColor(COLOR_TEXT_SHADOW
));
268 canvas
.drawText(message
, x
+ 1, y
+ 1, paint
);
269 paint
.setColor(Color
.parseColor(COLOR_TEXT
));
270 canvas
.drawText(message
, x
, y
, paint
);
273 public void repaint() {
274 if (surfaceHolder
== null) return;
276 if (canvas
== null) canvas
= surfaceHolder
.lockCanvas(null);
277 if (canvas
== null) return;
278 if (cstate
==StatePlay
.UNINITIALIZED
) initializeGame();
279 synchronized (surfaceHolder
) {
283 if (canvas
!= null) {
284 surfaceHolder
.unlockCanvasAndPost(canvas
);
290 protected void doDraw(Canvas canvas
) {
293 // Bitmap buffer = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
294 //Canvas cbuffer = new Canvas(buffer);
295 Canvas cbuffer
= canvas
;
296 if (canvas
== null) return;
298 //super.onDraw(canvas);
300 // Board upper left corner on screen
304 if (app
!=null && app
.board
!=null) {
305 x0
=(screenWidth
-app
.board
.boardSize
[1]*tileWidth
)/2;
306 y0
=(screenHeight
-app
.board
.boardSize
[0]*tileHeight
)/2;
309 int selectcolor
= Color
.parseColor(COLOR_SELECTED
);
310 int hintcolor
= Color
.parseColor(COLOR_HINT
);
311 Paint paint
= new Paint();
312 paint
.setFlags(Paint
.ANTI_ALIAS_FLAG
);
314 // Background & board painting
324 // Background painting
325 int bgWidth
= bg
.getWidth();
326 int bgHeight
= bg
.getHeight();
327 for (int i
=0; i
<screenHeight
/bgHeight
+1; i
++) {
328 for (int j
=0; j
<screenWidth
/bgWidth
+1; j
++) {
329 cbuffer
.drawBitmap(bg
, j
*bgWidth
, i
*bgHeight
, paint
);
334 // Max visible size: 7x17
335 if (app
!=null && app
.board
!=null) {
336 for (int i
=0;i
<app
.board
.boardSize
[0];i
++) {
337 for (int j
=0;j
<app
.board
.boardSize
[1];j
++) {
338 // Tiles are 56px height, 40px width each
339 char piece
=app
.board
.board
[i
][j
];
341 cbuffer
.drawBitmap(tile
[piece
], x0
+j
*tileWidth
, y0
+i
*tileHeight
, paint
);
349 // Red rectangle for selection 1
354 highlightTile(cbuffer
, x0
, y0
, selection1
, selectcolor
);
358 // Red rectangle for selection 2
362 highlightTile(cbuffer
, x0
, y0
, selection2
, selectcolor
);
369 paint
.setColor(selectcolor
);
370 paint
.setStyle(Style
.STROKE
);
371 paint
.setStrokeCap(Cap
.ROUND
);
372 paint
.setStrokeJoin(Join
.ROUND
);
373 paint
.setStrokeWidth(3);
377 for (Point p1
: path
) {
379 drawLine(cbuffer
, x0
, y0
, p0
, p1
, paint
);
387 // Orange hint rectangles
390 if (pairs
!= null && pairs
.size() > 0) {
391 Line pair
= pairs
.get(0);
394 path
= app
.board
.getPath(a
, b
);
395 paint
.setColor(hintcolor
);
396 paint
.setStyle(Style
.STROKE
);
397 paint
.setStrokeCap(Cap
.ROUND
);
398 paint
.setStrokeJoin(Join
.ROUND
);
399 paint
.setStrokeWidth(3);
401 highlightTile(cbuffer
, x0
, y0
, a
, hintcolor
);
405 for (Point p1
: path
) {
407 drawLine(cbuffer
, x0
, y0
, p0
, p1
, paint
);
414 highlightTile(cbuffer
, x0
, y0
, b
, hintcolor
);
419 // Win & loose notifications
422 drawMessage(cbuffer
, screenWidth
/ 2, screenHeight
/ 2, true,
426 drawMessage(cbuffer
, screenWidth
/ 2, screenHeight
/ 2, true,
431 if (app
.timeCounter
) switch (pstate
) {
441 int hours
= (int) (playTime
/ (60 * 60));
442 int minutes
= (int) ((playTime
/ 60) % 60);
443 int seconds
= (int) (playTime
% 60);
444 String time
= String
.format(Locale
.US
, "%01d:%02d:%02d",
445 hours
, minutes
, seconds
);
447 int timePosX
=screenWidth
-120;
448 int timePosY
=screenHeight
-10;
450 drawMessage(cbuffer
, timePosX
, timePosY
, false, time
, 30);
456 debugMessage="StatePlay: "+cstate+"\n"+"StatePaint: "+pstate;
457 if (debugMessage!=null && debugMessage.length()>0) {
459 String lines[] = debugMessage.split("\n");
460 for (int i=0; i<lines.length; i++) {
461 drawMessage(cbuffer,1,l,false,lines[i],"#FFFF00",30);
467 // Double buffer dumping
468 // canvas.drawBitmap(buffer, 0, 0, null);
470 } catch (Exception e
) {
476 private void drawLine(Canvas cbuffer
, int x0
, int y0
, Point p0
, Point p1
,
479 x0
+ p0
.j
* tileWidth
- 2 + (tileWidth
/ 2),
480 y0
+ p0
.i
* tileHeight
- 2 + (tileHeight
/ 2),
481 x0
+ p1
.j
* tileWidth
- 2 + (tileWidth
/ 2),
482 y0
+ p1
.i
* tileHeight
- 2 + (tileHeight
/ 2), paint
);
485 private void highlightTile(Canvas cbuffer
, int x0
, int y0
, Point p
, int color
) {
486 Paint paint
= new Paint();
487 paint
.setFlags(Paint
.ANTI_ALIAS_FLAG
);
488 paint
.setColor(color
);
489 paint
.setStyle(Style
.STROKE
);
490 paint
.setStrokeCap(Cap
.ROUND
);
491 paint
.setStrokeJoin(Join
.ROUND
);
492 paint
.setStrokeWidth(3);
494 x0
+ p
.j
* tileWidth
- 2,
495 y0
+ p
.i
* tileHeight
- 2,
496 x0
+ p
.j
* tileWidth
* 2 + 2,
497 y0
+ p
.i
* tileHeight
* 2 + 2);
498 cbuffer
.drawRect(r
, paint
);
502 public boolean onTouchEvent(MotionEvent event
) {
503 if (event
.getAction()==MotionEvent
.ACTION_DOWN
) {
504 onClick(Math
.round(event
.getX()),Math
.round(event
.getY()));
506 return super.onTouchEvent(event
);
509 private void onClick(int x
, int y
) {
511 int i
=(y
-(screenHeight
-app
.board
.boardSize
[0]*tileHeight
)/2)/tileHeight
;
512 int j
=(x
-(screenWidth
-app
.board
.boardSize
[1]*tileWidth
)/2)/tileWidth
;
516 if (i
>= 0 && i
< app
.board
.boardSize
[0] && j
>= 0
517 && j
< app
.board
.boardSize
[1]
518 && app
.board
.board
[i
][j
] != 0) {
519 selection1
.set(i
, j
);
520 paint(StatePaint
.SELECTED1
);
521 control(StatePlay
.SELECTED1
);
525 if (i
>= 0 && i
< app
.board
.boardSize
[0] && j
>= 0
526 && j
< app
.board
.boardSize
[1]
527 && app
.board
.board
[i
][j
] != 0) {
528 if (selection1
.equals(i
, j
)) {
529 paint(StatePaint
.BOARD
);
530 control(StatePlay
.IDLE
);
532 selection2
.set(i
, j
);
533 paint(StatePaint
.SELECTED2
);
535 Point a
= selection1
.copy();
536 Point b
= selection2
.copy();
537 path
= app
.board
.getPath(a
, b
);
538 paint(StatePaint
.MATCHED
);
540 paint(StatePaint
.BOARD
);
541 if (path
.size() > 0) {
542 app
.board
.play(a
, b
);
545 paint(StatePaint
.BOARD
);
547 pairs
= app
.board
.getPairs(1);
548 if (pairs
.size() == 0) {
549 if (app
.board
.getNumPieces() == 0) {
550 paint(StatePaint
.WIN
);
552 paint(StatePaint
.LOSE
);
554 control(StatePlay
.GAMEOVER
);
556 control(StatePlay
.IDLE
);
558 //undo.sensitive=app.board.getCanUndo();
564 paint(StatePaint
.BOARD
);
567 } catch (Exception e
) {
572 public void surfaceChanged(SurfaceHolder holder
, int format
, int width
,
574 surfaceHolder
= holder
;
575 if (cstate
!=StatePlay
.GAMEOVER
&& app
.timeCounter
&& !timerRegistered
) {
581 public void surfaceCreated(SurfaceHolder holder
) {
582 surfaceHolder
= holder
;
586 public void surfaceDestroyed(SurfaceHolder holder
) {
587 surfaceHolder
= null;
588 if (timerRegistered
) {