ecb476387bc7f3e62d74deebf183b7fad07a8cf0
[FreeShisen] / src / de / cwde / freeshisen / ShisenShoView.java
1 package de.cwde.freeshisen;
2
3 import java.util.List;
4 import java.util.Locale;
5 import java.util.Timer;
6 import java.util.TimerTask;
7
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;
28
29 class ShisenShoView extends SurfaceView implements SurfaceHolder.Callback {
30
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";
35
36 private enum StatePlay { UNINITIALIZED, IDLE, SELECTED1, SELECTED2, GAMEOVER };
37 private enum StatePaint { BOARD, SELECTED1, SELECTED2, MATCHED, WIN, LOSE, HINT, TIME };
38
39 private int screenWidth;
40 private int screenHeight;
41 private int tilesetRows;
42 private int tilesetCols;
43 private int tileHeight;
44 private int tileWidth;
45 private Bitmap bg;
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;
54 private Timer timer;
55 private static Handler timerHandler;
56
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);
69 }
70
71 public ShisenShoView(Context ctx) {
72 super((Context)ctx);
73 // silence lint?
74 }
75
76 private void paint(StatePaint pstate) {
77 this.pstate=pstate;
78 repaint();
79 }
80
81 private void control(StatePlay cstate) {
82 this.cstate=cstate;
83 }
84
85 private void loadTileset() {
86 BitmapFactory.Options ops = new BitmapFactory.Options();
87 ops.inScaled = false;
88 Bitmap tileset = BitmapFactory.decodeResource(getResources(), R.drawable.tileset, ops);
89 tileset.setDensity(Bitmap.DENSITY_NONE);
90
91 // The tile set has 4 rows x 9 columns
92 tilesetRows = 4;
93 tilesetCols = 9;
94 int loadedtileWidth = tileset.getWidth()/tilesetCols;
95 int loadedtileHeight = tileset.getHeight()/tilesetRows;
96 tile = new Bitmap[tilesetRows*tilesetCols];
97
98 // align to screen:
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) {
104 scalex = scaley;
105 } else {
106 scaley = scalex;
107 }
108 Matrix matrix = new Matrix();
109 matrix.setScale(scalex, scaley);
110
111 int k=0;
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);
117 k++;
118 }
119 }
120 tileWidth = tile[0].getWidth();
121 tileHeight = tile[0].getHeight();
122 }
123
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);
129 }
130
131 private void registerTimer() {
132 if (timer!=null) return; // Already registered
133 timerHandler = new Handler() {
134 public void handleMessage(Message msg) {
135 onUpdateTime();
136 }
137 };
138 timer=new Timer();
139 timer.scheduleAtFixedRate(new TimerTask() {
140 public void run() {
141 timerHandler.sendEmptyMessage(Activity.RESULT_OK);
142 }
143 }, 0, 1000);
144 timerRegistered=true;
145 }
146
147 private void unregisterTimer() {
148 if (timer==null) return; // Already unregistered
149 timer.cancel();
150 timer = null;
151 timerHandler = null;
152 timerRegistered=false;
153 }
154
155 public void pauseTime() {
156 updateTime();
157 baseTime = playTime;
158 startTime = System.currentTimeMillis();
159
160 }
161
162 public void resumeTime() {
163 startTime = System.currentTimeMillis();
164 updateTime();
165 }
166
167 private void updateTime() {
168 if (cstate!=StatePlay.GAMEOVER) {
169 playTime = (System.currentTimeMillis()-startTime)/1000+baseTime;
170 }
171 }
172
173 private void initializeGame() {
174 loadBackground();
175 screenWidth=getWidth();
176 screenHeight=getHeight();
177 loadTileset();
178 //undo.sensitive=false;
179 pstate=StatePaint.BOARD;
180 app.newPlay();
181 control(StatePlay.IDLE);
182 startTime=System.currentTimeMillis();
183 playTime=0;
184 baseTime=0;
185 if (app.timeCounter && !timerRegistered) {
186 registerTimer();
187 }
188 pairs=app.board.getPairs(1);
189 }
190
191 public boolean onOptionsItemSelected(MenuItem item) {
192 // Handle item selection
193 switch (item.getItemId()) {
194 case R.id.hint:
195 this.postDelayed(new Runnable() { public void run() { onHintActivate(); } }, 100);
196 return true;
197 case R.id.undo:
198 this.postDelayed(new Runnable() { public void run() { onUndoActivate(); } }, 100);
199 return true;
200 case R.id.clean:
201 this.postDelayed(new Runnable() { public void run() { reset(); } }, 100);
202 return true;
203 case R.id.options:
204 return true;
205 case R.id.about:
206 return true;
207 default:
208 return false;
209 }
210 }
211
212 public void reset() {
213 control(StatePlay.UNINITIALIZED);
214 paint(StatePaint.BOARD);
215 }
216
217 private void onHintActivate() {
218 if (cstate!=StatePlay.GAMEOVER) {
219 pairs=app.board.getPairs(1);
220 paint(StatePaint.HINT);
221 app.sleep(10);
222 paint(StatePaint.BOARD);
223 control(StatePlay.IDLE);
224 }
225 }
226
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
232 registerTimer();
233 }
234 app.board.undo();
235 paint(StatePaint.BOARD);
236 //undo.sensitive=app.board.getCanUndo();
237 control(StatePlay.IDLE);
238 }
239 }
240
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
245 registerTimer();
246 }
247 }
248
249 private void onUpdateTime() {
250 paint(pstate);
251 if (!(app.timeCounter && cstate!=StatePlay.GAMEOVER)) {
252 unregisterTimer();
253 }
254 }
255
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);
270 }
271
272 public void repaint() {
273 if (surfaceHolder == null) return;
274 try {
275 if (canvas == null) canvas = surfaceHolder.lockCanvas(null);
276 if (canvas == null) return;
277 if (cstate==StatePlay.UNINITIALIZED) initializeGame();
278 synchronized (surfaceHolder) {
279 doDraw(canvas);
280 }
281 } finally {
282 if (canvas != null) {
283 surfaceHolder.unlockCanvasAndPost(canvas);
284 canvas = null;
285 }
286 }
287 }
288
289 protected void doDraw(Canvas canvas) {
290 try {
291 // Double buffering
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;
296
297 //super.onDraw(canvas);
298
299 // Board upper left corner on screen
300 int x0=0;
301 int y0=0;
302
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;
306 }
307
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);
312
313 // Background & board painting
314 switch (pstate) {
315 case BOARD:
316 case SELECTED1:
317 case SELECTED2:
318 case MATCHED:
319 case WIN:
320 case LOSE:
321 case HINT:
322 case TIME:
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);
329 }
330 }
331
332 // Board painting
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];
339 if (piece!=0) {
340 cbuffer.drawBitmap(tile[piece], x0+j*tileWidth, y0+i*tileHeight, paint);
341 }
342 }
343 }
344 }
345 break;
346 }
347
348 // Red rectangle for selection 1
349 switch (pstate) {
350 case SELECTED1:
351 case SELECTED2:
352 case MATCHED:
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),
363 paint);
364 break;
365 }
366
367 // Red rectangle for selection 2
368 switch (pstate) {
369 case SELECTED2:
370 case MATCHED:
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),
381 paint);
382 break;
383 }
384
385 // Matching path
386 switch (pstate) {
387 case MATCHED:
388 paint.setColor(selectcolor);
389 paint.setStyle(Style.STROKE);
390 paint.setStrokeCap(Cap.ROUND);
391 paint.setStrokeJoin(Join.ROUND);
392 paint.setStrokeWidth(3);
393
394 if (path!=null) {
395 Point p0=null;
396 for (Point p1 : path) {
397 if (p0!=null) {
398 cbuffer.drawLine(
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),
403 paint);
404 }
405 p0=p1;
406 }
407 }
408 break;
409 }
410
411 // Orange hint rectangles
412 switch (pstate) {
413 case HINT:
414 if (pairs!=null && pairs.size()>0) {
415 Line pair=pairs.get(0);
416 Point a=pair.a;
417 Point b=pair.b;
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);
424
425 cbuffer.drawRect(new Rect(
426 x0+a.j*tileWidth-2,
427 y0+a.i*tileHeight-2,
428 x0+a.j*tileWidth-2+tileWidth+2*2,
429 y0+a.i*tileHeight-2+tileHeight+2*2),
430 paint);
431
432 if (path!=null) {
433 Point p0=null;
434 for (Point p1 : path) {
435 if (p0!=null) {
436 cbuffer.drawLine(
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),
441 paint);
442 }
443 p0=p1;
444 }
445 path=null;
446 }
447
448 cbuffer.drawRect(new Rect(
449 x0+b.j*tileWidth-2,
450 y0+b.i*tileHeight-2,
451 x0+b.j*tileWidth-2+tileWidth+2*2,
452 y0+b.i*tileHeight-2+tileHeight+2*2),
453 paint);
454 }
455 break;
456 }
457
458 // Win & loose notifications
459 switch (pstate) {
460 case WIN:
461 drawMessage(cbuffer, screenWidth / 2, screenHeight / 2, true,
462 "You Win!", 100);
463 break;
464 case LOSE:
465 drawMessage(cbuffer, screenWidth / 2, screenHeight / 2, true,
466 "Game Over", 100);
467 break;
468 }
469
470 if (app.timeCounter) switch (pstate) {
471 case BOARD:
472 case SELECTED1:
473 case SELECTED2:
474 case MATCHED:
475 case WIN:
476 case LOSE:
477 case HINT:
478 case TIME:
479 updateTime();
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);
485
486 int timePosX=screenWidth-120;
487 int timePosY=screenHeight-10;
488
489 drawMessage(cbuffer, timePosX, timePosY, false, time, 30);
490 break;
491 }
492
493 // Debug messages
494 /*
495 debugMessage="StatePlay: "+cstate+"\n"+"StatePaint: "+pstate;
496 if (debugMessage!=null && debugMessage.length()>0) {
497 int l = 20;
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);
501 l+=30;
502 }
503 }
504 */
505
506 // Double buffer dumping
507 // canvas.drawBitmap(buffer, 0, 0, null);
508
509 } catch (Exception e) {
510 e.printStackTrace();
511 }
512
513 }
514
515 @Override
516 public boolean onTouchEvent(MotionEvent event) {
517 if (event.getAction()==MotionEvent.ACTION_DOWN) {
518 onClick(Math.round(event.getX()),Math.round(event.getY()));
519 }
520 return super.onTouchEvent(event);
521 }
522
523 private void onClick(int x, int y) {
524 try {
525 int i=(y-(screenHeight-app.board.boardSize[0]*tileHeight)/2)/tileHeight;
526 int j=(x-(screenWidth-app.board.boardSize[1]*tileWidth)/2)/tileWidth;
527
528 switch (cstate) {
529 case IDLE:
530 if (i>=0 &&
531 i<app.board.boardSize[0] &&
532 j>=0 && j<app.board.boardSize[1] &&
533 app.board.board[i][j]!=0) {
534 selection1[0]=i;
535 selection1[1]=j;
536 paint(StatePaint.SELECTED1);
537 control(StatePlay.SELECTED1);
538 }
539 break;
540 case 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);
547 } else {
548 selection2[0]=i;
549 selection2[1]=j;
550 paint(StatePaint.SELECTED2);
551
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);
556 app.sleep(2);
557 paint(StatePaint.BOARD);
558 if (path.size()>0) {
559 app.board.play(a,b);
560 }
561 path=null;
562 paint(StatePaint.BOARD);
563
564 pairs=app.board.getPairs(1);
565 if (pairs.size()==0) {
566 if (app.board.getNumPieces()==0) {
567 paint(StatePaint.WIN);
568 } else {
569 paint(StatePaint.LOSE);
570 }
571 control(StatePlay.GAMEOVER);
572 } else {
573 control(StatePlay.IDLE);
574 }
575 //undo.sensitive=app.board.getCanUndo();
576 }
577 }
578 break;
579 case GAMEOVER:
580 reset();
581 paint(StatePaint.BOARD);
582 break;
583 }
584 } catch (Exception e) {
585 e.printStackTrace();
586 }
587 }
588
589 public void surfaceChanged(SurfaceHolder holder, int format, int width,
590 int height) {
591 surfaceHolder = holder;
592 if (cstate!=StatePlay.GAMEOVER && app.timeCounter && !timerRegistered) {
593 registerTimer();
594 }
595 repaint();
596 }
597
598 public void surfaceCreated(SurfaceHolder holder) {
599 surfaceHolder = holder;
600 repaint();
601 }
602
603 public void surfaceDestroyed(SurfaceHolder holder) {
604 surfaceHolder = null;
605 if (timerRegistered) {
606 unregisterTimer();
607 }
608 }
609
610 /*
611 @Override
612 protected void onDraw(Canvas canvas) {
613 super.onDraw(canvas);
614
615 if (!initialized) initialize();
616
617 long currTime = System.currentTimeMillis();
618
619 a = (float)(currTime - startTime) / (float)duration;
620 if (a > (float)1.0) a = (float)1.0;
621
622 x = Math.round(nextx*a + prevx*(1-a));
623 y = Math.round(nexty*a + prevy*(1-a));
624
625 if (a == (float)1.0) computeNextTarget();
626
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);
632 }
633 }
634
635 canvas.drawBitmap(tile[randomtile], x, y, paint);
636
637 repaint();
638 }
639
640 @Override
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());
646 }
647 return super.onTouchEvent(event);
648 }
649
650 private void initialize() {
651 width = getWidth();
652 height = getHeight();
653
654 bg = BitmapFactory.decodeResource(getResources(), R.drawable.kshisen_bgnd);
655 Bitmap tileset = BitmapFactory.decodeResource(getResources(), R.drawable.tileset);
656
657 // The tile set has 4 rows x 9 columns
658 tsrows = 4;
659 tscols = 9;
660 twidth = tileset.getWidth()/tscols;
661 theight = tileset.getHeight()/tsrows;
662 tile = new Bitmap[tsrows*tscols];
663 int k=0;
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);
667 k++;
668 }
669 }
670
671 x = width/2;
672 y = height/2;
673
674 computeNextTarget();
675
676 initialized = true;
677 }
678
679 private void computeNextTarget() {
680 startTime = System.currentTimeMillis();
681 prevx = x;
682 prevy = y;
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);
686
687 paint = new Paint();
688 paint.setColor(Color.parseColor("#006666"));
689 paint.setFlags(Paint.ANTI_ALIAS_FLAG);
690 }
691 */
692 }
Impressum, Datenschutz