package rabbitescape.ui.android; import android.app.AlertDialog; import android.content.Intent; import android.content.res.Resources; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.RadioGroup; import android.widget.TextView; import java.util.HashMap; import java.util.Map; import java.util.Set; import rabbitescape.engine.CompletedLevelWinListener; import rabbitescape.engine.LevelWinListener; import rabbitescape.engine.LoadWorldFile; import rabbitescape.engine.MultiLevelWinListener; import rabbitescape.engine.Token; import rabbitescape.engine.World; import rabbitescape.engine.WorldStatsListener; import rabbitescape.engine.err.RabbitEscapeException; import rabbitescape.engine.menu.ByNameConfigBasedLevelsCompleted; import rabbitescape.engine.menu.LevelsCompleted; import rabbitescape.engine.menu.LevelsList; import rabbitescape.engine.menu.LoadLevelsList; import rabbitescape.engine.menu.MenuDefinition; import rabbitescape.engine.textworld.TextWorldManip; import rabbitescape.engine.util.RealFileSystem; import static android.text.TextUtils.split; import static rabbitescape.engine.i18n.Translation.t; import net.artificialworlds.rabbitescape.R; public class AndroidGameActivity extends RabbitEscapeActivity implements NumLeftListener, WorldStatsListener { // Constants public static final String INTENT_LEVELS_DIR = "rabbitescape.levelsdir"; public static final String INTENT_LEVEL = "rabbitescape.level"; public static final String INTENT_LEVEL_NUMBER = "rabbitescape.levelnumber"; public static final String STATE_CHECKED_ABILITY_INDEX = "rabbitescape.checkedAbilityIndex"; public AlertDialog currentDialog; public RadioGroup abilitiesGroup; // System private LevelsCompleted levelsCompleted; private Button muteButton; private Button pauseButton; private Button speedButton; private LinearLayout topLayout; public GameSurfaceView gameSurface; private Token.Type[] abilities; private TextView worldStatsTextView; public World world; public Game game; @Override protected void onCreate( Bundle savedInstanceState ) { super.onCreate( savedInstanceState ); Intent intent = getIntent(); String levelsDir = intent.getStringExtra( INTENT_LEVELS_DIR ); String levelFileName = intent.getStringExtra( INTENT_LEVEL ); int levelNum = intent.getIntExtra( INTENT_LEVEL_NUMBER, 0 ); Resources resources = getResources(); staticInit(); world = loadWorld( levelFileName, savedInstanceState ); sound.setMusic( world.music ); LevelWinListener winListener = new MultiLevelWinListener( new CompletedLevelWinListener( levelsDir, levelNum, levelsCompleted ), new AndroidAlertWinListener( this ) ); game = new Game( SingletonBitmapCache.instance( resources ), getConfig(), world, winListener, savedInstanceState ); buildDynamicUi( resources, game, world, savedInstanceState ); if ( savedInstanceState == null ) { Dialogs.intro( this, world ); } else { restoreFromState( savedInstanceState ); } } @Override protected void onResume() { super.onResume(); sound.setMusic( world.music ); } private World loadWorld( String levelFileName, Bundle savedInstanceState ) { if ( savedInstanceState != null ) { String savedWorld = savedInstanceState.getString( AndroidGameLaunch.STATE_WORLD ); if ( savedWorld != null ) { return TextWorldManip.createWorld( this, split( savedWorld, "\n" ) ); } } return new LoadWorldFile( new RealFileSystem() ).load( this, levelFileName ); } private void buildDynamicUi( Resources resources, Game game, World world, Bundle savedInstanceState ) { createAbilities( world, resources ); updatePauseButton( world.paused ); gameSurface = new GameSurfaceView( this, this, sound, game, world ); if ( savedInstanceState != null ) { // Get the "fast" state from savedInstanceState because we have no // game yet - that won't be created until we have a surface. updateSpeedButton( savedInstanceState.getBoolean( AndroidGameLaunch.STATE_FAST_PRESSED, false ) ); } topLayout.addView( gameSurface ); } private void staticInit() { LevelsList levelsList = LoadLevelsList.load( MenuDefinition.allLevels ); levelsCompleted = new ByNameConfigBasedLevelsCompleted( getConfig(), levelsList ); setContentView( R.layout.activity_android_game ); muteButton = (Button)findViewById( R.id.muteButton ); pauseButton = (Button)findViewById( R.id.pauseButton ); speedButton = (Button)findViewById( R.id.speedButton ); topLayout = (LinearLayout)findViewById( R.id.topLayout ); abilitiesGroup = (RadioGroup)findViewById( R.id.abilitiesGroup ); worldStatsTextView = (TextView)findViewById( R.id.worldStats ); } private void createAbilities( World world, Resources resources ) { Set<Map.Entry<Token.Type, Integer>> entries = world.abilities.entrySet(); abilities = new Token.Type[ entries.size() ]; int i = 0; for ( Map.Entry<Token.Type, Integer> entry : entries ) { abilities[i] = entry.getKey(); abilitiesGroup.addView( new AbilityButton( this, resources, abilitiesGroup, entry.getKey().name(), entry.getValue(), i ) ); ++i; } abilitiesGroup.setOnCheckedChangeListener( new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged( RadioGroup radioGroup, int buttonIndex ) { game.gameLaunch.chosenAbility = abilities[buttonIndex]; for ( int i = 0; i < radioGroup.getChildCount(); i++ ) { AbilityButton button = (AbilityButton)( radioGroup.getChildAt( i ) ); button.setChecked( i == buttonIndex ); } } } ); } @Override public void updateMuteButton( boolean muted ) { muteButton.setCompoundDrawablesWithIntrinsicBounds( getResources().getDrawable( muted ? R.drawable.menu_muted : R.drawable.menu_unmuted ), null, null, null ); muteButton.invalidate(); } public void onPauseClicked( View view ) { updatePauseButton( gameSurface.togglePaused() ); } private void updatePauseButton( boolean paused ) { pauseButton.setCompoundDrawablesWithIntrinsicBounds( getResources().getDrawable( paused ? R.drawable.menu_unpause : R.drawable.menu_pause ), null, null, null ); pauseButton.invalidate(); // Show Overlay game.gameLaunch.graphics.redraw(); } public void onSpeedClicked( View view ) { updateSpeedButton( gameSurface.toggleSpeed() ); } private void updateSpeedButton( boolean fast ) { int speedRes = fast ? R.drawable.menu_speedup_active : R.drawable.menu_speedup_inactive; speedButton.setCompoundDrawablesWithIntrinsicBounds( getResources().getDrawable( speedRes ), null, null, null ); speedButton.invalidate(); } public void onExplodeAllClicked( View view ) { World world = game.gameLaunch.physics.world; switch ( world.completionState() ) { case RUNNING: case PAUSED: { Dialogs.explode( this, world ); break; } default: { // Don't do anything if we've finished already break; } } } public void setPaused( World world, boolean paused ) { world.setPaused( paused ); updatePauseButton( paused ); } @Override public void onSaveInstanceState( Bundle outState ) { super.onSaveInstanceState( outState ); // The super call will call onSaveInstanceState on: // - GameSurfaceView // - each AbilityButton (but we do the saving here because it's easier outState.putInt( STATE_CHECKED_ABILITY_INDEX, checkedAbilityIndex() ); game.gameLaunch.onSaveInstanceState( outState ); // Mute state is stored in a preference, so no need to store it here. } private void restoreFromState( Bundle savedInstanceState ) { int checkedAbility = savedInstanceState.getInt( STATE_CHECKED_ABILITY_INDEX, -1 ); if ( checkedAbility != -1 ) { abilitiesGroup.check( checkedAbility ); } } private int checkedAbilityIndex() { for ( int i = 0; i < abilitiesGroup.getChildCount(); i++ ) { AbilityButton button = (AbilityButton)( abilitiesGroup.getChildAt( i ) ); if ( button.isChecked() ) { return i; } } return -1; } @Override public boolean onCreateOptionsMenu( Menu menu ) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate( R.menu.game, menu ); return true; } @Override public boolean onOptionsItemSelected( MenuItem item ) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if ( id == R.id.action_settings ) { return true; } return super.onOptionsItemSelected( item ); } @Override public void numLeft( Token.Type ability, int numLeft ) { AbilityButton button = buttonForAbility( ability ); button.setNumLeft( numLeft ); if ( numLeft == 0 ) { button.disable(); button.setChecked( false ); } } @Override public void worldStats( int num_saved, int num_to_save ) { final Map<String, Object> values = new HashMap<String, Object>(); values.put( "num_saved", num_saved ); values.put( "num_to_save", num_to_save ); this.runOnUiThread( new Runnable() { @Override public void run() { worldStatsTextView.setText( t( "${num_saved} / ${num_to_save}", values ) ); } } ); } public static class UnrecognisedAbility extends RabbitEscapeException { public final Token.Type ability; public UnrecognisedAbility( Token.Type ability ) { this.ability = ability; } } private AbilityButton buttonForAbility( Token.Type ability ) { int i = 0; for ( Token.Type ab : abilities ) { if ( ab == ability ) { return (AbilityButton)abilitiesGroup.getChildAt( i ); } ++i; } throw new UnrecognisedAbility( ability ); } }