v2.5.0: converted notes tab to use new grid UI

This commit is contained in:
Evan Debenham
2024-06-24 12:34:11 -04:00
parent c861f8cef8
commit dd9f90c372
4 changed files with 189 additions and 59 deletions

View File

@@ -112,13 +112,12 @@ windows.wndinfotalent.upgrade=Upgrade Talent
windows.wndinfotrap.inactive=This trap is inactive, and can no longer be triggered.
windows.wndjournal.guide=Guide
windows.wndjournal.notes=Notes
windows.wndjournal.items=Items
windows.wndjournal$guidetab.title=Guidebooks
windows.wndjournal$guidetab.title=Tome of Dungeon Mastery
windows.wndjournal$alchemytab.title=Alchemy Guide
windows.wndjournal$guidetab.missing=page missing
windows.wndjournal$notestab.keys=Keys
windows.wndjournal$notestab.landmarks=Landmarks
windows.wndjournal$notestab.title=Adventuring Notes
windows.wndjournal$notestab.desc=As you journey through the dungeon, you will automatically record noteworthy things here.
windows.wndjournal$notestab.floor_header=Floor %d
windows.wndjournal$catalogtab.title=Catalogs
windows.wndjournal$catalogtab.title_equipment=Equipment
windows.wndjournal$catalogtab.title_consumables=Consumables

View File

