v2.5.0: finished custom note functionality!

This commit is contained in:
Evan Debenham
2024-07-03 21:37:55 -04:00
parent 5139de2800
commit 4071a47a56
12 changed files with 332 additions and 21 deletions

View File

@@ -1,5 +1,17 @@
ui.customnotebutton.hover_text=Add a Custom Note
ui.customnotebutton.default_title_text=Custom Text Note
ui.customnotebutton.title=Add a Custom Note
ui.customnotebutton.limit_title=Note Limit Reached
ui.customnotebutton.limit_text=You can delete existing notes to make room for more.
ui.customnotebutton.desc=Custom Notes let you record anything you'd like in the journal.\n\nThey can be just text on its own, or they can be associated with a dungeon floor, one of your items, or a type of potion/scroll/ring.
ui.customnotebutton.new_text=New Text Note
ui.customnotebutton.new_text_title=Write a title for this custom text note:
ui.customnotebutton.new_floor=New Dungeon Floor Note
ui.customnotebutton.new_floor_prompt=Select a floor of the dungeon to associate this note with.
ui.customnotebutton.new_floor_title=Write a title for this custom note for _floor %d_:
ui.customnotebutton.new_inv=New Inventory Item Note
ui.customnotebutton.new_inv_prompt=Select a Note Item
ui.customnotebutton.new_item_title=Write a title for this custom note for the item:\n_%s_
ui.customnotebutton.new_type=New Item Type Note
ui.customnotebutton.new_type_prompt=Select a type of item you'd like to associate this note with.
ui.customnotebutton$customnotewindow.edit_title=Edit Title
ui.customnotebutton$customnotewindow.add_text=Add Text
ui.customnotebutton$customnotewindow.edit_text=Edit Text

View File

@@ -117,7 +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.custom_notes=Custom
windows.wndjournal$notestab.floor_header=Floor %d
windows.wndjournal$catalogtab.title=Catalogs
windows.wndjournal$catalogtab.title_equipment=Equipment

View File

@@ -27,12 +27,12 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MagicImmune;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.journal.Guidebook;
import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Bundle;
import java.util.ArrayList;
@@ -41,6 +41,8 @@ public abstract class EquipableItem extends Item {
public static final String AC_EQUIP = "EQUIP";
public static final String AC_UNEQUIP = "UNEQUIP";
public int customNoteID = -1;
{
bones = true;
}
@@ -157,4 +159,18 @@ public abstract class EquipableItem extends Item {
}
public void activate( Char ch ){}
private static final String CUSTOM_NOTE_ID = "custom_note_id";
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
if (customNoteID != -1) bundle.put(CUSTOM_NOTE_ID, customNoteID);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
if (bundle.contains(CUSTOM_NOTE_ID)) customNoteID = bundle.getInt(CUSTOM_NOTE_ID);
}
}

View File

