diff --git a/app/build.gradle b/app/build.gradle index 2139083d..38b07836 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,7 +8,7 @@ android { minSdkVersion 17 targetSdkVersion 28 versionCode 21 - versionName "0.5-1" + versionName "0.5-2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/app/src/androidTest/java/ch/logixisland/anuto/DefaultGameSimulator.java b/app/src/androidTest/java/ch/logixisland/anuto/DefaultGameSimulator.java index 7ca6a45d..00953b35 100755 --- a/app/src/androidTest/java/ch/logixisland/anuto/DefaultGameSimulator.java +++ b/app/src/androidTest/java/ch/logixisland/anuto/DefaultGameSimulator.java @@ -21,6 +21,7 @@ import ch.logixisland.anuto.util.math.Intersections; import ch.logixisland.anuto.util.math.Line; import ch.logixisland.anuto.util.math.Vector2; +import ch.logixisland.anuto.view.game.GameActivity; public class DefaultGameSimulator extends GameSimulator { @@ -32,10 +33,13 @@ public class DefaultGameSimulator extends GameSimulator { private final TowerTiers mTowerTiers; private final Random mRandom = new Random(); private final TickTimer mSaveAndLoadTimer = TickTimer.createInterval(120f); + private final TickTimer mSGTimer = TickTimer.createInterval(30f); private final TickTimer mSimulationTickTimer = TickTimer.createInterval(0.5f); - DefaultGameSimulator(GameFactory gameFactory) { - super(gameFactory); + private int mSGMode = 0; + + DefaultGameSimulator(GameActivity gameActivity, GameFactory gameFactory) { + super(gameActivity, gameFactory); mTowerTiers = new TowerTiers(gameFactory); } @@ -51,6 +55,24 @@ protected void tick() { if (mSaveAndLoadTimer.tick()) { saveAndLoad(); } + + if (mSGTimer.tick()) { + switch (++mSGMode) { + default: + //for mSaveAndLoadTimer + mSGMode = 0; + break; + case 1: + saveSG(); + break; + case 2: + loadSG(); + break; + case 3: + deleteSG(); + break; + } + } } private void tryUpgradeTower() { diff --git a/app/src/androidTest/java/ch/logixisland/anuto/GameSimulator.java b/app/src/androidTest/java/ch/logixisland/anuto/GameSimulator.java index 5bf70e05..bf42995e 100755 --- a/app/src/androidTest/java/ch/logixisland/anuto/GameSimulator.java +++ b/app/src/androidTest/java/ch/logixisland/anuto/GameSimulator.java @@ -1,21 +1,31 @@ package ch.logixisland.anuto; import android.util.Log; +import android.widget.Toast; +import java.io.File; import java.util.concurrent.CountDownLatch; import ch.logixisland.anuto.business.game.GameState; +import ch.logixisland.anuto.business.game.SaveGameInfo; import ch.logixisland.anuto.engine.logic.loop.TickListener; +import ch.logixisland.anuto.util.iterator.Predicate; +import ch.logixisland.anuto.util.iterator.StreamIterator; +import ch.logixisland.anuto.view.game.GameActivity; public abstract class GameSimulator { private final static String TAG = GameSimulator.class.getSimpleName(); + private final GameActivity mGameActivity; private final GameFactory mGameFactory; private CountDownLatch mFinishedLatch; - GameSimulator(GameFactory gameFactory) { + private SaveGameInfo mLastSG = null; + + GameSimulator(GameActivity gameActivity, GameFactory gameFactory) { + mGameActivity = gameActivity; mGameFactory = gameFactory; } @@ -28,6 +38,7 @@ void startSimulation() { void waitForFinished() { try { mFinishedLatch.await(); + deleteSG(); } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -39,6 +50,49 @@ protected GameFactory getGameFactory() { return mGameFactory; } + protected void saveSG() { + deleteSG(); + mLastSG = SaveGameInfo.createSGI(mGameFactory.getGameLoader(), mGameFactory.getGameLoader().makeNewSavegame()); + Thread thread = new Thread() { + public void run() { + mGameActivity.runOnUiThread(new Runnable() { + public void run() { + Toast.makeText(mGameActivity, mGameActivity.getString(ch.logixisland.anuto.R.string.saveGameSuccessful), Toast.LENGTH_LONG).show(); + } + }); + } + }; + thread.start(); + } + + + private static Predicate byFolder(final File folder) { + return new Predicate() { + @Override + public boolean apply(SaveGameInfo value) { + return value.getFolder().equals(folder); + } + }; + } + + protected void loadSG() { + if (mLastSG != null) { + if (!StreamIterator.fromIterable(mGameFactory.getGameLoader().getSaveGameRepository() + .getSavegameInfos()).filter(byFolder(mLastSG.getFolder())).isEmpty()) + mGameFactory.getGameLoader().loadGameState(mLastSG.getSavegameState()); + else + mLastSG = null; + installTickHandler(); + } + } + + protected void deleteSG() { + if (mLastSG != null) { + mGameFactory.getGameLoader().deleteSavegame(mLastSG.getFolder()); + mLastSG = null; + } + } + protected void saveAndLoad() { mGameFactory.getGameLoader().saveGame(); mGameFactory.getGameLoader().loadGame(); @@ -106,5 +160,4 @@ private void simulationFinished() { mFinishedLatch.countDown(); } } - } diff --git a/app/src/androidTest/java/ch/logixisland/anuto/IntegrationTest.java b/app/src/androidTest/java/ch/logixisland/anuto/IntegrationTest.java index 30decde7..eda0ce6c 100755 --- a/app/src/androidTest/java/ch/logixisland/anuto/IntegrationTest.java +++ b/app/src/androidTest/java/ch/logixisland/anuto/IntegrationTest.java @@ -19,7 +19,7 @@ public class IntegrationTest { @Test public void integrationTest() { - GameSimulator simulator = new DefaultGameSimulator(AnutoApplication.getInstance().getGameFactory()); + GameSimulator simulator = new DefaultGameSimulator(mActivityRule.getActivity(), AnutoApplication.getInstance().getGameFactory()); simulator.startSimulation(); simulator.waitForFinished(); } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bdda0953..3e86c764 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -41,6 +41,19 @@ android:icon="@drawable/settings" android:theme="@android:style/Theme.DeviceDefault"> + + + + + diff --git a/app/src/main/java/ch/logixisland/anuto/AnutoApplication.java b/app/src/main/java/ch/logixisland/anuto/AnutoApplication.java index 24b011fa..800885ce 100755 --- a/app/src/main/java/ch/logixisland/anuto/AnutoApplication.java +++ b/app/src/main/java/ch/logixisland/anuto/AnutoApplication.java @@ -1,19 +1,26 @@ package ch.logixisland.anuto; import android.app.Application; +import android.content.Context; public class AnutoApplication extends Application { + private static Context appContext; private static AnutoApplication sInstance; private GameFactory mGameFactory; @Override public void onCreate() { super.onCreate(); + appContext = getApplicationContext(); sInstance = this; mGameFactory = new GameFactory(getApplicationContext()); } + public static Context getContext() { + return appContext; + } + public static AnutoApplication getInstance() { return sInstance; } diff --git a/app/src/main/java/ch/logixisland/anuto/GameFactory.java b/app/src/main/java/ch/logixisland/anuto/GameFactory.java index b29da969..86a79ad9 100755 --- a/app/src/main/java/ch/logixisland/anuto/GameFactory.java +++ b/app/src/main/java/ch/logixisland/anuto/GameFactory.java @@ -130,7 +130,7 @@ private void initializeBusiness(Context context) { mMapRepository = new MapRepository(); mScoreBoard = new ScoreBoard(mGameEngine); mTowerSelector = new TowerSelector(mGameEngine, mScoreBoard); - mGameLoader = new GameLoader(context, mGameEngine, mGamePersister, mViewport, mEntityRegistry, mMapRepository); + mGameLoader = new GameLoader(context, mGameEngine, mGamePersister, mViewport, mEntityRegistry, mMapRepository, mRenderer); mHighScores = new HighScores(context, mGameEngine, mScoreBoard, mGameLoader); mGameState = new GameState(mScoreBoard, mHighScores, mTowerSelector); mTowerAging = new TowerAging(mGameEngine); diff --git a/app/src/main/java/ch/logixisland/anuto/business/game/GameLoader.java b/app/src/main/java/ch/logixisland/anuto/business/game/GameLoader.java index e5b0ab1d..adbf9a7b 100755 --- a/app/src/main/java/ch/logixisland/anuto/business/game/GameLoader.java +++ b/app/src/main/java/ch/logixisland/anuto/business/game/GameLoader.java @@ -1,17 +1,27 @@ package ch.logixisland.anuto.business.game; import android.content.Context; +import android.graphics.Bitmap; import android.util.Log; +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import ch.logixisland.anuto.AnutoApplication; import ch.logixisland.anuto.BuildConfig; +import ch.logixisland.anuto.GameFactory; import ch.logixisland.anuto.R; +import ch.logixisland.anuto.business.wave.WaveManager; import ch.logixisland.anuto.engine.logic.GameEngine; import ch.logixisland.anuto.engine.logic.entity.EntityRegistry; import ch.logixisland.anuto.engine.logic.loop.ErrorListener; @@ -20,6 +30,7 @@ import ch.logixisland.anuto.engine.logic.map.PlateauInfo; import ch.logixisland.anuto.engine.logic.map.WaveInfo; import ch.logixisland.anuto.engine.logic.persistence.GamePersister; +import ch.logixisland.anuto.engine.render.Renderer; import ch.logixisland.anuto.engine.render.Viewport; import ch.logixisland.anuto.entity.plateau.Plateau; import ch.logixisland.anuto.util.container.KeyValueStore; @@ -27,7 +38,10 @@ public class GameLoader implements ErrorListener { private static final String TAG = GameLoader.class.getSimpleName(); - private static final String SAVED_GAME_FILE = "saved_game.json"; + public static final String SAVED_GAME_FILE = "saved_game.json"; + public static final String SAVED_SCREENSHOT_FILE = "screen.png"; + public static final String SAVED_GAMEINFO_FILE = "gameinfo.json"; + public interface Listener { void gameLoaded(); @@ -39,18 +53,20 @@ public interface Listener { private final Viewport mViewport; private final EntityRegistry mEntityRegistry; private final MapRepository mMapRepository; + private final Renderer mRenderer; private String mCurrentMapId; private List mListeners = new CopyOnWriteArrayList<>(); public GameLoader(Context context, GameEngine gameEngine, GamePersister gamePersister, - Viewport viewport, EntityRegistry entityRegistry, MapRepository mapRepository) { + Viewport viewport, EntityRegistry entityRegistry, MapRepository mapRepository, Renderer renderer) { mContext = context; mGameEngine = gameEngine; mGamePersister = gamePersister; mViewport = viewport; mEntityRegistry = entityRegistry; mMapRepository = mapRepository; + mRenderer = renderer; mGameEngine.registerErrorListener(this); } @@ -86,7 +102,7 @@ public void execute() { } public void saveGame() { - if (mGameEngine.isThreadChangeNeeded()) { + if (mGameEngine.isThreadRunning() && mGameEngine.isThreadChangeNeeded()) { mGameEngine.post(new Message() { @Override public void execute() { @@ -96,29 +112,165 @@ public void execute() { return; } + saveGame("", false); + } + + private void saveGame(final String rootdir, final boolean userSavegame) { Log.i(TAG, "Saving game..."); + String fileName = userSavegame ? rootdir + File.separator + SAVED_GAME_FILE : SAVED_GAME_FILE; KeyValueStore gameState = new KeyValueStore(); mGamePersister.writeState(gameState); gameState.putInt("appVersion", BuildConfig.VERSION_CODE); gameState.putString("mapId", mCurrentMapId); try { - FileOutputStream outputStream = mContext.openFileOutput(SAVED_GAME_FILE, Context.MODE_PRIVATE); + FileOutputStream outputStream = userSavegame ? (new FileOutputStream(fileName, false)) : mContext.openFileOutput(fileName, Context.MODE_PRIVATE); gameState.toStream(outputStream); outputStream.close(); Log.i(TAG, "Game saved."); } catch (Exception e) { - mContext.deleteFile(SAVED_GAME_FILE); + mContext.deleteFile(fileName); + throw new RuntimeException("Could not save game!", e); + } + } + + public File makeNewSavegame() { + /*if (mGameEngine.isThreadRunning() && mGameEngine.isThreadChangeNeeded()) { + mGameEngine.post(new Message() { + @Override + public void execute() { + makeNewSavegame(); + } + }); + return; + }*/ + + GameFactory daFac = AnutoApplication.getInstance().getGameFactory(); + WaveManager mWaveManager = daFac.getWaveManager(); + ScoreBoard mScoreBoard = daFac.getScoreBoard(); + + Date now = new Date(); + SimpleDateFormat dtFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS"); + File rootdir = new File(mContext.getFilesDir() + File.separator + + "savegame" + File.separator + + getCurrentMapId() + File.separator + + dtFormat.format(now)); + rootdir.mkdirs(); + + Bitmap bitmap = mRenderer.getScreenshot(); + + try { + //FileOutputStream outputStream = mContext.openFileOutput("screen.png", Context.MODE_PRIVATE); + Log.i(TAG, "Saving screenshot..."); + + FileOutputStream outputStream = new FileOutputStream(new File(rootdir, SAVED_SCREENSHOT_FILE), false); + + int destWidth = 600; + int origWidth = bitmap.getWidth(); + + if (destWidth < origWidth) { + int origHeight = bitmap.getHeight(); + + int destHeight = (int) (((float) origHeight) / (((float) origWidth) / destWidth)); + bitmap = Bitmap.createScaledBitmap(bitmap, destWidth, destHeight, false); + } + + bitmap.compress(Bitmap.CompressFormat.PNG, 30, outputStream); + outputStream.flush(); + outputStream.close(); + Log.i(TAG, "Screenshot saved."); + + //MediaStore.Images.Media.insertImage(getContentResolver(),filename.getAbsolutePath(),filename.getName(),filename.getName()); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException("Could not save game!", e); + } + + try { + Log.i(TAG, "Creating savegame info..."); + saveGame(rootdir.getAbsolutePath(), true); + + KeyValueStore savegameInfo = new KeyValueStore(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + savegameInfo.putInt("appVersion", BuildConfig.VERSION_CODE); + savegameInfo.putString("dateTime", dateFormat.format(now)); + savegameInfo.putInt("waveNumber", mWaveManager.getWaveNumber()); + savegameInfo.putInt("remainingEnemiesCount", mWaveManager.getRemainingEnemiesCount()); + savegameInfo.putInt("score", mScoreBoard.getScore()); + savegameInfo.putInt("credits", mScoreBoard.getCredits()); + savegameInfo.putInt("lives", mScoreBoard.getLives()); + + FileOutputStream outputStream = new FileOutputStream(new File(rootdir, SAVED_GAMEINFO_FILE), false); + savegameInfo.toStream(outputStream); + outputStream.close(); + Log.i(TAG, "Savegame info saved."); + } catch (Exception e) { + //deleteSavegame? + e.printStackTrace(); throw new RuntimeException("Could not save game!", e); } + + return rootdir; + } + + public void deleteSavegame(File rootdir) { + File[] files = rootdir.listFiles(); + + if ((files == null) || (files.length == 0)) { + Log.d(TAG, "No Files Found"); + } else if (files.length != 3) { + Log.d(TAG, "Incorrect File Count"); + } else { + Log.d(TAG, "Size: " + files.length); + + final List ldeleteThis = Arrays.asList(SAVED_GAME_FILE, SAVED_SCREENSHOT_FILE, SAVED_GAMEINFO_FILE); + final List lfiles = new ArrayList<>(Arrays.asList(files)); + List result = new ArrayList<>(); + for (File one : lfiles) { + Log.d(TAG, "FileName:" + one.getName()); + if (ldeleteThis.contains(one.getName())) + result.add(one); + } + if (result.size() != lfiles.size()) + return; + for (File one : result) { + one.delete(); + } + rootdir.delete(); + } + } + + public KeyValueStore readSaveGame(final String fileName, final boolean userSavegame) { + KeyValueStore gameState; + + try { + gameState = getGameState(fileName, userSavegame); + } catch (FileNotFoundException e) { + Log.i(TAG, "No save game file found."); + return null; + } catch (Exception e) { + Log.i(TAG, "Could not read save game!"); + return null; + } + + if (gameState.getInt("appVersion") != BuildConfig.VERSION_CODE) { + Log.i(TAG, "App version mismatch."); + return null; + } + return gameState; } public void loadGame() { - if (mGameEngine.isThreadChangeNeeded()) { + loadGame(SAVED_GAME_FILE, false); + } + + public void loadGame(final String fileName, final boolean userSavegame) { + if (//mGameEngine.isThreadRunning() && + mGameEngine.isThreadChangeNeeded()) { mGameEngine.post(new Message() { @Override public void execute() { - loadGame(); + loadGame(fileName, userSavegame); } }); return; @@ -128,9 +280,7 @@ public void execute() { KeyValueStore gameState; try { - FileInputStream inputStream = mContext.openFileInput(SAVED_GAME_FILE); - gameState = KeyValueStore.fromStream(inputStream); - inputStream.close(); + gameState = getGameState(fileName, userSavegame); } catch (FileNotFoundException e) { Log.i(TAG, "No save game file found."); loadMap(mMapRepository.getDefaultMapId()); @@ -149,6 +299,37 @@ public void execute() { initializeGame(mCurrentMapId, gameState); } + public KeyValueStore getGameState(final String fileName, final boolean userSavegame) throws IOException { + Log.i(TAG, "Reading state..."); + KeyValueStore gameState; + + FileInputStream inputStream = null; + try { + inputStream = userSavegame ? new FileInputStream(fileName) : mContext.openFileInput(fileName); + gameState = KeyValueStore.fromStream(inputStream); + } finally { + if (inputStream != null) + inputStream.close(); + } + + return gameState; + } + + public void loadGameState(final KeyValueStore gameState) { + if (mGameEngine.isThreadChangeNeeded()) { + mGameEngine.post(new Message() { + @Override + public void execute() { + loadGameState(gameState); + } + }); + return; + } + + mCurrentMapId = gameState.getString("mapId"); + initializeGame(mCurrentMapId, gameState); + } + public void loadMap(final String mapId) { if (mGameEngine.isThreadChangeNeeded()) { mGameEngine.post(new Message() { @@ -212,4 +393,39 @@ public void error(Exception e, int loopCount) { } } + public boolean hasSavegames() { + File rootdir = new File(mContext.getFilesDir() + File.separator + + "savegame" + File.separator + + getCurrentMapId()); + + File[] files = rootdir.listFiles(); + + return (files != null) && (files.length > 0); + + } + + public SaveGameRepository getSaveGameRepository() { + SaveGameRepository sgr = new SaveGameRepository(this); + File rootdir = new File(mContext.getFilesDir() + File.separator + + "savegame" + File.separator + + getCurrentMapId()); + + File[] files = rootdir.listFiles(); + + if ((files == null) || (files.length == 0)) { + Log.d(TAG, "No Files Found"); + } else { + Log.d(TAG, "Size: " + files.length); + final List lfiles = new ArrayList<>(Arrays.asList(files)); + Collections.sort(lfiles, Collections.reverseOrder()); + + for (File one : lfiles) { + Log.d(TAG, "FileName:" + one.getName()); + SaveGameInfo sgi = SaveGameInfo.createSGI(this, one); + if (sgi != null) + sgr.addSGI(sgi); + } + } + return sgr; + } } diff --git a/app/src/main/java/ch/logixisland/anuto/business/game/GameSpeed.java b/app/src/main/java/ch/logixisland/anuto/business/game/GameSpeed.java index 7689eda5..8c637bcd 100755 --- a/app/src/main/java/ch/logixisland/anuto/business/game/GameSpeed.java +++ b/app/src/main/java/ch/logixisland/anuto/business/game/GameSpeed.java @@ -7,8 +7,8 @@ import ch.logixisland.anuto.engine.logic.loop.Message; public class GameSpeed { - - private static final int FAST_FORWARD_SPEED = 4; + private static final int FAST_FORWARD_SPEED = 2; + private static final int MAX_FAST_FORWARD_SPEED = 128; public interface Listener { void gameSpeedChanged(); @@ -18,6 +18,7 @@ public interface Listener { private final List mListeners = new CopyOnWriteArrayList<>(); private boolean mFastForwardActive = false; + private int mFastForwardMultiplier = FAST_FORWARD_SPEED; public GameSpeed(GameEngine gameEngine) { mGameEngine = gameEngine; @@ -27,18 +28,40 @@ public boolean isFastForwardActive() { return mFastForwardActive; } - public void toggleFastForward() { + public void setFastForwardActive(final boolean active) { if (mGameEngine.isThreadChangeNeeded()) { mGameEngine.post(new Message() { @Override public void execute() { - toggleFastForward(); + setFastForwardActive(active); } }); return; } - setFastForwardActive(!mFastForwardActive); + if (mFastForwardActive != active) { + mFastForwardActive = active; + updateTicks(); + } + } + + + public int fastForwardMultiplier() { + return mFastForwardMultiplier; + } + + public void cycleFastForward() { + if (mGameEngine.isThreadChangeNeeded()) { + mGameEngine.post(new Message() { + @Override + public void execute() { + cycleFastForward(); + } + }); + return; + } + + cycleThroughMultiplier(); } public void addListener(Listener listener) { @@ -49,14 +72,20 @@ public void removeListener(Listener listener) { mListeners.remove(listener); } - private void setFastForwardActive(boolean fastForwardActive) { - if (mFastForwardActive != fastForwardActive) { - mFastForwardActive = fastForwardActive; - mGameEngine.setTicksPerLoop(mFastForwardActive ? FAST_FORWARD_SPEED : 1); + private void cycleThroughMultiplier() { + mFastForwardMultiplier = mFastForwardMultiplier < MAX_FAST_FORWARD_SPEED ? mFastForwardMultiplier * 2 : FAST_FORWARD_SPEED; + + updateTicks(); + } + + private void updateTicks() { + if (mFastForwardActive) + mGameEngine.setTicksPerLoop(mFastForwardMultiplier); + else + mGameEngine.setTicksPerLoop(1); - for (Listener listener : mListeners) { - listener.gameSpeedChanged(); - } + for (Listener listener : mListeners) { + listener.gameSpeedChanged(); } } } diff --git a/app/src/main/java/ch/logixisland/anuto/business/game/GameState.java b/app/src/main/java/ch/logixisland/anuto/business/game/GameState.java index 1f66d31a..d4035d61 100755 --- a/app/src/main/java/ch/logixisland/anuto/business/game/GameState.java +++ b/app/src/main/java/ch/logixisland/anuto/business/game/GameState.java @@ -11,6 +11,7 @@ public class GameState implements ScoreBoard.Listener, Persister { public interface Listener { void gameRestart(); + void gameOver(); } diff --git a/app/src/main/java/ch/logixisland/anuto/business/game/SaveGameInfo.java b/app/src/main/java/ch/logixisland/anuto/business/game/SaveGameInfo.java new file mode 100644 index 00000000..f294c7a4 --- /dev/null +++ b/app/src/main/java/ch/logixisland/anuto/business/game/SaveGameInfo.java @@ -0,0 +1,71 @@ +package ch.logixisland.anuto.business.game; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import java.io.File; +import java.io.FileInputStream; + +import ch.logixisland.anuto.util.container.KeyValueStore; + +public final class SaveGameInfo { + final private File mFolder; + final private KeyValueStore mSavegameState; + final private KeyValueStore mSavegameInfoStore; + final private Bitmap mCachedScreenshot; + + public static SaveGameInfo createSGI(GameLoader gameLoader, File folder) { + try { + KeyValueStore savegameState = gameLoader.readSaveGame(new File(folder, GameLoader.SAVED_GAME_FILE).getAbsolutePath(), true); + KeyValueStore savegameInfoStore = KeyValueStore.fromStream(new FileInputStream(new File(folder, GameLoader.SAVED_GAMEINFO_FILE))); + Bitmap cachedScreenshot = BitmapFactory.decodeFile(new File(folder, GameLoader.SAVED_SCREENSHOT_FILE).getAbsolutePath()); + return new SaveGameInfo(folder, savegameState, savegameInfoStore, cachedScreenshot); + } catch (Exception e) { + //throw new RuntimeException("Could not read save game!", e); + return null; + } + } + + private SaveGameInfo(File folder, KeyValueStore savegameState, KeyValueStore savegameInfoStore, Bitmap cachedScreenshot) { + mFolder = folder; + mSavegameState = savegameState; + mSavegameInfoStore = savegameInfoStore; + mCachedScreenshot = cachedScreenshot; + } + + public File getFolder() { + return mFolder; + } + + public KeyValueStore getSavegameState() { + return mSavegameState; + } + + public Bitmap getCachedScreenshot() { + return mCachedScreenshot; + } + + public String getDatetime() { + return mSavegameInfoStore.getString("dateTime"); + } + + public int getScore() { + return mSavegameInfoStore.getInt("score"); + } + + public int getWave() { + return mSavegameInfoStore.getInt("waveNumber"); + } + + public int getRemainingEnemiesCount() { + return mSavegameInfoStore.getInt("remainingEnemiesCount"); + } + + public int getCredits() { + return mSavegameInfoStore.getInt("credits"); + } + + public int getLives() { + return mSavegameInfoStore.getInt("lives"); + } +} diff --git a/app/src/main/java/ch/logixisland/anuto/business/game/SaveGameRepository.java b/app/src/main/java/ch/logixisland/anuto/business/game/SaveGameRepository.java new file mode 100644 index 00000000..00abd712 --- /dev/null +++ b/app/src/main/java/ch/logixisland/anuto/business/game/SaveGameRepository.java @@ -0,0 +1,31 @@ +package ch.logixisland.anuto.business.game; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class SaveGameRepository { + + private final List mSavegameInfos; + private final GameLoader mGameLoader; + + public SaveGameRepository(GameLoader gameLoader) { + mSavegameInfos = new ArrayList<>(); + mGameLoader = gameLoader; + } + + public void addSGI(SaveGameInfo saveGameInfo) { + mSavegameInfos.add(saveGameInfo); + } + + public void removeSGIAt(int position) { + if (mSavegameInfos.size() > position) { + mGameLoader.deleteSavegame(mSavegameInfos.get(position).getFolder()); + mSavegameInfos.remove(position); + } + } + + public List getSavegameInfos() { + return Collections.unmodifiableList(mSavegameInfos); + } +} diff --git a/app/src/main/java/ch/logixisland/anuto/business/game/ScoreBoard.java b/app/src/main/java/ch/logixisland/anuto/business/game/ScoreBoard.java index b93ce2ee..481531af 100755 --- a/app/src/main/java/ch/logixisland/anuto/business/game/ScoreBoard.java +++ b/app/src/main/java/ch/logixisland/anuto/business/game/ScoreBoard.java @@ -13,7 +13,9 @@ public class ScoreBoard implements Persister { public interface Listener { void creditsChanged(int credits); + void bonusChanged(int waveBonus, int earlyBonus); + void livesChanged(int lives); } diff --git a/app/src/main/java/ch/logixisland/anuto/business/game/TutorialControl.java b/app/src/main/java/ch/logixisland/anuto/business/game/TutorialControl.java index a04b4882..f9e6b1f8 100755 --- a/app/src/main/java/ch/logixisland/anuto/business/game/TutorialControl.java +++ b/app/src/main/java/ch/logixisland/anuto/business/game/TutorialControl.java @@ -14,6 +14,7 @@ public class TutorialControl implements TowerInserter.Listener, WaveManager.List public interface TutorialView { void showHint(int textId, boolean showSkipButton); + void tutorialFinished(); } diff --git a/app/src/main/java/ch/logixisland/anuto/business/tower/TowerSelector.java b/app/src/main/java/ch/logixisland/anuto/business/tower/TowerSelector.java index eab58adc..c9d7e97d 100755 --- a/app/src/main/java/ch/logixisland/anuto/business/tower/TowerSelector.java +++ b/app/src/main/java/ch/logixisland/anuto/business/tower/TowerSelector.java @@ -15,11 +15,13 @@ public class TowerSelector implements ScoreBoard.Listener, Entity.Listener, Towe public interface TowerInfoView { void showTowerInfo(TowerInfo towerInfo); + void hideTowerInfo(); } public interface TowerBuildView { void toggleTowerBuildView(); + void hideTowerBuildView(); } @@ -200,6 +202,11 @@ public void valueChanged(int value) { updateTowerInfo(); } + @Override + public void strengthChanged() { + updateTowerInfo(); + } + @Override public void creditsChanged(int credits) { if (mTowerInfo != null) { diff --git a/app/src/main/java/ch/logixisland/anuto/business/wave/WaveManager.java b/app/src/main/java/ch/logixisland/anuto/business/wave/WaveManager.java index 3f3944b0..484e2bff 100755 --- a/app/src/main/java/ch/logixisland/anuto/business/wave/WaveManager.java +++ b/app/src/main/java/ch/logixisland/anuto/business/wave/WaveManager.java @@ -26,8 +26,11 @@ public class WaveManager implements Persister, GameState.Listener { public interface Listener { void waveStarted(); + void waveNumberChanged(); + void nextWaveReadyChanged(); + void remainingEnemiesCountChanged(); } @@ -85,6 +88,9 @@ public void execute() { return; } + setNextWaveReady(false); + nextWaveReadyDelayed(NEXT_WAVE_MIN_DELAY); + mGameState.gameStarted(); giveWaveRewardAndEarlyBonus(); @@ -93,8 +99,6 @@ public void execute() { updateRemainingEnemiesCount(); setWaveNumber(mWaveNumber + 1); - setNextWaveReady(false); - nextWaveReadyDelayed(NEXT_WAVE_MIN_DELAY); for (Listener listener : mListeners) { listener.waveStarted(); diff --git a/app/src/main/java/ch/logixisland/anuto/engine/logic/GameEngine.java b/app/src/main/java/ch/logixisland/anuto/engine/logic/GameEngine.java index 06df1957..cf670dac 100755 --- a/app/src/main/java/ch/logixisland/anuto/engine/logic/GameEngine.java +++ b/app/src/main/java/ch/logixisland/anuto/engine/logic/GameEngine.java @@ -151,6 +151,10 @@ public void setTicksPerLoop(int ticksPerLoop) { mGameLoop.setTicksPerLoop(ticksPerLoop); } + public boolean isThreadRunning() { + return mGameLoop.isRunning(); + } + public boolean isThreadChangeNeeded() { return mGameLoop.isThreadChangeNeeded(); } diff --git a/app/src/main/java/ch/logixisland/anuto/engine/logic/loop/GameLoop.java b/app/src/main/java/ch/logixisland/anuto/engine/logic/loop/GameLoop.java index 50caf8a2..ab2d0019 100755 --- a/app/src/main/java/ch/logixisland/anuto/engine/logic/loop/GameLoop.java +++ b/app/src/main/java/ch/logixisland/anuto/engine/logic/loop/GameLoop.java @@ -53,6 +53,10 @@ public void clear() { mTickListeners.clear(); } + public boolean isRunning() { + return mRunning; + } + public void start() { if (!mRunning) { Log.i(TAG, "Starting game loop"); diff --git a/app/src/main/java/ch/logixisland/anuto/engine/logic/persistence/Persister.java b/app/src/main/java/ch/logixisland/anuto/engine/logic/persistence/Persister.java index 31ff4266..63f5dd86 100755 --- a/app/src/main/java/ch/logixisland/anuto/engine/logic/persistence/Persister.java +++ b/app/src/main/java/ch/logixisland/anuto/engine/logic/persistence/Persister.java @@ -4,6 +4,8 @@ public interface Persister { void resetState(); + void readState(KeyValueStore gameState); + void writeState(KeyValueStore gameState); } diff --git a/app/src/main/java/ch/logixisland/anuto/engine/render/Renderer.java b/app/src/main/java/ch/logixisland/anuto/engine/render/Renderer.java index 6dfc2d8b..1a527b23 100755 --- a/app/src/main/java/ch/logixisland/anuto/engine/render/Renderer.java +++ b/app/src/main/java/ch/logixisland/anuto/engine/render/Renderer.java @@ -1,7 +1,9 @@ package ch.logixisland.anuto.engine.render; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.RectF; import android.view.View; import java.lang.ref.WeakReference; @@ -60,6 +62,22 @@ public void invalidate() { } } + public Bitmap getScreenshot() { + RectF mapRect = mViewport.getMapRect(); + Bitmap bitmap = Bitmap.createBitmap(Math.round(mapRect.width()), Math.round(mapRect.height()), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + + //that call would eventually end in Renderer.draw(), so just take a shortcut. + //it should be sufficient, since we only need our drawables + //mViewRef.get().draw(canvas); + + //perhaps build another draw routine, so the game map is better visible + //e.g. an independent theme just for savegame screenshot + draw(canvas); + + return bitmap; + } + public void draw(Canvas canvas) { mLock.lock(); diff --git a/app/src/main/java/ch/logixisland/anuto/engine/render/Viewport.java b/app/src/main/java/ch/logixisland/anuto/engine/render/Viewport.java index cd13da57..2887ee26 100755 --- a/app/src/main/java/ch/logixisland/anuto/engine/render/Viewport.java +++ b/app/src/main/java/ch/logixisland/anuto/engine/render/Viewport.java @@ -14,6 +14,7 @@ public class Viewport { private float mScreenWidth; private float mScreenHeight; private RectF mScreenClipRect; + private RectF mMapRect; public void setGameSize(int width, int height) { mGameWidth = width; @@ -36,6 +37,10 @@ public RectF getScreenClipRect() { return mScreenClipRect; } + public RectF getMapRect() { + return mMapRect; + } + public Vector2 screenToGame(Vector2 pos) { float[] pts = {pos.x(), pos.y()}; mScreenMatrixInverse.mapPoints(pts); @@ -46,11 +51,22 @@ private void calcScreenMatrix() { mScreenMatrix = new Matrix(); float tileSize = Math.min(mScreenWidth / mGameWidth, mScreenHeight / mGameHeight); + + float width = (tileSize * mGameWidth); + float height = (tileSize * mGameHeight); + + float left = (mScreenWidth - width) / 2f; + float top = 0f; + float right = left + width; + float bottom = top + height; + + mMapRect = new RectF(left, top, right, bottom); + mScreenMatrix.postTranslate(0.5f, 0.5f); mScreenMatrix.postScale(tileSize, tileSize); - float paddingLeft = (mScreenWidth - (tileSize * mGameWidth)) / 2f; - float paddingBottom = (mScreenHeight - (tileSize * mGameHeight)); + float paddingLeft = (mScreenWidth - width) / 2f; + float paddingBottom = (mScreenHeight - height); mScreenMatrix.postTranslate(paddingLeft, paddingBottom); mScreenMatrix.postScale(1f, -1f); diff --git a/app/src/main/java/ch/logixisland/anuto/engine/render/sprite/AnimatedSprite.java b/app/src/main/java/ch/logixisland/anuto/engine/render/sprite/AnimatedSprite.java index 879d7d68..f2c1d15a 100755 --- a/app/src/main/java/ch/logixisland/anuto/engine/render/sprite/AnimatedSprite.java +++ b/app/src/main/java/ch/logixisland/anuto/engine/render/sprite/AnimatedSprite.java @@ -37,7 +37,7 @@ public void setSequence(int[] sequence) { public void setSequenceForward() { int bitmapCount = getTemplate().getBitmapCount(); - int seq[] = new int[bitmapCount]; + int[] seq = new int[bitmapCount]; for (int i = 0; i < seq.length; i++) { seq[i] = i; @@ -48,7 +48,7 @@ public void setSequenceForward() { public void setSequenceForwardBackward() { int bitmapCount = getTemplate().getBitmapCount(); - int seq[] = new int[bitmapCount * 2 - 2]; + int[] seq = new int[bitmapCount * 2 - 2]; for (int i = 0; i < seq.length; i++) { if (i < bitmapCount) { @@ -63,7 +63,7 @@ public void setSequenceForwardBackward() { public void setSequenceBackward() { int bitmapCount = getTemplate().getBitmapCount(); - int seq[] = new int[bitmapCount]; + int[] seq = new int[bitmapCount]; for (int i = 0; i < seq.length; i++) { seq[i] = bitmapCount - 1 - i; diff --git a/app/src/main/java/ch/logixisland/anuto/entity/effect/AreaObserver.java b/app/src/main/java/ch/logixisland/anuto/entity/effect/AreaObserver.java index 61f4ea53..c51e8bdd 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/effect/AreaObserver.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/effect/AreaObserver.java @@ -24,6 +24,7 @@ public class AreaObserver implements Entity.Listener { public interface Listener { void enemyEntered(Enemy enemy); + void enemyExited(Enemy enemy); } diff --git a/app/src/main/java/ch/logixisland/anuto/entity/effect/GlueEffect.java b/app/src/main/java/ch/logixisland/anuto/entity/effect/GlueEffect.java index 0c4eb172..b00e148f 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/effect/GlueEffect.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/effect/GlueEffect.java @@ -12,7 +12,6 @@ import ch.logixisland.anuto.engine.render.sprite.SpriteTransformer; import ch.logixisland.anuto.engine.render.sprite.StaticSprite; import ch.logixisland.anuto.entity.enemy.Enemy; -import ch.logixisland.anuto.entity.enemy.Flyer; import ch.logixisland.anuto.util.RandomUtils; import ch.logixisland.anuto.util.math.Vector2; @@ -91,15 +90,11 @@ public void tick() { @Override public void enemyEntered(Enemy e) { - if (!(e instanceof Flyer)) { - e.modifySpeed(1f / mIntensity); - } + e.modifySpeed(1f / mIntensity, getOrigin()); } @Override public void enemyExited(Enemy e) { - if (!(e instanceof Flyer)) { - e.modifySpeed(mIntensity); - } + e.modifySpeed(mIntensity, getOrigin()); } } diff --git a/app/src/main/java/ch/logixisland/anuto/entity/effect/TeleportEffect.java b/app/src/main/java/ch/logixisland/anuto/entity/effect/TeleportEffect.java index 53d50ddf..062f2ab8 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/effect/TeleportEffect.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/effect/TeleportEffect.java @@ -39,6 +39,7 @@ public void draw(Canvas canvas) { } } + private Enemy mTarget; private float mDistance; diff --git a/app/src/main/java/ch/logixisland/anuto/entity/enemy/Blob.java b/app/src/main/java/ch/logixisland/anuto/entity/enemy/Blob.java index 99ad1006..71be0083 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/enemy/Blob.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/enemy/Blob.java @@ -15,16 +15,10 @@ public class Blob extends Enemy implements SpriteTransformation { - public final static String ENTITY_NAME = "blob"; + public final static String ENTITY_NAME = EnemyType.blob.name(); private final static float ANIMATION_SPEED = 1.5f; - private final static EnemyProperties ENEMY_PROPERTIES = new EnemyProperties.Builder() - .setHealth(600) - .setSpeed(0.5f) - .setReward(20) - .setWeakAgainst(WeaponType.Explosive) - .setStrongAgainst(WeaponType.Bullet) - .build(); + private final static EnemyProperties ENEMY_PROPERTIES = new EnemyProperties.Builder(ENTITY_NAME).build(); public static class Factory extends EntityFactory { @Override diff --git a/app/src/main/java/ch/logixisland/anuto/entity/enemy/Enemy.java b/app/src/main/java/ch/logixisland/anuto/entity/enemy/Enemy.java index da9de448..b2e6d9ad 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/enemy/Enemy.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/enemy/Enemy.java @@ -35,7 +35,9 @@ public Float apply(Enemy input) { public interface Listener { void enemyKilled(Enemy enemy); + void enemyFinished(Enemy enemy); + void enemyRemoved(Enemy enemy); } @@ -177,8 +179,14 @@ public float getSpeed() { return mEnemyProperties.getSpeed() * Math.max(mSpeedModifier, GameSettings.MIN_SPEED_MODIFIER); } - public void modifySpeed(float f) { - mSpeedModifier = mSpeedModifier * f; + public void modifySpeed(float f, Entity origin) { + if (origin instanceof Tower) { + Tower originTower = (Tower) origin; + + if (!mEnemyProperties.getStrongAgainst().contains(originTower.getWeaponType())) { + mSpeedModifier = mSpeedModifier * f; + } + } } private float getDistanceRemaining() { diff --git a/app/src/main/java/ch/logixisland/anuto/entity/enemy/EnemyProperties.java b/app/src/main/java/ch/logixisland/anuto/entity/enemy/EnemyProperties.java index 8e5fe53f..f2e6c5e2 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/enemy/EnemyProperties.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/enemy/EnemyProperties.java @@ -6,6 +6,7 @@ public class EnemyProperties { + private EnemyType mEnemyType; private int mHealth; private float mSpeed; private int mReward; @@ -16,27 +17,74 @@ public static class Builder { private EnemyProperties mResult = new EnemyProperties(); - public Builder setHealth(int health) { + public Builder(String entityName) { + + this.setEnemyType(EnemyType.valueOf(entityName)); + + switch (mResult.getEnemyType()) { + case soldier: + this.setHealth(300) + .setSpeed(1.0f) + .setReward(10); + break; + case blob: + this.setHealth(600) + .setSpeed(0.5f) + .setReward(20) + .setWeakAgainst(WeaponType.Explosive) + .setStrongAgainst(WeaponType.Bullet); + break; + case sprinter: + this.setHealth(200) + .setSpeed(3.0f) + .setReward(15) + .setWeakAgainst(WeaponType.Explosive) + .setStrongAgainst(WeaponType.Laser); + break; + case flyer: + this.setHealth(400) + .setSpeed(1.3f) + .setReward(30) + .setWeakAgainst(WeaponType.Laser, WeaponType.Bullet) + .setStrongAgainst(WeaponType.Glue); + break; + case healer: + this.setHealth(400) + .setSpeed(1.2f) + .setReward(30) + .setWeakAgainst(WeaponType.Laser, WeaponType.Bullet); + break; + default: + throw new RuntimeException("Unknown enemy!"); + } + } + + private Builder setEnemyType(EnemyType enemyType) { + mResult.mEnemyType = enemyType; + return this; + } + + private Builder setHealth(int health) { mResult.mHealth = health; return this; } - public Builder setSpeed(float speed) { + private Builder setSpeed(float speed) { mResult.mSpeed = speed; return this; } - public Builder setReward(int reward) { + private Builder setReward(int reward) { mResult.mReward = reward; return this; } - public Builder setWeakAgainst(WeaponType... weakAgainst) { + private Builder setWeakAgainst(WeaponType... weakAgainst) { mResult.mWeakAgainst = Arrays.asList(weakAgainst); return this; } - public Builder setStrongAgainst(WeaponType... strongAgainst) { + private Builder setStrongAgainst(WeaponType... strongAgainst) { mResult.mStrongAgainst = Arrays.asList(strongAgainst); return this; } @@ -47,6 +95,10 @@ public EnemyProperties build() { } + public EnemyType getEnemyType() { + return mEnemyType; + } + public int getHealth() { return mHealth; } diff --git a/app/src/main/java/ch/logixisland/anuto/entity/enemy/EnemyType.java b/app/src/main/java/ch/logixisland/anuto/entity/enemy/EnemyType.java new file mode 100644 index 00000000..943416be --- /dev/null +++ b/app/src/main/java/ch/logixisland/anuto/entity/enemy/EnemyType.java @@ -0,0 +1,9 @@ +package ch.logixisland.anuto.entity.enemy; + +public enum EnemyType { + soldier, + blob, + sprinter, + flyer, + healer +} diff --git a/app/src/main/java/ch/logixisland/anuto/entity/enemy/Flyer.java b/app/src/main/java/ch/logixisland/anuto/entity/enemy/Flyer.java index a89c811f..a91719bc 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/enemy/Flyer.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/enemy/Flyer.java @@ -15,15 +15,10 @@ public class Flyer extends Enemy implements SpriteTransformation { - public final static String ENTITY_NAME = "flyer"; + public final static String ENTITY_NAME = EnemyType.flyer.name(); private final static float ANIMATION_SPEED = 1.0f; - private final static EnemyProperties ENEMY_PROPERTIES = new EnemyProperties.Builder() - .setHealth(400) - .setSpeed(1.3f) - .setReward(30) - .setWeakAgainst(WeaponType.Laser, WeaponType.Bullet) - .build(); + private final static EnemyProperties ENEMY_PROPERTIES = new EnemyProperties.Builder(ENTITY_NAME).build(); public static class Factory extends EntityFactory { @Override diff --git a/app/src/main/java/ch/logixisland/anuto/entity/enemy/Healer.java b/app/src/main/java/ch/logixisland/anuto/entity/enemy/Healer.java index aa617b55..60ab2706 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/enemy/Healer.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/enemy/Healer.java @@ -22,7 +22,7 @@ public class Healer extends Enemy implements SpriteTransformation { - public final static String ENTITY_NAME = "healer"; + public final static String ENTITY_NAME = EnemyType.healer.name(); private final static float ANIMATION_SPEED = 1.5f; private final static float HEAL_SCALE_FACTOR = 2f; private final static float HEAL_ROTATION = 2.5f; @@ -32,12 +32,7 @@ public class Healer extends Enemy implements SpriteTransformation { private final static float HEAL_DURATION = 1.5f; private final static float HEAL_RADIUS = 0.7f; - private final static EnemyProperties ENEMY_PROPERTIES = new EnemyProperties.Builder() - .setHealth(400) - .setSpeed(1.2f) - .setReward(30) - .setWeakAgainst(WeaponType.Laser, WeaponType.Bullet) - .build(); + private final static EnemyProperties ENEMY_PROPERTIES = new EnemyProperties.Builder(ENTITY_NAME).build(); public static class Factory extends EntityFactory { @Override diff --git a/app/src/main/java/ch/logixisland/anuto/entity/enemy/Soldier.java b/app/src/main/java/ch/logixisland/anuto/entity/enemy/Soldier.java index 6af8f04e..d71bd815 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/enemy/Soldier.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/enemy/Soldier.java @@ -15,14 +15,10 @@ public class Soldier extends Enemy implements SpriteTransformation { - public final static String ENTITY_NAME = "soldier"; + public final static String ENTITY_NAME = EnemyType.soldier.name(); private final static float ANIMATION_SPEED = 1f; - private final static EnemyProperties ENEMY_PROPERTIES = new EnemyProperties.Builder() - .setHealth(300) - .setSpeed(1.0f) - .setReward(10) - .build(); + private final static EnemyProperties ENEMY_PROPERTIES = new EnemyProperties.Builder(ENTITY_NAME).build(); public static class Factory extends EntityFactory { @Override diff --git a/app/src/main/java/ch/logixisland/anuto/entity/enemy/Sprinter.java b/app/src/main/java/ch/logixisland/anuto/entity/enemy/Sprinter.java index ccd5447d..15b94d71 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/enemy/Sprinter.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/enemy/Sprinter.java @@ -17,16 +17,10 @@ public class Sprinter extends Enemy implements SpriteTransformation { - public final static String ENTITY_NAME = "sprinter"; + public final static String ENTITY_NAME = EnemyType.sprinter.name(); private final static float ANIMATION_SPEED = 0.7f; - private final static EnemyProperties ENEMY_PROPERTIES = new EnemyProperties.Builder() - .setHealth(200) - .setSpeed(3.0f) - .setReward(15) - .setWeakAgainst(WeaponType.Explosive) - .setStrongAgainst(WeaponType.Laser) - .build(); + private final static EnemyProperties ENEMY_PROPERTIES = new EnemyProperties.Builder(ENTITY_NAME).build(); public static class Factory extends EntityFactory { @Override diff --git a/app/src/main/java/ch/logixisland/anuto/entity/enemy/WeaponType.java b/app/src/main/java/ch/logixisland/anuto/entity/enemy/WeaponType.java index 5c184780..5c24918c 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/enemy/WeaponType.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/enemy/WeaponType.java @@ -4,5 +4,6 @@ public enum WeaponType { None, Bullet, Laser, - Explosive + Explosive, + Glue } diff --git a/app/src/main/java/ch/logixisland/anuto/entity/shot/GlueShot.java b/app/src/main/java/ch/logixisland/anuto/entity/shot/GlueShot.java index 7f85138e..6a6f13a3 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/shot/GlueShot.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/shot/GlueShot.java @@ -22,6 +22,7 @@ private class StaticData { public SpriteTemplate mSpriteTemplate; } + private float mIntensity; private float mDuration; @@ -29,6 +30,7 @@ private class StaticData { private AnimatedSprite mSprite; private Sound mSound; + public GlueShot(Entity origin, Vector2 position, Vector2 target, float intensity, float duration) { super(origin); setPosition(position); diff --git a/app/src/main/java/ch/logixisland/anuto/entity/shot/TargetTracker.java b/app/src/main/java/ch/logixisland/anuto/entity/shot/TargetTracker.java index 0fae2a77..99d63cf0 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/shot/TargetTracker.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/shot/TargetTracker.java @@ -9,6 +9,7 @@ public class TargetTracker implements Entity.Listener { public interface Listener { void targetReached(Enemy target); + void targetLost(Enemy target); } diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/BouncingLaser.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/BouncingLaser.java index e459a413..80e8e09b 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/BouncingLaser.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/BouncingLaser.java @@ -41,6 +41,7 @@ public class BouncingLaser extends Tower implements SpriteTransformation { .setEnhanceReload(0.1f) .setUpgradeTowerName(ch.logixisland.anuto.entity.tower.StraightLaser.ENTITY_NAME) .setUpgradeCost(96450) + .setUpgradeLevel(2) .build(); public static class Factory extends EntityFactory { @@ -163,6 +164,7 @@ public List getTowerInfoValues() { List properties = new ArrayList<>(); properties.add(new TowerInfoValue(R.string.damage, getDamage())); properties.add(new TowerInfoValue(R.string.reload, getReloadTime())); + properties.add(new TowerInfoValue(R.string.dps, getDamage() / getReloadTime())); properties.add(new TowerInfoValue(R.string.range, getRange())); properties.add(new TowerInfoValue(R.string.inflicted, getDamageInflicted())); return properties; diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/Canon.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/Canon.java index 3d731d00..e678e263 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/Canon.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/Canon.java @@ -45,6 +45,7 @@ public class Canon extends Tower implements SpriteTransformation { .setEnhanceReload(0.05f) .setUpgradeTowerName(DualCanon.ENTITY_NAME) .setUpgradeCost(5600) + .setUpgradeLevel(1) .build(); public static class Factory extends EntityFactory { @@ -183,6 +184,7 @@ public List getTowerInfoValues() { List properties = new ArrayList<>(); properties.add(new TowerInfoValue(R.string.damage, getDamage())); properties.add(new TowerInfoValue(R.string.reload, getReloadTime())); + properties.add(new TowerInfoValue(R.string.dps, getDamage() / getReloadTime())); properties.add(new TowerInfoValue(R.string.range, getRange())); properties.add(new TowerInfoValue(R.string.inflicted, getDamageInflicted())); return properties; diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/DualCanon.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/DualCanon.java index f54567f8..703a523a 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/DualCanon.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/DualCanon.java @@ -45,6 +45,7 @@ public class DualCanon extends Tower implements SpriteTransformation { .setEnhanceReload(0.03f) .setUpgradeTowerName(MachineGun.ENTITY_NAME) .setUpgradeCost(88500) + .setUpgradeLevel(2) .build(); public static class Factory extends EntityFactory { @@ -247,6 +248,7 @@ public List getTowerInfoValues() { List properties = new ArrayList<>(); properties.add(new TowerInfoValue(R.string.damage, getDamage())); properties.add(new TowerInfoValue(R.string.reload, getReloadTime())); + properties.add(new TowerInfoValue(R.string.dps, getDamage() / getReloadTime())); properties.add(new TowerInfoValue(R.string.range, getRange())); properties.add(new TowerInfoValue(R.string.inflicted, getDamageInflicted())); return properties; diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/GlueGun.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/GlueGun.java index 7e1bea4d..eb52b1a3 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/GlueGun.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/GlueGun.java @@ -37,7 +37,7 @@ public class GlueGun extends Tower implements SpriteTransformation { .setRange(2.5f) .setReload(3.0f) .setMaxLevel(5) - .setWeaponType(WeaponType.None) + .setWeaponType(WeaponType.Glue) .setEnhanceBase(1.2f) .setEnhanceCost(200) .setEnhanceDamage(0) @@ -45,6 +45,7 @@ public class GlueGun extends Tower implements SpriteTransformation { .setEnhanceReload(0f) .setUpgradeTowerName(Teleporter.ENTITY_NAME) .setUpgradeCost(1700) + .setUpgradeLevel(2) .build(); public static class Factory extends EntityFactory { diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/GlueTower.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/GlueTower.java index 8b491624..ff31d522 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/GlueTower.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/GlueTower.java @@ -43,7 +43,7 @@ public class GlueTower extends Tower implements SpriteTransformation { .setRange(1.5f) .setReload(2.0f) .setMaxLevel(5) - .setWeaponType(WeaponType.None) + .setWeaponType(WeaponType.Glue) .setEnhanceBase(1.2f) .setEnhanceCost(100) .setEnhanceDamage(0) @@ -51,6 +51,7 @@ public class GlueTower extends Tower implements SpriteTransformation { .setEnhanceReload(0f) .setUpgradeTowerName(GlueGun.ENTITY_NAME) .setUpgradeCost(800) + .setUpgradeLevel(1) .build(); public static class Factory extends EntityFactory { diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/MachineGun.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/MachineGun.java index cc831b77..3b1c6624 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/MachineGun.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/MachineGun.java @@ -43,6 +43,7 @@ public class MachineGun extends Tower implements SpriteTransformation { .setEnhanceDamage(120) .setEnhanceRange(0.05f) .setEnhanceReload(0.005f) + .setUpgradeLevel(3) .build(); public static class Factory extends EntityFactory { @@ -175,6 +176,7 @@ public List getTowerInfoValues() { List properties = new ArrayList<>(); properties.add(new TowerInfoValue(R.string.damage, getDamage())); properties.add(new TowerInfoValue(R.string.reload, getReloadTime())); + properties.add(new TowerInfoValue(R.string.dps, getDamage() / getReloadTime())); properties.add(new TowerInfoValue(R.string.range, getRange())); properties.add(new TowerInfoValue(R.string.inflicted, getDamageInflicted())); return properties; diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/MineLayer.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/MineLayer.java index f92b05a9..1d41e3bd 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/MineLayer.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/MineLayer.java @@ -49,6 +49,7 @@ public class MineLayer extends Tower implements SpriteTransformation { .setEnhanceReload(0.05f) .setUpgradeTowerName(RocketLauncher.ENTITY_NAME) .setUpgradeCost(102850) + .setUpgradeLevel(2) .build(); public static class Factory extends EntityFactory { @@ -230,6 +231,7 @@ public List getTowerInfoValues() { properties.add(new TowerInfoValue(R.string.damage, getDamage())); properties.add(new TowerInfoValue(R.string.splash, mExplosionRadius)); properties.add(new TowerInfoValue(R.string.reload, getReloadTime())); + properties.add(new TowerInfoValue(R.string.dps, getDamage() / getReloadTime())); properties.add(new TowerInfoValue(R.string.range, getRange())); properties.add(new TowerInfoValue(R.string.inflicted, getDamageInflicted())); return properties; diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/Mortar.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/Mortar.java index 91bdb310..9b95a836 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/Mortar.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/Mortar.java @@ -46,6 +46,7 @@ public class Mortar extends Tower implements SpriteTransformation { .setEnhanceReload(0.05f) .setUpgradeTowerName(MineLayer.ENTITY_NAME) .setUpgradeCost(10000) + .setUpgradeLevel(1) .build(); public static class Factory extends EntityFactory { @@ -180,6 +181,7 @@ public List getTowerInfoValues() { properties.add(new TowerInfoValue(R.string.damage, getDamage())); properties.add(new TowerInfoValue(R.string.splash, mExplosionRadius)); properties.add(new TowerInfoValue(R.string.reload, getReloadTime())); + properties.add(new TowerInfoValue(R.string.dps, getDamage() / getReloadTime())); properties.add(new TowerInfoValue(R.string.range, getRange())); properties.add(new TowerInfoValue(R.string.inflicted, getDamageInflicted())); return properties; diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/RocketLauncher.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/RocketLauncher.java index fe7084c2..008f08c3 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/RocketLauncher.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/RocketLauncher.java @@ -40,6 +40,7 @@ public class RocketLauncher extends Tower implements SpriteTransformation { .setEnhanceDamage(410) .setEnhanceRange(0.1f) .setEnhanceReload(0.07f) + .setUpgradeLevel(3) .build(); public static class Factory extends EntityFactory { @@ -180,6 +181,7 @@ public List getTowerInfoValues() { properties.add(new TowerInfoValue(R.string.damage, getDamage())); properties.add(new TowerInfoValue(R.string.splash, mExplosionRadius)); properties.add(new TowerInfoValue(R.string.reload, getReloadTime())); + properties.add(new TowerInfoValue(R.string.dps, getDamage() / getReloadTime())); properties.add(new TowerInfoValue(R.string.range, getRange())); properties.add(new TowerInfoValue(R.string.inflicted, getDamageInflicted())); return properties; diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/SimpleLaser.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/SimpleLaser.java index d3801d1d..18b87ffe 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/SimpleLaser.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/SimpleLaser.java @@ -40,6 +40,7 @@ public class SimpleLaser extends Tower implements SpriteTransformation { .setEnhanceReload(0.1f) .setUpgradeTowerName(ch.logixisland.anuto.entity.tower.BouncingLaser.ENTITY_NAME) .setUpgradeCost(7000) + .setUpgradeLevel(1) .build(); public static class Factory extends EntityFactory { @@ -153,6 +154,7 @@ public List getTowerInfoValues() { List properties = new ArrayList<>(); properties.add(new TowerInfoValue(R.string.damage, getDamage())); properties.add(new TowerInfoValue(R.string.reload, getReloadTime())); + properties.add(new TowerInfoValue(R.string.dps, getDamage() / getReloadTime())); properties.add(new TowerInfoValue(R.string.range, getRange())); properties.add(new TowerInfoValue(R.string.inflicted, getDamageInflicted())); return properties; diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/StraightLaser.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/StraightLaser.java index 9110482d..174771f2 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/StraightLaser.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/StraightLaser.java @@ -38,6 +38,7 @@ public class StraightLaser extends Tower implements SpriteTransformation { .setEnhanceDamage(410) .setEnhanceRange(0.07f) .setEnhanceReload(0.07f) + .setUpgradeLevel(3) .build(); public static class Factory extends EntityFactory { @@ -152,6 +153,7 @@ public List getTowerInfoValues() { List properties = new ArrayList<>(); properties.add(new TowerInfoValue(R.string.damage, getDamage())); properties.add(new TowerInfoValue(R.string.reload, getReloadTime())); + properties.add(new TowerInfoValue(R.string.dps, getDamage() / getReloadTime())); properties.add(new TowerInfoValue(R.string.range, getRange())); properties.add(new TowerInfoValue(R.string.inflicted, getDamageInflicted())); return properties; diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/Teleporter.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/Teleporter.java index 531e0963..f84f400d 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/Teleporter.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/Teleporter.java @@ -41,6 +41,7 @@ public class Teleporter extends Tower implements SpriteTransformation { .setEnhanceDamage(0) .setEnhanceRange(0f) .setEnhanceReload(0.5f) + .setUpgradeLevel(3) .build(); public static class Factory extends EntityFactory { diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/Tower.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/Tower.java index b69d6e14..3348777f 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/Tower.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/Tower.java @@ -18,7 +18,10 @@ public abstract class Tower extends Entity { public interface Listener { void damageInflicted(float totalDamage); + void valueChanged(int value); + + void strengthChanged(); } private TowerProperties mTowerProperties; @@ -110,6 +113,7 @@ public boolean isBuilt() { public void setBuilt() { mBuilt = true; mReloaded = true; + updateStrength(); } public WeaponType getWeaponType() { @@ -176,6 +180,17 @@ public int getUpgradeCost() { return mTowerProperties.getUpgradeCost(); } + public int getUpgradeLevel() { + return mTowerProperties.getUpgradeLevel(); + } + + private void updateStrength() { + + for (Listener listener : mListeners) { + listener.strengthChanged(); + } + } + public void enhance() { mValue += getEnhanceCost(); mDamage += mTowerProperties.getEnhanceDamage() * (float) Math.pow(mTowerProperties.getEnhanceBase(), mLevel - 1); @@ -185,6 +200,7 @@ public void enhance() { mLevel++; mReloadTimer.setInterval(mReloadTime); + updateStrength(); } public boolean isEnhanceable() { diff --git a/app/src/main/java/ch/logixisland/anuto/entity/tower/TowerProperties.java b/app/src/main/java/ch/logixisland/anuto/entity/tower/TowerProperties.java index 1bb26fda..71b9bea3 100755 --- a/app/src/main/java/ch/logixisland/anuto/entity/tower/TowerProperties.java +++ b/app/src/main/java/ch/logixisland/anuto/entity/tower/TowerProperties.java @@ -17,6 +17,7 @@ public class TowerProperties { private float mEnhanceReload; private String mUpgradeTowerName; private int mUpgradeCost; + private int mUpgradeLevel; public static class Builder { @@ -87,6 +88,11 @@ public Builder setUpgradeCost(int upgradeCost) { return this; } + public Builder setUpgradeLevel(int upgradeLevel) { + mResult.mUpgradeLevel = upgradeLevel; + return this; + } + public TowerProperties build() { return mResult; } @@ -144,4 +150,8 @@ public int getUpgradeCost() { return mUpgradeCost; } + public int getUpgradeLevel() { + return mUpgradeLevel; + } + } diff --git a/app/src/main/java/ch/logixisland/anuto/util/StringUtils.java b/app/src/main/java/ch/logixisland/anuto/util/StringUtils.java index b3655611..a6d9dcc6 100755 --- a/app/src/main/java/ch/logixisland/anuto/util/StringUtils.java +++ b/app/src/main/java/ch/logixisland/anuto/util/StringUtils.java @@ -24,23 +24,46 @@ public static String formatSuffix(float value) { public static String formatSuffix(float value, boolean integer) { String suffix = ""; - boolean big = false; + boolean usePointedFmt = false; + /*if (value >= 1e25f) { + suffix = "Y"; + value /= 1e24f; + usePointedFmt = true; + } else if (value >= 1e22f) { + suffix = "Z"; + value /= 1e21f; + usePointedFmt = true; + } else if (value >= 1e19f) { + suffix = "E"; + value /= 1e18f; + usePointedFmt = true; + } else if (value >= 1e16f) { + suffix = "P"; + value /= 1e15f; + usePointedFmt = true; + } else if (value >= 1e13f) { + suffix = "T"; + value /= 1e12f; + usePointedFmt = true; + } else*/ if (value >= 1e10f) { suffix = "G"; value /= 1e9f; - big = true; + usePointedFmt = true; } else if (value >= 1e7f) { suffix = "M"; value /= 1e6f; - big = true; + usePointedFmt = true; } else if (value >= 1e4f) { suffix = "k"; value /= 1e3f; - big = true; + usePointedFmt = true; + } else if (value < 1e2f) { + usePointedFmt = (value > 0f) && !integer; } - DecimalFormat fmt = (value < 1e2f && (!integer || big)) ? fmt1 : fmt0; + DecimalFormat fmt = (usePointedFmt) ? fmt1 : fmt0; return fmt.format(value) + suffix; } diff --git a/app/src/main/java/ch/logixisland/anuto/view/faq/EnemiesAdapter.java b/app/src/main/java/ch/logixisland/anuto/view/faq/EnemiesAdapter.java new file mode 100644 index 00000000..b6d22b0a --- /dev/null +++ b/app/src/main/java/ch/logixisland/anuto/view/faq/EnemiesAdapter.java @@ -0,0 +1,212 @@ +package ch.logixisland.anuto.view.faq; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import java.lang.ref.WeakReference; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import ch.logixisland.anuto.R; +import ch.logixisland.anuto.engine.theme.Theme; +import ch.logixisland.anuto.entity.enemy.EnemyProperties; +import ch.logixisland.anuto.entity.enemy.EnemyType; + +public class EnemiesAdapter extends BaseAdapter { + private static String sTheme; + private static Map sEnemyCache; + + private final List mEnemyProperties; + + private final WeakReference mActivityRef; + private Context appContext; + private Theme mTheme; + + public EnemiesAdapter(Activity activity, Context context, Theme theme) { + mActivityRef = new WeakReference<>(activity); + appContext = context; + mTheme = theme; + + if ((sTheme != theme.getName()) || (sEnemyCache == null)) { + sTheme = theme.getName(); + sEnemyCache = new HashMap<>(); + } + + mEnemyProperties = new ArrayList<>(); + for (EnemyType x : EnemyType.values()) { + mEnemyProperties.add(new EnemyProperties.Builder(x.name()).build()); + } + } + + static private class ViewHolder { + ImageView img_enemy; + TextView txt_name; + TextView txt_health; + TextView txt_speed; + TextView txt_reward; + TextView txt_weak_against; + TextView txt_strong_against; + + ViewHolder(View view) { + img_enemy = view.findViewById(R.id.img_enemy); + txt_name = view.findViewById(R.id.txt_name); + txt_health = view.findViewById(R.id.txt_health); + txt_speed = view.findViewById(R.id.txt_speed); + txt_reward = view.findViewById(R.id.txt_reward); + txt_weak_against = view.findViewById(R.id.txt_weak_against); + txt_strong_against = view.findViewById(R.id.txt_strong_against); + } + } + + @Override + public int getCount() { + return mEnemyProperties.size(); + } + + @Override + public Object getItem(int position) { + return mEnemyProperties.get(position); + } + + @Override + public long getItemId(int position) { + return 0; + } + + private Bitmap extractSingleBmp(EnemyType enemyType) { + int attrId, spriteCount, spriteId; + + switch (enemyType) { + case soldier: + attrId = R.attr.soldier; + spriteCount = 12; + spriteId = 0; + break; + case blob: + attrId = R.attr.blob; + spriteCount = 9; + spriteId = 0; + break; + case sprinter: + attrId = R.attr.sprinter; + spriteCount = 6; + spriteId = 3; + break; + case flyer: + attrId = R.attr.flyer; + spriteCount = 6; + spriteId = 4; + break; + case healer: + attrId = R.attr.healer; + spriteCount = 4; + spriteId = 0; + break; + default: + throw new RuntimeException("Unknown enemy!"); + } + + Bitmap sheet = BitmapFactory.decodeResource(appContext.getResources(), mTheme.getResourceId(attrId)); + int spriteWidth = sheet.getWidth() / spriteCount; + int spriteHeight = sheet.getHeight(); + + + float aspect = (float) spriteWidth / spriteHeight; + + float newHeight = 120; + float newWidth = newHeight * aspect; + float scaleHeight = (newHeight) / spriteHeight; + float scaleWidth = (newWidth) / spriteWidth; + Matrix matrix = new Matrix(); + matrix.postScale(scaleWidth, scaleHeight); + + Bitmap resizedBitmap = Bitmap.createBitmap(sheet, spriteWidth * spriteId, 0, spriteWidth, spriteHeight, matrix, false); + sheet.recycle(); + return resizedBitmap; + } + + + public View getView(int position, View convertView, ViewGroup parent) { + Activity activity = mActivityRef.get(); + + if (activity == null) { + return convertView; + } + + View enemyItemView; + + if (convertView == null) { + enemyItemView = LayoutInflater.from(activity).inflate(R.layout.item_enemy, parent, false); + } else { + enemyItemView = convertView; + } + EnemyProperties enemyProperties = mEnemyProperties.get(position); + ViewHolder viewHolder = new ViewHolder(enemyItemView); + String dmp; + + switch (enemyProperties.getEnemyType()) { + case soldier: + dmp = activity.getString(R.string.soldier); + break; + case blob: + dmp = activity.getString(R.string.blob); + break; + case sprinter: + dmp = activity.getString(R.string.sprinter); + break; + case flyer: + dmp = activity.getString(R.string.flyer); + break; + case healer: + dmp = activity.getString(R.string.healer); + break; + default: + throw new RuntimeException("Unknown enemy!"); + } + viewHolder.txt_name.setText(dmp); + + DecimalFormat fmt = new DecimalFormat(); + dmp = fmt.format(enemyProperties.getHealth()); + viewHolder.txt_health.setText(dmp); + + DecimalFormat df2 = new DecimalFormat("#0 '%'"); + dmp = df2.format(enemyProperties.getSpeed() * 100); + viewHolder.txt_speed.setText(dmp); + + dmp = fmt.format(enemyProperties.getReward()); + viewHolder.txt_reward.setText(dmp); + + dmp = TextUtils.join("\n", enemyProperties.getWeakAgainst()); + viewHolder.txt_weak_against.setText(dmp.length() > 0 ? dmp : activity.getString(R.string.none)); + viewHolder.txt_weak_against.setTextColor(mTheme.getColor(R.attr.weakAgainstColor)); + + + dmp = TextUtils.join("\n", enemyProperties.getStrongAgainst()); + viewHolder.txt_strong_against.setText(dmp.length() > 0 ? dmp : activity.getString(R.string.none)); + viewHolder.txt_strong_against.setTextColor(mTheme.getColor(R.attr.strongAgainstColor)); + + + if (!sEnemyCache.containsKey(enemyProperties.getEnemyType())) { + Bitmap bmp = extractSingleBmp(enemyProperties.getEnemyType()); + sEnemyCache.put(enemyProperties.getEnemyType(), bmp); + } + + viewHolder.img_enemy.setImageBitmap(sEnemyCache.get(enemyProperties.getEnemyType())); + + return enemyItemView; + } + +} diff --git a/app/src/main/java/ch/logixisland/anuto/view/faq/EnemyStatsActivity.java b/app/src/main/java/ch/logixisland/anuto/view/faq/EnemyStatsActivity.java new file mode 100644 index 00000000..9ff32bcd --- /dev/null +++ b/app/src/main/java/ch/logixisland/anuto/view/faq/EnemyStatsActivity.java @@ -0,0 +1,44 @@ +package ch.logixisland.anuto.view.faq; + +import android.content.Context; +import android.os.Bundle; +import android.widget.GridView; + +import ch.logixisland.anuto.AnutoApplication; +import ch.logixisland.anuto.R; +import ch.logixisland.anuto.engine.theme.ActivityType; +import ch.logixisland.anuto.engine.theme.Theme; +import ch.logixisland.anuto.engine.theme.ThemeManager; +import ch.logixisland.anuto.view.AnutoActivity; + +public class EnemyStatsActivity extends AnutoActivity implements ThemeManager.Listener { + + private EnemiesAdapter mAdapter; + + private GridView grid_enemies; + private Context appContext; + private Theme mTheme; + + public EnemyStatsActivity() { + AnutoApplication app = AnutoApplication.getInstance(); + appContext = app.getApplicationContext(); + mTheme = app.getGameFactory().getGameEngine().getThemeManager().getTheme(); + + } + + @Override + protected ActivityType getActivityType() { + return ActivityType.Normal; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_enemy_stats); + + mAdapter = new EnemiesAdapter(this, appContext, mTheme); + + grid_enemies = findViewById(R.id.grid_enemies); + grid_enemies.setAdapter(mAdapter); + } +} diff --git a/app/src/main/java/ch/logixisland/anuto/view/game/GameActivity.java b/app/src/main/java/ch/logixisland/anuto/view/game/GameActivity.java index 0fbd8dcf..7a93731f 100755 --- a/app/src/main/java/ch/logixisland/anuto/view/game/GameActivity.java +++ b/app/src/main/java/ch/logixisland/anuto/view/game/GameActivity.java @@ -47,7 +47,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_game); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - view_tower_defense = (GameView) findViewById(R.id.view_tower_defense); + view_tower_defense = findViewById(R.id.view_tower_defense); } @Override diff --git a/app/src/main/java/ch/logixisland/anuto/view/game/GameOverFragment.java b/app/src/main/java/ch/logixisland/anuto/view/game/GameOverFragment.java index 0c263758..4d58d706 100755 --- a/app/src/main/java/ch/logixisland/anuto/view/game/GameOverFragment.java +++ b/app/src/main/java/ch/logixisland/anuto/view/game/GameOverFragment.java @@ -37,7 +37,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_game_over, container, false); - txt_score = (TextView) v.findViewById(R.id.txt_score); + txt_score = v.findViewById(R.id.txt_score); mHandler = new Handler(); diff --git a/app/src/main/java/ch/logixisland/anuto/view/game/HeaderFragment.java b/app/src/main/java/ch/logixisland/anuto/view/game/HeaderFragment.java index d5cbbfa0..caa16b92 100755 --- a/app/src/main/java/ch/logixisland/anuto/view/game/HeaderFragment.java +++ b/app/src/main/java/ch/logixisland/anuto/view/game/HeaderFragment.java @@ -8,6 +8,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; +import android.widget.CheckBox; import android.widget.TextView; import java.util.ArrayList; @@ -42,6 +43,7 @@ public class HeaderFragment extends AnutoFragment implements WaveManager.Listene private Button btn_next_wave; private Button btn_fast_forward; + private CheckBox checkbox_fast_active; private Button btn_menu; private Button btn_build_tower; @@ -63,18 +65,20 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, View v = inflater.inflate(R.layout.fragment_header, container, false); fragment_header = v; - txt_credits = (TextView) v.findViewById(R.id.txt_credits); - txt_lives = (TextView) v.findViewById(R.id.txt_lives); - txt_wave = (TextView) v.findViewById(R.id.txt_wave); - txt_bonus = (TextView) v.findViewById(R.id.txt_bonus); + txt_credits = v.findViewById(R.id.txt_credits); + txt_lives = v.findViewById(R.id.txt_lives); + txt_wave = v.findViewById(R.id.txt_wave); + txt_bonus = v.findViewById(R.id.txt_bonus); - btn_next_wave = (Button) v.findViewById(R.id.btn_next_wave); - btn_fast_forward = (Button) v.findViewById(R.id.btn_fast_forward); - btn_menu = (Button) v.findViewById(R.id.btn_menu); - btn_build_tower = (Button) v.findViewById(R.id.btn_build_tower); + btn_next_wave = v.findViewById(R.id.btn_next_wave); + btn_fast_forward = v.findViewById(R.id.btn_fast_forward); + checkbox_fast_active = v.findViewById(R.id.checkbox_fast_active); + btn_menu = v.findViewById(R.id.btn_menu); + btn_build_tower = v.findViewById(R.id.btn_build_tower); btn_next_wave.setOnClickListener(this); btn_fast_forward.setOnClickListener(this); + checkbox_fast_active.setOnClickListener(this); btn_menu.setOnClickListener(this); btn_build_tower.setOnClickListener(this); fragment_header.setOnClickListener(this); @@ -84,7 +88,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, txt_credits.setText(getString(R.string.credits) + ": " + StringUtils.formatSuffix(mScoreBoard.getCredits())); txt_lives.setText(getString(R.string.lives) + ": " + mScoreBoard.getLives()); txt_bonus.setText(getString(R.string.bonus) + ": " + StringUtils.formatSuffix(mScoreBoard.getWaveBonus() + mScoreBoard.getEarlyBonus())); - btn_fast_forward.setText(getString(mSpeedManager.isFastForwardActive() ? R.string.fast_speed : R.string.normal_speed)); + btn_fast_forward.setText(getString(R.string.var_speed, mSpeedManager.fastForwardMultiplier())); final List towerViews = new ArrayList<>(); towerViews.add((TowerView) v.findViewById(R.id.view_tower_1)); @@ -135,24 +139,36 @@ public void onDetach() { public void onClick(View v) { if (v == fragment_header) { mTowerSelector.selectTower(null); + return; } if (v == btn_next_wave) { mWaveManager.startNextWave(); + return; } if (v == btn_fast_forward) { - mSpeedManager.toggleFastForward(); + mSpeedManager.cycleFastForward(); + return; + } + + if (v == checkbox_fast_active) { + boolean checked = checkbox_fast_active.isChecked(); + if (checked != mSpeedManager.isFastForwardActive()) + mSpeedManager.setFastForwardActive(checked); + return; } if (v == btn_menu) { mTowerSelector.selectTower(null); Intent intent = new Intent(getActivity(), MenuActivity.class); startActivity(intent); + return; } if (v == btn_build_tower) { mTowerSelector.toggleTowerBuildView(); + return; } } @@ -226,7 +242,11 @@ public void gameSpeedChanged() { mHandler.post(new Runnable() { @Override public void run() { - btn_fast_forward.setText(getString(mSpeedManager.isFastForwardActive() ? R.string.fast_speed : R.string.normal_speed)); + btn_fast_forward.setText(getString(R.string.var_speed, mSpeedManager.fastForwardMultiplier())); + + boolean checked = mSpeedManager.isFastForwardActive(); + if (checked != checkbox_fast_active.isChecked()) + checkbox_fast_active.setChecked(checked); } }); } diff --git a/app/src/main/java/ch/logixisland/anuto/view/game/MenuActivity.java b/app/src/main/java/ch/logixisland/anuto/view/game/MenuActivity.java index 1f1f70c2..4ca038b4 100755 --- a/app/src/main/java/ch/logixisland/anuto/view/game/MenuActivity.java +++ b/app/src/main/java/ch/logixisland/anuto/view/game/MenuActivity.java @@ -5,13 +5,17 @@ import android.view.MotionEvent; import android.view.View; import android.widget.Button; +import android.widget.Toast; import ch.logixisland.anuto.AnutoApplication; import ch.logixisland.anuto.GameFactory; import ch.logixisland.anuto.R; import ch.logixisland.anuto.business.game.GameLoader; +import ch.logixisland.anuto.business.game.GameState; import ch.logixisland.anuto.engine.theme.ActivityType; import ch.logixisland.anuto.view.AnutoActivity; +import ch.logixisland.anuto.view.faq.EnemyStatsActivity; +import ch.logixisland.anuto.view.loadmenu.LoadMenuActivity; import ch.logixisland.anuto.view.map.ChangeMapActivity; import ch.logixisland.anuto.view.setting.SettingsActivity; @@ -19,19 +23,26 @@ public class MenuActivity extends AnutoActivity implements View.OnClickListener, private static final int REQUEST_CHANGE_MAP = 1; private static final int REQUEST_SETTINGS = 2; + private static final int REQUEST_LOADMENU = 3; + private static final int REQUEST_ENEMY_STATS = 4; private final GameLoader mGameLoader; + private final GameState mGameState; private View activity_menu; private View menu_layout; private Button btn_restart; private Button btn_change_map; + private Button btn_quicksave; + private Button btn_loadmenu; + private Button btn_enemy_stats; private Button btn_settings; public MenuActivity() { GameFactory factory = AnutoApplication.getInstance().getGameFactory(); mGameLoader = factory.getGameLoader(); + mGameState = factory.getGameState(); } @Override @@ -44,16 +55,24 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_menu); - btn_restart = (Button) findViewById(R.id.btn_restart); - btn_change_map = (Button) findViewById(R.id.btn_change_map); - btn_settings = (Button) findViewById(R.id.btn_settings); + btn_restart = findViewById(R.id.btn_restart); + btn_change_map = findViewById(R.id.btn_change_map); + btn_quicksave = findViewById(R.id.btn_quicksave); + btn_loadmenu = findViewById(R.id.btn_loadmenu); + btn_enemy_stats = findViewById(R.id.btn_enemy_stats); + btn_settings = findViewById(R.id.btn_settings); activity_menu = findViewById(R.id.activity_menu); menu_layout = findViewById(R.id.menu_layout); btn_restart.setOnClickListener(this); btn_change_map.setOnClickListener(this); + btn_quicksave.setOnClickListener(this); + btn_loadmenu.setOnClickListener(this); + btn_enemy_stats.setOnClickListener(this); btn_settings.setOnClickListener(this); + btn_quicksave.setEnabled(mGameState.isGameStarted()); + btn_loadmenu.setEnabled(mGameLoader.hasSavegames()); activity_menu.setOnTouchListener(this); menu_layout.setOnTouchListener(this); @@ -64,16 +83,38 @@ public void onClick(View view) { if (view == btn_restart) { mGameLoader.restart(); finish(); + return; } if (view == btn_change_map) { Intent intent = new Intent(this, ChangeMapActivity.class); startActivityForResult(intent, REQUEST_CHANGE_MAP); + return; + } + + if (view == btn_quicksave) { + mGameLoader.makeNewSavegame(); + btn_loadmenu.setEnabled(true); + Toast.makeText(this, getString(ch.logixisland.anuto.R.string.saveGameSuccessful), Toast.LENGTH_LONG).show(); + return; + } + + if (view == btn_loadmenu) { + Intent intent = new Intent(this, LoadMenuActivity.class); + startActivityForResult(intent, REQUEST_LOADMENU); + return; + } + + if (view == btn_enemy_stats) { + Intent intent = new Intent(this, EnemyStatsActivity.class); + startActivityForResult(intent, REQUEST_ENEMY_STATS); + return; } if (view == btn_settings) { Intent intent = new Intent(this, SettingsActivity.class); startActivityForResult(intent, REQUEST_SETTINGS); + return; } } @@ -99,9 +140,16 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { finish(); } + if (requestCode == REQUEST_LOADMENU) { + finish(); + } + + if (requestCode == REQUEST_ENEMY_STATS) { + finish(); + } + if (requestCode == REQUEST_SETTINGS) { finish(); } } - } diff --git a/app/src/main/java/ch/logixisland/anuto/view/game/TowerInfoFragment.java b/app/src/main/java/ch/logixisland/anuto/view/game/TowerInfoFragment.java index 0cf444de..3613edd3 100755 --- a/app/src/main/java/ch/logixisland/anuto/view/game/TowerInfoFragment.java +++ b/app/src/main/java/ch/logixisland/anuto/view/game/TowerInfoFragment.java @@ -9,6 +9,7 @@ import android.widget.Button; import android.widget.TextView; +import java.text.DecimalFormat; import java.util.List; import ch.logixisland.anuto.AnutoApplication; @@ -31,8 +32,8 @@ public class TowerInfoFragment extends AnutoFragment implements View.OnClickList private Handler mHandler; private TextView txt_level; - private TextView[] txt_property = new TextView[5]; - private TextView[] txt_property_text = new TextView[5]; + private TextView[] txt_property = new TextView[6]; + private TextView[] txt_property_text = new TextView[6]; private Button btn_strategy; private Button btn_lock_target; @@ -52,27 +53,29 @@ public TowerInfoFragment() { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_tower_info, container, false); - txt_level = (TextView) v.findViewById(R.id.txt_level); - txt_property[0] = (TextView) v.findViewById(R.id.txt_property1); - txt_property[1] = (TextView) v.findViewById(R.id.txt_property2); - txt_property[2] = (TextView) v.findViewById(R.id.txt_property3); - txt_property[3] = (TextView) v.findViewById(R.id.txt_property4); - txt_property[4] = (TextView) v.findViewById(R.id.txt_property5); + txt_level = v.findViewById(R.id.txt_level); + txt_property[0] = v.findViewById(R.id.txt_property1); + txt_property[1] = v.findViewById(R.id.txt_property2); + txt_property[2] = v.findViewById(R.id.txt_property3); + txt_property[3] = v.findViewById(R.id.txt_property4); + txt_property[4] = v.findViewById(R.id.txt_property5); + txt_property[5] = v.findViewById(R.id.txt_property6); - TextView txt_level_text = (TextView) v.findViewById(R.id.txt_level_text); + TextView txt_level_text = v.findViewById(R.id.txt_level_text); txt_level_text.setText(getResources().getString(R.string.level) + ":"); - txt_property_text[0] = (TextView) v.findViewById(R.id.txt_property_text1); - txt_property_text[1] = (TextView) v.findViewById(R.id.txt_property_text2); - txt_property_text[2] = (TextView) v.findViewById(R.id.txt_property_text3); - txt_property_text[3] = (TextView) v.findViewById(R.id.txt_property_text4); - txt_property_text[4] = (TextView) v.findViewById(R.id.txt_property_text5); + txt_property_text[0] = v.findViewById(R.id.txt_property_text1); + txt_property_text[1] = v.findViewById(R.id.txt_property_text2); + txt_property_text[2] = v.findViewById(R.id.txt_property_text3); + txt_property_text[3] = v.findViewById(R.id.txt_property_text4); + txt_property_text[4] = v.findViewById(R.id.txt_property_text5); + txt_property_text[5] = v.findViewById(R.id.txt_property_text6); - btn_strategy = (Button) v.findViewById(R.id.btn_strategy); - btn_lock_target = (Button) v.findViewById(R.id.btn_lock_target); - btn_upgrade = (Button) v.findViewById(R.id.btn_upgrade); - btn_enhance = (Button) v.findViewById(R.id.btn_enhance); - btn_sell = (Button) v.findViewById(R.id.btn_sell); + btn_strategy = v.findViewById(R.id.btn_strategy); + btn_lock_target = v.findViewById(R.id.btn_lock_target); + btn_upgrade = v.findViewById(R.id.btn_upgrade); + btn_enhance = v.findViewById(R.id.btn_enhance); + btn_sell = v.findViewById(R.id.btn_sell); btn_strategy.setOnClickListener(this); btn_lock_target.setOnClickListener(this); @@ -117,22 +120,27 @@ public void onDetach() { public void onClick(View v) { if (v == btn_strategy) { mTowerControl.cycleTowerStrategy(); + return; } if (v == btn_lock_target) { mTowerControl.toggleLockTarget(); + return; } if (v == btn_enhance) { mTowerControl.enhanceTower(); + return; } if (v == btn_upgrade) { mTowerControl.upgradeTower(); + return; } if (v == btn_sell) { mTowerControl.sellTower(); + return; } } @@ -180,7 +188,9 @@ private void hide() { } private void refresh(TowerInfo towerInfo) { - txt_level.setText(towerInfo.getLevel() + " / " + towerInfo.getLevelMax()); + DecimalFormat fmt = new DecimalFormat(); + String level = fmt.format(towerInfo.getLevel()) + " / " + fmt.format(towerInfo.getLevelMax()); + txt_level.setText(level); List properties = towerInfo.getProperties(); for (int i = 0; i < properties.size(); i++) { diff --git a/app/src/main/java/ch/logixisland/anuto/view/loadmenu/LoadMenuActivity.java b/app/src/main/java/ch/logixisland/anuto/view/loadmenu/LoadMenuActivity.java new file mode 100644 index 00000000..9ed8e2c1 --- /dev/null +++ b/app/src/main/java/ch/logixisland/anuto/view/loadmenu/LoadMenuActivity.java @@ -0,0 +1,131 @@ +package ch.logixisland.anuto.view.loadmenu; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.util.Log; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewTreeObserver; +import android.widget.AdapterView; +import android.widget.GridView; +import android.widget.ImageView; + +import ch.logixisland.anuto.AnutoApplication; +import ch.logixisland.anuto.GameFactory; +import ch.logixisland.anuto.R; +import ch.logixisland.anuto.business.game.GameLoader; +import ch.logixisland.anuto.business.game.SaveGameRepository; +import ch.logixisland.anuto.view.AnutoActivity; + +import static android.content.ContentValues.TAG; + +public class LoadMenuActivity extends AnutoActivity implements AdapterView.OnItemClickListener, + ViewTreeObserver.OnScrollChangedListener { + + public static int CONTEXT_MENU__DELETE_ID = 0; + + private final GameLoader mGameLoader; + private final SaveGameRepository mSaveGameRepository; + + private SaveGamesAdapter mAdapter; + + private ImageView arrow_up; + private ImageView arrow_down; + private GridView grid_savegames; + + + public LoadMenuActivity() { + GameFactory factory = AnutoApplication.getInstance().getGameFactory(); + mGameLoader = factory.getGameLoader(); + mSaveGameRepository = mGameLoader.getSaveGameRepository(); + } + + @Override + protected ch.logixisland.anuto.engine.theme.ActivityType getActivityType() { + return ch.logixisland.anuto.engine.theme.ActivityType.Normal; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_load_menu); + + mAdapter = new SaveGamesAdapter(this, mSaveGameRepository); + + arrow_up = findViewById(R.id.arrow_up); + arrow_down = findViewById(R.id.arrow_down); + + grid_savegames = findViewById(R.id.grid_savegames); + grid_savegames.setOnItemClickListener(this); + grid_savegames.getViewTreeObserver().addOnScrollChangedListener(this); + grid_savegames.post(new Runnable() { + @Override + public void run() { + updateArrowVisibility(); + } + }); + grid_savegames.setAdapter(mAdapter); + registerForContextMenu(grid_savegames); + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + mGameLoader.loadGameState(mAdapter.getItem(position).getSavegameState()); + + finish(); + } + + public void onCreateContextMenu(android.view.ContextMenu menu, View v, android.view.ContextMenu.ContextMenuInfo menuInfo) { + menu.add(0, CONTEXT_MENU__DELETE_ID, 0, R.string.delete); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + + if (item.getItemId() == CONTEXT_MENU__DELETE_ID) { + final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + Log.d(TAG, "Item Timestamp at POSITION:" + mAdapter.getItem(info.position).getDatetime()); + new AlertDialog.Builder(this) + .setTitle(R.string.delete) + .setMessage(R.string.deleteConfirmation) + .setIcon(android.R.drawable.ic_dialog_alert) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int whichButton) { + mSaveGameRepository.removeSGIAt(info.position); + mAdapter.notifyDataSetChanged(); + } + }) + .setNegativeButton(android.R.string.no, null).show(); + return true; + } + return false; + } + + @Override + public void onScrollChanged() { + updateArrowVisibility(); + } + + private void updateArrowVisibility() { + if (grid_savegames.getChildCount() <= 0) { + arrow_up.setVisibility(View.INVISIBLE); + arrow_down.setVisibility(View.INVISIBLE); + return; + } + + if (grid_savegames.getFirstVisiblePosition() == 0) { + arrow_up.setVisibility(grid_savegames.getChildAt(0).getTop() < -10 ? View.VISIBLE : View.INVISIBLE); + } else { + arrow_up.setVisibility(grid_savegames.getFirstVisiblePosition() > 0 ? View.VISIBLE : View.INVISIBLE); + } + + if (grid_savegames.getLastVisiblePosition() == mAdapter.getCount() - 1) { + arrow_down.setVisibility(grid_savegames.getChildAt(grid_savegames.getChildCount() - 1).getBottom() > grid_savegames.getHeight() + 10 ? View.VISIBLE : View.INVISIBLE); + } else { + arrow_down.setVisibility(grid_savegames.getLastVisiblePosition() < mAdapter.getCount() - 1 ? View.VISIBLE : View.INVISIBLE); + } + } +} diff --git a/app/src/main/java/ch/logixisland/anuto/view/loadmenu/SaveGamesAdapter.java b/app/src/main/java/ch/logixisland/anuto/view/loadmenu/SaveGamesAdapter.java new file mode 100644 index 00000000..172baae5 --- /dev/null +++ b/app/src/main/java/ch/logixisland/anuto/view/loadmenu/SaveGamesAdapter.java @@ -0,0 +1,98 @@ +package ch.logixisland.anuto.view.loadmenu; + +import android.app.Activity; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import ch.logixisland.anuto.R; +import ch.logixisland.anuto.business.game.SaveGameInfo; +import ch.logixisland.anuto.business.game.SaveGameRepository; +import ch.logixisland.anuto.util.StringUtils; + +public class SaveGamesAdapter extends BaseAdapter { + + private static final Map sThumbCache = new HashMap<>(); + + private final WeakReference mActivityRef; + private final List mSaveGameInfos; + + SaveGamesAdapter(Activity activity, SaveGameRepository saveGameRepository) { + mActivityRef = new WeakReference<>(activity); + mSaveGameInfos = saveGameRepository.getSavegameInfos(); + } + + static private class ViewHolder { + ImageView img_thumb; + TextView txt_datetime; + TextView txt_score; + TextView txt_waveNumber; + TextView txt_lives; + + ViewHolder(View view) { + img_thumb = view.findViewById(R.id.img_thumb); + txt_datetime = view.findViewById(R.id.txt_datetime); + txt_score = view.findViewById(R.id.txt_score); + txt_waveNumber = view.findViewById(R.id.txt_waveNumber); + txt_lives = view.findViewById(R.id.txt_lives); + } + } + + @Override + public int getCount() { + return mSaveGameInfos.size(); + } + + @Override + public SaveGameInfo getItem(int position) { + return mSaveGameInfos.get(position); + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Activity activity = mActivityRef.get(); + + if (activity == null) { + return convertView; + } + + View sgItemView; + + if (convertView == null) { + sgItemView = LayoutInflater.from(activity).inflate(R.layout.item_savegame, parent, false); + } else { + sgItemView = convertView; + } + + Resources resources = activity.getResources(); + SaveGameInfo saveGameInfo = mSaveGameInfos.get(position); + ViewHolder viewHolder = new ViewHolder(sgItemView); + + viewHolder.txt_datetime.setText(saveGameInfo.getDatetime()); + String tmp = resources.getString(R.string.score) + ": " + StringUtils.formatSuffix(saveGameInfo.getScore()); + viewHolder.txt_score.setText(tmp); + tmp = resources.getString(R.string.wave) + ": " + StringUtils.formatSuffix(saveGameInfo.getWave()); + viewHolder.txt_waveNumber.setText(tmp); + tmp = resources.getString(R.string.lives) + ": " + StringUtils.formatSuffix(saveGameInfo.getLives()); + viewHolder.txt_lives.setText(tmp); + + viewHolder.img_thumb.setImageBitmap(saveGameInfo.getCachedScreenshot()); + + return sgItemView; + } +} diff --git a/app/src/main/java/ch/logixisland/anuto/view/map/ChangeMapActivity.java b/app/src/main/java/ch/logixisland/anuto/view/map/ChangeMapActivity.java index 5522bf41..597c6ce4 100755 --- a/app/src/main/java/ch/logixisland/anuto/view/map/ChangeMapActivity.java +++ b/app/src/main/java/ch/logixisland/anuto/view/map/ChangeMapActivity.java @@ -48,10 +48,10 @@ protected void onCreate(Bundle savedInstanceState) { mAdapter = new MapsAdapter(this, mMapRepository, mHighScores); - arrow_up = (ImageView) findViewById(R.id.arrow_up); - arrow_down = (ImageView) findViewById(R.id.arrow_down); + arrow_up = findViewById(R.id.arrow_up); + arrow_down = findViewById(R.id.arrow_down); - grid_maps = (GridView) findViewById(R.id.grid_maps); + grid_maps = findViewById(R.id.grid_maps); grid_maps.setOnItemClickListener(this); grid_maps.getViewTreeObserver().addOnScrollChangedListener(this); grid_maps.post(new Runnable() { diff --git a/app/src/main/java/ch/logixisland/anuto/view/map/MapsAdapter.java b/app/src/main/java/ch/logixisland/anuto/view/map/MapsAdapter.java index 0f32237d..4a2e3093 100755 --- a/app/src/main/java/ch/logixisland/anuto/view/map/MapsAdapter.java +++ b/app/src/main/java/ch/logixisland/anuto/view/map/MapsAdapter.java @@ -41,9 +41,9 @@ static private class ViewHolder { TextView txt_highscore; ViewHolder(View view) { - img_thumb = (ImageView) view.findViewById(R.id.img_thumb); - txt_name = (TextView) view.findViewById(R.id.txt_name); - txt_highscore = (TextView) view.findViewById(R.id.txt_highscore); + img_thumb = view.findViewById(R.id.img_thumb); + txt_name = view.findViewById(R.id.txt_name); + txt_highscore = view.findViewById(R.id.txt_highscore); } } diff --git a/app/src/main/res/drawable/background_header.xml b/app/src/main/res/drawable/background_header.xml index 53b9c935..192d8eec 100644 --- a/app/src/main/res/drawable/background_header.xml +++ b/app/src/main/res/drawable/background_header.xml @@ -5,8 +5,7 @@ - + diff --git a/app/src/main/res/drawable/background_header_co.xml b/app/src/main/res/drawable/background_header_co.xml index ea2e9240..da7bfcbc 100644 --- a/app/src/main/res/drawable/background_header_co.xml +++ b/app/src/main/res/drawable/background_header_co.xml @@ -5,8 +5,7 @@ - + diff --git a/app/src/main/res/drawable/background_header_dk.xml b/app/src/main/res/drawable/background_header_dk.xml index 54bb4c5e..9e3e5b67 100644 --- a/app/src/main/res/drawable/background_header_dk.xml +++ b/app/src/main/res/drawable/background_header_dk.xml @@ -5,8 +5,7 @@ - + diff --git a/app/src/main/res/drawable/background_popup_co.xml b/app/src/main/res/drawable/background_popup_co.xml index 5702ba86..274f36c3 100644 --- a/app/src/main/res/drawable/background_popup_co.xml +++ b/app/src/main/res/drawable/background_popup_co.xml @@ -3,7 +3,8 @@ - diff --git a/app/src/main/res/drawable/select_map_arrow.xml b/app/src/main/res/drawable/select_map_arrow.xml index 63828062..904ae846 100644 --- a/app/src/main/res/drawable/select_map_arrow.xml +++ b/app/src/main/res/drawable/select_map_arrow.xml @@ -1,8 +1,10 @@ - + - - + + @@ -11,7 +13,7 @@ android:fromDegrees="-45" android:toDegrees="45"> - + @@ -22,7 +24,7 @@ android:fromDegrees="45" android:toDegrees="45"> - + diff --git a/app/src/main/res/layout/activity_change_map.xml b/app/src/main/res/layout/activity_change_map.xml index ad39277a..cd669f36 100755 --- a/app/src/main/res/layout/activity_change_map.xml +++ b/app/src/main/res/layout/activity_change_map.xml @@ -1,5 +1,4 @@ - + android:scrollbarStyle="outsideOverlay" /> + android:src="@drawable/select_map_arrow" /> + android:src="@drawable/select_map_arrow" /> diff --git a/app/src/main/res/layout/activity_enemy_stats.xml b/app/src/main/res/layout/activity_enemy_stats.xml new file mode 100644 index 00000000..813c5fd2 --- /dev/null +++ b/app/src/main/res/layout/activity_enemy_stats.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/app/src/main/res/layout/activity_game.xml b/app/src/main/res/layout/activity_game.xml index dcb6ea9f..a4d8ba02 100755 --- a/app/src/main/res/layout/activity_game.xml +++ b/app/src/main/res/layout/activity_game.xml @@ -1,5 +1,4 @@ - + tools:layout="@layout/fragment_header" /> + android:layout_below="@id/fragment_header" /> + tools:layout="@layout/fragment_tower_build" /> + + + + + + + + diff --git a/app/src/main/res/layout/activity_menu.xml b/app/src/main/res/layout/activity_menu.xml index b473bf42..76b5955a 100755 --- a/app/src/main/res/layout/activity_menu.xml +++ b/app/src/main/res/layout/activity_menu.xml @@ -1,5 +1,4 @@ - + android:src="@mipmap/icon" /> + android:layout_height="20dp" />