package com.example.advancewars; import java.util.*; import com.example.gui.*; import android.content.Context; import android.graphics.*; import android.os.*; import android.view.*; import android.util.AttributeSet; import android.util.Log; import android.widget.TextView; /** * View that draws, takes keystrokes, etc. for a simple LunarLander game. * * Has a mode which RUNNING, PAUSED, etc. Has a x, y, dx, dy, ... capturing the * current ship physics. All x/y etc. are measured with (0,0) at the lower left. * updatePhysics() advances the physics based on realtime. draw() renders the * ship, and does an invalidate() to prompt another draw() as soon as possible * by the system. */ class GameView extends SurfaceView implements SurfaceHolder.Callback { class DrawingThread extends Thread { public AppState mAppState; public GameState mGameState; /* * UI constants (i.e. the speed & fuel bars) */ public static final int UI_BAR = 100; // width of the bar(s) public static final int UI_BAR_HEIGHT = 10; // height of the bar(s) /* * Member (state) fields */ private int mCanvasHeight = 1; private int mCanvasWidth = 1; public int mPlayerWins = 0, mPlayerLosses = 0; /** Message handler used by thread to interact with TextView */ private Handler mHandler; /** Used to figure out elapsed time between frames */ private long mLastTime; /** Paint to draw the lines on screen. */ private Paint mLinePaint, mTextPaint, mButtonPaint, mTilePaint1, mTilePaint2; private Map mMap; /** Indicate whether the surface has been created & is ready to draw */ private boolean mRun = false; /** Handle to the surface manager object we interact with */ private SurfaceHolder mSurfaceHolder; private Hashtable drawableObjects; public DrawingThread(SurfaceHolder surfaceHolder, Context context, Handler handler) { // get handles to some important objects mSurfaceHolder = surfaceHolder; mHandler = handler; mContext = context; mLinePaint = new Paint(); mLinePaint.setAntiAlias(true); mLinePaint.setARGB(255, 0, 255, 0); mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); mTextPaint.setARGB(255, 255, 255, 255); mTextPaint.setTextSize(12); mTextPaint.setTextAlign(Paint.Align.CENTER); mButtonPaint = new Paint(); mButtonPaint.setAntiAlias(true); mButtonPaint.setARGB(255, 0, 0, 0); mButtonPaint.setTextSize(20); mButtonPaint.setTextAlign(Paint.Align.CENTER); mTilePaint1 = new Paint(); mTilePaint1.setAntiAlias(true); mTilePaint1.setARGB(255, 0, 255, 0); mTilePaint2 = new Paint(); mTilePaint2.setAntiAlias(true); mTilePaint2.setARGB(255, 0, 0, 255); drawableObjects = new Hashtable(); drawableObjects.put("txtTitle", new Text("Main Menu", 100, 30, 120, 20, mTextPaint)); drawableObjects.put("btnNewGame", new Button("New Game", 100, 90, 120, 20, mLinePaint, mButtonPaint)); drawableObjects.put("btnLoadGame", new Button("Load Game", 100, 125, 120, 20, mLinePaint, mButtonPaint)); drawableObjects.put("btnMapEditor", new Button("Map Editor", 100, 160, 120, 20, mLinePaint, mButtonPaint)); drawableObjects.put("btnQuit", new Button("Quit", 100, 195, 120, 20, mLinePaint, mButtonPaint)); Tile grassTile = new Tile(mTilePaint1); Tile oceanTile = new Tile(mTilePaint2); mMap = new Map(grassTile, 6, 8); boolean land = true; for(int x=0; x start doStart(); return true; } else if (mAppState == AppState.PAUSE && okStart) { // paused -> running unpause(); return true; } else if (mAppState == AppState.RUNNING) { return true; } return false; } } /** * Handles a key-up event. * * @param keyCode the key that was pressed * @param msg the original event object * @return true if the key was handled and consumed, or else false */ boolean doKeyUp(int keyCode, KeyEvent msg) { boolean handled = false; synchronized (mSurfaceHolder) { if (mAppState == AppState.RUNNING) { if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_SPACE) { handled = true; } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_Q || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || keyCode == KeyEvent.KEYCODE_W) { handled = true; } } } return handled; } /** * Draws the ship, fuel/speed bars, and background to the provided * Canvas. */ private void doDraw(Canvas canvas) { canvas.drawColor(Color.BLACK); String text; Paint.FontMetrics metrics = mTextPaint.getFontMetrics(); switch(mGameState) { case MAIN_MENU: for (GUIObject o : drawableObjects.values()) { o.draw(canvas); } break; case BATTLE_MAP: mTextPaint.setTextSize(12); mMap.draw(canvas, 10, 25); text = "Advance Wars grid test"; canvas.drawText(text, 0, 450-(metrics.ascent+metrics.descent)/2, mTextPaint); break; } } /** * Figures the lander state (x, y, fuel, ...) based on the passage of * realtime. Does not invalidate(). Called at the start of draw(). * Detects the end-of-game and sets the UI to the next state. */ private void updatePhysics() { long now = System.currentTimeMillis(); // Do nothing if mLastTime is in the future. // This allows the game-start to delay the start of the physics // by 100ms or whatever. if (mLastTime > now) return; // DO SHIT HERE mLastTime = now+50; } } /** Handle to the application context, used to e.g. fetch Drawables. */ private Context mContext; /** Pointer to the text view to display "Paused.." etc. */ private TextView mStatusText; /** The thread that actually draws the animation */ private DrawingThread thread; public Game mGame; public GameView(Context context, AttributeSet attrs) { super(context, attrs); // register our interest in hearing about changes to our surface SurfaceHolder holder = getHolder(); holder.addCallback(this); // create thread only; it's started in surfaceCreated() thread = new DrawingThread(holder, context, new Handler() { @Override public void handleMessage(Message m) { mStatusText.setVisibility(m.getData().getInt("viz")); mStatusText.setText(m.getData().getString("text")); } }); setFocusable(true); // make sure we get key events } @Override public boolean onTouchEvent(MotionEvent event) { Log.i("AdvanceWars", "Detected touch event"); if(event.getAction() == MotionEvent.ACTION_UP) { Log.i("AdvanceWars", "Detected UP touch action"); switch(thread.mGameState) { case MAIN_MENU: Log.i("AdvanceWars", "Switching to battle map"); if(thread.drawableObjects.get("btnNewGame").isClicked(event.getX(), event.getY())) { thread.mGameState = GameState.BATTLE_MAP; }else if(thread.drawableObjects.get("btnLoadGame").isClicked(event.getX(), event.getY())) { thread.mGameState = GameState.BATTLE_MAP; }else if(thread.drawableObjects.get("btnQuit").isClicked(event.getX(), event.getY())) { mGame.finish(); } break; case BATTLE_MAP: Log.i("AdvanceWars", "Touch event detected on battle map"); thread.mGameState = GameState.MAIN_MENU; break; } }else if(event.getAction() == MotionEvent.ACTION_DOWN) { } return true; } /** * Fetches the animation thread corresponding to this LunarView. * * @return the animation thread */ public DrawingThread getThread() { return thread; } /** * Standard override to get key-press events. */ @Override public boolean onKeyDown(int keyCode, KeyEvent msg) { return thread.doKeyDown(keyCode, msg); } /** * Standard override for key-up. We actually care about these, so we can * turn off the engine or stop rotating. */ @Override public boolean onKeyUp(int keyCode, KeyEvent msg) { return thread.doKeyUp(keyCode, msg); } /** * Standard window-focus override. Notice focus lost so we can pause on * focus lost. e.g. user switches to take a call. */ @Override public void onWindowFocusChanged(boolean hasWindowFocus) { if (!hasWindowFocus) thread.pause(); } /** * Installs a pointer to the text view used for messages. */ public void setTextView(TextView textView) { mStatusText = textView; } /* Callback invoked when the surface dimensions change. */ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { thread.setSurfaceSize(width, height); } /* * Callback invoked when the Surface has been created and is ready to be * used. */ public void surfaceCreated(SurfaceHolder holder) { // start the thread here so that we don't busy-wait in run() // waiting for the surface to be created thread.setRunning(true); //if(thread.mAppState == AppState.PAUSE) //thread.unpause(); //else thread.start(); } /* * Callback invoked when the Surface has been destroyed and must no longer * be touched. WARNING: after this method returns, the Surface/Canvas must * never be touched again! */ public void surfaceDestroyed(SurfaceHolder holder) { // we have to tell thread to shut down & wait for it to finish, or else // it might touch the Surface after we return and explode boolean retry = true; thread.setRunning(false); while (retry) { try { thread.join(); retry = false; } catch (InterruptedException e) { } } } }