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