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