v0.8.2: Finished news implementation, including a full news scene
This commit is contained in:
@@ -47,6 +47,15 @@ scenes.interlevelscene.io_error=Cannot read save file. If this error persists af
|
||||
|
||||
scenes.introscene.text=Many heroes have ventured into the dungeon before you from the city above. Some have returned with treasures and magical artifacts, most have never been heard from again.\n\nNone, however, have ventured to the bottom and retrieved the Amulet of Yendor, which is said to be guarded by an ancient evil in the depths. Even now dark energy radiates from below, making its way up into the city.\n\nYou consider yourself ready for the challenge. Most importantly, you feel that fortune smiles upon you. It's time to start your own adventure!
|
||||
|
||||
scenes.newsscene.title=Game News
|
||||
scenes.newsscene.read_more=Read More
|
||||
scenes.newsscene$newsinfo.english_warn=News posts are written by the developer and are only available in English.
|
||||
scenes.newsscene$newsinfo.metered_network=Couldn't check for news posts, as you're connected to a metered network, such as mobile data.
|
||||
scenes.newsscene$newsinfo.enable_data=Check on Mobile Data
|
||||
scenes.newsscene$newsinfo.no_internet=Couldn't check for news posts, make sure you're connected to the internet.
|
||||
scenes.newsscene$newsinfo.news_disabled=You have disabled checking for news posts, so none will appear here.
|
||||
scenes.newsscene$newsinfo.enable_news=Enable News
|
||||
|
||||
scenes.rankingsscene.title=Top Rankings
|
||||
scenes.rankingsscene.total=Games Played:
|
||||
scenes.rankingsscene.no_games=No games have been played yet.
|
||||
|
||||
@@ -24,6 +24,7 @@ package com.shatteredpixel.shatteredpixeldungeon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Languages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
|
||||
import com.watabou.noosa.Game;
|
||||
import com.watabou.noosa.audio.Music;
|
||||
import com.watabou.noosa.audio.Sample;
|
||||
import com.watabou.utils.GameSettings;
|
||||
@@ -260,6 +261,8 @@ public class SPDSettings extends GameSettings {
|
||||
public static final String KEY_UPDATES = "updates";
|
||||
public static final String KEY_WIFI = "wifi";
|
||||
|
||||
public static final String KEY_NEWS_LAST_READ = "news_last_read";
|
||||
|
||||
public static void news(boolean value){
|
||||
put(KEY_NEWS, value);
|
||||
}
|
||||
@@ -283,7 +286,16 @@ public class SPDSettings extends GameSettings {
|
||||
public static boolean WiFi(){
|
||||
return getBoolean(KEY_WIFI, true);
|
||||
}
|
||||
|
||||
|
||||
public static void newsLastRead(long lastRead){
|
||||
put(KEY_NEWS_LAST_READ, lastRead);
|
||||
}
|
||||
|
||||
public static long newsLastRead(){
|
||||
//returns the current time when none is stored, so historical news isn't seen as unread
|
||||
return getLong(KEY_NEWS_LAST_READ, Game.realTime);
|
||||
}
|
||||
|
||||
//Window management (desktop only atm)
|
||||
|
||||
public static final String KEY_WINDOW_WIDTH = "window_width";
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Pixel Dungeon
|
||||
* Copyright (C) 2012-2015 Oleg Dolya
|
||||
*
|
||||
* Shattered Pixel Dungeon
|
||||
* Copyright (C) 2014-2020 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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package com.shatteredpixel.shatteredpixeldungeon.scenes;
|
||||
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Chrome;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.SPDSettings;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Languages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.services.news.News;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.services.news.NewsArticle;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Archs;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.ExitButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.StyledButton;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.windows.WndTitledMessage;
|
||||
import com.watabou.noosa.BitmapText;
|
||||
import com.watabou.noosa.Camera;
|
||||
import com.watabou.noosa.Game;
|
||||
import com.watabou.noosa.NinePatch;
|
||||
import com.watabou.noosa.ui.Component;
|
||||
import com.watabou.utils.DeviceCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
|
||||
public class NewsScene extends PixelScene {
|
||||
|
||||
boolean displayingNoArticles = false;
|
||||
|
||||
private static final int BTN_HEIGHT = 20;
|
||||
private static final int BTN_WIDTH = 100;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
super.create();
|
||||
|
||||
uiCamera.visible = false;
|
||||
|
||||
int w = Camera.main.width;
|
||||
int h = Camera.main.height;
|
||||
|
||||
int fullWidth = PixelScene.landscape() ? 202 : 100;
|
||||
int left = (w - fullWidth)/2;
|
||||
|
||||
Archs archs = new Archs();
|
||||
archs.setSize(w, h);
|
||||
add(archs);
|
||||
|
||||
ExitButton btnExit = new ExitButton();
|
||||
btnExit.setPos(w - btnExit.width(), 0);
|
||||
add(btnExit);
|
||||
|
||||
RenderedTextBlock title = PixelScene.renderTextBlock(Messages.get(this, "title"), 9);
|
||||
title.hardlight(Window.TITLE_COLOR);
|
||||
title.setPos(
|
||||
(w - title.width()) / 2f,
|
||||
(20 - title.height()) / 2f
|
||||
);
|
||||
align(title);
|
||||
add(title);
|
||||
|
||||
float top = 20;
|
||||
|
||||
displayingNoArticles = !News.articlesAvailable();
|
||||
if (displayingNoArticles || Messages.lang() != Languages.ENGLISH) {
|
||||
|
||||
Component newsInfo = new NewsInfo();
|
||||
newsInfo.setRect(left, 20, fullWidth, 0);
|
||||
add(newsInfo);
|
||||
|
||||
top = newsInfo.bottom();
|
||||
|
||||
}
|
||||
|
||||
if (!displayingNoArticles) {
|
||||
ArrayList<NewsArticle> articles = News.articles();
|
||||
|
||||
float articleSpace = h - top - 2;
|
||||
int rows = articles.size();
|
||||
if (PixelScene.landscape()){
|
||||
rows /= 2;
|
||||
}
|
||||
rows++;
|
||||
|
||||
while ((articleSpace) / (BTN_HEIGHT+1) < rows) {
|
||||
articles.remove(articles.size() - 1);
|
||||
if (PixelScene.landscape()) {
|
||||
articles.remove(articles.size() - 1);
|
||||
}
|
||||
rows--;
|
||||
}
|
||||
|
||||
float gap = ((articleSpace) - (BTN_HEIGHT * rows)) / (float)rows;
|
||||
|
||||
boolean rightCol = false;
|
||||
for (NewsArticle article : articles) {
|
||||
StyledButton b = new ArticleButton(article);
|
||||
if (!rightCol) {
|
||||
top += gap;
|
||||
b.setRect( left, top, BTN_WIDTH, BTN_HEIGHT);
|
||||
} else {
|
||||
b.setRect( left + fullWidth - BTN_WIDTH, top, BTN_WIDTH, BTN_HEIGHT);
|
||||
}
|
||||
align(b);
|
||||
add(b);
|
||||
if (!PixelScene.landscape()) {
|
||||
top += BTN_HEIGHT;
|
||||
} else {
|
||||
if (rightCol){
|
||||
top += BTN_HEIGHT;
|
||||
}
|
||||
rightCol = !rightCol;
|
||||
}
|
||||
}
|
||||
top += gap;
|
||||
} else {
|
||||
top += 20;
|
||||
}
|
||||
|
||||
StyledButton btnSite = new StyledButton(Chrome.Type.GREY_BUTTON_TR, Messages.get(this, "read_more")){
|
||||
@Override
|
||||
protected void onClick() {
|
||||
super.onClick();
|
||||
DeviceCompat.openURI("https://ShatteredPixel.com/");
|
||||
}
|
||||
};
|
||||
btnSite.icon(Icons.get(Icons.NEWS));
|
||||
btnSite.textColor(Window.TITLE_COLOR);
|
||||
btnSite.setRect(left, top, fullWidth, BTN_HEIGHT);
|
||||
add(btnSite);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
if (displayingNoArticles && News.articlesAvailable()){
|
||||
ShatteredPixelDungeon.seamlessResetScene();
|
||||
}
|
||||
super.update();
|
||||
}
|
||||
|
||||
private static class NewsInfo extends Component {
|
||||
|
||||
NinePatch bg;
|
||||
RenderedTextBlock text;
|
||||
RedButton button;
|
||||
|
||||
@Override
|
||||
protected void createChildren() {
|
||||
bg = Chrome.get(Chrome.Type.GREY_BUTTON_TR);
|
||||
add(bg);
|
||||
|
||||
String message = "";
|
||||
|
||||
if (Messages.lang() != Languages.ENGLISH){
|
||||
message += Messages.get(this, "english_warn");
|
||||
}
|
||||
|
||||
if (!News.articlesAvailable()){
|
||||
if (SPDSettings.news()) {
|
||||
if (SPDSettings.WiFi() && !Game.platform.connectedToUnmeteredNetwork()) {
|
||||
message += "\n\n" + Messages.get(this, "metered_network");
|
||||
|
||||
button = new RedButton(Messages.get(this, "enable_data")) {
|
||||
@Override
|
||||
protected void onClick() {
|
||||
super.onClick();
|
||||
SPDSettings.WiFi(false);
|
||||
News.checkForNews();
|
||||
ShatteredPixelDungeon.seamlessResetScene();
|
||||
}
|
||||
};
|
||||
add(button);
|
||||
} else {
|
||||
message += "\n\n" + Messages.get(this, "no_internet");
|
||||
}
|
||||
} else {
|
||||
message += "\n\n" + Messages.get(this, "news_disabled");
|
||||
|
||||
button = new RedButton(Messages.get(this, "enable_news")) {
|
||||
@Override
|
||||
protected void onClick() {
|
||||
super.onClick();
|
||||
SPDSettings.news(true);
|
||||
News.checkForNews();
|
||||
ShatteredPixelDungeon.seamlessResetScene();
|
||||
}
|
||||
};
|
||||
add(button);
|
||||
}
|
||||
}
|
||||
|
||||
if (message.startsWith("\n\n")) message = message.replaceFirst("\n\n", "");
|
||||
|
||||
text = PixelScene.renderTextBlock(message, 6);
|
||||
text.hardlight(CharSprite.WARNING);
|
||||
add(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layout() {
|
||||
bg.x = x;
|
||||
bg.y = y;
|
||||
|
||||
text.maxWidth((int)width - bg.marginHor());
|
||||
text.setPos(x + bg.marginLeft(), y + bg.marginTop());
|
||||
|
||||
height = (text.bottom()) - y;
|
||||
|
||||
if (button != null){
|
||||
height += 4;
|
||||
button.setSize(button.reqWidth()+2, 16);
|
||||
button.setPos(x + (width - button.width())/2, y + height);
|
||||
height = button.bottom() - y;
|
||||
}
|
||||
|
||||
height += bg.marginBottom();
|
||||
|
||||
bg.size(width, height);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static class ArticleButton extends StyledButton {
|
||||
|
||||
NewsArticle article;
|
||||
|
||||
BitmapText date;
|
||||
|
||||
public ArticleButton(NewsArticle article) {
|
||||
super(Chrome.Type.GREY_BUTTON_TR, article.title, 6);
|
||||
this.article = article;
|
||||
|
||||
icon(News.parseArticleIcon(article));
|
||||
if (article.date.getTime() > SPDSettings.newsLastRead()) textColor(Window.SHPX_COLOR);
|
||||
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(article.date);
|
||||
date = new BitmapText( News.parseArticleDate(article), pixelFont);
|
||||
date.scale.set(PixelScene.align(0.49f));
|
||||
date.hardlight( 0x888888 );
|
||||
date.measure();
|
||||
add(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layout() {
|
||||
text.maxWidth( (int)(width - icon.width() - bg.marginHor() - 2));
|
||||
|
||||
if (date != null) {
|
||||
date.x = x + width - bg.marginRight() - date.width() + 2;
|
||||
date.y = y + height - bg.marginBottom() - date.baseLine() + 2;
|
||||
}
|
||||
|
||||
super.layout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
super.onClick();
|
||||
textColor(Window.WHITE);
|
||||
if (article.date.getTime() > SPDSettings.newsLastRead()){
|
||||
SPDSettings.newsLastRead(article.date.getTime());
|
||||
}
|
||||
ShatteredPixelDungeon.scene().addToFront(new WndArticle(article));
|
||||
}
|
||||
}
|
||||
|
||||
private static class WndArticle extends WndTitledMessage {
|
||||
|
||||
public WndArticle(NewsArticle article ) {
|
||||
super(News.parseArticleIcon(article), article.title, article.summary);
|
||||
|
||||
RedButton link = new RedButton(Messages.get(NewsScene.class, "read_more")){
|
||||
@Override
|
||||
protected void onClick() {
|
||||
super.onClick();
|
||||
DeviceCompat.openURI(article.URL);
|
||||
}
|
||||
};
|
||||
link.setRect(0, height + 2, width, BTN_HEIGHT);
|
||||
add(link);
|
||||
resize(width, (int) link.bottom());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -151,7 +151,7 @@ public class TitleScene extends PixelScene {
|
||||
btnBadges.icon(Icons.get(Icons.BADGES));
|
||||
add(btnBadges);
|
||||
|
||||
StyledButton btnNews = new NewsButtons(GREY_TR, Messages.get(this, "news"));
|
||||
StyledButton btnNews = new NewsButton(GREY_TR, Messages.get(this, "news"));
|
||||
btnNews.icon(Icons.get(Icons.NEWS));
|
||||
add(btnNews);
|
||||
|
||||
@@ -214,9 +214,9 @@ public class TitleScene extends PixelScene {
|
||||
add( fb );
|
||||
}
|
||||
|
||||
private static class NewsButtons extends StyledButton {
|
||||
private static class NewsButton extends StyledButton {
|
||||
|
||||
public NewsButtons( Chrome.Type type, String label ){
|
||||
public NewsButton(Chrome.Type type, String label ){
|
||||
super(type, label);
|
||||
if (SPDSettings.news()) News.checkForNews();
|
||||
}
|
||||
@@ -232,8 +232,6 @@ public class TitleScene extends PixelScene {
|
||||
if (unreadCount > 0){
|
||||
unreadCount = Math.min(unreadCount, 9);
|
||||
text(text() + "(" + unreadCount + ")");
|
||||
} else {
|
||||
SPDSettings.newsLastRead(Game.realTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +243,7 @@ public class TitleScene extends PixelScene {
|
||||
@Override
|
||||
protected void onClick() {
|
||||
super.onClick();
|
||||
//ShatteredPixelDungeon.switchNoFade( NewsScene.class );
|
||||
ShatteredPixelDungeon.switchNoFade( NewsScene.class );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+46
-2
@@ -21,7 +21,15 @@
|
||||
|
||||
package com.shatteredpixel.shatteredpixeldungeon.services.news;
|
||||
|
||||
import com.shatteredpixel.shatteredpixeldungeon.SPDSettings;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
|
||||
import com.watabou.noosa.Image;
|
||||
import com.watabou.utils.DeviceCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
public class News {
|
||||
@@ -39,7 +47,7 @@ public class News {
|
||||
if (!supportsNews()) return;
|
||||
if (lastCheck != null && (new Date().getTime() - lastCheck.getTime()) < CHECK_DELAY) return;
|
||||
|
||||
service.checkForArticles(false, new NewsService.NewsResultCallback() {
|
||||
service.checkForArticles(!SPDSettings.WiFi(), !DeviceCompat.legacyDevice(), new NewsService.NewsResultCallback() {
|
||||
@Override
|
||||
public void onArticlesFound(ArrayList<NewsArticle> articles) {
|
||||
lastCheck = new Date();
|
||||
@@ -62,7 +70,7 @@ public class News {
|
||||
}
|
||||
|
||||
public static ArrayList<NewsArticle> articles(){
|
||||
return articles;
|
||||
return new ArrayList<>(articles);
|
||||
}
|
||||
|
||||
public static int unreadArticles(Date lastRead){
|
||||
@@ -78,4 +86,40 @@ public class News {
|
||||
lastCheck = null;
|
||||
}
|
||||
|
||||
public static Image parseArticleIcon(NewsArticle article){
|
||||
|
||||
try {
|
||||
|
||||
//recognized formats are:
|
||||
//"ICON: <name of enum constant in Icons.java>"
|
||||
if (article.icon.startsWith("ICON: ")){
|
||||
return Icons.get(Icons.valueOf(article.icon.replace("ICON: ", "")));
|
||||
//"ITEM: <integer constant corresponding to values in ItemSpriteSheet.java>"
|
||||
} else if (article.icon.startsWith("ITEM: ")){
|
||||
return new ItemSprite(Integer.parseInt(article.icon.replace("ITEM: ", "")));
|
||||
//"<asset filename>, <tx left>, <tx top>, <width>, <height>"
|
||||
} else {
|
||||
String[] split = article.icon.split(", ");
|
||||
return new Image( split[0],
|
||||
Integer.parseInt(split[1]),
|
||||
Integer.parseInt(split[2]),
|
||||
Integer.parseInt(split[3]),
|
||||
Integer.parseInt(split[4]));
|
||||
}
|
||||
|
||||
//if we run into any formatting errors (or icon is null), default to the news icon
|
||||
} catch (Exception e){
|
||||
if (article.icon != null) ShatteredPixelDungeon.reportException(e);
|
||||
return Icons.get(Icons.NEWS);
|
||||
}
|
||||
}
|
||||
|
||||
public static String parseArticleDate(NewsArticle article){
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(article.date);
|
||||
return cal.get(Calendar.YEAR)
|
||||
+ "-" + String.format("%02d", cal.get(Calendar.MONTH)+1)
|
||||
+ "-" + String.format("%02d", cal.get(Calendar.DAY_OF_MONTH));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user