v2.5.0: completely overhauled catalog UI, now uses a grid!

This commit is contained in:
Evan Debenham
2024-06-09 14:52:03 -04:00
parent 9d817725eb
commit 39209884f3
6 changed files with 341 additions and 117 deletions

View File

@@ -1,3 +1,11 @@
journal.catalog.weapons.title=weapons
journal.catalog.armor.title=armor
journal.catalog.wands.title=wands
journal.catalog.rings.title=rings
journal.catalog.artifacts.title=artifacts
journal.catalog.potions.title=potions
journal.catalog.scrolls.title=scrolls
journal.document.adventurers_guide.title=Tome of Dungeon Mastery
journal.document.adventurers_guide.intro.title=Introduction
journal.document.adventurers_guide.intro.body=Greetings Adventurer, you are reading the Tome of Dungeon Mastery. This book is full of tips and tricks to help budding adventurers survive and excel!\n\nThis guide is best used as a reference, and will magically let you know when is the best time to read each page.\n\n(Whenever the journal icon at the top-right blinks, the guidebook has something to tell you! Some pages seem to be ripped out of the book though, perhaps you can find them in the dungeon?)

View File

@@ -119,6 +119,10 @@ windows.wndjournal$guidetab.title=Guidebooks
windows.wndjournal$guidetab.missing=page missing
windows.wndjournal$notestab.keys=Keys
windows.wndjournal$notestab.landmarks=Landmarks
windows.wndjournal$catalogtab.title=Catalogs
windows.wndjournal$catalogtab.title_equipment=Equipment
windows.wndjournal$catalogtab.title_consumables=Consumables
windows.wndjournal$catalogtab.not_seen=You haven't identified this item yet.
windows.wndjournal$loretab.title=Documents
windows.wndkeybindings.controller_info=The left stick moves your character\nThe right stick controls an on-screen pointer

View File

