diff --git a/core/src/main/assets/messages/ui/ui.properties b/core/src/main/assets/messages/ui/ui.properties index bdfebac6b..2b67a10e4 100644 --- a/core/src/main/assets/messages/ui/ui.properties +++ b/core/src/main/assets/messages/ui/ui.properties @@ -1,3 +1,13 @@ +ui.customnotebutton.hover_text=Add a Custom Note +ui.customnotebutton.default_title_text=Custom Text Note +ui.customnotebutton$customnotewindow.edit_title=Edit Title +ui.customnotebutton$customnotewindow.add_text=Add Text +ui.customnotebutton$customnotewindow.edit_text=Edit Text +ui.customnotebutton$customnotewindow.delete=Delete +ui.customnotebutton$customnotewindow.delete_warn=Are you sure you want to delete this custom note? +ui.customnotebutton$customnotewindow.confirm=Confirm +ui.customnotebutton$customnotewindow.cancel=Cancel + ui.quickslotbutton.select_item=Quickslot an item ui.talentspane.tier=tier %d diff --git a/core/src/main/assets/messages/windows/windows.properties b/core/src/main/assets/messages/windows/windows.properties index 6389ceffd..457ce6926 100644 --- a/core/src/main/assets/messages/windows/windows.properties +++ b/core/src/main/assets/messages/windows/windows.properties @@ -117,6 +117,7 @@ windows.wndjournal$alchemytab.title=Alchemy Guide windows.wndjournal$guidetab.missing=page missing 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.custom_notes=Custom Notes windows.wndjournal$notestab.floor_header=Floor %d windows.wndjournal$catalogtab.title=Catalogs windows.wndjournal$catalogtab.title_equipment=Equipment diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/journal/Notes.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/journal/Notes.java index bcc9e57a0..a5d46e5dd 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/journal/Notes.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/journal/Notes.java @@ -36,6 +36,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.RatKing; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Shopkeeper; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Wandmaker; import com.shatteredpixel.shatteredpixeldungeon.items.Generator; +import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.keys.Key; import com.shatteredpixel.shatteredpixeldungeon.levels.Level; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.WeakFloorRoom; @@ -77,6 +78,8 @@ public class Notes { public Visual secondIcon() { return null; } public int quantity() { return 1; } + + protected abstract int order(); public abstract String title(); @@ -243,6 +246,11 @@ public class Notes { } } + @Override + protected int order(){ + return landmark.ordinal(); + } + @Override public boolean equals(Object obj) { return (obj instanceof LandmarkRecord) @@ -310,6 +318,11 @@ public class Notes { return key.getClass(); } + @Override + protected int order() { + return 1000 + Generator.Category.order(key); + } + public int quantity(){ return key.quantity(); } @@ -338,6 +351,128 @@ public class Notes { bundle.put( KEY, key ); } } + + public enum CustomType { + TEXT, + DEPTH, //TODO + ITEM, //TODO + ITEM_TYPE //TODO + } + + public static class CustomRecord extends Record { + + protected CustomType type; + + protected int ID; + protected Class itemClass; + + protected String title; + protected String body; + + public CustomRecord() {} + + public CustomRecord(String title, String desc) { + type = CustomType.TEXT; + this.title = title; + body = desc; + } + + public CustomRecord(int depth, String title, String desc) { + type = CustomType.DEPTH; + this.depth = depth; + this.title = title; + body = desc; + } + + public CustomRecord(Item item, String title, String desc) { + type = CustomType.ITEM; + itemClass = item.getClass(); + this.title = title; + body = desc; + } + + @Override + public int depth() { + if (type == CustomType.DEPTH){ + return depth; + } else { + return 0; + } + } + + @Override + public Image icon() { + switch (type){ + case TEXT: default: + return Icons.SCROLL_COLOR.get(); + case DEPTH: + return Icons.STAIRS.get(); + } + } + + @Override + public Visual secondIcon() { + switch (type){ + case TEXT: default: + //TODO perhaps use first few chars from title? + return null; + case DEPTH: + BitmapText text = new BitmapText(Integer.toString(depth()), PixelScene.pixelFont); + text.measure(); + return text; + } + } + + @Override + protected int order() { + return 2000 + ID; + } + + public void editText(String title, String desc){ + this.title = title; + this.body = desc; + } + + @Override + public String title() { + return title; + } + + @Override + public String desc() { + return body; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof CustomRecord && ((CustomRecord) obj).ID == ID; + } + + private static final String TYPE = "type"; + private static final String ID_NUMBER = "id_number"; + + private static final String TITLE = "title"; + private static final String BODY = "body"; + + @Override + public void storeInBundle(Bundle bundle) { + super.storeInBundle(bundle); + bundle.put(TYPE, type); + bundle.put(ID_NUMBER, ID); + bundle.put(TITLE, title); + bundle.put(BODY, body); + } + + @Override + public void restoreFromBundle(Bundle bundle) { + super.restoreFromBundle(bundle); + type = bundle.getEnum(TYPE, CustomType.class); + ID = bundle.getInt(ID_NUMBER); + + title = bundle.getString(TITLE); + body = bundle.getString(BODY); + } + } private static ArrayList records; @@ -345,14 +480,19 @@ public class Notes { records = new ArrayList<>(); } - private static final String RECORDS = "records"; - + private static final String RECORDS = "records"; + private static final String NEXT_CUSTOM_ID = "next_custom_id"; + + protected static int nextCustomID = 0; + public static void storeInBundle( Bundle bundle ) { bundle.put( RECORDS, records ); + bundle.put( NEXT_CUSTOM_ID, nextCustomID ); } public static void restoreFromBundle( Bundle bundle ) { records = new ArrayList<>(); + nextCustomID = bundle.getInt( NEXT_CUSTOM_ID ); for (Bundlable rec : bundle.getCollection( RECORDS ) ) { records.add( (Record) rec ); } @@ -362,7 +502,7 @@ public class Notes { LandmarkRecord l = new LandmarkRecord( landmark, Dungeon.depth ); if (!records.contains(l)) { boolean result = records.add(new LandmarkRecord(landmark, Dungeon.depth)); - Collections.sort(records); + Collections.sort(records, comparator); return result; } return false; @@ -380,7 +520,7 @@ public class Notes { KeyRecord k = new KeyRecord(key); if (!records.contains(k)){ boolean result = records.add(k); - Collections.sort(records); + Collections.sort(records, comparator); return result; } else { k = (KeyRecord) records.get(records.indexOf(k)); @@ -412,9 +552,23 @@ public class Notes { return 0; } } - - public static ArrayList getRecords(){ - return getRecords(Record.class); + + public static boolean add( CustomRecord rec ){ + rec.ID = nextCustomID++; + if (!records.contains(rec)){ + boolean result = records.add(rec); + Collections.sort(records, comparator); + return result; + } + return false; + } + + public static boolean remove( CustomRecord rec ){ + if (records.contains(rec)){ + records.remove(rec); + return true; + } + return false; } public static ArrayList getRecords( Class recordType ){ @@ -443,23 +597,8 @@ public class Notes { private static final Comparator comparator = new Comparator() { @Override public int compare(Record r1, Record r2) { - if (r1 instanceof LandmarkRecord){ - if (r2 instanceof LandmarkRecord){ - return ((LandmarkRecord) r1).landmark.ordinal() - ((LandmarkRecord) r2).landmark.ordinal(); - } else { - return -1; - } - } else if (r2 instanceof LandmarkRecord){ - return 1; - } else { - //matches order in key display - return Generator.Category.order(((KeyRecord)r2).key) - Generator.Category.order(((KeyRecord)r1).key); - } + return r1.order() - r2.order(); } }; - public static void remove( Record rec ){ - records.remove(rec); - } - } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/CustomNoteButton.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/CustomNoteButton.java new file mode 100644 index 000000000..f160ec0fa --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/CustomNoteButton.java @@ -0,0 +1,160 @@ +/* + * 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 + */ + +package com.shatteredpixel.shatteredpixeldungeon.ui; + +import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; +import com.shatteredpixel.shatteredpixeldungeon.journal.Notes; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; +import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndJournalItem; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndTextInput; +import com.watabou.noosa.Game; +import com.watabou.utils.Callback; + +//this is contained in its own class as custom notes have a lot of messy window UI logic +public class CustomNoteButton extends IconButton { + + public CustomNoteButton () { + super(Icons.PLUS.get()); + + width = 11; + height = 11; + } + + @Override + protected void onClick() { + super.onClick(); + + //TODO we want a selection window for note type here, atm it's only plaintext notes + Notes.CustomRecord custom = new Notes.CustomRecord(Messages.get(this, "default_title_text"), ""); + Notes.add(custom); + refreshScene(custom); + } + + @Override + protected String hoverText() { + return Messages.get(this, "hover_text"); + } + + public static class CustomNoteWindow extends WndJournalItem { + + public CustomNoteWindow(Notes.CustomRecord rec) { + super(rec.icon(), rec.title(), rec.desc()); + + RedButton title = new RedButton( Messages.get(CustomNoteWindow.class, "edit_title") ){ + @Override + protected void onClick() { + GameScene.show(new WndTextInput(Messages.get(CustomNoteWindow.class, "edit_title"), + "", + rec.title(), + 50, + false, + Messages.get(CustomNoteWindow.class, "confirm"), + Messages.get(CustomNoteWindow.class, "cancel")){ + @Override + public void onSelect(boolean positive, String text) { + if (positive){ + rec.editText(text, rec.desc()); + refreshScene(rec); + } + } + }); + } + }; + add(title); + title.setRect(0, Math.min(height+2, PixelScene.uiCamera.height-50), width/2-1, 16); + + String editBodyText = rec.desc().isEmpty() ? Messages.get(CustomNoteWindow.class, "add_text") : Messages.get(CustomNoteWindow.class, "edit_text"); + RedButton body = new RedButton(editBodyText){ + @Override + protected void onClick() { + GameScene.show(new WndTextInput(editBodyText, + "", + rec.desc(), + 500, + true, + Messages.get(CustomNoteWindow.class, "confirm"), + Messages.get(CustomNoteWindow.class, "cancel")){ + @Override + public void onSelect(boolean positive, String text) { + if (positive){ + rec.editText(rec.title(), text); + refreshScene(rec); + } + } + }); + } + }; + add(body); + body.setRect(title.right()+2, title.top(), width/2-1, 16); + + RedButton delete = new RedButton( Messages.get(CustomNoteWindow.class, "delete") ){ + @Override + protected void onClick() { + GameScene.show(new WndOptions(Icons.WARNING.get(), + Messages.get(CustomNoteWindow.class, "delete"), + Messages.get(CustomNoteWindow.class, "delete_warn"), + Messages.get(CustomNoteWindow.class, "confirm"), + Messages.get(CustomNoteWindow.class, "cancel")){ + @Override + protected void onSelect(int index) { + if (index == 0){ + Notes.remove(rec); + refreshScene(null); + } + } + }); + } + }; + add(delete); + delete.setRect(0, title.bottom()+1, width, 16); + + resize(width, (int)delete.bottom()); + } + + } + + private static void refreshScene(Notes.CustomRecord recToShow){ + if (recToShow == null){ + ShatteredPixelDungeon.seamlessResetScene(); + } else { + ShatteredPixelDungeon.seamlessResetScene(new Game.SceneChangeCallback() { + @Override + public void beforeCreate() { + + } + + @Override + public void afterCreate() { + Game.runOnRenderThread(new Callback() { + @Override + public void call() { + ShatteredPixelDungeon.scene().addToFront(new CustomNoteWindow(recToShow)); + } + }); + } + }); + } + } +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndJournal.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndJournal.java index 94c7f4fc5..aaa813a33 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndJournal.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndJournal.java @@ -53,6 +53,7 @@ import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; import com.shatteredpixel.shatteredpixeldungeon.tiles.TerrainFeaturesTilemap; +import com.shatteredpixel.shatteredpixeldungeon.ui.CustomNoteButton; import com.shatteredpixel.shatteredpixeldungeon.ui.Icons; import com.shatteredpixel.shatteredpixeldungeon.ui.QuickRecipe; import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton; @@ -424,6 +425,7 @@ public class WndJournal extends WndTabbed { private static class NotesTab extends Component { private ScrollingGridPane grid; + private CustomNoteButton custom; @Override protected void createChildren() { @@ -443,6 +445,33 @@ public class WndJournal extends WndTabbed { grid.addHeader(Messages.get(this, "desc"), 6, true); + ArrayList customRecs = Notes.getRecords(Notes.CustomRecord.class); + + if (!customRecs.isEmpty()){ + grid.addHeader("_" + Messages.get(this, "custom_notes") + "_"); + + for (Notes.CustomRecord rec : customRecs){ + ScrollingGridPane.GridItem gridItem = new ScrollingGridPane.GridItem(rec.icon()){ + @Override + public boolean onClick(float x, float y) { + if (inside(x, y)) { + GameScene.show(new CustomNoteButton.CustomNoteWindow(rec)); + return true; + } else { + return false; + } + } + }; + + Visual secondIcon = rec.secondIcon(); + if (secondIcon != null){ + gridItem.addSecondIcon( secondIcon ); + } + + grid.addItem(gridItem); + } + } + for (int i = Statistics.deepestFloor; i > 0; i--){ ArrayList recs = Notes.getRecords(i); @@ -473,6 +502,10 @@ public class WndJournal extends WndTabbed { } } + custom = new CustomNoteButton(); + grid.content().add(custom); + custom.setPos(width-custom.width()-1, 0); + grid.setRect(x, y, width, height); }