@@ -21,9 +21,24 @@
package com.shatteredpixel.shatteredpixeldungeon.journal;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.Key;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.plants.Sungrass;
import com.shatteredpixel.shatteredpixeldungeon.sprites.BlacksmithSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.GhostSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ImpSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ShopkeeperSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.SpawnerSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.StatueSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.WandmakerSprite;
import com.shatteredpixel.shatteredpixeldungeon.tiles.TerrainFeaturesTilemap;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIcon;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
import com.watabou.noosa.Image;
import com.watabou.utils.Bundlable;
import com.watabou.utils.Bundle;
@@ -40,7 +55,13 @@ public class Notes {
public int depth(){
return depth;
}
public Image icon() { return Icons.STAIRS.get(); }
public int quantity() { return 1; }
public abstract String title();
public abstract String desc();
@Override
@@ -79,8 +100,8 @@ public class Notes {
IMP,
DEMON_SPAWNER;
public String desc() {
public String title() {
return Messages.get(this, name());
}
}
@@ -95,12 +116,51 @@ public class Notes {
this.landmark = landmark;
this.depth = depth;
}
public Image icon(){
switch (landmark){
default:
return super.icon();
//TODO we probably want a separate image file for landmark visuals, especially if we expand this
case WELL_OF_HEALTH:
case WELL_OF_AWARENESS:
return new Image(Assets.Environment.TILES_SEWERS, 48, 16, 16, 16);
case ALCHEMY:
return new Image(Assets.Environment.TILES_SEWERS, 0, 64, 16, 16);
case GARDEN:
return TerrainFeaturesTilemap.getPlantVisual(new Sungrass());
case STATUE:
return new Image(new StatueSprite());
case SACRIFICIAL_FIRE:
return new BuffIcon(BuffIndicator.SACRIFICE, true);
case SHOP:
return new Image(new ShopkeeperSprite());
case GHOST:
return new Image(new GhostSprite());
case WANDMAKER:
return new Image(new WandmakerSprite());
case TROLL:
return new Image(new BlacksmithSprite());
case IMP:
return new Image(new ImpSprite());
case DEMON_SPAWNER:
return new Image(new SpawnerSprite());
}
}
@Override
public String title() {
return landmark.title();
}
@Override
public String desc() {
return landmark.desc();
return "";
}
@Override
public boolean equals(Object obj) {
return (obj instanceof LandmarkRecord)
@@ -137,16 +197,26 @@ public class Notes {
public int depth() {
return key.depth;
}
@Override
public Image icon() {
return new ItemSprite(key);
}
@Override
public String title() {
return key.title();
}
@Override
public String desc() {
return key.title();
return key.desc();
}
public Class<? extends Key> type(){
return key.getClass();
}
public int quantity(){
return key.quantity();
}
@@ -259,6 +329,21 @@ public class Notes {
}
return filtered;
}
public static ArrayList<Record> getRecords(int depth){
ArrayList<Record> filtered = new ArrayList<>();
for (Record rec : records){
if (rec.depth() == depth){
if (rec instanceof KeyRecord){
filtered.add(rec); //key records always go at the end
} else {
filtered.add(0, rec);
}
}
}
return filtered;
}
public static void remove( Record rec ){
records.remove(rec);

View File

@@ -35,6 +35,7 @@ public class ScrollingGridPane extends ScrollPane {
private ArrayList<Component> items = new ArrayList<>();
private static final int ITEM_SIZE = 17;
private static final int MIN_GROUP_SIZE = 3*(ITEM_SIZE+1);
public ScrollingGridPane(){
super(new Component());
@@ -77,15 +78,24 @@ public class ScrollingGridPane extends ScrollPane {
super.layout();
float left = 0;
boolean freshRow = true;
float top = 0;
//these variables help control logic for laying out multiple grid groups on one line
boolean freshRow = true; //whether the previous group is still on its first row
boolean lastWasSmallheader = false; //whether the last UI element was a header on its own
float widthThisGroup = 0; //how wide the current group is (we use a min of 3 items)
for (int i = 0; i < items.size(); i++){
Component item = items.get(i);
if (item instanceof GridHeader){
if (left > 0){
//this bit of logic here exists so that multiple headers can be on one row in landscape
// if both of their groups have a small number of items (e.g. 6)
float spaceLeft = width() - left;
//we can sometimes get two smaller headers next to each other if a group has no items in it
//so we need to treat it as if there were grid items for proper layout
if (left > 0 || lastWasSmallheader){
//this bit of logic exists so that multiple headers can be on one row
// if all of their groups have a small number of items, with a min space for 3
float spacing = Math.max(0, MIN_GROUP_SIZE - widthThisGroup);
float spaceLeft = width() - (left + spacing);
int spaceReq = 0;
for (int j = i+1; j < items.size(); j++){
if (items.get(j) instanceof GridItem){
@@ -94,7 +104,9 @@ public class ScrollingGridPane extends ScrollPane {
break;
}
}
if (freshRow && spaceLeft >= spaceReq){
spaceReq = Math.max(spaceReq, MIN_GROUP_SIZE);
if (!((GridHeader) item).center && freshRow && spaceLeft >= spaceReq){
left = left + spacing;
top -= item.height()+1;
} else {
left = 0;
@@ -104,15 +116,27 @@ public class ScrollingGridPane extends ScrollPane {
}
item.setRect(left, top, width(), item.height());
top += item.height()+1;
widthThisGroup = 0;
if (!((GridHeader) item).center){
lastWasSmallheader = true;
} else {
lastWasSmallheader = false;
}
} if (item instanceof GridItem){
if (left + ITEM_SIZE > width()) {
left = 0;
widthThisGroup = 0;
top += ITEM_SIZE+1;
freshRow = false;
}
item.setRect(left, top, ITEM_SIZE, ITEM_SIZE);
left += ITEM_SIZE+1;
widthThisGroup += ITEM_SIZE+1;
lastWasSmallheader = false;
}
}
if (left > 0){
left = 0;
@@ -186,7 +210,6 @@ public class ScrollingGridPane extends ScrollPane {
protected RenderedTextBlock text;
boolean center;
protected ColorBlock sep;
public GridHeader( String text ){
this(text, 7, false);
@@ -199,10 +222,6 @@ public class ScrollingGridPane extends ScrollPane {
this.text = PixelScene.renderTextBlock(text, size);
add(this.text);
if (center) {
sep = new ColorBlock(1, 1, 0xFF222222);
//add(sep);
}
}
@Override
@@ -215,11 +234,11 @@ public class ScrollingGridPane extends ScrollPane {
super.layout();
if (center){
text.align(RenderedTextBlock.CENTER_ALIGN);
text.maxWidth((int)width());
text.setPos(x + (width() - text.width()) / 2, y+1);
sep.size(width(), 1);
sep.x = x;
sep.y = bottom();
} else {
text.maxWidth((int)width());
text.setPos(x, y+1);
}
}

View File

@@ -22,8 +22,8 @@
package com.shatteredpixel.shatteredpixeldungeon.windows;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.Statistics;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
@@ -119,6 +119,11 @@ public class WndJournal extends WndTabbed {
guideTab.active = guideTab.visible = value;
if (value) last_index = 0;
}
@Override
protected String hoverText() {
return Messages.get(guideTab, "title");
}
},
new IconTab( new ItemSprite(ItemSpriteSheet.ALCH_PAGE, null) ) {
protected void select( boolean value ) {
@@ -126,6 +131,11 @@ public class WndJournal extends WndTabbed {
alchemyTab.active = alchemyTab.visible = value;
if (value) last_index = 1;
}
@Override
protected String hoverText() {
return Messages.get(alchemyTab, "title");
}
},
new IconTab( Icons.get(Icons.STAIRS) ) {
protected void select( boolean value ) {
@@ -133,6 +143,11 @@ public class WndJournal extends WndTabbed {
notesTab.active = notesTab.visible = value;
if (value) last_index = 2;
}
@Override
protected String hoverText() {
return Messages.get(notesTab, "title");
}
},
new IconTab( Icons.CATALOG.get() ) {
protected void select( boolean value ) {
@@ -140,6 +155,11 @@ public class WndJournal extends WndTabbed {
catalogTab.active = catalogTab.visible = value;
if (value) last_index = 3;
}
@Override
protected String hoverText() {
return Messages.get(catalogTab, "title");
}
}
};
@@ -401,52 +421,59 @@ public class WndJournal extends WndTabbed {
private static class NotesTab extends Component {
private ScrollingListPane list;
private ScrollingGridPane grid;
@Override
protected void createChildren() {
list = new ScrollingListPane();
add( list );
grid = new ScrollingGridPane();
add(grid);
}
@Override
protected void layout() {
super.layout();
list.setRect( x, y, width, height);
grid.setRect( x, y, width, height);
}
private void updateList(){
//Keys
ArrayList<Notes.KeyRecord> keys = Notes.getRecords(Notes.KeyRecord.class);
if (!keys.isEmpty()){
list.addTitle(Messages.get(this, "keys"));
for(Notes.Record rec : keys){
ScrollingListPane.ListItem item = new ScrollingListPane.ListItem( Icons.get(Icons.STAIRS),
Integer.toString(rec.depth()),
Messages.titleCase(rec.desc()));
if (Dungeon.depth == rec.depth()) item.hardlight(TITLE_COLOR);
list.addItem(item);
grid.addHeader("_" + Messages.get(this, "title") + "_", 9, true);
grid.addHeader(Messages.get(this, "desc"), 6, true);
for (int i = Statistics.deepestFloor; i > 0; i--){
ArrayList<Notes.Record> recs = Notes.getRecords(i);
grid.addHeader("_" + Messages.get(this, "floor_header", i) + "_");
for( Notes.Record rec : recs){
ScrollingGridPane.GridItem gridItem = new ScrollingGridPane.GridItem(rec.icon()){
@Override
public boolean onClick(float x, float y) {
if (inside(x, y)) {
GameScene.show(new WndTitledMessage(rec.icon(),
Messages.titleCase(rec.title()),
rec.desc()));
return true;
} else {
return false;
}
}
};
if (rec.quantity() > 1){
BitmapText text = new BitmapText(Integer.toString(rec.quantity()), PixelScene.pixelFont);
text.measure();
gridItem.addSecondIcon( text );
}
grid.addItem(gridItem);
}
}
//Landmarks
ArrayList<Notes.LandmarkRecord> landmarks = Notes.getRecords(Notes.LandmarkRecord.class);
if (!landmarks.isEmpty()){
list.addTitle(Messages.get(this, "landmarks"));
grid.setRect(x, y, width, height);
for (Notes.Record rec : landmarks) {
ScrollingListPane.ListItem item = new ScrollingListPane.ListItem( Icons.get(Icons.STAIRS),
Integer.toString(rec.depth()),
Messages.titleCase(rec.desc()));
if (Dungeon.depth == rec.depth()) item.hardlight(TITLE_COLOR);
list.addItem(item);
}
}
list.setRect(x, y, width, height);
}
}