]> cvs.zerfleddert.de Git - FreeShisen/blob - src/de/cwde/freeshisen/ShisenShoView.java
use andriod defaults for "ok" and "cancel"
[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 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;
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
64 public ShisenShoView(ShisenSho shishenSho) {
65 super((Context) shishenSho);
66 this.app = shishenSho;
67 cstate = StatePlay.UNINITIALIZED;
68 surfaceHolder = getHolder();
69 surfaceHolder.addCallback(this);
70 }
71
72 public ShisenShoView(Context ctx) {
73 super((Context) ctx);
74 // silence lint?
75 }
76
77 private void paint(StatePaint pstate) {
78 this.pstate=pstate;
79 repaint();
80 }
81
82 private void control(StatePlay cstate) {
83 this.cstate=cstate;
84 }
85
86 private void loadTileset() {
87 BitmapFactory.Options ops = new BitmapFactory.Options();
88 ops.inScaled = false;
89 Bitmap tileset = BitmapFactory.decodeResource(getResources(), R.drawable.tileset, ops);
90 tileset.setDensity(Bitmap.DENSITY_NONE);
91
92 // The tile set has 4 rows x 9 columns
93 tilesetRows = 4;
94 tilesetCols = 9;
95 int loadedtileWidth = tileset.getWidth()/tilesetCols;
96 int loadedtileHeight = tileset.getHeight()/tilesetRows;
97 tile = new Bitmap[tilesetRows*tilesetCols];
98
99 // align to screen:
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) {
105 scalex = scaley;
106 } else {
107 scaley = scalex;
108 }
109 Matrix matrix = new Matrix();
110 matrix.setScale(scalex, scaley);
111
112 int k=0;
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);
118 k++;
119 }
120 }
121 tileWidth = tile[0].getWidth();
122 tileHeight = tile[0].getHeight();
123 }
124
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);
130 }
131
132 private void registerTimer() {
133 if (timer!=null) return; // Already registered
134 timerHandler = new Handler() {
135 public void handleMessage(Message msg) {
136 onUpdateTime();
137 }
138 };
139 timer=new Timer();
140 timer.scheduleAtFixedRate(new TimerTask() {
141 public void run() {
142 timerHandler.sendEmptyMessage(Activity.RESULT_OK);
143 }
144 }, 0, 1000);
145 timerRegistered=true;
146 }
147
148 private void unregisterTimer() {
149 if (timer==null) return; // Already unregistered
150 timer.cancel();
151 timer = null;
152 timerHandler = null;
153 timerRegistered=false;
154 }
155
156 public void pauseTime() {
157 updateTime();
158 baseTime = playTime;
159 startTime = System.currentTimeMillis();
160
161 }
162
163 public void resumeTime() {
164 startTime = System.currentTimeMillis();
165 updateTime();
166 }
167
168 private void updateTime() {
169 if (cstate!=StatePlay.GAMEOVER) {
170 playTime = (System.currentTimeMillis()-startTime)/1000+baseTime;
171 }
172 }
173
174 private void initializeGame() {
175 loadBackground();
176 screenWidth=getWidth();
177 screenHeight=getHeight();
178 loadTileset();
179 //undo.sensitive=false;
180 pstate=StatePaint.BOARD;
181 app.newPlay();
182 control(StatePlay.IDLE);
183 startTime=System.currentTimeMillis();
184 playTime=0;
185 baseTime=0;
186 if (app.timeCounter && !timerRegistered) {
187 registerTimer();
188 }
189 pairs=app.board.getPairs(1);
190 }
191
192 public boolean onOptionsItemSelected(MenuItem item) {
193 // Handle item selection
194 switch (item.getItemId()) {
195 case R.id.hint:
196 this.postDelayed(new Runnable() { public void run() { onHintActivate(); } }, 100);
197 return true;
198 case R.id.undo:
199 this.postDelayed(new Runnable() { public void run() { onUndoActivate(); } }, 100);
200 return true;
201 case R.id.clean:
202 this.postDelayed(new Runnable() { public void run() { reset(); } }, 100);
203 return true;
204 case R.id.options:
205 return true;
206 case R.id.about:
207 return true;
208 default:
209 return false;
210 }
211 }
212
213 public void reset() {
214 control(StatePlay.UNINITIALIZED);
215 paint(StatePaint.BOARD);
216 }
217
218 private void onHintActivate() {
219 if (cstate!=StatePlay.GAMEOVER) {
220 pairs=app.board.getPairs(1);
221 paint(StatePaint.HINT);
222 app.sleep(10);
223 paint(StatePaint.BOARD);
224 control(StatePlay.IDLE);
225 }
226 }
227
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
233 registerTimer();
234 }
235 app.board.undo();
236 paint(StatePaint.BOARD);
237 //undo.sensitive=app.board.getCanUndo();
238 control(StatePlay.IDLE);
239 }
240 }
241
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
246 registerTimer();
247 }
248 }
249
250 private void onUpdateTime() {
251 paint(pstate);
252 if (!(app.timeCounter && cstate!=StatePlay.GAMEOVER)) {
253 unregisterTimer();
254 }
255 }
256
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);
271 }
272
273 public void repaint() {
274 if (surfaceHolder == null) return;
275 try {
276 if (canvas == null) canvas = surfaceHolder.lockCanvas(null);
277 if (canvas == null) return;
278 if (cstate==StatePlay.UNINITIALIZED) initializeGame();
279 synchronized (surfaceHolder) {
280 doDraw(canvas);
281 }
282 } finally {
283 if (canvas != null) {
284 surfaceHolder.unlockCanvasAndPost(canvas);
285 canvas = null;
286 }
287 }
288 }
289
290 protected void doDraw(Canvas canvas) {
291 try {
292 // Double buffering
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;
297
298 //super.onDraw(canvas);
299
300 // Board upper left corner on screen
301 int x0=0;
302 int y0=0;
303
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;
307 }
308
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);
313
314 // Background & board painting
315 switch (pstate) {
316 case BOARD:
317 case SELECTED1:
318 case SELECTED2:
319 case MATCHED:
320 case WIN:
321 case LOSE:
322 case HINT:
323 case TIME:
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);
330 }
331 }
332
333 // Board painting
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];
340 if (piece!=0) {
341 cbuffer.drawBitmap(tile[piece], x0+j*tileWidth, y0+i*tileHeight, paint);
342 }
343 }
344 }
345 }
346 break;
347 }
348
349 // Red rectangle for selection 1
350 switch (pstate) {
351 case SELECTED1:
352 case SELECTED2:
353 case MATCHED:
354 highlightTile(cbuffer, x0, y0, selection1, selectcolor);
355 break;
356 }
357
358 // Red rectangle for selection 2
359 switch (pstate) {
360 case SELECTED2:
361 case MATCHED:
362 highlightTile(cbuffer, x0, y0, selection2, selectcolor);
363 break;
364 }
365
366 // Matching path
367 switch (pstate) {
368 case MATCHED:
369 paint.setColor(selectcolor);
370 paint.setStyle(Style.STROKE);
371 paint.setStrokeCap(Cap.ROUND);
372 paint.setStrokeJoin(Join.ROUND);
373 paint.setStrokeWidth(3);
374
375 if (path!=null) {
376 Point p0=null;
377 for (Point p1 : path) {
378 if (p0!=null) {
379 drawLine(cbuffer, x0, y0, p0, p1, 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(hintcolor);
396 paint.setStyle(Style.STROKE);
397 paint.setStrokeCap(Cap.ROUND);
398 paint.setStrokeJoin(Join.ROUND);
399 paint.setStrokeWidth(3);
400
401 highlightTile(cbuffer, x0, y0, a, hintcolor);
402
403 if (path != null) {
404 Point p0 = null;
405 for (Point p1 : path) {
406 if (p0 != null) {
407 drawLine(cbuffer, x0, y0, p0, p1, paint);
408 }
409 p0 = p1;
410 }
411 path = null;
412 }
413
414 highlightTile(cbuffer, x0, y0, b, hintcolor);
415 }
416 break;
417 }
418
419 // Win & loose notifications
420 switch (pstate) {
421 case WIN:
422 drawMessage(cbuffer, screenWidth / 2, screenHeight / 2, true,
423 "You Win!", 100);
424 break;
425 case LOSE:
426 drawMessage(cbuffer, screenWidth / 2, screenHeight / 2, true,
427 "Game Over", 100);
428 break;
429 }
430
431 if (app.timeCounter) switch (pstate) {
432 case BOARD:
433 case SELECTED1:
434 case SELECTED2:
435 case MATCHED:
436 case WIN:
437 case LOSE:
438 case HINT:
439 case TIME:
440 updateTime();
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);
446
447 int timePosX=screenWidth-120;
448 int timePosY=screenHeight-10;
449
450 drawMessage(cbuffer, timePosX, timePosY, false, time, 30);
451 break;
452 }
453
454 // Debug messages
455 /*
456 debugMessage="StatePlay: "+cstate+"\n"+"StatePaint: "+pstate;
457 if (debugMessage!=null && debugMessage.length()>0) {
458 int l = 20;
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);
462 l+=30;
463 }
464 }
465 */
466
467 // Double buffer dumping
468 // canvas.drawBitmap(buffer, 0, 0, null);
469
470 } catch (Exception e) {
471 e.printStackTrace();
472 }
473
474 }
475
476 private void drawLine(Canvas cbuffer, int x0, int y0, Point p0, Point p1,
477 Paint paint) {
478 cbuffer.drawLine(
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);
483 }
484
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);
493 Rect r = new Rect(
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);
499 }
500
501 @Override
502 public boolean onTouchEvent(MotionEvent event) {
503 if (event.getAction()==MotionEvent.ACTION_DOWN) {
504 onClick(Math.round(event.getX()),Math.round(event.getY()));
505 }
506 return super.onTouchEvent(event);
507 }
508
509 private void onClick(int x, int y) {
510 try {
511 int i=(y-(screenHeight-app.board.boardSize[0]*tileHeight)/2)/tileHeight;
512 int j=(x-(screenWidth-app.board.boardSize[1]*tileWidth)/2)/tileWidth;
513
514 switch (cstate) {
515 case IDLE:
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);
522 }
523 break;
524 case 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);
531 } else {
532 selection2.set(i, j);
533 paint(StatePaint.SELECTED2);
534
535 Point a = selection1.copy();
536 Point b = selection2.copy();
537 path = app.board.getPath(a, b);
538 paint(StatePaint.MATCHED);
539 app.sleep(2);
540 paint(StatePaint.BOARD);
541 if (path.size() > 0) {
542 app.board.play(a, b);
543 }
544 path = null;
545 paint(StatePaint.BOARD);
546
547 pairs = app.board.getPairs(1);
548 if (pairs.size() == 0) {
549 if (app.board.getNumPieces() == 0) {
550 paint(StatePaint.WIN);
551 } else {
552 paint(StatePaint.LOSE);
553 }
554 control(StatePlay.GAMEOVER);
555 } else {
556 control(StatePlay.IDLE);
557 }
558 //undo.sensitive=app.board.getCanUndo();
559 }
560 }
561 break;
562 case GAMEOVER:
563 reset();
564 paint(StatePaint.BOARD);
565 break;
566 }
567 } catch (Exception e) {
568 e.printStackTrace();
569 }
570 }
571
572 public void surfaceChanged(SurfaceHolder holder, int format, int width,
573 int height) {
574 surfaceHolder = holder;
575 if (cstate!=StatePlay.GAMEOVER && app.timeCounter && !timerRegistered) {
576 registerTimer();
577 }
578 repaint();
579 }
580
581 public void surfaceCreated(SurfaceHolder holder) {
582 surfaceHolder = holder;
583 repaint();
584 }
585
586 public void surfaceDestroyed(SurfaceHolder holder) {
587 surfaceHolder = null;
588 if (timerRegistered) {
589 unregisterTimer();
590 }
591 }
592 }
Impressum, Datenschutz