@@ -590,7 +590,7 @@ public class Badges {
public static void validateItemsIdentified() {
for (Catalog cat : Catalog.values()){
if (cat.allSeen()){
if (cat.totalItems() == cat.totalSeen()){
Badge b = Catalog.catalogBadges.get(cat);
if (!isUnlocked(b)){
displayBadge(b);

View File

@@ -24,6 +24,7 @@ package com.shatteredpixel.shatteredpixeldungeon.journal;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.watabou.utils.Bundle;
import java.util.ArrayList;
@@ -33,12 +34,15 @@ import java.util.LinkedHashMap;
import java.util.List;
public enum Catalog {
//EQUIPMENT
WEAPONS,
ARMOR,
WANDS,
RINGS,
ARTIFACTS,
//CONSUMABLES
POTIONS,
SCROLLS;
@@ -47,14 +51,21 @@ public enum Catalog {
public Collection<Class<? extends Item>> items(){
return seen.keySet();
}
public boolean allSeen(){
for (Class<?extends Item> item : items()){
if (!seen.get(item)){
return false;
}
public String title(){
return Messages.get(this, name() + ".title");
}
public int totalItems(){
return seen.size();
}
public int totalSeen(){
int seenTotal = 0;
for (boolean itemSeen : seen.values()){
if (itemSeen) seenTotal++;
}
return true;
return seenTotal;
}
static {

View File

@@ -0,0 +1,213 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2024 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.ui;
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.watabou.noosa.ColorBlock;
import com.watabou.noosa.Image;
import com.watabou.noosa.ui.Component;
import java.util.ArrayList;
public class ScrollingGridPane extends ScrollPane {
private ArrayList<Component> items = new ArrayList<>();
private static final int ITEM_SIZE = 17;
public ScrollingGridPane(){
super(new Component());
}
@Override
public void onClick(float x, float y) {
for (Component item : items) {
if ((item instanceof ScrollingGridPane.GridItem) && ((ScrollingGridPane.GridItem) item).onClick( x, y )) {
break;
}
}
}
public void addItem( ScrollingGridPane.GridItem item ){
content.add(item);
items.add(item);
layout();
}
public void addHeader( String text ){
addHeader( text, 7, false );
}
public void addHeader( String text, int size, boolean center ){
GridHeader header = new GridHeader(text, size, center);
content.add(header);
items.add(header);
layout();
}
@Override
public synchronized void clear() {
content.clear();
items.clear();
}
@Override
protected void layout() {
super.layout();
float left = 0;
float top = 0;
for (Component item : items){
if (item instanceof GridHeader){
if (left > 0){
left = 0;
top += ITEM_SIZE+2;
}
item.setRect(left, top, width(), item.height());
top += item.height()+1;
} if (item instanceof GridItem){
if (left + ITEM_SIZE > width()) {
left = 0;
top += ITEM_SIZE+1;
}
item.setRect(left, top, ITEM_SIZE, ITEM_SIZE);
left += ITEM_SIZE+1;
}
}
if (left > 0){
left = 0;
top += ITEM_SIZE+1;
}
content.setSize(width, top);
}
public static class GridItem extends Component {
protected Image icon;
protected Image secondIcon;
protected ColorBlock bg;
public GridItem( Image icon ) {
super();
this.icon.copy(icon);
}
public void addSecondIcon( Image icon ){
secondIcon = icon;
add(secondIcon);
layout();
}
public void hardLightBG( float r, float g, float b ){
bg.hardlight(r, g, b);
}
public boolean onClick( float x, float y ){
return false;
}
@Override
protected void createChildren() {
bg = new ColorBlock( 1, 1, 0x9953564D);
add(bg);
icon = new Image();
add( icon );
}
@Override
protected void layout() {
bg.x = x;
bg.y = y;
bg.size(width(), height());
icon.y = y + (height() - icon.height()) / 2f;
icon.x = x + (width() - icon.width())/2f;
PixelScene.align(icon);
if (secondIcon != null){
secondIcon.x = x + width()-secondIcon.width()-1;
secondIcon.y = y + height()-secondIcon.height()-1;
}
}
}
public static class GridHeader extends Component {
protected RenderedTextBlock text;
boolean center;
protected ColorBlock sep;
public GridHeader( String text ){
this(text, 7, false);
}
public GridHeader( String text, int size, boolean center ){
super();
this.center = center;
this.text = PixelScene.renderTextBlock(text, size);
add(this.text);
if (center) {
sep = new ColorBlock(1, 1, 0xFF222222);
//add(sep);
}
}
@Override
protected void createChildren() {
super.createChildren();
}
@Override
protected void layout() {
super.layout();
if (center){
text.setPos(x + (width() - text.width()) / 2, y+1);
sep.size(width(), 1);
sep.x = x;
sep.y = bottom();
} else {
text.setPos(x, y+1);
}
}
@Override
public float height() {
if (center){
return text.height() + 3;
} else {
return text.height() + 2;
}
}
}
}

View File

@@ -21,6 +21,7 @@
package com.shatteredpixel.shatteredpixeldungeon.windows;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
@@ -41,6 +42,7 @@ import com.shatteredpixel.shatteredpixeldungeon.ui.QuickRecipe;
import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
import com.shatteredpixel.shatteredpixeldungeon.ui.ScrollPane;
import com.shatteredpixel.shatteredpixeldungeon.ui.ScrollingGridPane;
import com.shatteredpixel.shatteredpixeldungeon.ui.ScrollingListPane;
import com.watabou.noosa.ColorBlock;
import com.watabou.noosa.Image;
@@ -48,16 +50,14 @@ import com.watabou.noosa.ui.Component;
import com.watabou.utils.Reflection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Collection;
public class WndJournal extends WndTabbed {
public static final int WIDTH_P = 126;
public static final int HEIGHT_P = 180;
public static final int WIDTH_L = 200;
public static final int WIDTH_L = 216;
public static final int HEIGHT_L = 130;
private static final int ITEM_HEIGHT = 18;
@@ -451,29 +451,19 @@ public class WndJournal extends WndTabbed {
private static class CatalogTab extends Component{
private RedButton[] itemButtons;
private static final int NUM_BUTTONS = 7;
private static final int NUM_BUTTONS = 2;
private static int currentItemIdx = 0;
//sprite locations
private static final int WEAPON_IDX = 0;
private static final int ARMOR_IDX = 1;
private static final int WAND_IDX = 2;
private static final int RING_IDX = 3;
private static final int ARTIF_IDX = 4;
private static final int POTION_IDX = 5;
private static final int SCROLL_IDX = 6;
private static final int EQUIP_IDX = 0;
private static final int CONSUM_IDX = 1;
private static final int spriteIndexes[] =
{ItemSpriteSheet.WEAPON_HOLDER,
ItemSpriteSheet.ARMOR_HOLDER,
ItemSpriteSheet.WAND_HOLDER,
ItemSpriteSheet.RING_HOLDER,
ItemSpriteSheet.ARTIFACT_HOLDER,
ItemSpriteSheet.POTION_HOLDER,
ItemSpriteSheet.SCROLL_HOLDER};
ItemSpriteSheet.POTION_HOLDER};
private ScrollingListPane list;
private ScrollingGridPane grid;
@Override
protected void createChildren() {
@@ -491,8 +481,8 @@ public class WndJournal extends WndTabbed {
add( itemButtons[i] );
}
list = new ScrollingListPane();
add( list );
grid = new ScrollingGridPane();
add( grid );
}
@Override
@@ -508,13 +498,13 @@ public class WndJournal extends WndTabbed {
PixelScene.align(itemButtons[i]);
}
list.setRect(0, itemButtons[NUM_BUTTONS-1].bottom() + 1, width,
grid.setRect(0, itemButtons[NUM_BUTTONS-1].bottom() + 1, width,
height - itemButtons[NUM_BUTTONS-1].bottom() - 1);
}
private void updateList() {
list.clear();
grid.clear();
for (int i = 0; i < NUM_BUTTONS; i++){
if (i == currentItemIdx){
@@ -524,101 +514,99 @@ public class WndJournal extends WndTabbed {
}
}
list.scrollTo( 0, 0 );
ArrayList<Class<? extends Item>> itemClasses;
final HashMap<Class<? extends Item>, Boolean> known = new HashMap<>();
if (currentItemIdx == WEAPON_IDX) {
itemClasses = new ArrayList<>(Catalog.WEAPONS.items());
for (Class<? extends Item> cls : itemClasses) known.put(cls, true);
} else if (currentItemIdx == ARMOR_IDX){
itemClasses = new ArrayList<>(Catalog.ARMOR.items());
for (Class<? extends Item> cls : itemClasses) known.put(cls, true);
} else if (currentItemIdx == WAND_IDX){
itemClasses = new ArrayList<>(Catalog.WANDS.items());
for (Class<? extends Item> cls : itemClasses) known.put(cls, true);
} else if (currentItemIdx == RING_IDX){
itemClasses = new ArrayList<>(Catalog.RINGS.items());
for (Class<? extends Item> cls : itemClasses) known.put(cls, Ring.getKnown().contains(cls));
} else if (currentItemIdx == ARTIF_IDX){
itemClasses = new ArrayList<>(Catalog.ARTIFACTS.items());
for (Class<? extends Item> cls : itemClasses) known.put(cls, true);
} else if (currentItemIdx == POTION_IDX){
itemClasses = new ArrayList<>(Catalog.POTIONS.items());
for (Class<? extends Item> cls : itemClasses) known.put(cls, Potion.getKnown().contains(cls));
} else if (currentItemIdx == SCROLL_IDX) {
itemClasses = new ArrayList<>(Catalog.SCROLLS.items());
for (Class<? extends Item> cls : itemClasses) known.put(cls, Scroll.getKnown().contains(cls));
} else {
itemClasses = new ArrayList<>();
}
Collections.sort(itemClasses, new Comparator<Class<? extends Item>>() {
@Override
public int compare(Class<? extends Item> a, Class<? extends Item> b) {
int result = 0;
//specifically known items appear first, then seen items, then unknown items.
if (known.get(a) && Catalog.isSeen(a)) result -= 2;
if (known.get(b) && Catalog.isSeen(b)) result += 2;
if (Catalog.isSeen(a)) result --;
if (Catalog.isSeen(b)) result ++;
return result;
}
});
for (Class<? extends Item> itemClass : itemClasses) {
Item item = Reflection.newInstance(itemClass);
boolean itemIDed = known.get(itemClass);
boolean itemSeen = Catalog.isSeen(itemClass);
if ( itemSeen && !itemIDed ){
if (item instanceof Ring){
((Ring) item).anonymize();
} else if (item instanceof Potion){
((Potion) item).anonymize();
} else if (item instanceof Scroll){
((Scroll) item).anonymize();
}
}
ScrollingListPane.ListItem listItem = new ScrollingListPane.ListItem(
(itemIDed && itemSeen) ? new ItemSprite(item) : new ItemSprite( ItemSpriteSheet.SOMETHING + spriteIndexes[currentItemIdx]),
null,
itemSeen ? Messages.titleCase(item.trueName()) : "???"
){
@Override
public boolean onClick(float x, float y) {
if (inside( x, y ) && itemSeen) {
if (item instanceof ClassArmor){
GameScene.show(new WndTitledMessage(new Image(icon),
Messages.titleCase(item.trueName()), item.desc()));
} else {
GameScene.show(new WndTitledMessage(new Image(icon),
Messages.titleCase(item.trueName()), item.info()));
}
return true;
} else {
return false;
}
}
};
grid.scrollTo( 0, 0 );
if (!itemSeen) {
listItem.hardlight( 0x999999 );
} else if (!itemIDed) {
listItem.hardlight( 0xCCCCCC );
}
if (currentItemIdx == EQUIP_IDX) {
int totalItems = Catalog.WEAPONS.totalItems() + Catalog.ARMOR.totalItems() + Catalog.WANDS.totalItems() + Catalog.RINGS.totalItems() + Catalog.ARTIFACTS.totalItems();
int totalSeen = Catalog.WEAPONS.totalSeen() + Catalog.ARMOR.totalSeen() + Catalog.WANDS.totalSeen() + Catalog.RINGS.totalSeen() + Catalog.ARTIFACTS.totalSeen();
grid.addHeader("_" + Messages.get(this, "title_equipment") + "_ (" + totalSeen + "/" + totalItems + ")", 9, true);
list.addItem(listItem);
grid.addHeader("_" + Messages.capitalize(Catalog.WEAPONS.title()) + "_ (" + Catalog.WEAPONS.totalSeen() + "/" + Catalog.WEAPONS.totalItems() + "):");
addGridItems(grid, Catalog.WEAPONS.items());
grid.addHeader("_" + Messages.capitalize(Catalog.ARMOR.title()) + "_ (" + Catalog.ARMOR.totalSeen() + "/" + Catalog.ARMOR.totalItems() + "):");
addGridItems(grid, Catalog.ARMOR.items());
grid.addHeader("_" + Messages.capitalize(Catalog.WANDS.title()) + "_ (" + Catalog.WANDS.totalSeen() + "/" + Catalog.WANDS.totalItems() + "):");
addGridItems(grid, Catalog.WANDS.items());
grid.addHeader("_" + Messages.capitalize(Catalog.RINGS.title()) + "_ (" + Catalog.RINGS.totalSeen() + "/" + Catalog.RINGS.totalItems() + "):");
addGridItems(grid, Catalog.RINGS.items());
grid.addHeader("_" + Messages.capitalize(Catalog.ARTIFACTS.title()) + "_ (" + Catalog.ARTIFACTS.totalSeen() + "/" + Catalog.ARTIFACTS.totalItems() + "):");
addGridItems(grid, Catalog.ARTIFACTS.items());
} else if (currentItemIdx == CONSUM_IDX){
int totalItems = Catalog.POTIONS.totalItems() + Catalog.SCROLLS.totalItems();
int totalSeen = Catalog.POTIONS.totalSeen() + Catalog.SCROLLS.totalSeen();
grid.addHeader("_" + Messages.get(this, "title_consumables") + "_ (" + totalSeen + "/" + totalItems + ")", 9, true);
grid.addHeader("_" + Messages.capitalize(Catalog.POTIONS.title()) + "_ (" + Catalog.POTIONS.totalSeen() + "/" + Catalog.POTIONS.totalItems() + "):");
addGridItems(grid, Catalog.POTIONS.items());
grid.addHeader("_" + Messages.capitalize(Catalog.SCROLLS.title()) + "_ (" + Catalog.SCROLLS.totalSeen() + "/" + Catalog.SCROLLS.totalItems() + "):");
addGridItems(grid, Catalog.SCROLLS.items());
}
list.setRect(x, itemButtons[NUM_BUTTONS-1].bottom() + 1, width,
grid.setRect(x, itemButtons[NUM_BUTTONS-1].bottom() + 1, width,
height - itemButtons[NUM_BUTTONS-1].bottom() - 1);
}
}
private static void addGridItems( ScrollingGridPane grid, Collection<Class<? extends Item>> itemClasses) {
for (Class<? extends Item> itemClass : itemClasses) {
Item item = Reflection.newInstance(itemClass);
boolean itemSeen = Catalog.isSeen(itemClass);
if (itemSeen) {
if (item instanceof Ring) {
((Ring) item).anonymize();
} else if (item instanceof Potion) {
((Potion) item).anonymize();
} else if (item instanceof Scroll) {
((Scroll) item).anonymize();
}
}
Image sprite = new ItemSprite(item);
if (!itemSeen) sprite.lightness(0);
ScrollingGridPane.GridItem gridItem = new ScrollingGridPane.GridItem(
sprite) {
@Override
public boolean onClick(float x, float y) {
if (inside(x, y)) {
Image sprite = new Image(icon);
if (itemSeen) {
GameScene.show(new WndTitledMessage(sprite,
Messages.titleCase(item.trueName()),
item instanceof ClassArmor ? item.desc() : item.info()));
} else {
sprite.lightness(0);
GameScene.show(new WndTitledMessage(sprite,
"???",
Messages.get(CatalogTab.class, "not_seen")));
}
return true;
} else {
return false;
}
}
};
if (itemSeen) {
if (item instanceof Potion || item instanceof Scroll || item instanceof Ring) {
Image icon = new Image(Assets.Sprites.ITEM_ICONS);
icon.frame(ItemSpriteSheet.Icons.film.get(item.icon));
gridItem.addSecondIcon(icon);
}
} else {
gridItem.hardLightBG(2f, 1f, 2f);
}
grid.addItem(gridItem);
}
}
public static class LoreTab extends Component{
private ScrollingListPane list;