@@ -37,6 +37,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWea
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.darts.Dart;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.darts.TippedDart;
import com.shatteredpixel.shatteredpixeldungeon.journal.Catalog;
import com.shatteredpixel.shatteredpixeldungeon.journal.Notes;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.CellSelector;
@@ -502,6 +503,17 @@ public class Item implements Bundlable {
public Emitter emitter() { return null; }
public String info() {
Notes.CustomRecord note;
if (this instanceof EquipableItem){
note = Notes.findCustomRecord(((EquipableItem) this).customNoteID);
} else {
note = Notes.findCustomRecord(getClass());
}
if (note != null){
return "This item has a custom note: \"_" + note.title() + "_\"\n\n" + desc();
}
return desc();
}

View File

@@ -369,6 +369,12 @@ public class Potion extends Item {
return isKnown() ? super.name() : Messages.get(this, color);
}
@Override
public String info() {
//skip custom notes if anonymized and un-Ided
return (anonymous && !isIdentified()) ? super.desc() : super.info();
}
@Override
public String desc() {
return isKnown() ? super.desc() : Messages.get(this, "unknown_desc");

View File

@@ -169,7 +169,8 @@ public class Ring extends KindofMisc {
@Override
public String info(){
String desc = super.info();
//skip custom notes if anonymized and un-Ided
String desc = (anonymous && !isIdentified()) ? super.desc() : super.info();
if (cursed && isEquipped( Dungeon.hero )) {
desc += "\n\n" + Messages.get(Ring.class, "cursed_worn");

View File

@@ -230,6 +230,12 @@ public abstract class Scroll extends Item {
return isKnown() ? super.name() : Messages.get(this, rune);
}
@Override
public String info() {
//skip custom notes if anonymized and un-Ided
return (anonymous && !isIdentified()) ? super.desc() : super.info();
}
@Override
public String desc() {
return isKnown() ? super.desc() : Messages.get(this, "unknown_desc");

View File

@@ -21,6 +21,7 @@
package com.shatteredpixel.shatteredpixeldungeon.journal;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Foliage;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.SacrificialFire;
@@ -46,6 +47,7 @@ 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.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.sprites.RatKingSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ShopkeeperSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.SpawnerSprite;
@@ -57,6 +59,7 @@ import com.watabou.noosa.Image;
import com.watabou.noosa.Visual;
import com.watabou.utils.Bundlable;
import com.watabou.utils.Bundle;
import com.watabou.utils.Reflection;
import java.util.ArrayList;
import java.util.Collections;
@@ -354,9 +357,8 @@ public class Notes {
public enum CustomType {
TEXT,
DEPTH, //TODO
ITEM, //TODO
ITEM_TYPE //TODO
DEPTH,
ITEM,
}
public static class CustomRecord extends Record {
@@ -391,6 +393,10 @@ public class Notes {
body = desc;
}
public int ID(){
return ID;
}
@Override
public int depth() {
if (type == CustomType.DEPTH){
@@ -407,6 +413,9 @@ public class Notes {
return Icons.SCROLL_COLOR.get();
case DEPTH:
return Icons.STAIRS.get();
case ITEM:
Item i = (Item) Reflection.newInstance(itemClass);
return new ItemSprite(i);
}
}
@@ -414,12 +423,19 @@ public class Notes {
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;
case ITEM:
Item item = (Item) Reflection.newInstance(itemClass);
if (item.isIdentified() && item.icon != -1) {
Image secondIcon = new Image(Assets.Sprites.ITEM_ICONS);
secondIcon.frame(ItemSpriteSheet.Icons.film.get(item.icon));
return secondIcon;
}
return null;
}
}
@@ -451,6 +467,8 @@ public class Notes {
private static final String TYPE = "type";
private static final String ID_NUMBER = "id_number";
private static final String ITEM_CLASS = "item_class";
private static final String TITLE = "title";
private static final String BODY = "body";
@@ -459,6 +477,7 @@ public class Notes {
super.storeInBundle(bundle);
bundle.put(TYPE, type);
bundle.put(ID_NUMBER, ID);
if (itemClass != null) bundle.put(ITEM_CLASS, itemClass);
bundle.put(TITLE, title);
bundle.put(BODY, body);
}
@@ -469,6 +488,8 @@ public class Notes {
type = bundle.getEnum(TYPE, CustomType.class);
ID = bundle.getInt(ID_NUMBER);
if (bundle.contains(ITEM_CLASS)) itemClass = bundle.getClass(ITEM_CLASS);
title = bundle.getString(TITLE);
body = bundle.getString(BODY);
}
@@ -584,7 +605,7 @@ public class Notes {
public static ArrayList<Record> getRecords(int depth){
ArrayList<Record> filtered = new ArrayList<>();
for (Record rec : records){
if (rec.depth() == depth){
if (rec.depth() == depth && !(rec instanceof CustomRecord)){
filtered.add(rec);
}
}
@@ -594,6 +615,26 @@ public class Notes {
return filtered;
}
public static CustomRecord findCustomRecord( int ID ){
for (Record rec : records){
if (rec instanceof CustomRecord && ((CustomRecord) rec).ID == ID)
return (CustomRecord) rec;
}
return null;
}
public static CustomRecord findCustomRecord( Class itemClass ){
for (Record rec : records){
if (rec instanceof CustomRecord && ((CustomRecord) rec).itemClass == itemClass)
return (CustomRecord) rec;
}
return null;
}
public static int customRecordLimit(){
return 5;
}
private static final Comparator<Record> comparator = new Comparator<Record>() {
@Override
public int compare(Record r1, Record r2) {

View File

@@ -272,7 +272,7 @@ public class PixelScene extends Scene {
}
}
//FIXME this system currently only works for a subset of windows
//this system only preserves windows with a public zero-arg constructor
private static ArrayList<Class<?extends Window>> savedWindows = new ArrayList<>();
private static Class<?extends PixelScene> savedClass = null;
@@ -294,7 +294,7 @@ public class PixelScene extends Scene {
try{
add(Reflection.newInstanceUnhandled(w));
} catch (Exception e){
//window has no public zero-arg constructor, just eat the exception
//just eat the exception
}
}
}

View File

@@ -22,15 +22,28 @@
package com.shatteredpixel.shatteredpixeldungeon.ui;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.Statistics;
import com.shatteredpixel.shatteredpixeldungeon.items.EquipableItem;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.Scroll;
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.WndBag;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndJournalItem;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndTextInput;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndTitledMessage;
import com.watabou.noosa.Game;
import com.watabou.utils.Callback;
import com.watabou.utils.Reflection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
//this is contained in its own class as custom notes have a lot of messy window UI logic
public class CustomNoteButton extends IconButton {
@@ -46,17 +59,195 @@ public class CustomNoteButton extends IconButton {
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);
if (Notes.getRecords(Notes.CustomRecord.class).size() >= Notes.customRecordLimit()){
GameScene.show(new WndTitledMessage(Icons.INFO.get(),
Messages.get(this, "limit_title"),
Messages.get(this, "limit_text")));
return;
}
GameScene.show(new WndOptions(Icons.SCROLL_COLOR.get(),
Messages.get(CustomNoteButton.class, "title"),
Messages.get(CustomNoteButton.class, "desc"),
Messages.get(CustomNoteButton.class, "new_text"),
Messages.get(CustomNoteButton.class, "new_floor"),
Messages.get(CustomNoteButton.class, "new_inv"),
Messages.get(CustomNoteButton.class, "new_type")){
@Override
protected void onSelect(int index) {
if (index == 0){
Notes.CustomRecord custom = new Notes.CustomRecord("", "");
addNote(custom,
Messages.get(CustomNoteButton.class, "new_text"),
Messages.get(CustomNoteButton.class, "new_text_title"));
} else if (index == 1){
GameScene.show(new WndDepthSelect());
} else if (index == 2){
GameScene.selectItem(itemSelector);
} else {
GameScene.show(new WndItemtypeSelect());
}
}
@Override
public void hide() {
//do nothing, prevents window closing when user steps back in note creation process
}
@Override
public void onBackPressed() {
super.hide(); //actually hide in this case
}
});
}
@Override
protected String hoverText() {
return Messages.get(this, "hover_text");
return Messages.get(this, "title");
}
private class WndDepthSelect extends WndTitledMessage {
public WndDepthSelect(){
super(Icons.STAIRS.get(),
Messages.get(CustomNoteButton.class, "new_floor"),
Messages.get(CustomNoteButton.class, "new_floor_prompt"));
int top = height+2;
int left = 0;
for (int i = Statistics.deepestFloor; i > 0; i --){
if (i % 5 == 0 && left > 0){
left = 0;
top += 17;
}
int finalI = i;
RedButton btnDepth = new RedButton(Integer.toString(finalI)){
@Override
protected void onClick() {
addNote(new Notes.CustomRecord(finalI, "", ""),
Messages.get(CustomNoteButton.class, "new_floor"),
Messages.get(CustomNoteButton.class, "new_floor_title", finalI));
}
};
btnDepth.setRect(left, top, 23, 16);
left += 24;
add(btnDepth);
}
resize(width, top + (left == 0 ? 0 : 16));
}
}
private WndBag.ItemSelector itemSelector = new WndBag.ItemSelector() {
@Override
public String textPrompt() {
return Messages.get(CustomNoteButton.class, "new_inv_prompt");
}
@Override
public boolean hideAfterSelecting() {
return false;
}
@Override
public boolean itemSelectable(Item item) {
if (item instanceof EquipableItem){
return ((EquipableItem) item).customNoteID == -1
|| Notes.findCustomRecord(((EquipableItem) item).customNoteID) == null;
} else {
return Notes.findCustomRecord(item.getClass()) == null;
}
}
@Override
public void onSelect( Item item ) {
if (item != null){
Notes.CustomRecord custom = new Notes.CustomRecord(item, "", "");
if (item instanceof EquipableItem){
((EquipableItem) item).customNoteID = custom.ID();
}
addNote(custom,
Messages.get(CustomNoteButton.class, "new_inv"),
Messages.get(CustomNoteButton.class, "new_item_title", Messages.titleCase(item.name())));
}
}
};
private static class WndItemtypeSelect extends WndTitledMessage {
public WndItemtypeSelect() {
super(Icons.SCROLL_COLOR.get(),
Messages.get(CustomNoteButton.class, "new_type"),
Messages.get(CustomNoteButton.class, "new_type_prompt"));
int top = height + 2;
int left = 0;
ArrayList<Item> items = new ArrayList<>();
for (Class<?> potionCls : Generator.Category.POTION.classes) {
items.add((Item) Reflection.newInstance(potionCls));
}
for (Class<?> potionCls : Generator.Category.SCROLL.classes) {
items.add((Item) Reflection.newInstance(potionCls));
}
for (Class<?> potionCls : Generator.Category.RING.classes) {
items.add((Item) Reflection.newInstance(potionCls));
}
Collections.sort(items, itemVisualcomparator);
for (Item item : items) {
ItemButton itemButton = new ItemButton(){
@Override
protected void onClick() {
addNote(new Notes.CustomRecord(item, "", ""),
Messages.get(CustomNoteButton.class, "new_type"),
Messages.get(CustomNoteButton.class, "new_item_title", Messages.titleCase(item.name())));
}
};
itemButton.item(item);
itemButton.setRect(left, top, 19, 19);
add(itemButton);
if (Notes.findCustomRecord(item.getClass()) != null){
itemButton.slot.enable(false);
}
left += 20;
if (left >= width - 19){
top += 20;
left = 0;
}
}
if (left > 0){
top += 20;
left = 0;
}
resize(width, top);
}
}
//items are sorted first sorted potions -> scrolls -> rings, and then based on their sprites.
private static Comparator<Item> itemVisualcomparator = new Comparator<Item>() {
@Override
public int compare(Item i1, Item i2) {
int i1Idx = i1.image();
int i2Idx = i2.image();
if (i1 instanceof Scroll) i1Idx += 1000;
if (i1 instanceof Ring) i1Idx += 2000;
if (i2 instanceof Scroll) i2Idx += 1000;
if (i2 instanceof Ring) i2Idx += 2000;
return i1Idx - i2Idx;
}
};
public static class CustomNoteWindow extends WndJournalItem {
public CustomNoteWindow(Notes.CustomRecord rec) {
@@ -74,7 +265,7 @@ public class CustomNoteButton extends IconButton {
Messages.get(CustomNoteWindow.class, "cancel")){
@Override
public void onSelect(boolean positive, String text) {
if (positive){
if (positive && !text.isEmpty()){
rec.editText(text, rec.desc());
refreshScene(rec);
}
@@ -135,6 +326,25 @@ public class CustomNoteButton extends IconButton {
}
private static void addNote(Notes.CustomRecord note, String promptTitle, String prompttext){
GameScene.show(new WndTextInput(promptTitle,
prompttext,
"",
50,
false,
Messages.get(CustomNoteWindow.class, "confirm"),
Messages.get(CustomNoteWindow.class, "cancel")){
@Override
public void onSelect(boolean positive, String text) {
if (positive && !text.isEmpty()){
Notes.add(note);
note.editText(text, "");
refreshScene(null);
}
}
});
}
private static void refreshScene(Notes.CustomRecord recToShow){
if (recToShow == null){
ShatteredPixelDungeon.seamlessResetScene();

View File

@@ -286,7 +286,9 @@ public class WndBag extends WndTabbed {
} else if (selector != null) {
hide();
if (selector.hideAfterSelecting()){
hide();
}
selector.onSelect( item );
} else {
@@ -304,7 +306,9 @@ public class WndBag extends WndTabbed {
} else if (selector != null) {
hide();
if (selector.hideAfterSelecting()){
hide();
}
selector.onSelect( item );
} else {
@@ -469,6 +473,9 @@ public class WndBag extends WndTabbed {
public Class<?extends Bag> preferredBag(){
return null; //defaults to last bag opened
}
public boolean hideAfterSelecting(){
return true; //defaults to hiding the window when an item is picked
}
public abstract boolean itemSelectable( Item item );
public abstract void onSelect( Item item );
}

View File

@@ -448,7 +448,7 @@ public class WndJournal extends WndTabbed {
ArrayList<Notes.CustomRecord> customRecs = Notes.getRecords(Notes.CustomRecord.class);
if (!customRecs.isEmpty()){
grid.addHeader("_" + Messages.get(this, "custom_notes") + "_");
grid.addHeader("_" + Messages.get(this, "custom_notes") + "_ (" + customRecs.size() + "/" + Notes.customRecordLimit() + ")");
for (Notes.CustomRecord rec : customRecs){
ScrollingGridPane.GridItem gridItem = new ScrollingGridPane.GridItem(rec.icon()){