From 49c93b68a5ac932d9fb4bc36e9d06a0b6421a47d Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Wed, 22 Jun 2022 14:27:46 -0400 Subject: [PATCH] v1.3.0: implemented rankings support for daily runs --- .../main/java/com/watabou/utils/Bundle.java | 27 ++++ .../messages/windows/windows.properties | 5 + .../shatteredpixeldungeon/Rankings.java | 67 +++++++-- .../scenes/RankingsScene.java | 38 +++++- .../windows/WndDailies.java | 128 ++++++++++++++++++ .../windows/WndRanking.java | 7 +- 6 files changed, 254 insertions(+), 18 deletions(-) create mode 100644 core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndDailies.java diff --git a/SPD-classes/src/main/java/com/watabou/utils/Bundle.java b/SPD-classes/src/main/java/com/watabou/utils/Bundle.java index 6e170c811..4c88beda2 100644 --- a/SPD-classes/src/main/java/com/watabou/utils/Bundle.java +++ b/SPD-classes/src/main/java/com/watabou/utils/Bundle.java @@ -194,6 +194,21 @@ public class Bundle { } } + public long[] getLongArray( String key ) { + try { + JSONArray array = data.getJSONArray( key ); + int length = array.length(); + long[] result = new long[length]; + for (int i=0; i < length; i++) { + result[i] = array.getLong( i ); + } + return result; + } catch (JSONException e) { + Game.reportException(e); + return null; + } + } + public float[] getFloatArray( String key ) { try { JSONArray array = data.getJSONArray( key ); @@ -386,6 +401,18 @@ public class Bundle { } } + public void put( String key, long[] array ) { + try { + JSONArray jsonArray = new JSONArray(); + for (int i=0; i < array.length; i++) { + jsonArray.put( i, array[i] ); + } + data.put( key, jsonArray ); + } catch (JSONException e) { + Game.reportException(e); + } + } + public void put( String key, float[] array ) { try { JSONArray jsonArray = new JSONArray(); diff --git a/core/src/main/assets/messages/windows/windows.properties b/core/src/main/assets/messages/windows/windows.properties index 7752523f6..7865cff5c 100644 --- a/core/src/main/assets/messages/windows/windows.properties +++ b/core/src/main/assets/messages/windows/windows.properties @@ -21,6 +21,11 @@ windows.wndclass.mastery=Mastery windows.wndcombo.title=choose a combo move +windows.wnddailies.title=Daily History +windows.wnddailies.date=Date +windows.wnddailies.score=Score + + windows.wnddocument.missing=page missing windows.wndenergizeitem.prompt=Energize an Item diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Rankings.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Rankings.java index fdfb49446..7a55dff34 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Rankings.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Rankings.java @@ -46,6 +46,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.LinkedHashMap; import java.util.UUID; public enum Rankings { @@ -61,15 +62,16 @@ public enum Rankings { public int totalNumber; public int wonNumber; + //The number of runs which are only present locally, not in the cloud + public int localTotal; + public int localWon; + + public Record latestDaily; + public LinkedHashMap dailyScoreHistory = new LinkedHashMap<>(); + public void submit( boolean win, Class cause ) { load(); - - //TODO need separate storage for daily data - //when loading data, make sure to check for latestDaily errors and correct - if (Dungeon.daily){ - return; - } Record rec = new Record(); @@ -87,13 +89,21 @@ public enum Rankings { } rec.score = calculateScore(); rec.customSeed = Dungeon.customSeedText; + rec.daily = Dungeon.daily; Badges.validateHighScore( rec.score ); INSTANCE.saveGameData(rec); rec.gameID = UUID.randomUUID().toString(); - + + if (rec.daily){ + latestDaily = rec; + dailyScoreHistory.put(Dungeon.seed, rec.score); + save(); + return; + } + records.add( rec ); Collections.sort( records, scoreComparator ); @@ -308,6 +318,10 @@ public enum Rankings { private static final String TOTAL = "total"; private static final String WON = "won"; + public static final String LATEST_DAILY = "latest_daily"; + public static final String DAILY_HISTORY_DATES = "daily_history_dates"; + public static final String DAILY_HISTORY_SCORES = "daily_history_scores"; + public void save() { Bundle bundle = new Bundle(); bundle.put( RECORDS, records ); @@ -315,6 +329,19 @@ public enum Rankings { bundle.put( TOTAL, totalNumber ); bundle.put( WON, wonNumber ); + bundle.put(LATEST_DAILY, latestDaily); + + long[] dates = new long[dailyScoreHistory.size()]; + int[] scores = new int[dailyScoreHistory.size()]; + int i = 0; + for (Long l : dailyScoreHistory.keySet()){ + dates[i] = l; + scores[i] = dailyScoreHistory.get(l); + i++; + } + bundle.put(DAILY_HISTORY_DATES, dates); + bundle.put(DAILY_HISTORY_SCORES, scores); + try { FileUtils.bundleToFile( RANKINGS_FILE, bundle); } catch (IOException e) { @@ -353,6 +380,23 @@ public enum Rankings { } } + if (bundle.contains(LATEST_DAILY)){ + latestDaily = (Record) bundle.get(LATEST_DAILY); + + dailyScoreHistory.clear(); + int[] scores = bundle.getIntArray(DAILY_HISTORY_SCORES); + int i = 0; + long latestDate = 0; + for (long date : bundle.getLongArray(DAILY_HISTORY_DATES)){ + dailyScoreHistory.put(date, scores[i]); + if (date > latestDate) latestDate = date; + i++; + } + if (latestDate > SPDSettings.lastDaily()){ + SPDSettings.lastDaily(latestDate); + } + } + } catch (IOException e) { } } @@ -370,6 +414,7 @@ public enum Rankings { private static final String DATA = "gameData"; private static final String ID = "gameID"; private static final String SEED = "custom_seed"; + private static final String DAILY = "daily"; public Class cause; public boolean win; @@ -387,6 +432,7 @@ public enum Rankings { public int score; public String customSeed; + public boolean daily; public String desc(){ if (win){ @@ -419,7 +465,8 @@ public enum Rankings { win = bundle.getBoolean( WIN ); score = bundle.getInt( SCORE ); customSeed = bundle.getString( SEED ); - + daily = bundle.getBoolean( DAILY ); + heroClass = bundle.getEnum( CLASS, HeroClass.class ); armorTier = bundle.getInt( TIER ); herolevel = bundle.getInt( LEVEL ); @@ -441,7 +488,8 @@ public enum Rankings { bundle.put( WIN, win ); bundle.put( SCORE, score ); bundle.put( SEED, customSeed ); - + bundle.put( DAILY, daily ); + bundle.put( CLASS, heroClass ); bundle.put( TIER, armorTier ); bundle.put( LEVEL, herolevel ); @@ -456,6 +504,7 @@ public enum Rankings { public static final Comparator scoreComparator = new Comparator() { @Override public int compare( Record lhs, Record rhs ) { + //this covers custom seeded runs and dailies if (rhs.customSeed.isEmpty() && !lhs.customSeed.isEmpty()){ return +1; } else if (lhs.customSeed.isEmpty() && !rhs.customSeed.isEmpty()){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/RankingsScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/RankingsScene.java index 0a6262d39..1bacf5c20 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/RankingsScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/RankingsScene.java @@ -31,9 +31,11 @@ import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; import com.shatteredpixel.shatteredpixeldungeon.ui.Archs; import com.shatteredpixel.shatteredpixeldungeon.ui.ExitButton; +import com.shatteredpixel.shatteredpixeldungeon.ui.IconButton; import com.shatteredpixel.shatteredpixeldungeon.ui.Icons; import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock; import com.shatteredpixel.shatteredpixeldungeon.ui.Window; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndDailies; import com.shatteredpixel.shatteredpixeldungeon.windows.WndError; import com.shatteredpixel.shatteredpixeldungeon.windows.WndRanking; import com.watabou.noosa.BitmapText; @@ -139,6 +141,26 @@ public class RankingsScene extends PixelScene { btnExit.setPos( Camera.main.width - btnExit.width(), 0 ); add( btnExit ); + int left = 0; + + if (Rankings.INSTANCE.latestDaily != null) { + IconButton btnDailies = new IconButton(Icons.CALENDAR.get()) { + @Override + protected void onClick() { + ShatteredPixelDungeon.scene().addToFront(new WndDailies()); + } + + @Override + protected void onPointerUp() { + icon.hardlight(0.5f, 1f, 2f); + } + }; + btnDailies.icon().hardlight(0.5f, 1f, 2f); + btnDailies.setRect( left, 0, 20, 20 ); + left += 20; + add(btnDailies); + } + fadeIn(); } @@ -158,7 +180,7 @@ public class RankingsScene extends PixelScene { private Rankings.Record rec; - protected ItemSprite shield; + protected Image shield; private Flare flare; private BitmapText position; private RenderedTextBlock desc; @@ -190,7 +212,7 @@ public class RankingsScene extends PixelScene { int odd = pos % 2; if (rec.win) { - shield.view( ItemSpriteSheet.AMULET, null ); + shield.copy( new ItemSprite(ItemSpriteSheet.AMULET, null) ); position.hardlight( TEXT_WIN[odd] ); desc.hardlight( TEXT_WIN[odd] ); depth.hardlight( TEXT_WIN[odd] ); @@ -211,14 +233,18 @@ public class RankingsScene extends PixelScene { } if (rec.ascending){ - shield.view( ItemSpriteSheet.AMULET, null ); + shield.copy( new ItemSprite(ItemSpriteSheet.AMULET, null) ); shield.hardlight(0.4f, 0.4f, 0.7f); } } - if (!rec.customSeed.isEmpty()){ - shield.view( ItemSpriteSheet.SEED_SUNGRASS, null ); + if (rec.daily){ + shield.copy( Icons.get(Icons.CALENDAR) ); + shield.hardlight(0.5f, 1f, 2f); + } else if (!rec.customSeed.isEmpty()){ + shield.copy( Icons.get(Icons.SEED) ); + shield.hardlight(1f, 1.5f, 0.67f); } if (rec.herolevel != 0){ @@ -239,7 +265,7 @@ public class RankingsScene extends PixelScene { super.createChildren(); - shield = new ItemSprite( ItemSpriteSheet.TOMB, null ); + shield = new Image(new ItemSprite( ItemSpriteSheet.TOMB, null )); add( shield ); position = new BitmapText( PixelScene.pixelFont); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndDailies.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndDailies.java new file mode 100644 index 000000000..f6b1cab1b --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndDailies.java @@ -0,0 +1,128 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2022 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.shatteredpixel.shatteredpixeldungeon.windows; + +import com.shatteredpixel.shatteredpixeldungeon.Rankings; +import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene; +import com.shatteredpixel.shatteredpixeldungeon.ui.IconButton; +import com.shatteredpixel.shatteredpixeldungeon.ui.Icons; +import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock; +import com.shatteredpixel.shatteredpixeldungeon.ui.ScrollPane; +import com.shatteredpixel.shatteredpixeldungeon.ui.Window; +import com.watabou.noosa.ColorBlock; +import com.watabou.noosa.ui.Component; + +import java.text.DateFormat; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +public class WndDailies extends Window { + + private static final int WIDTH = 115; + private static final int HEIGHT = 144; + + public WndDailies(){ + + resize(WIDTH, HEIGHT); + + ScrollPane pane = new ScrollPane(new Component()); + add(pane); + pane.setRect(0, 0, WIDTH, HEIGHT); + + Component content = pane.content(); + + IconTitle title = new IconTitle(Icons.CALENDAR.get(), Messages.get(this, "title")); + title.imIcon.hardlight(0.5f, 1f, 2f); + title.setRect(0, 0, WIDTH, 0); + title.setPos(0, 0); + content.add(title); + + int top = (int)title.bottom()+3; + + RenderedTextBlock day = PixelScene.renderTextBlock(Messages.get(this, "date"), 7); + day.hardlight(TITLE_COLOR); + day.setPos(0, top); + content.add(day); + + RenderedTextBlock score = PixelScene.renderTextBlock(Messages.get(this, "score"), 7); + score.hardlight(TITLE_COLOR); + score.setPos(WIDTH - score.width(), top); + content.add(score); + + top = (int) score.bottom() + 6; + + NumberFormat num = NumberFormat.getInstance(Locale.US); + DateFormat format = DateFormat.getDateInstance(); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + Date date = new Date(); + + //reverse order so that latest dailies are on top + ArrayList dates = new ArrayList<>(Rankings.INSTANCE.dailyScoreHistory.keySet()); + Collections.reverse(dates); + + boolean first = Rankings.INSTANCE.latestDaily != null; + for (long l : dates) { + if (first) top += 2; + + ColorBlock sep = new ColorBlock(WIDTH, 1, 0xFF000000); + sep.y = top - 3 - (first ? 2 : 0); + content.add(sep); + + date.setTime(l); + day = PixelScene.renderTextBlock(format.format(date), 7); + day.setPos(0, top); + content.add(day); + + if (first){ + IconButton latestInfo = new IconButton(Icons.INFO.get()){ + @Override + protected void onClick() { + ShatteredPixelDungeon.scene().addToFront(new WndRanking(Rankings.INSTANCE.latestDaily)); + } + }; + latestInfo.setRect(day.right()+2, top - 5, 16, 16); + content.add(latestInfo); + } + + score = PixelScene.renderTextBlock(num.format(Rankings.INSTANCE.dailyScoreHistory.get(l)), 7); + score.setPos(WIDTH - score.width(), top); + content.add(score); + + top = (int) day.bottom() + 6; + if (first){ + top += 2; + first = false; + } + } + + content.setRect(0, 0, WIDTH, top); + pane.setRect(0, 0, WIDTH, HEIGHT); + + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndRanking.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndRanking.java index 6d9052fd6..f99fbba0e 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndRanking.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndRanking.java @@ -217,9 +217,9 @@ public class WndRanking extends WndTabbed { } if (Dungeon.seed != -1) { if (Dungeon.daily){ - pos = statSlot(this, Messages.get(this, "daily_for"), "_" + DungeonSeed.convertToCode(Dungeon.seed) + "_", pos); + pos = statSlot(this, Messages.get(this, "daily_for"), "_" + Dungeon.customSeedText + "_", pos); } else if (!Dungeon.customSeedText.isEmpty()){ - pos = statSlot(this, Messages.get(this, "custom_seed"), "_" + DungeonSeed.convertToCode(Dungeon.seed) + "_", pos); + pos = statSlot(this, Messages.get(this, "custom_seed"), "_" + Dungeon.customSeedText + "_", pos); } else { pos = statSlot(this, Messages.get(this, "seed"), DungeonSeed.convertToCode(Dungeon.seed), pos); } @@ -236,7 +236,8 @@ public class WndRanking extends WndTabbed { int buttontop = HEIGHT - 16; - if (Dungeon.seed != -1 && (DeviceCompat.isDebug() || Badges.isUnlocked(Badges.Badge.VICTORY))){ + if (Dungeon.seed != -1 && !Dungeon.daily && + (DeviceCompat.isDebug() || Badges.isUnlocked(Badges.Badge.VICTORY))){ final Image icon = Icons.get(Icons.SEED); RedButton btnSeed = new RedButton(Messages.get(this, "copy_seed")){ @Override