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