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