From 070b380b8a013fe760d088351eeca75dff29faa7 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Thu, 4 May 2023 17:44:17 -0400 Subject: [PATCH] v2.1.0: added a basic UI for shopkeeper interaction, with buyback! --- .../assets/messages/actors/actors.properties | 10 +- .../actors/mobs/npcs/Shopkeeper.java | 102 ++++++++++++++++-- .../windows/WndTradeItem.java | 44 +++++++- 3 files changed, 144 insertions(+), 12 deletions(-) diff --git a/core/src/main/assets/messages/actors/actors.properties b/core/src/main/assets/messages/actors/actors.properties index 30ed27d5a..491935003 100644 --- a/core/src/main/assets/messages/actors/actors.properties +++ b/core/src/main/assets/messages/actors/actors.properties @@ -978,7 +978,8 @@ actors.mobs.npcs.impshopkeeper.name=ambitious imp actors.mobs.npcs.impshopkeeper.greetings=Hello, %s! actors.mobs.npcs.impshopkeeper.greetings_ascent=What did you do %s? Shop quickly, I've got to get out of here! actors.mobs.npcs.impshopkeeper.thief=I thought I could trust you! -actors.mobs.npcs.impshopkeeper.desc=Imps are lesser demons. They are notable for neither their strength nor their magic talent. But they are quite smart and sociable, and many of imps prefer to live and do business among non-demons. +actors.mobs.npcs.impshopkeeper.buyback=The imp cheerily returns your item. +actors.mobs.npcs.impshopkeeper.desc=The imp has set up a little shop on the border of the inner halls. It's nice to see a friendly face, but his prices certainly don't seem friendly. actors.mobs.npcs.mirrorimage.name=mirror image actors.mobs.npcs.mirrorimage.desc=This illusion bears a close resemblance to you, even seeming to wield your current weapon and armor.\n\nMirror images will seek and attack enemies using their mimicked weapon, which behaves the same as yours, but deals less damage. They start out nearly invisible, but must take on a form that's more easily seen in order to attack.\n\nWhile their offensive power can be potent, mirror images have no durability, and will fade the moment they take damage. @@ -1012,6 +1013,13 @@ actors.mobs.npcs.sheep.desc=This is a magic sheep. What's so magical about it? Y actors.mobs.npcs.shopkeeper.name=shopkeeper actors.mobs.npcs.shopkeeper.thief=Thief, Thief! actors.mobs.npcs.shopkeeper.sell=Sell an item +actors.mobs.npcs.shopkeeper.talk=Talk +actors.mobs.npcs.shopkeeper.buyback=The shopkeeper reluctantly returns your item. +actors.mobs.npcs.shopkeeper.talk_prison="I have everything you need for a successful adventure!" +actors.mobs.npcs.shopkeeper.talk_caves="Spend money, live longer." +actors.mobs.npcs.shopkeeper.talk_city="My wares can keep you safe down here." +actors.mobs.npcs.shopkeeper.talk_halls="Hey there, I've got special prices for demon hunters!" +actors.mobs.npcs.shopkeeper.talk_ascent="I don't know what you're planning to do with that amulet but I want no part of it. Buy something and move on, we both need to leave this place!" actors.mobs.npcs.shopkeeper.desc=This stout guy looks more appropriate for a trade district in some large city than for a dungeon. His prices explain why he prefers to do business here. actors.mobs.npcs.wandmaker.name=old wandmaker diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/Shopkeeper.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/Shopkeeper.java index 0b5e2fde7..00376ad84 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/Shopkeeper.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/Shopkeeper.java @@ -35,12 +35,21 @@ import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor; import com.shatteredpixel.shatteredpixeldungeon.journal.Notes; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; +import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.ShopkeeperSprite; +import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; import com.shatteredpixel.shatteredpixeldungeon.windows.WndBag; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndTitledMessage; import com.shatteredpixel.shatteredpixeldungeon.windows.WndTradeItem; import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.utils.Bundlable; +import com.watabou.utils.Bundle; import com.watabou.utils.Callback; +import java.util.ArrayList; + public class Shopkeeper extends NPC { { @@ -48,6 +57,9 @@ public class Shopkeeper extends NPC { properties.add(Property.IMMOVABLE); } + + public static int MAX_BUYBACK_HISTORY = 3; + public ArrayList buybackItems = new ArrayList<>(); @Override protected boolean act() { @@ -56,11 +68,7 @@ public class Shopkeeper extends NPC { Notes.add(Notes.Landmark.SHOP); } - /*if (Statistics.highestAscent < 20 && Dungeon.hero.buff(AscensionChallenge.class) != null){ - flee(); - return true; - }*/ - + sprite.turnTo( pos, Dungeon.hero.pos ); spend( TICK ); return super.act(); @@ -159,9 +167,91 @@ public class Shopkeeper extends NPC { Game.runOnRenderThread(new Callback() { @Override public void call() { - sell(); + String[] options = new String[2+ buybackItems.size()]; + int i = 0; + options[i++] = Messages.get(Shopkeeper.this, "sell"); + options[i++] = Messages.get(Shopkeeper.this, "talk"); + for (Item item : buybackItems){ + options[i++] = Messages.get(Heap.class, "for_sale", item.value(), Messages.titleCase(item.title())); + } + GameScene.show(new WndOptions(sprite(), Messages.titleCase(name()), description(), options){ + @Override + protected void onSelect(int index) { + super.onSelect(index); + if (index == 0){ + sell(); + } else if (index == 1){ + GameScene.show(new WndTitledMessage(sprite(), Messages.titleCase(name()), chatText())); + } else if (index > 1){ + GLog.i(Messages.get(Shopkeeper.this, "buyback")); + Item returned = buybackItems.remove(index-2); + Dungeon.gold -= returned.value(); + Statistics.goldCollected -= returned.value(); + if (!returned.doPickUp(Dungeon.hero)){ + Dungeon.level.drop(returned, Dungeon.hero.pos); + } + } + } + + @Override + protected boolean enabled(int index) { + if (index > 1){ + return Dungeon.gold >= buybackItems.get(index-2).value(); + } else { + return super.enabled(index); + } + } + + @Override + protected boolean hasIcon(int index) { + return index > 1; + } + + @Override + protected Image getIcon(int index) { + if (index > 1){ + return new ItemSprite(buybackItems.get(index-2)); + } + return null; + } + }); } }); return true; } + + public String chatText(){ + if (Dungeon.hero.buff(AscensionChallenge.class) != null){ + return Messages.get(this, "talk_ascent"); + } + switch (Dungeon.depth){ + case 6: default: + return Messages.get(this, "talk_prison"); + case 11: + return Messages.get(this, "talk_caves"); + case 16: + return Messages.get(this, "talk_city"); + case 20: + return Messages.get(this, "talk_halls"); + } + } + + public static String BUYBACK_ITEMS = "buyback_items"; + + @Override + public void storeInBundle(Bundle bundle) { + super.storeInBundle(bundle); + bundle.put(BUYBACK_ITEMS, buybackItems); + } + + @Override + public void restoreFromBundle(Bundle bundle) { + super.restoreFromBundle(bundle); + buybackItems.clear(); + if (bundle.contains(BUYBACK_ITEMS)){ + for (Bundlable i : bundle.getCollection(BUYBACK_ITEMS)){ + buybackItems.add((Item) i); + } + } + } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTradeItem.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTradeItem.java index edc90607a..3315ff3f5 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTradeItem.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTradeItem.java @@ -22,6 +22,8 @@ package com.shatteredpixel.shatteredpixeldungeon.windows; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; +import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Shopkeeper; @@ -55,12 +57,22 @@ public class WndTradeItem extends WndInfoItem { float pos = height; + //find the shopkeeper in the current level + Shopkeeper shop = null; + for (Char ch : Actor.chars()){ + if (ch instanceof Shopkeeper){ + shop = (Shopkeeper) ch; + break; + } + } + final Shopkeeper finalShop = shop; + if (item.quantity() == 1) { RedButton btnSell = new RedButton( Messages.get(this, "sell", item.value()) ) { @Override protected void onClick() { - sell( item ); + sell( item, finalShop); hide(); } }; @@ -76,7 +88,7 @@ public class WndTradeItem extends WndInfoItem { RedButton btnSell1 = new RedButton( Messages.get(this, "sell_1", priceAll / item.quantity()) ) { @Override protected void onClick() { - sellOne( item ); + sellOne( item, finalShop ); hide(); } }; @@ -86,7 +98,7 @@ public class WndTradeItem extends WndInfoItem { RedButton btnSellAll = new RedButton( Messages.get(this, "sell_all", priceAll ) ) { @Override protected void onClick() { - sell( item ); + sell( item, finalShop ); hide(); } }; @@ -176,8 +188,12 @@ public class WndTradeItem extends WndInfoItem { } if (selling) Shopkeeper.sell(); } - + public static void sell( Item item ) { + sell(item, null); + } + + public static void sell( Item item, Shopkeeper shop ) { Hero hero = Dungeon.hero; @@ -190,12 +206,23 @@ public class WndTradeItem extends WndInfoItem { hero.spend(-hero.cooldown()); new Gold( item.value() ).doPickUp( hero ); + + if (shop != null){ + shop.buybackItems.add(item); + while (shop.buybackItems.size() > Shopkeeper.MAX_BUYBACK_HISTORY){ + shop.buybackItems.remove(0); + } + } } public static void sellOne( Item item ) { + sellOne( item, null ); + } + + public static void sellOne( Item item, Shopkeeper shop ) { if (item.quantity() <= 1) { - sell( item ); + sell( item, shop ); } else { Hero hero = Dungeon.hero; @@ -206,6 +233,13 @@ public class WndTradeItem extends WndInfoItem { hero.spend(-hero.cooldown()); new Gold( item.value() ).doPickUp( hero ); + + if (shop != null){ + shop.buybackItems.add(item); + while (shop.buybackItems.size() > Shopkeeper.MAX_BUYBACK_HISTORY){ + shop.buybackItems.remove(0); + } + } } }