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