diff --git a/core/src/main/assets/messages/scenes/scenes.properties b/core/src/main/assets/messages/scenes/scenes.properties index f7f36a162..24da23657 100644 --- a/core/src/main/assets/messages/scenes/scenes.properties +++ b/core/src/main/assets/messages/scenes/scenes.properties @@ -102,7 +102,13 @@ scenes.rankingsscene.no_games=No games have been played yet. scenes.rankingsscene.no_info=No additional information scenes.startscene.title=Games in Progress +scenes.startscene.one_minute_ago=1 minute ago +scenes.startscene.minutes_ago=%d minutes ago +scenes.startscene.hours_ago=%d hours ago +scenes.startscene.days_ago=%d days ago scenes.startscene.new=New Game +scenes.startscene.sort_level=Sort: Level +scenes.startscene.sort_recent=Sort: Recent scenes.supporterscene.title=Support the Game scenes.supporterscene.intro=I want Shattered Pixel Dungeon to be free of ads and invasive microtransactions, which ruin so many free games. Instead, I'd rather ask players to support the game directly! diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java index bc3da6236..876d08534 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java @@ -217,6 +217,7 @@ public class Dungeon { public static boolean dailyReplay; public static String customSeedText = ""; public static long seed; + public static long lastPlayed; //we initialize the seed separately so that things like interlevelscene can access it early public static void initSeed(){ @@ -603,6 +604,7 @@ public class Dungeon { private static final String CUSTOM_SEED = "custom_seed"; private static final String DAILY = "daily"; private static final String DAILY_REPLAY= "daily_replay"; + private static final String LAST_PLAYED = "last_played"; private static final String CHALLENGES = "challenges"; private static final String MOBS_TO_CHAMPION = "mobs_to_champion"; private static final String HERO = "hero"; @@ -629,6 +631,7 @@ public class Dungeon { bundle.put( CUSTOM_SEED, customSeedText ); bundle.put( DAILY, daily ); bundle.put( DAILY_REPLAY, dailyReplay ); + bundle.put( LAST_PLAYED, lastPlayed = Game.realTime); bundle.put( CHALLENGES, challenges ); bundle.put( MOBS_TO_CHAMPION, mobsToChampion ); bundle.put( HERO, hero ); @@ -872,6 +875,7 @@ public class Dungeon { info.customSeed = bundle.getString( CUSTOM_SEED ); info.daily = bundle.getBoolean( DAILY ); info.dailyReplay = bundle.getBoolean( DAILY_REPLAY ); + info.lastPlayed = bundle.getLong( LAST_PLAYED ); Hero.preview( info, bundle.getBundle( HERO ) ); Statistics.preview( info, bundle ); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/GamesInProgress.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/GamesInProgress.java index f0a4cb0aa..8f0d95567 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/GamesInProgress.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/GamesInProgress.java @@ -82,7 +82,15 @@ public class GamesInProgress { Info curr = check(i); if (curr != null) result.add(curr); } - Collections.sort(result, scoreComparator); + switch (SPDSettings.gamesInProgressSort()){ + case "level": default: + Collections.sort(result, levelComparator); + break; + case "last_played": + Collections.sort(result, lastPlayedComparator); + break; + } + return result; } @@ -129,6 +137,8 @@ public class GamesInProgress { public static void set(int slot) { Info info = new Info(); info.slot = slot; + + info.lastPlayed = Dungeon.lastPlayed; info.depth = Dungeon.depth; info.challenges = Dungeon.challenges; @@ -165,7 +175,7 @@ public class GamesInProgress { public static class Info { public int slot; - + public int depth; public int version; public int challenges; @@ -174,6 +184,7 @@ public class GamesInProgress { public String customSeed; public boolean daily; public boolean dailyReplay; + public long lastPlayed; public int level; public int str; @@ -190,12 +201,21 @@ public class GamesInProgress { public int maxDepth; } - public static final Comparator scoreComparator = new Comparator() { + public static final Comparator levelComparator = new Comparator() { @Override public int compare(GamesInProgress.Info lhs, GamesInProgress.Info rhs ) { - int lScore = (lhs.level * lhs.maxDepth * 100) + lhs.goldCollected; - int rScore = (rhs.level * rhs.maxDepth * 100) + rhs.goldCollected; - return (int)Math.signum( rScore - lScore ); + if (rhs.level != lhs.level){ + return (int)Math.signum( rhs.level - lhs.level ); + } else { + return lastPlayedComparator.compare(lhs, rhs); + } + } + }; + + public static final Comparator lastPlayedComparator = new Comparator() { + @Override + public int compare(GamesInProgress.Info lhs, GamesInProgress.Info rhs ) { + return (int)Math.signum( rhs.lastPlayed - lhs.lastPlayed ); } }; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/SPDSettings.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/SPDSettings.java index f4cb5b215..4649d9483 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/SPDSettings.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/SPDSettings.java @@ -146,6 +146,8 @@ public class SPDSettings extends GameSettings { public static final String KEY_SYSTEMFONT = "system_font"; public static final String KEY_VIBRATION = "vibration"; + public static final String KEY_GAMES_SORT = "games_sort"; + //0 = mobile, 1 = mixed (large without inventory in main UI), 2 = large public static void interfaceSize( int value ){ put( KEY_UI_SIZE, value ); @@ -221,6 +223,14 @@ public class SPDSettings extends GameSettings { return getBoolean(KEY_VIBRATION, true); } + public static String gamesInProgressSort(){ + return getString(KEY_GAMES_SORT, "level"); + } + + public static void gamesInProgressSort(String value){ + put(KEY_GAMES_SORT, value); + } + //Game State public static final String KEY_LAST_CLASS = "last_class"; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/StartScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/StartScene.java index 8419ceb66..83bbc92bc 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/StartScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/StartScene.java @@ -24,6 +24,7 @@ package com.shatteredpixel.shatteredpixeldungeon.scenes; import com.shatteredpixel.shatteredpixeldungeon.Badges; import com.shatteredpixel.shatteredpixeldungeon.Chrome; import com.shatteredpixel.shatteredpixeldungeon.GamesInProgress; +import com.shatteredpixel.shatteredpixeldungeon.SPDSettings; import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass; import com.shatteredpixel.shatteredpixeldungeon.journal.Journal; @@ -33,11 +34,13 @@ import com.shatteredpixel.shatteredpixeldungeon.ui.Button; import com.shatteredpixel.shatteredpixeldungeon.ui.ExitButton; import com.shatteredpixel.shatteredpixeldungeon.ui.Icons; import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock; +import com.shatteredpixel.shatteredpixeldungeon.ui.StyledButton; import com.shatteredpixel.shatteredpixeldungeon.ui.Window; import com.shatteredpixel.shatteredpixeldungeon.windows.IconTitle; import com.shatteredpixel.shatteredpixeldungeon.windows.WndGameInProgress; import com.watabou.noosa.BitmapText; import com.watabou.noosa.Camera; +import com.watabou.noosa.Game; import com.watabou.noosa.Image; import com.watabou.noosa.NinePatch; @@ -82,18 +85,21 @@ public class StartScene extends PixelScene { int slotCount = Math.min(GamesInProgress.MAX_SLOTS, games.size()+1); int slotGap = 10 - slotCount; int slotsHeight = slotCount*SLOT_HEIGHT + (slotCount-1)* slotGap; + slotsHeight += 14; - while (slotsHeight > (h-title.bottom()-2)){ + while (slotGap >= 2 && slotsHeight > (h-title.bottom()-2)){ slotGap--; slotsHeight -= slotCount-1; } - float yPos = (h - slotsHeight + title.bottom() + 2)/2f; + float yPos = (h - slotsHeight + title.bottom() + 2)/2f - 4; + yPos = Math.max(yPos, title.bottom()+2); + float slotLeft = (w - SLOT_WIDTH) / 2f; for (GamesInProgress.Info game : games) { SaveSlotButton existingGame = new SaveSlotButton(); existingGame.set(game.slot); - existingGame.setRect((w - SLOT_WIDTH) / 2f, yPos, SLOT_WIDTH, SLOT_HEIGHT); + existingGame.setRect(slotLeft, yPos, SLOT_WIDTH, SLOT_HEIGHT); yPos += SLOT_HEIGHT + slotGap; align(existingGame); add(existingGame); @@ -103,18 +109,51 @@ public class StartScene extends PixelScene { if (games.size() < GamesInProgress.MAX_SLOTS){ SaveSlotButton newGame = new SaveSlotButton(); newGame.set(GamesInProgress.firstEmpty()); - newGame.setRect((w - SLOT_WIDTH) / 2f, yPos, SLOT_WIDTH, SLOT_HEIGHT); + newGame.setRect(slotLeft, yPos, SLOT_WIDTH, SLOT_HEIGHT); yPos += SLOT_HEIGHT + slotGap; align(newGame); add(newGame); } GamesInProgress.curSlot = 0; - + + String sortText = ""; + switch (SPDSettings.gamesInProgressSort()){ + case "level": + sortText = Messages.get(this, "sort_level"); + break; + case "last_played": + sortText = Messages.get(this, "sort_recent"); + break; + } + + StyledButton btnSort = new StyledButton(Chrome.Type.TOAST_TR, sortText, 6){ + @Override + protected void onClick() { + super.onClick(); + + if (SPDSettings.gamesInProgressSort().equals("level")){ + SPDSettings.gamesInProgressSort("last_played"); + } else { + SPDSettings.gamesInProgressSort("level"); + } + + ShatteredPixelDungeon.seamlessResetScene(); + } + }; + btnSort.textColor(0xCCCCCC); + + if (yPos + 10 > Camera.main.height) { + btnSort.setRect(slotLeft - btnSort.reqWidth() - 6, Camera.main.height - 14, btnSort.reqWidth() + 4, 12); + } else { + btnSort.setRect(slotLeft, yPos, btnSort.reqWidth() + 4, 12); + } + if (games.size() >= 2) add(btnSort); + fadeIn(); } - + @Override protected void onBackPressed() { ShatteredPixelDungeon.switchNoFade( TitleScene.class ); @@ -126,6 +165,7 @@ public class StartScene extends PixelScene { private Image hero; private RenderedTextBlock name; + private RenderedTextBlock lastPlayed; private Image steps; private BitmapText depth; @@ -144,6 +184,9 @@ public class StartScene extends PixelScene { name = PixelScene.renderTextBlock(9); add(name); + + lastPlayed = PixelScene.renderTextBlock(6); + add(lastPlayed); } public void set( int slot ){ @@ -191,6 +234,21 @@ public class StartScene extends PixelScene { classIcon.copy(Icons.get(info.heroClass)); } + + long diff = Game.realTime - info.lastPlayed; + if (diff > 99L * 30 * 24 * 60 * 60_000){ + lastPlayed.text(" "); //show no text for >99 months ago + } else if (diff < 60_000){ + lastPlayed.text(Messages.get(StartScene.class, "one_minute_ago")); + } else if (diff < 2 * 60 * 60_000){ + lastPlayed.text(Messages.get(StartScene.class, "minutes_ago", diff / 60_000)); + } else if (diff < 2 * 24 * 60 * 60_000){ + lastPlayed.text(Messages.get(StartScene.class, "hours_ago", diff / (60 * 60_000))); + } else if (diff < 2L * 30 * 24 * 60 * 60_000){ + lastPlayed.text(Messages.get(StartScene.class, "days_ago", diff / (24 * 60 * 60_000))); + } else { + lastPlayed.text(Messages.get(StartScene.class, "months_ago", diff / (30L * 24 * 60 * 60_000))); + } depth.text(Integer.toString(info.depth)); depth.measure(); @@ -200,10 +258,12 @@ public class StartScene extends PixelScene { if (info.challenges > 0){ name.hardlight(Window.TITLE_COLOR); + lastPlayed.hardlight(Window.TITLE_COLOR); depth.hardlight(Window.TITLE_COLOR); level.hardlight(Window.TITLE_COLOR); } else { name.resetColor(); + lastPlayed.resetColor(); depth.resetColor(); level.resetColor(); } @@ -238,9 +298,14 @@ public class StartScene extends PixelScene { name.setPos( hero.x + hero.width() + 6, - y + (height - name.height())/2f + y + (height - name.height() - lastPlayed.height() - 2)/2f ); align(name); + + lastPlayed.setPos( + hero.x + hero.width() + 6, + name.bottom()+2 + ); classIcon.x = x + width - 24 + (16 - classIcon.width())/2f; classIcon.y = y + (height - classIcon.height())/2f;