9872c6644654d91fa868035e15c5d04dc7d7b913
[FreeShisen] / src / de / cwde / shisensho / ShisenShoView.java
1 package de.cwde.shisensho;
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 enum StatePlay { UNINITIALIZED, IDLE, SELECTED1, SELECTED2, GAMEOVER };
32 private enum StatePaint { BOARD, SELECTED1, SELECTED2, MATCHED, WIN, LOSE, HINT, TIME };
33
34 private int screenWidth;
35 private int screenHeight;
36 private int tilesetRows;
37 private int tilesetCols;
38 private int tileHeight;
39 private int tileWidth;
40 private Bitmap bg;
41 private Bitmap tile[];
42 private int[] selection1=new int[2];
43 private int[] selection2=new int[2];
44 private List<Point> path=null;
45 private List<Line> pairs=null;
46 private long startTime;
47 private long playTime;
48 private long baseTime;
49 private Timer timer;
50 private static Handler timerHandler;
51
52 private boolean timerRegistered=false;
53 private ShisenSho app;
54 private StatePlay cstate;
55 private StatePaint pstate;
56 private Canvas canvas = null;
57 private SurfaceHolder surfaceHolder = null;
58 public ShisenShoView(ShisenSho shishenSho) {
59 super((Context)shishenSho);
60 this.app = shishenSho;
61 cstate = StatePlay.UNINITIALIZED;
62 surfaceHolder = getHolder();
63 surfaceHolder.addCallback(this);
64 }
65
66 public ShisenShoView(Context ctx) {
67 super((Context)ctx);
68 // silence lint?
69 }
70
71 private void paint(StatePaint pstate) {
72 this.pstate=pstate;
73 repaint();
74 }
75
76 private void control(StatePlay cstate) {
77 this.cstate=cstate;
78 }
79
80 private void loadTileset() {
81 BitmapFactory.Options ops = new BitmapFactory.Options();
82 ops.inScaled = false;
83 Bitmap tileset = BitmapFactory.decodeResource(getResources(), R.drawable.tileset, ops);
84 tileset.setDensity(Bitmap.DENSITY_NONE);
85
86 // The tile set has 4 rows x 9 columns
87 tilesetRows = 4;
88 tilesetCols = 9;
89 tileWidth = tileset.getWidth()/tilesetCols;
90 tileHeight = tileset.getHeight()/tilesetRows;
91 tile = new Bitmap[tilesetRows*tilesetCols];
92
93 // align to screen
94 Matrix matrix = new Matrix();
95 matrix.setScale(1.0f, 1.0f); // FIXME!
96
97 // TODO: go on.
98
99 int k=0;
100 for (int i=0; i<tilesetRows; i++) {
101 for (int j=0; j<tilesetCols; j++) {
102 tile[k] = Bitmap.createBitmap(tileset, j*tileWidth, i*tileHeight, tileWidth, tileHeight, matrix, false);
103 tile[k].setDensity(Bitmap.DENSITY_NONE);
104 k++;
105 }
106 }
107 tileWidth = tile[0].getWidth();
108 tileHeight = tile[0].getHeight();
109 }
110
111 private void loadBackground() {
112 BitmapFactory.Options ops = new BitmapFactory.Options();
113 ops.inScaled = false;
114 bg = BitmapFactory.decodeResource(getResources(), R.drawable.kshisen_bgnd, ops);
115 bg.setDensity(Bitmap.DENSITY_NONE);
116 }
117
118 private void registerTimer() {
119 if (timer!=null) return; // Already registered
120 timerHandler = new Handler() {
121 public void handleMessage(Message msg) {
122 onUpdateTime();
123 }
124 };
125 timer=new Timer();
126 timer.scheduleAtFixedRate(new TimerTask() {
127 public void run() {
128 timerHandler.sendEmptyMessage(Activity.RESULT_OK);
129 }
130 }, 0, 1000);
131 timerRegistered=true;
132 }
133
134 private void unregisterTimer() {
135 if (timer==null) return; // Already unregistered
136 timer.cancel();
137 timer = null;
138 timerHandler = null;
139 timerRegistered=false;
140 }
141
142 public void pauseTime() {
143 updateTime();
144 baseTime = playTime;
145 startTime = System.currentTimeMillis();
146
147 }
148
149 public void resumeTime() {
150 startTime = System.currentTimeMillis();
151 updateTime();
152 }
153
154 private void updateTime() {
155 if (cstate!=StatePlay.GAMEOVER) {
156 playTime = (System.currentTimeMillis()-startTime)/1000+baseTime;
157 }
158 }
159
160 private void initializeGame() {
161 loadBackground();
162 screenWidth=getWidth();
163 screenHeight=getHeight();
164 loadTileset();
165 //undo.sensitive=false;
166 pstate=StatePaint.BOARD;
167 app.newPlay();
168 control(StatePlay.IDLE);
169 startTime=System.currentTimeMillis();
170 playTime=0;
171 baseTime=0;
172 if (app.timeCounter && !timerRegistered) {
173 registerTimer();
174 }
175 pairs=app.board.getPairs(1);
176 }
177
178 public boolean onOptionsItemSelected(MenuItem item) {
179 // Handle item selection
180 switch (item.getItemId()) {
181 case R.id.hint:
182 this.postDelayed(new Runnable() { public void run() { onHintActivate(); } }, 100);
183 return true;
184 case R.id.undo:
185 this.postDelayed(new Runnable() { public void run() { onUndoActivate(); } }, 100);
186 return true;
187 case R.id.clean:
188 this.postDelayed(new Runnable() { public void run() { reset(); } }, 100);
189 return true;
190 case R.id.options:
191 return true;
192 case R.id.about:
193 return true;
194 default:
195 return false;
196 }
197 }
198
199 public void reset() {
200 control(StatePlay.UNINITIALIZED);
201 paint(StatePaint.BOARD);
202 }
203
204 private void onHintActivate() {
205 if (cstate!=StatePlay.GAMEOVER) {
206 pairs=app.board.getPairs(1);
207 paint(StatePaint.HINT);
208 app.sleep(10);
209 paint(StatePaint.BOARD);
210 control(StatePlay.IDLE);
211 }
212 }
213
214 private void onUndoActivate() {
215 if (app.board.getCanUndo()) {
216 if (cstate==StatePlay.GAMEOVER && app.timeCounter && !timerRegistered) {
217 // Reprogram the time update that had been
218 // deactivated with the game over status
219 registerTimer();
220 }
221 app.board.undo();
222 paint(StatePaint.BOARD);
223 //undo.sensitive=app.board.getCanUndo();
224 control(StatePlay.IDLE);
225 }
226 }
227
228 public void onTimeCounterActivate() {
229 if (app.timeCounter && cstate!=StatePlay.GAMEOVER && !timerRegistered) {
230 // Reprogram the time update that had been
231 // deactivated with the time_counter=false
232 registerTimer();
233 }
234 }
235
236 private void onUpdateTime() {
237 paint(pstate);
238 if (!(app.timeCounter && cstate!=StatePlay.GAMEOVER)) {
239 unregisterTimer();
240 }
241 }
242
243 @SuppressWarnings("deprecation")
244 public void drawMessage(Canvas canvas, int x, int y, boolean centered, String message, String color, float textSize) {
245 Paint paint = new Paint();
246 paint.setColor(Color.parseColor(color));
247 paint.setLinearText(true);
248 paint.setAntiAlias(true);
249 paint.setTextAlign(centered?Align.CENTER:Align.LEFT);
250 paint.setTypeface(Typeface.SANS_SERIF);
251 paint.setFakeBoldText(true);
252 paint.setTextSize(textSize);
253 canvas.drawText(message, x, y, paint);
254 }
255
256 public void repaint() {
257 if (surfaceHolder == null) return;
258 try {
259 if (canvas == null) canvas = surfaceHolder.lockCanvas(null);
260 if (canvas == null) return;
261 if (cstate==StatePlay.UNINITIALIZED) initializeGame();
262 synchronized (surfaceHolder) {
263 doDraw(canvas);
264 }
265 } finally {
266 if (canvas != null) {
267 surfaceHolder.unlockCanvasAndPost(canvas);
268 canvas = null;
269 }
270 }
271 }
272
273 protected void doDraw(Canvas canvas) {
274 try {
275 // Double buffering
276 // Bitmap buffer = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
277 //Canvas cbuffer = new Canvas(buffer);
278 Canvas cbuffer = canvas;
279 if (canvas == null) return;
280
281 //super.onDraw(canvas);
282
283 // Board upper left corner on screen
284 int x0=0;
285 int y0=0;
286
287 if (app!=null && app.board!=null) {
288 x0=(screenWidth-app.board.boardSize[1]*tileWidth)/2;
289 y0=(screenHeight-app.board.boardSize[0]*tileHeight)/2;
290 }
291
292 int red = Color.parseColor("#FF0000");
293 int orange = Color.parseColor("#F0C000");
294 Paint paint = new Paint();
295 paint.setFlags(Paint.ANTI_ALIAS_FLAG);
296
297 // Background & board painting
298 switch (pstate) {
299 case BOARD:
300 case SELECTED1:
301 case SELECTED2:
302 case MATCHED:
303 case WIN:
304 case LOSE:
305 case HINT:
306 case TIME:
307 // Background painting
308 int bgWidth = bg.getWidth();
309 int bgHeight = bg.getHeight();
310 for (int i=0; i<screenHeight/bgHeight+1; i++) {
311 for (int j=0; j<screenWidth/bgWidth+1; j++) {
312 cbuffer.drawBitmap(bg, j*bgWidth, i*bgHeight, paint);
313 }
314 }
315
316 // Board painting
317 // Max visible size: 7x17
318 if (app!=null && app.board!=null) {
319 for (int i=0;i<app.board.boardSize[0];i++) {
320 for (int j=0;j<app.board.boardSize[1];j++) {
321 // Tiles are 56px height, 40px width each
322 char piece=app.board.board[i][j];
323 if (piece!=0) {
324 cbuffer.drawBitmap(tile[piece], x0+j*tileWidth, y0+i*tileHeight, paint);
325 }
326 }
327 }
328 }
329 break;
330 }
331
332 // Red rectangle for selection 1
333 switch (pstate) {
334 case SELECTED1:
335 case SELECTED2:
336 case MATCHED:
337 paint.setColor(red);
338 paint.setStyle(Style.STROKE);
339 paint.setStrokeCap(Cap.ROUND);
340 paint.setStrokeJoin(Join.ROUND);
341 paint.setStrokeWidth(3);
342 cbuffer.drawRect(new Rect(
343 x0+selection1[1]*tileWidth-2,
344 y0+selection1[0]*tileHeight-2,
345 x0+selection1[1]*tileWidth-2+tileWidth+2*2,
346 y0+selection1[0]*tileHeight-2+tileHeight+2*2),
347 paint);
348 break;
349 }
350
351 // Red rectangle for selection 2
352 switch (pstate) {
353 case SELECTED2:
354 case MATCHED:
355 paint.setColor(red);
356 paint.setStyle(Style.STROKE);
357 paint.setStrokeCap(Cap.ROUND);
358 paint.setStrokeJoin(Join.ROUND);
359 paint.setStrokeWidth(3);
360 cbuffer.drawRect(new Rect(
361 x0+selection2[1]*tileWidth-2,
362 y0+selection2[0]*tileHeight-2,
363 x0+selection2[1]*tileWidth-2+tileWidth+2*2,
364 y0+selection2[0]*tileHeight-2+tileHeight+2*2),
365 paint);
366 break;
367 }
368
369 // Matching path
370 switch (pstate) {
371 case MATCHED:
372 paint.setColor(red);
373 paint.setStyle(Style.STROKE);
374 paint.setStrokeCap(Cap.ROUND);
375 paint.setStrokeJoin(Join.ROUND);
376 paint.setStrokeWidth(3);
377
378 if (path!=null) {
379 Point p0=null;
380 for (Point p1 : path) {
381 if (p0!=null) {
382 cbuffer.drawLine(
383 x0+p0.j*tileWidth-2+(tileWidth/2),
384 y0+p0.i*tileHeight-2+(tileHeight/2),
385 x0+p1.j*tileWidth-2+(tileWidth/2),
386 y0+p1.i*tileHeight-2+(tileHeight/2),
387 paint);
388 }
389 p0=p1;
390 }
391 }
392 break;
393 }
394
395 // Orange hint rectangles
396 switch (pstate) {
397 case HINT:
398 if (pairs!=null && pairs.size()>0) {
399 Line pair=pairs.get(0);
400 Point a=pair.a;
401 Point b=pair.b;
402 path=app.board.getPath(a,b);
403 paint.setColor(orange);
404 paint.setStyle(Style.STROKE);
405 paint.setStrokeCap(Cap.ROUND);
406 paint.setStrokeJoin(Join.ROUND);
407 paint.setStrokeWidth(3);
408
409 cbuffer.drawRect(new Rect(
410 x0+a.j*tileWidth-2,
411 y0+a.i*tileHeight-2,
412 x0+a.j*tileWidth-2+tileWidth+2*2,
413 y0+a.i*tileHeight-2+tileHeight+2*2),
414 paint);
415
416 if (path!=null) {
417 Point p0=null;
418 for (Point p1 : path) {
419 if (p0!=null) {
420 cbuffer.drawLine(
421 x0+p0.j*tileWidth-2+(tileWidth/2),
422 y0+p0.i*tileHeight-2+(tileHeight/2),
423 x0+p1.j*tileWidth-2+(tileWidth/2),
424 y0+p1.i*tileHeight-2+(tileHeight/2),
425 paint);
426 }
427 p0=p1;
428 }
429 path=null;
430 }
431
432 cbuffer.drawRect(new Rect(
433 x0+b.j*tileWidth-2,
434 y0+b.i*tileHeight-2,
435 x0+b.j*tileWidth-2+tileWidth+2*2,
436 y0+b.i*tileHeight-2+tileHeight+2*2),
437 paint);
438 }
439 break;
440 }
441
442 // Win & loose notifications
443 switch (pstate) {
444 case WIN:
445 drawMessage(cbuffer, screenWidth/2,screenHeight/2,true,"You Win!", "#FFFFFF", 100);
446 break;
447 case LOSE:
448 drawMessage(cbuffer, screenWidth/2,screenHeight/2,true,"Game Over", "#FFFFFF", 100);
449 break;
450 }
451
452 if (app.timeCounter) switch (pstate) {
453 case BOARD:
454 case SELECTED1:
455 case SELECTED2:
456 case MATCHED:
457 case WIN:
458 case LOSE:
459 case HINT:
460 case TIME:
461 updateTime();
462 int hours=(int)(playTime/(60*60));
463 int minutes=(int)((playTime/60)%60);
464 int seconds=(int)(playTime%60);
465 String time=String.format(Locale.US, "%01d:%02d:%02d", hours, minutes, seconds);
466
467 int timePosX=screenWidth-120;
468 int timePosY=screenHeight-10;
469
470 drawMessage(cbuffer, timePosX+1,timePosY+1,false,time,"#000000",30);
471 drawMessage(cbuffer, timePosX,timePosY,false,time,"#FFFFFF",30);
472 break;
473 }
474
475 // Debug messages
476 /*
477 debugMessage="StatePlay: "+cstate+"\n"+"StatePaint: "+pstate;
478 if (debugMessage!=null && debugMessage.length()>0) {
479 int l = 20;
480 String lines[] = debugMessage.split("\n");
481 for (int i=0; i<lines.length; i++) {
482 drawMessage(cbuffer,1,l,false,lines[i],"#FFFF00",30);
483 l+=30;
484 }
485 }
486 */
487
488 // Double buffer dumping
489 // canvas.drawBitmap(buffer, 0, 0, null);
490
491 } catch (Exception e) {
492 e.printStackTrace();
493 }
494
495 }
496
497 @Override
498 public boolean onTouchEvent(MotionEvent event) {
499 if (event.getAction()==MotionEvent.ACTION_DOWN) {
500 onClick(Math.round(event.getX()),Math.round(event.getY()));
501 }
502 return super.onTouchEvent(event);
503 }
504
505 private void onClick(int x, int y) {
506 try {
507 int i=(y-(screenHeight-app.board.boardSize[0]*tileHeight)/2)/tileHeight;
508 int j=(x-(screenWidth-app.board.boardSize[1]*tileWidth)/2)/tileWidth;
509
510 switch (cstate) {
511 case IDLE:
512 if (i>=0 &&
513 i<app.board.boardSize[0] &&
514 j>=0 && j<app.board.boardSize[1] &&
515 app.board.board[i][j]!=0) {
516 selection1[0]=i;
517 selection1[1]=j;
518 paint(StatePaint.SELECTED1);
519 control(StatePlay.SELECTED1);
520 }
521 break;
522 case SELECTED1:
523 if (i>=0 && i<app.board.boardSize[0] &&
524 j>=0 && j<app.board.boardSize[1] &&
525 app.board.board[i][j]!=0) {
526 if (i==selection1[0] && j==selection1[1]) {
527 paint(StatePaint.BOARD);
528 control(StatePlay.IDLE);
529 } else {
530 selection2[0]=i;
531 selection2[1]=j;
532 paint(StatePaint.SELECTED2);
533
534 Point a=new Point(selection1[0],selection1[1]);
535 Point b=new Point(selection2[0],selection2[1]);
536 path=app.board.getPath(a,b);
537 paint(StatePaint.MATCHED);
538 app.sleep(2);
539 paint(StatePaint.BOARD);
540 if (path.size()>0) {
541 app.board.play(a,b);
542 }
543 path=null;
544 paint(StatePaint.BOARD);
545
546 pairs=app.board.getPairs(1);
547 if (pairs.size()==0) {
548 if (app.board.getNumPieces()==0) {
549 paint(StatePaint.WIN);
550 } else {
551 paint(StatePaint.LOSE);
552 }
553 control(StatePlay.GAMEOVER);
554 } else {
555 control(StatePlay.IDLE);
556 }
557 //undo.sensitive=app.board.getCanUndo();
558 }
559 }
560 break;
561 case GAMEOVER:
562 reset();
563 paint(StatePaint.BOARD);
564 break;
565 }
566 } catch (Exception e) {
567 e.printStackTrace();
568 }
569 }
570
571 public void surfaceChanged(SurfaceHolder holder, int format, int width,
572 int height) {
573 surfaceHolder = holder;
574 if (cstate!=StatePlay.GAMEOVER && app.timeCounter && !timerRegistered) {
575 registerTimer();
576 }
577 repaint();
578 }
579
580 public void surfaceCreated(SurfaceHolder holder) {
581 surfaceHolder = holder;
582 repaint();
583 }
584
585 public void surfaceDestroyed(SurfaceHolder holder) {
586 surfaceHolder = null;
587 if (timerRegistered) {
588 unregisterTimer();
589 }
590 }
591
592 /*
593 @Override
594 protected void onDraw(Canvas canvas) {
595 super.onDraw(canvas);
596
597 if (!initialized) initialize();
598
599 long currTime = System.currentTimeMillis();
600
601 a = (float)(currTime - startTime) / (float)duration;
602 if (a > (float)1.0) a = (float)1.0;
603
604 x = Math.round(nextx*a + prevx*(1-a));
605 y = Math.round(nexty*a + prevy*(1-a));
606
607 if (a == (float)1.0) computeNextTarget();
608
609 int bgWidth = bg.getWidth();
610 int bgHeight = bg.getHeight();
611 for (int i=0; i<height/bgHeight+1; i++) {
612 for (int j=0; j<width/bgWidth+1; j++) {
613 canvas.drawBitmap(bg, j*bgWidth, i*bgHeight, paint);
614 }
615 }
616
617 canvas.drawBitmap(tile[randomtile], x, y, paint);
618
619 repaint();
620 }
621
622 @Override
623 public boolean onTouchEvent(MotionEvent event) {
624 if (event.getActionMasked()==MotionEvent.ACTION_DOWN) {
625 //computeNextTarget();
626 //nextx=Math.round(event.getX());
627 //nexty=Math.round(event.getY());
628 }
629 return super.onTouchEvent(event);
630 }
631
632 private void initialize() {
633 width = getWidth();
634 height = getHeight();
635
636 bg = BitmapFactory.decodeResource(getResources(), R.drawable.kshisen_bgnd);
637 Bitmap tileset = BitmapFactory.decodeResource(getResources(), R.drawable.tileset);
638
639 // The tile set has 4 rows x 9 columns
640 tsrows = 4;
641 tscols = 9;
642 twidth = tileset.getWidth()/tscols;
643 theight = tileset.getHeight()/tsrows;
644 tile = new Bitmap[tsrows*tscols];
645 int k=0;
646 for (int i=0; i<tsrows; i++) {
647 for (int j=0; j<tscols; j++) {
648 tile[k] = Bitmap.createBitmap(tileset, j*twidth, i*theight, twidth, theight, null, false);
649 k++;
650 }
651 }
652
653 x = width/2;
654 y = height/2;
655
656 computeNextTarget();
657
658 initialized = true;
659 }
660
661 private void computeNextTarget() {
662 startTime = System.currentTimeMillis();
663 prevx = x;
664 prevy = y;
665 nextx = (int) Math.floor(Math.random() * width);
666 nexty = (int) Math.floor(Math.random() * height);
667 randomtile = (int) Math.floor(Math.random() * tile.length);
668
669 paint = new Paint();
670 paint.setColor(Color.parseColor("#006666"));
671 paint.setFlags(Paint.ANTI_ALIAS_FLAG);
672 }
673 */
674 }
Impressum, Datenschutz