source: advance-wars/src/com/medievaltech/advancewars/GameView.java@ 379005b

Last change on this file since 379005b was 379005b, checked in by dportnoy <devnull@…>, 14 years ago

Moved all the enums to an Enum.java file.

  • Property mode set to 100644
File size: 20.0 KB
Line 
1package com.medievaltech.advancewars;
2
3import java.io.*;
4
5import com.medievaltech.advancewars.Enum.*;
6import com.medievaltech.unit.*;
7import com.medievaltech.gui.*;
8
9import android.content.Context;
10import android.graphics.*;
11import android.os.*;
12import android.view.*;
13import android.util.AttributeSet;
14import android.util.Log;
15import android.widget.TextView;
16
17class GameView extends SurfaceView implements SurfaceHolder.Callback {
18
19 class DrawingThread extends Thread {
20 public AppState mAppState;
21 public GameState mGameState;
22
23 /*
24 * Member (state) fields
25 */
26
27 private int mCanvasHeight = 1;
28 private int mCanvasWidth = 1;
29
30 /** Message handler used by thread to interact with TextView */
31 private Handler mHandler;
32
33 /** Used to figure out elapsed time between frames */
34 private long mLastTime;
35
36 /** Paint to draw the lines on screen. */
37 private Paint mLinePaint, mTextPaint, mButtonPaint, mTilePaint1, mTilePaint2, mSelectionPaint,
38 mUnitPaint;
39
40 //maybe make this private and make an accessor for it
41 public Map mMap;
42
43 public Tile grassTile, oceanTile;
44
45 /** Indicate whether the surface has been created & is ready to draw */
46 private boolean mRun = false;
47
48 /** Handle to the surface manager object we interact with */
49 private SurfaceHolder mSurfaceHolder;
50
51 private com.medievaltech.gui.Window wndMainMenu;
52 private Unit selectedUnit;
53
54 public DrawingThread(SurfaceHolder surfaceHolder, Context context, Handler handler) {
55 // get handles to some important objects
56 mSurfaceHolder = surfaceHolder;
57 mHandler = handler;
58
59 mLinePaint = new Paint();
60 mLinePaint.setAntiAlias(true);
61 mLinePaint.setARGB(255, 0, 255, 0);
62
63 mTextPaint = new Paint();
64 mTextPaint.setAntiAlias(true);
65 mTextPaint.setARGB(255, 255, 255, 255);
66 mTextPaint.setTextSize(12);
67 mTextPaint.setTextAlign(Paint.Align.CENTER);
68
69 mButtonPaint = new Paint();
70 mButtonPaint.setAntiAlias(true);
71 mButtonPaint.setARGB(255, 0, 0, 0);
72 mButtonPaint.setTextSize(20);
73 mButtonPaint.setTextAlign(Paint.Align.CENTER);
74
75 mTilePaint1 = new Paint();
76 mTilePaint1.setAntiAlias(true);
77 mTilePaint1.setARGB(255, 0, 255, 0);
78
79 mTilePaint2 = new Paint();
80 mTilePaint2.setAntiAlias(true);
81 mTilePaint2.setARGB(255, 0, 0, 255);
82
83 mUnitPaint = new Paint();
84 mUnitPaint.setAntiAlias(true);
85 mUnitPaint.setARGB(255, 255, 0, 0);
86
87 mSelectionPaint = new Paint();
88 mSelectionPaint.setAntiAlias(true);
89 mSelectionPaint.setARGB(255, 255, 127, 0);
90
91 wndMainMenu = new com.medievaltech.gui.Window(0, 0, 320, 450);;
92 wndMainMenu.addGUIObject("txtTitle", new Text("Main Menu", 100, 30, 120, 20, mTextPaint));
93 wndMainMenu.addGUIObject("btnNewGame", new Button("New Game", 100, 90, 120, 20, mLinePaint, mButtonPaint));
94 wndMainMenu.addGUIObject("btnLoadGame", new Button("Load Game", 100, 125, 120, 20, mLinePaint, mButtonPaint));
95 wndMainMenu.addGUIObject("btnMapEditor", new Button("Map Editor", 100, 160, 120, 20, mLinePaint, mButtonPaint));
96 wndMainMenu.addGUIObject("btnQuit", new Button("Quit", 100, 195, 120, 20, mLinePaint, mButtonPaint));
97
98 grassTile = new Tile(mTilePaint1, TerrainType.LAND);
99 oceanTile = new Tile(mTilePaint2, TerrainType.SEA);
100
101 mMap = new Map(grassTile, 6, 8, new Point(10, 25));
102
103 boolean land = true;
104
105 for(int x=0; x<mMap.getWidth(); x++) {
106 for(int y=0; y<mMap.getHeight(); y++) {
107 if(land)
108 mMap.setTile(x, y, new Tile(grassTile, new Point(x, y)));
109 else
110 mMap.setTile(x, y, new Tile(oceanTile, new Point(x, y)));
111 land = !land;
112 }
113 land = !land;
114 }
115
116 mMap.getTile(2, 3).addUnit(new Soldier(mUnitPaint));
117 mMap.getTile(5, 6).addUnit(new Soldier(mUnitPaint));
118
119 mGameState = GameState.MAIN_MENU;
120 }
121
122 /**
123 * Starts the game, setting parameters for the current difficulty.
124 */
125 // I don't think this gets called now. maybe we should call it in the thread constructor
126 public void doStart() {
127 synchronized (mSurfaceHolder) {
128 mLastTime = System.currentTimeMillis() + 100;
129 setState(AppState.RUNNING);
130 Log.i("AdvanceWars", "Player's turn starting now");
131 mGameState = GameState.MAIN_MENU;
132 }
133 }
134
135 /**
136 * Pauses the physics update & animation.
137 */
138 public void pause() {
139 synchronized (mSurfaceHolder) {
140 if (mAppState == AppState.RUNNING) setState(AppState.PAUSE);
141 }
142 }
143
144 @Override
145 public void run() {
146 while (mRun) {
147 Canvas c = null;
148 try {
149 c = mSurfaceHolder.lockCanvas(null);
150 synchronized(mSurfaceHolder) {
151 if(mAppState == AppState.RUNNING)
152 updatePhysics();
153 doDraw(c);
154 }
155 } finally {
156 // do this in a finally so that if an exception is thrown
157 // during the above, we don't leave the Surface in an
158 // inconsistent state
159 if (c != null) {
160 mSurfaceHolder.unlockCanvasAndPost(c);
161 }
162 }
163 }
164 }
165
166 /**
167 * Used to signal the thread whether it should be running or not.
168 * Passing true allows the thread to run; passing false will shut it
169 * down if it's already running. Calling start() after this was most
170 * recently called with false will result in an immediate shutdown.
171 *
172 * @param b true to run, false to shut down
173 */
174 public void setRunning(boolean b) {
175 mRun = b;
176 }
177
178 /**
179 * Sets the game mode. That is, whether we are running, paused, in the
180 * failure state, in the victory state, etc.
181 *
182 * @see #setState(int, CharSequence)
183 * @param mode one of the STATE_* constants
184 */
185 public void setState(AppState state) {
186 synchronized (mSurfaceHolder) {
187 setState(state, null);
188 }
189 }
190
191 public void setGameState(GameState state) {
192 synchronized (mSurfaceHolder) {
193 mGameState = state;
194 }
195 }
196
197 /**
198 * Sets the game mode. That is, whether we are running, paused, in the
199 * failure state, in the victory state, etc.
200 *
201 * @param mode one of the STATE_* constants
202 * @param message string to add to screen or null
203 */
204 public void setState(AppState mode, CharSequence message) {
205 /*
206 * This method optionally can cause a text message to be displayed
207 * to the user when the mode changes. Since the View that actually
208 * renders that text is part of the main View hierarchy and not
209 * owned by this thread, we can't touch the state of that View.
210 * Instead we use a Message + Handler to relay commands to the main
211 * thread, which updates the user-text View.
212 */
213 synchronized (mSurfaceHolder) {
214 mAppState = mode;
215
216 if (mAppState == AppState.RUNNING) {
217 Message msg = mHandler.obtainMessage();
218 Bundle b = new Bundle();
219 b.putString("text", "");
220 b.putInt("viz", GameView.INVISIBLE);
221 msg.setData(b);
222 mHandler.sendMessage(msg);
223 } else {
224 CharSequence str = "";
225 str = "Mode probably changed";
226
227 if (message != null) {
228 str = message + "\n" + str;
229 }
230
231 Message msg = mHandler.obtainMessage();
232 Bundle b = new Bundle();
233 b.putString("text", str.toString());
234 b.putInt("viz", GameView.VISIBLE);
235 msg.setData(b);
236 //mHandler.sendMessage(msg);
237 }
238 }
239 }
240
241 /* Callback invoked when the surface dimensions change. */
242 public void setSurfaceSize(int width, int height) {
243 // synchronized to make sure these all change atomically
244 synchronized (mSurfaceHolder) {
245 mCanvasWidth = width;
246 mCanvasHeight = height;
247
248 Log.i("AdvanceWars", "width: "+mCanvasWidth+", height: "+mCanvasHeight);
249 }
250 }
251
252 /**
253 * Resumes from a pause.
254 */
255 public void unpause() {
256 // Move the real time clock up to now
257 synchronized (mSurfaceHolder) {
258 mLastTime = System.currentTimeMillis() + 100;
259 }
260 setState(AppState.RUNNING);
261 }
262
263 /**
264 * Handles a key-down event.
265 *
266 * @param keyCode the key that was pressed
267 * @param msg the original event object
268 * @return true
269 */
270 boolean doKeyDown(int keyCode, KeyEvent msg) {
271 synchronized (mSurfaceHolder) {
272 boolean okStart = false;
273 if (keyCode == KeyEvent.KEYCODE_DPAD_UP) okStart = true;
274 if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) okStart = true;
275 if (keyCode == KeyEvent.KEYCODE_S) okStart = true;
276
277 if (okStart
278 && (mAppState == AppState.READY || mAppState == AppState.LOSE || mAppState == AppState.WIN)) {
279 // ready-to-start -> start
280 doStart();
281 return true;
282 } else if (mAppState == AppState.PAUSE && okStart) {
283 // paused -> running
284 unpause();
285 return true;
286 } else if (mAppState == AppState.RUNNING) {
287 return true;
288 }
289
290 return false;
291 }
292 }
293
294 /**
295 * Handles a key-up event.
296 *
297 * @param keyCode the key that was pressed
298 * @param msg the original event object
299 * @return true if the key was handled and consumed, or else false
300 */
301 boolean doKeyUp(int keyCode, KeyEvent msg) {
302 boolean handled = false;
303
304 synchronized (mSurfaceHolder) {
305 if (mAppState == AppState.RUNNING) {
306 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
307 || keyCode == KeyEvent.KEYCODE_SPACE) {
308 handled = true;
309 } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
310 || keyCode == KeyEvent.KEYCODE_Q
311 || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
312 || keyCode == KeyEvent.KEYCODE_W) {
313 handled = true;
314 }
315 }
316 }
317
318 return handled;
319 }
320
321 /**
322 * Draws the ship, fuel/speed bars, and background to the provided
323 * Canvas.
324 */
325 private void doDraw(Canvas canvas) {
326 canvas.drawColor(Color.BLACK);
327
328 switch(mGameState) {
329 case MAIN_MENU:
330 wndMainMenu.draw(canvas);
331 break;
332 case BATTLE_MAP:
333 mTextPaint.setTextSize(12);
334
335 mMap.draw(canvas);
336
337 if(selectedUnit != null) {
338 for(Point p : selectedUnit.getMovementRange()) {
339 canvas.drawRect(p.x*50+10, p.y*50+25, p.x*50+50+10, p.y*50+50+25, mSelectionPaint);
340 }
341 }
342
343 mMap.drawUnits(canvas);
344
345 break;
346 }
347 }
348
349 /**
350 * Figures the lander state (x, y, fuel, ...) based on the passage of
351 * realtime. Does not invalidate(). Called at the start of draw().
352 * Detects the end-of-game and sets the UI to the next state.
353 */
354 private void updatePhysics() {
355 long now = System.currentTimeMillis();
356
357 // Do nothing if mLastTime is in the future.
358 // This allows the game-start to delay the start of the physics
359 // by 100ms or whatever.
360 if (mLastTime > now) return;
361
362 // DO SHIT HERE
363
364 mLastTime = now+50;
365 }
366 }
367
368 /** Pointer to the text view to display "Paused.." etc. */
369 private TextView mStatusText;
370
371 /** The thread that actually draws the animation */
372 private DrawingThread thread;
373
374 public Game mGame;
375
376 public GameView(Context context, AttributeSet attrs) {
377 super(context, attrs);
378
379 // register our interest in hearing about changes to our surface
380 SurfaceHolder holder = getHolder();
381 holder.addCallback(this);
382
383 // create thread only; it's started in surfaceCreated()
384 thread = new DrawingThread(holder, context, new Handler() {
385 @Override
386 public void handleMessage(Message m) {
387 mStatusText.setVisibility(m.getData().getInt("viz"));
388 mStatusText.setText(m.getData().getString("text"));
389 }
390 });
391
392 setFocusable(true); // make sure we get key events
393 }
394
395 @Override public boolean onTouchEvent(MotionEvent event) {
396 Log.i("AdvanceWars", "Detected touch event");
397
398 if(event.getAction() == MotionEvent.ACTION_UP) {
399 Log.i("AdvanceWars", "Detected UP touch action");
400 switch(thread.mGameState) {
401 case MAIN_MENU:
402 Log.i("AdvanceWars", "Switching to battle map");
403 if(thread.wndMainMenu.getGUIObject("btnNewGame").isClicked(event.getX(), event.getY())) {
404 thread.mGameState = GameState.BATTLE_MAP;
405 }else if(thread.wndMainMenu.getGUIObject("btnLoadGame").isClicked(event.getX(), event.getY())) {
406 BufferedReader b;
407 try {
408 b = new BufferedReader(new FileReader(android.os.Environment.getExternalStorageDirectory()+"/save.txt"));
409
410 int width = Integer.parseInt(b.readLine());
411 int height = Integer.parseInt(b.readLine());
412
413 String offset = b.readLine();
414 Log.i("GameSave", offset);
415 int offsetX = Integer.parseInt(offset.substring(0, offset.indexOf("x")));
416 int offsetY = Integer.parseInt(offset.substring(offset.indexOf("x")+1));
417
418 thread.mMap = new Map(thread.grassTile, width, height, new Point(offsetX, offsetY));
419
420 Log.i("GameSave", "Created the map");
421
422 for(int x=0; x<width; x++) {
423 String line = b.readLine();
424 Log.i("GameSave", line);
425 String[] arr = line.split(",");
426 for(int y=0; y<arr.length; y++) {
427 TerrainType type = TerrainType.values()[Integer.parseInt(arr[y])];
428 if(type.equals(TerrainType.LAND))
429 thread.mMap.setTile(x, y, new Tile(thread.grassTile, new Point(10, 25)));
430 else
431 thread.mMap.setTile(x, y, new Tile(thread.oceanTile, new Point(10, 25)));
432 }
433 }
434
435 while(b.ready()) {
436 String unit = b.readLine();
437 Log.i("GameSave", unit);
438 int x = Integer.parseInt(unit.substring(0, unit.indexOf(",")));
439 int y = Integer.parseInt(unit.substring(unit.indexOf(",")+1));
440
441 mGame.mThread.mMap.getTile(x, y).addUnit(new Soldier(mGame.mThread.mUnitPaint));
442 }
443
444 b.close();
445 }catch(IOException ioe) {
446 ioe.printStackTrace();
447 }
448 thread.mGameState = GameState.BATTLE_MAP;
449 }else if(thread.wndMainMenu.getGUIObject("btnQuit").isClicked(event.getX(), event.getY())) {
450 mGame.finish();
451 }
452 break;
453 case BATTLE_MAP:
454 Log.i("AdvanceWars", "Touch event detected on battle map");
455
456 if(event.getX() >= thread.mMap.offset.x && event.getY() >= thread.mMap.offset.y) {
457 int x = ((int)event.getX() - thread.mMap.offset.x) / 50;
458 int y = ((int)event.getY() - thread.mMap.offset.y) / 50;
459
460 Unit target = thread.mMap.getTile(x, y).currentUnit;
461
462 if(thread.selectedUnit != null && thread.selectedUnit.getMovementRange().contains(new Point(x, y))) {
463 if(target == null || target == thread.selectedUnit) {
464 thread.mMap.getTile(thread.selectedUnit.location.x, thread.selectedUnit.location.y).removeUnit();
465 thread.mMap.getTile(x, y).addUnit(thread.selectedUnit);
466 }else {
467 // the target contains another unit. If the unit is enemy-controlled, attack it
468 }
469 thread.selectedUnit = null;
470 }else
471 thread.selectedUnit = target;
472 }
473
474 break;
475 }
476 }else if(event.getAction() == MotionEvent.ACTION_DOWN) {
477
478 }
479
480 return true;
481 }
482
483 /**
484 * Fetches the animation thread corresponding to this LunarView.
485 *
486 * @return the animation thread
487 */
488 public DrawingThread getThread() {
489 return thread;
490 }
491
492 /**
493 * Standard override to get key-press events.
494 */
495 @Override
496 public boolean onKeyDown(int keyCode, KeyEvent msg) {
497 return thread.doKeyDown(keyCode, msg);
498 }
499
500 /**
501 * Standard override for key-up. We actually care about these, so we can
502 * turn off the engine or stop rotating.
503 */
504 @Override
505 public boolean onKeyUp(int keyCode, KeyEvent msg) {
506 return thread.doKeyUp(keyCode, msg);
507 }
508
509 /**
510 * Standard window-focus override. Notice focus lost so we can pause on
511 * focus lost. e.g. user switches to take a call.
512 */
513 @Override
514 public void onWindowFocusChanged(boolean hasWindowFocus) {
515 if (!hasWindowFocus) thread.pause();
516 }
517
518 /**
519 * Installs a pointer to the text view used for messages.
520 */
521 public void setTextView(TextView textView) {
522 mStatusText = textView;
523 }
524
525 /* Callback invoked when the surface dimensions change. */
526 public void surfaceChanged(SurfaceHolder holder, int format, int width,
527 int height) {
528 thread.setSurfaceSize(width, height);
529 }
530
531 /*
532 * Callback invoked when the Surface has been created and is ready to be
533 * used.
534 */
535 public void surfaceCreated(SurfaceHolder holder) {
536 // start the thread here so that we don't busy-wait in run()
537 // waiting for the surface to be created
538 thread.setRunning(true);
539
540 //if(thread.mAppState == AppState.PAUSE)
541 //thread.unpause();
542 //else
543 thread.start();
544 }
545
546 /*
547 * Callback invoked when the Surface has been destroyed and must no longer
548 * be touched. WARNING: after this method returns, the Surface/Canvas must
549 * never be touched again!
550 */
551 public void surfaceDestroyed(SurfaceHolder holder) {
552 // we have to tell thread to shut down & wait for it to finish, or else
553 // it might touch the Surface after we return and explode
554 boolean retry = true;
555 thread.setRunning(false);
556 while (retry) {
557 try {
558 thread.join();
559 retry = false;
560 } catch (InterruptedException e) {
561 }
562 }
563 }
564}
Note: See TracBrowser for help on using the repository browser.