diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index 6b30f6d60..43916b813 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -1019,7 +1019,7 @@ items.scrolls.scroll.no_magic=You can't read a scroll while magic immune. items.scrolls.scroll.cursed=Your cursed spellbook prevents you from invoking this scroll's magic! A scroll of remove curse might be strong enough to still work though... items.scrolls.scroll$placeholder.name=scroll -items.scrolls.inventoryscroll.warning=Do you really want to cancel this scroll usage? It will be consumed anyway. +items.scrolls.inventoryscroll.warning=Do you really want to cancel this scroll usage? The scroll wasn't previously identified, so it will be consumed anyway. items.scrolls.inventoryscroll.yes=Yes, I'm positive items.scrolls.inventoryscroll.no=No, I changed my mind diff --git a/core/src/main/assets/messages/windows/windows.properties b/core/src/main/assets/messages/windows/windows.properties index 4d85d464e..1a82a883f 100644 --- a/core/src/main/assets/messages/windows/windows.properties +++ b/core/src/main/assets/messages/windows/windows.properties @@ -346,6 +346,28 @@ windows.wndtradeitem.sell=Sell for %dg windows.wndtradeitem.sell_1=Sell 1 for %dg windows.wndtradeitem.sell_all=Sell all for %dg +windows.wndupgrade.title=Upgrade an Item +windows.wndupgrade.desc=Upgrading an item permanently boosts its power: +windows.wndupgrade.unided=You don't know the properties of this unidentified item, its un-upgraded state is shown here. +windows.wndupgrade.cursed=Upgrading this item also has a 33% chance to cleanse its curse. +windows.wndupgrade.cursed_weaken=Upgrading this item will also weaken its curse, and has a 33% chance to cleanse the curse entirely. +windows.wndupgrade.curse_infusion=If the curse is cleansed, the item will no longer benefit from curse infusion! +windows.wndupgrade.enchant=Upgrading this item also has a %d%% chance to destroy its enchantment! +windows.wndupgrade.glyph=Upgrading this item also has a %d%% chance to destroy its glyph! +windows.wndupgrade.harden=Upgrading this item also has a %d%% chance to break its hardening! +windows.wndupgrade.resin=This wand has been enhanced with arcane resin, normal upgrades will override resin upgrades! +windows.wndupgrade.damage=Damage +windows.wndupgrade.blocking=Blocking +windows.wndupgrade.weight=Weight +windows.wndupgrade.durability=Durability +windows.wndupgrade.zap_damage=Zap Damage +windows.wndupgrade.corrosion_damage=Corrosion Damage +windows.wndupgrade.ward_damage=Ward Damage +windows.wndupgrade.charges=Max Charges +windows.wndupgrade.ring_boost=Ring Boost +windows.wndupgrade.upgrade=Upgrade +windows.wndupgrade.cancel=Cancel + windows.wndwandmaker.dust=Oh, I see you have the dust! Don't worry about the wraiths, I can deal with them. As I promised, you can choose one of my high quality wands. windows.wndwandmaker.ember=Oh, I see you have the embers! I do hope the fire elemental wasn't too much trouble. As I promised, you can choose one of my high quality wands. windows.wndwandmaker.berry=Oh, I see you have the berry! I do hope the rotberry plant didn't trouble you too much. As I promised, you can choose one of my high quality wands. diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/Armor.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/Armor.java index 08f20f22c..0d51bc23b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/Armor.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/Armor.java @@ -588,17 +588,17 @@ public class Armor extends EquipableItem { } public int STRReq(){ - int req = STRReq(level()); + return STRReq(level()); + } + + public int STRReq(int lvl){ + int req = STRReq(tier, lvl); if (masteryPotionBonus){ req -= 2; } return req; } - public int STRReq(int lvl){ - return STRReq(tier, lvl); - } - protected static int STRReq(int tier, int lvl){ lvl = Math.max(0, lvl); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/InventoryScroll.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/InventoryScroll.java index a1bec342a..67f5021cf 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/InventoryScroll.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/InventoryScroll.java @@ -115,9 +115,12 @@ public abstract class InventoryScroll extends Scroll { curItem = detach(curUser.belongings.backpack); } ((InventoryScroll)curItem).onItemSelected( item ); - ((InventoryScroll)curItem).readAnimation(); - - Sample.INSTANCE.play( Assets.Sounds.READ ); + + //SoU still does these things, but after the upgrade window + if (!(curItem instanceof ScrollOfUpgrade)) { + ((InventoryScroll) curItem).readAnimation(); + Sample.INSTANCE.play(Assets.Sounds.READ); + } } else if (identifiedByUse && !((Scroll)curItem).anonymous) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/Scroll.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/Scroll.java index fc020c2a5..d70bfa0e6 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/Scroll.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/Scroll.java @@ -183,7 +183,7 @@ public abstract class Scroll extends Item { public abstract void doRead(); - protected void readAnimation() { + public void readAnimation() { Invisibility.dispel(); curUser.spend( TIME_TO_READ ); curUser.busy(); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/ScrollOfUpgrade.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/ScrollOfUpgrade.java index 50e5aeebb..9186f235d 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/ScrollOfUpgrade.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/ScrollOfUpgrade.java @@ -36,8 +36,10 @@ import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon; import com.shatteredpixel.shatteredpixeldungeon.journal.Catalog; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndUpgrade; public class ScrollOfUpgrade extends InventoryScroll { @@ -58,6 +60,11 @@ public class ScrollOfUpgrade extends InventoryScroll { @Override protected void onItemSelected( Item item ) { + GameScene.show(new WndUpgrade(this, item, identifiedByUse)); + + } + + public Item upgradeItem( Item item ){ upgrade( curUser ); Degrade.detach( curUser, Degrade.class ); @@ -71,7 +78,7 @@ public class ScrollOfUpgrade extends InventoryScroll { boolean hadCursedEnchant = w.hasCurseEnchant(); boolean hadGoodEnchant = w.hasGoodEnchant(); - w.upgrade(); + item = w.upgrade(); if (w.cursedKnown && hadCursedEnchant && !w.hasCurseEnchant()){ removeCurse( Dungeon.hero ); @@ -91,7 +98,7 @@ public class ScrollOfUpgrade extends InventoryScroll { boolean hadCursedGlyph = a.hasCurseGlyph(); boolean hadGoodGlyph = a.hasGoodGlyph(); - a.upgrade(); + item = a.upgrade(); if (a.cursedKnown && hadCursedGlyph && !a.hasCurseGlyph()){ removeCurse( Dungeon.hero ); @@ -107,22 +114,24 @@ public class ScrollOfUpgrade extends InventoryScroll { } else if (item instanceof Wand || item instanceof Ring) { boolean wasCursed = item.cursed; - item.upgrade(); + item = item.upgrade(); if (item.cursedKnown && wasCursed && !item.cursed){ removeCurse( Dungeon.hero ); } } else { - item.upgrade(); + item = item.upgrade(); } - + Badges.validateItemLevelAquired( item ); Statistics.upgradesUsed++; Badges.validateMageUnlock(); Catalog.countUse(item.getClass()); - Catalog.countUse(getClass()); + Catalog.countUse(ScrollOfUpgrade.class); + + return item; } public static void upgrade( Hero hero ) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/spells/InventorySpell.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/spells/InventorySpell.java index 6b45971b2..7f88214e2 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/spells/InventorySpell.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/spells/InventorySpell.java @@ -84,16 +84,19 @@ public abstract class InventorySpell extends Spell { curItem = detach(curUser.belongings.backpack); ((InventorySpell)curItem).onItemSelected( item ); - curUser.spend( 1f ); - curUser.busy(); - (curUser.sprite).operate( curUser.pos ); - - Sample.INSTANCE.play( Assets.Sounds.READ ); - Invisibility.dispel(); + //Magical Infusion still does these things, but after the upgrade window + if (!(curItem instanceof MagicalInfusion)) { + curUser.spend(1f); + curUser.busy(); + (curUser.sprite).operate(curUser.pos); - Catalog.countUse(curItem.getClass()); - if (Random.Float() < ((Spell)curItem).talentChance){ - Talent.onScrollUsed(curUser, curUser.pos, ((Spell)curItem).talentFactor); + Sample.INSTANCE.play(Assets.Sounds.READ); + Invisibility.dispel(); + + Catalog.countUse(curItem.getClass()); + if (Random.Float() < ((Spell) curItem).talentChance) { + Talent.onScrollUsed(curUser, curUser.pos, ((Spell) curItem).talentFactor); + } } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/spells/MagicalInfusion.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/spells/MagicalInfusion.java index 93884b030..6e78f0aec 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/spells/MagicalInfusion.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/spells/MagicalInfusion.java @@ -21,9 +21,12 @@ package com.shatteredpixel.shatteredpixeldungeon.items.spells; +import com.shatteredpixel.shatteredpixeldungeon.Assets; import com.shatteredpixel.shatteredpixeldungeon.Badges; import com.shatteredpixel.shatteredpixeldungeon.Statistics; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Degrade; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade; @@ -31,8 +34,12 @@ import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon; import com.shatteredpixel.shatteredpixeldungeon.journal.Catalog; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndUpgrade; +import com.watabou.noosa.audio.Sample; +import com.watabou.utils.Random; public class MagicalInfusion extends InventorySpell { @@ -52,22 +59,41 @@ public class MagicalInfusion extends InventorySpell { @Override protected void onItemSelected( Item item ) { + GameScene.show(new WndUpgrade(this, item, false)); + + } + + public void useAnimation(){ + curUser.spend(1f); + curUser.busy(); + (curUser.sprite).operate(curUser.pos); + + Sample.INSTANCE.play(Assets.Sounds.READ); + Invisibility.dispel(); + + Catalog.countUse(curItem.getClass()); + if (Random.Float() < ((Spell) curItem).talentChance) { + Talent.onScrollUsed(curUser, curUser.pos, ((Spell) curItem).talentFactor); + } + } + + public Item upgradeItem( Item item ){ ScrollOfUpgrade.upgrade(curUser); Degrade.detach( curUser, Degrade.class ); if (item instanceof Weapon && ((Weapon) item).enchantment != null) { - ((Weapon) item).upgrade(true); + item = ((Weapon) item).upgrade(true); } else if (item instanceof Armor && ((Armor) item).glyph != null) { - ((Armor) item).upgrade(true); + item = ((Armor) item).upgrade(true); } else { boolean wasCursed = item.cursed; boolean wasCurseInfused = item instanceof Wand && ((Wand) item).curseInfusionBonus; - item.upgrade(); + item = item.upgrade(); if (wasCursed) item.cursed = true; if (wasCurseInfused) ((Wand) item).curseInfusionBonus = true; } - + GLog.p( Messages.get(this, "infuse") ); Badges.validateItemLevelAquired(item); @@ -75,6 +101,8 @@ public class MagicalInfusion extends InventorySpell { Catalog.countUse(getClass()); Statistics.upgradesUsed++; + + return item; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/Wand.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/Wand.java index 144705fd8..d00fde7bc 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/Wand.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/Wand.java @@ -377,7 +377,7 @@ public abstract class Wand extends Item { curCharges = Math.min( curCharges, maxCharges ); } - protected int initialCharges() { + public int initialCharges() { return 2; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfMagicMissile.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfMagicMissile.java index 8b870c280..c4a86f6cc 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfMagicMissile.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfMagicMissile.java @@ -87,8 +87,8 @@ public class WandOfMagicMissile extends DamageWand { } } - - protected int initialCharges() { + + public int initialCharges() { return 3; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/Weapon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/Weapon.java index 4f9a18ff8..efa609a93 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/Weapon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/Weapon.java @@ -261,11 +261,7 @@ abstract public class Weapon extends KindOfWeapon { } public int STRReq(){ - int req = STRReq(level()); - if (masteryPotionBonus){ - req -= 2; - } - return req; + return STRReq(level()); } public abstract int STRReq(int lvl); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Greataxe.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Greataxe.java index 908ff0376..59de09c3f 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Greataxe.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Greataxe.java @@ -52,7 +52,11 @@ public class Greataxe extends MeleeWeapon { @Override public int STRReq(int lvl) { - return STRReq(tier+1, lvl); //20 base strength req, up from 18 + int req = STRReq(tier+1, lvl); //20 base strength req, up from 18 + if (masteryPotionBonus){ + req -= 2; + } + return req; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Greatshield.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Greatshield.java index 49bfa8332..1639ca7e4 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Greatshield.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Greatshield.java @@ -42,7 +42,16 @@ public class Greatshield extends MeleeWeapon { @Override public int defenseFactor( Char owner ) { - return 6+2*buffedLvl(); //6 extra defence, plus 2 per level + return DRMax(); + } + + public int DRMax(){ + return DRMax(buffedLvl()); + } + + //6 extra defence, plus 2 per level + public int DRMax(int lvl){ + return 6 + 2*lvl; } public String statsInfo(){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/MeleeWeapon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/MeleeWeapon.java index 5f7404778..9958beb3b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/MeleeWeapon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/MeleeWeapon.java @@ -263,7 +263,11 @@ public class MeleeWeapon extends Weapon { } public int STRReq(int lvl){ - return STRReq(tier, lvl); + int req = STRReq(tier, lvl); + if (masteryPotionBonus){ + req -= 2; + } + return req; } private static boolean evaluatingTwinUpgrades = false; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/RoundShield.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/RoundShield.java index 994a6761f..332375c8e 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/RoundShield.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/RoundShield.java @@ -51,7 +51,16 @@ public class RoundShield extends MeleeWeapon { @Override public int defenseFactor( Char owner ) { - return 4+buffedLvl(); //4 extra defence, plus 1 per level + return DRMax(); + } + + public int DRMax(){ + return DRMax(buffedLvl()); + } + + //4 extra defence, plus 1 per level + public int DRMax(int lvl){ + return 4 + lvl; } public String statsInfo(){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndUpgrade.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndUpgrade.java new file mode 100644 index 000000000..bbbaf8e0f --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndUpgrade.java @@ -0,0 +1,452 @@ +/* + * 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.windows; + +import com.shatteredpixel.shatteredpixeldungeon.Assets; +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.items.Item; +import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor; +import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring; +import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.InventoryScroll; +import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade; +import com.shatteredpixel.shatteredpixeldungeon.items.spells.MagicalInfusion; +import com.shatteredpixel.shatteredpixeldungeon.items.wands.DamageWand; +import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand; +import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfCorrosion; +import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfWarding; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Greatshield; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MagesStaff; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.RoundShield; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon; +import com.shatteredpixel.shatteredpixeldungeon.messages.Languages; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; +import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene; +import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite; +import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite; +import com.shatteredpixel.shatteredpixeldungeon.ui.ItemSlot; +import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton; +import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock; +import com.shatteredpixel.shatteredpixeldungeon.ui.Window; +import com.watabou.noosa.BitmapText; +import com.watabou.noosa.ColorBlock; +import com.watabou.noosa.audio.Sample; +import com.watabou.utils.Reflection; + +public class WndUpgrade extends Window { + + private static final int WIDTH = 120; + + private static final float COL_1 = WIDTH/4f; + private static final float COL_2 = 5*WIDTH/8f; + private static final float COL_3 = 7*WIDTH/8f; + + protected static final int GAP = 2; + + private boolean force; + + private RedButton btnUpgrade; + private RedButton btnCancel; + + public WndUpgrade( Item upgrader, Item toUpgrade, boolean force){ + + this.force = force; + + IconTitle title = new IconTitle( new ItemSprite(upgrader), Messages.get(this, "title") ); + + title.setRect(0, 0, WIDTH, 0); + add(title); + + RenderedTextBlock message = PixelScene.renderTextBlock( 6 ); + message.text( Messages.get(this, "desc"), WIDTH); + message.setPos(0, title.bottom()+GAP); + add(message); + + // *** Computing current and next level to display *** + + int levelFrom = toUpgrade.isIdentified() ? toUpgrade.level() : 0; + int levelTo = levelFrom + 1; + + if (toUpgrade instanceof Wand && ((Wand) toUpgrade).resinBonus > 0){ + levelTo--; + } + + boolean curseInfused = (toUpgrade instanceof Weapon && ((Weapon) toUpgrade).curseInfusionBonus) + || (toUpgrade instanceof Armor && ((Armor) toUpgrade).curseInfusionBonus) + || (toUpgrade instanceof Wand && ((Wand) toUpgrade).curseInfusionBonus); + + if (curseInfused){ + if (toUpgrade.trueLevel()/6 < (toUpgrade.trueLevel()+1)/6){ + //new level bracket for curse infusion bonus + levelTo++; + } + } + + // *** Sprites, showing item at current level and with +1 *** + + ItemSprite i1 = new ItemSprite(); + add(i1); + i1.view(toUpgrade); + i1.x = COL_2 - i1.width()/2f; + i1.y = message.bottom() + GAP + (16-i1.height())/2f; + PixelScene.align(i1); + add(i1); + + ItemSprite i2 = new ItemSprite(); + add(i2); + i2.view(toUpgrade); + i2.x = COL_3 - i2.width()/2f; + i2.y = i1.y; + PixelScene.align(i2); + add(i2); + + BitmapText t1 = new BitmapText(PixelScene.pixelFont); + BitmapText t2 = new BitmapText(PixelScene.pixelFont); + if (toUpgrade.isIdentified()){ + if (levelFrom > 0){ + t1.text("+" + levelFrom); + } else { + t1.text(""); + } + t1.hardlight(ItemSlot.UPGRADED); + t2.text("+" + levelTo); + t2.hardlight(ItemSlot.UPGRADED); + + if (curseInfused){ + t1.hardlight(ItemSlot.CURSE_INFUSED); + t2.hardlight(ItemSlot.CURSE_INFUSED); + } + + } else { + t1.text("?"); + t1.hardlight(0.6f, 0.3f, 0.6f); + t2.text("+1?"); + t2.hardlight(0.6f, 0.3f, 0.6f); + } + t1.measure(); + t1.x = COL_2 + 8 - t1.width(); + t1.y = message.bottom() + GAP + 16 - t1.baseLine(); + add(t1); + + t2.measure(); + t2.x = COL_3 + 8 - t2.width(); + t2.y = message.bottom() + GAP + 16 - t2.baseLine(); + add(t2); + + float bottom = i1.y + 16 + GAP; + + final String LINE = Messages.lang() == Languages.CHINESE ? "~" : "-"; + + // *** Various lines for stats, highlighting differences between current level and +1 *** + + //physical damage + if (toUpgrade instanceof Weapon){ + bottom = fillFields(Messages.get(this, "damage"), + ((Weapon) toUpgrade).min(levelFrom) + LINE + ((Weapon) toUpgrade).max(levelFrom), + ((Weapon) toUpgrade).min(levelTo) + LINE + ((Weapon) toUpgrade).max(levelTo), + bottom); + } + + //blocking (armor and shields) + if (toUpgrade instanceof Armor){ + bottom = fillFields(Messages.get(this, "blocking"), + ((Armor) toUpgrade).DRMin(levelFrom) + LINE + ((Armor) toUpgrade).DRMax(levelFrom), + ((Armor) toUpgrade).DRMin(levelTo) + LINE + ((Armor) toUpgrade).DRMax(levelTo), + bottom); + } else if (toUpgrade instanceof RoundShield){ + bottom = fillFields(Messages.get(this, "blocking"), + 0 + LINE + ((RoundShield) toUpgrade).DRMax(levelFrom), + 0 + LINE + ((RoundShield) toUpgrade).DRMax(levelTo), + bottom); + } else if (toUpgrade instanceof Greatshield){ + bottom = fillFields(Messages.get(this, "blocking"), + 0 + LINE + ((Greatshield) toUpgrade).DRMax(levelFrom), + 0 + LINE + ((Greatshield) toUpgrade).DRMax(levelTo), + bottom); + } + + //weight (i.e. strength requirement) + if (toUpgrade instanceof Weapon){ + bottom = fillFields(Messages.get(this, "weight"), + Integer.toString((((Weapon) toUpgrade).STRReq(levelFrom))), + Integer.toString((((Weapon) toUpgrade).STRReq(levelTo))), + bottom); + } else if (toUpgrade instanceof Armor) { + bottom = fillFields(Messages.get(this, "weight"), + Integer.toString((((Armor) toUpgrade).STRReq(levelFrom))), + Integer.toString((((Armor) toUpgrade).STRReq(levelTo))), + bottom); + } + + //durability + if (toUpgrade instanceof MissileWeapon){ + //missile weapons are always IDed currently, so we always use true level + int uses1 = (int)Math.ceil(100f/((MissileWeapon) toUpgrade).durabilityPerUse()); + int uses2 = (int)Math.ceil(300f/((MissileWeapon) toUpgrade).durabilityPerUse()); + bottom = fillFields(Messages.get(this, "durability"), + uses1 >= 100 ? "∞" : Integer.toString(uses1), + uses2 >= 100 ? "∞" : Integer.toString(uses2), + bottom); + } + + //we use a separate reference for wand properties so that mage's staff can include them + Item wand = toUpgrade; + if (toUpgrade instanceof MagesStaff && ((MagesStaff) toUpgrade).wandClass() != null){ + wand = Reflection.newInstance(((MagesStaff) toUpgrade).wandClass()); + } + + //direct damage and damage-adjacent effects + if (wand instanceof DamageWand) { + bottom = fillFields(Messages.get(this, "zap_damage"), + ((DamageWand) wand).min(levelFrom) + LINE + ((DamageWand) wand).max(levelFrom), + ((DamageWand) wand).min(levelTo) + LINE + ((DamageWand) wand).max(levelTo), + bottom); + } else if (wand instanceof WandOfCorrosion){ + //TODO externalize! + bottom = fillFields(Messages.get(this, "corrosion_damage"), + Integer.toString(2+levelFrom), + Integer.toString(2+levelTo), + bottom); + } else if (wand instanceof WandOfWarding){ + //TODO externalize! + bottom = fillFields(Messages.get(this, "ward_damage"), + (2 + levelFrom) + LINE + (8 + 4*levelFrom), + (2 + levelTo) + LINE + (8 + 4*levelTo), + bottom); + } + + //TODO various extra wand effects + //disintegration range? + //corrosion AOE? + //blast wave knockback? + //rock guardian powers? + //frost chill duration? + //prismatic light blinding/lighting? + //warding max energy + //transfusion stats? and also de-emphasize damage? + //corruption power? + //regrowth power? + + //max charges + if (wand instanceof Wand){ + int chargeboost = levelFrom + (toUpgrade instanceof MagesStaff ? 1 : 0); + bottom = fillFields(Messages.get(this, "charges"), + Integer.toString(Math.min(10, ((Wand) wand).initialCharges() + chargeboost)), + Integer.toString(Math.min(10, ((Wand) wand).initialCharges() + chargeboost + 1)), + bottom); + } + + //TODO ring stats + if (toUpgrade instanceof Ring){ + + } + + //visual separators for each column + ColorBlock sep = new ColorBlock(1, 1, 0xFF222222); + sep.size(1, bottom - message.bottom()); + sep.x = WIDTH/2f; + sep.y = message.bottom(); + add(sep); + + sep = new ColorBlock(1, 1, 0xFF222222); + sep.size(1, bottom - message.bottom()); + sep.x = 3*WIDTH/4f; + sep.y = message.bottom(); + add(sep); + + // *** Various extra info texts that can appear underneath stats *** + + //warning relating to identification + if (!toUpgrade.isIdentified()){ + bottom = addMessage(Messages.get(this, "unided"), CharSprite.WARNING, bottom); + } + + // various messages relating to enchantments and curses + if (!(upgrader instanceof MagicalInfusion)) { + + if ((toUpgrade instanceof Weapon && ((Weapon) toUpgrade).hasGoodEnchant()) + || (toUpgrade instanceof Armor && ((Armor) toUpgrade).hasGoodGlyph())) { + int lossChance; + if ((toUpgrade instanceof Weapon && ((Weapon) toUpgrade).enchantHardened) + || (toUpgrade instanceof Armor && ((Armor) toUpgrade).glyphHardened)) { + lossChance = Math.min(100, 10 * (int) Math.pow(2, levelFrom - 6)); + } else { + lossChance = Math.min(100, 10 * (int) Math.pow(2, levelFrom - 4)); + } + + if (lossChance >= 10) { + String warn; + if (toUpgrade instanceof Weapon) { + if (((Weapon) toUpgrade).enchantHardened) { + warn = Messages.get(this, "harden", lossChance); + } else { + warn = Messages.get(this, "enchant", lossChance); + } + } else { + if (((Armor) toUpgrade).glyphHardened) { + warn = Messages.get(this, "harden", lossChance); + } else { + warn = Messages.get(this, "glyph", lossChance); + } + } + bottom = addMessage(warn, CharSprite.WARNING, bottom); + } + } + + if ((toUpgrade.cursed + || (toUpgrade instanceof Weapon && ((Weapon) toUpgrade).hasCurseEnchant()) + || (toUpgrade instanceof Armor && ((Armor) toUpgrade).hasCurseGlyph())) + && toUpgrade.cursedKnown) { + + if (toUpgrade.cursed && (toUpgrade instanceof Weapon && ((Weapon) toUpgrade).hasCurseEnchant()) + || (toUpgrade instanceof Armor && ((Armor) toUpgrade).hasCurseGlyph())){ + bottom = addMessage(Messages.get(this, "cursed_weaken"), CharSprite.POSITIVE, bottom); + } else { + bottom = addMessage(Messages.get(this, "cursed"), CharSprite.POSITIVE, bottom); + } + + if (curseInfused) { + bottom = addMessage(Messages.get(this, "curse_infusion"), CharSprite.WARNING, bottom); + } + } + } + + //warning relating to arcane resin + if (toUpgrade instanceof Wand && ((Wand) toUpgrade).resinBonus > 0){ + bottom = addMessage(Messages.get(this, "resin"), CharSprite.WARNING, bottom); + } + + // *** Buttons for confirming/cancelling *** + + btnUpgrade = new RedButton(Messages.get(this, "upgrade")){ + @Override + protected void onClick() { + super.onClick(); + + ScrollOfUpgrade.upgrade(Dungeon.hero); + + Item upgraded = toUpgrade; + if (upgrader instanceof ScrollOfUpgrade){ + ((ScrollOfUpgrade) upgrader).readAnimation(); + upgraded = ((ScrollOfUpgrade) upgrader).upgradeItem(toUpgrade); + Sample.INSTANCE.play( Assets.Sounds.READ ); + } else if (upgrader instanceof MagicalInfusion){ + ((MagicalInfusion) upgrader).useAnimation(); + upgraded = ((MagicalInfusion) upgrader).upgradeItem(toUpgrade); + } + + Item moreUpgradeItem = Dungeon.hero.belongings.getItem(upgrader.getClass()); + + hide(); + + if (moreUpgradeItem != null && toUpgrade.isIdentified()){ + moreUpgradeItem = moreUpgradeItem.detach(Dungeon.hero.belongings.backpack); + GameScene.show(new WndUpgrade(moreUpgradeItem, upgraded, false)); + } + } + }; + btnUpgrade.setRect(0, bottom+2*GAP, WIDTH/2f, 16); + add(btnUpgrade); + + btnCancel = new RedButton(Messages.get(this, "cancel")){ + @Override + protected void onClick() { + super.onClick(); + if (!force) { + upgrader.collect(); + hide(); + } else { + GameScene.show( new WndOptions(new ItemSprite(upgrader), + Messages.titleCase(upgrader.name()), + Messages.get(InventoryScroll.class, "warning"), + Messages.get(InventoryScroll.class, "yes"), + Messages.get(InventoryScroll.class, "no") ) { + @Override + protected void onSelect( int index ) { + if (index == 0){ + WndUpgrade.this.hide(); + } + } + public void onBackPressed() {} + } ); + } + } + + }; + btnCancel.setRect(btnUpgrade.right()+1, bottom+2*GAP, WIDTH/2f, 16); + add(btnCancel); + + btnUpgrade.enable(Dungeon.hero.ready); + + bottom = (int)btnCancel.bottom(); + + resize(WIDTH, (int)bottom); + + } + + @Override + public synchronized void update() { + super.update(); + if (!btnUpgrade.active && Dungeon.hero.ready){ + btnUpgrade.enable(true); + } + } + + @Override + public void onBackPressed() { + //don't let this window be closed if + if (!force) super.onBackPressed(); + } + + private float fillFields(String title, String msg1, String msg2, float bottom){ + + RenderedTextBlock ttl = PixelScene.renderTextBlock( title , 6); + ttl.setPos(COL_1 - ttl.width() / 2f, bottom + GAP); + PixelScene.align(ttl); + add(ttl); + + RenderedTextBlock m1 = PixelScene.renderTextBlock(msg1, 6); + m1.setPos(COL_2 - m1.width() / 2f, ttl.top()); + PixelScene.align(m1); + add(m1); + + RenderedTextBlock m2 = PixelScene.renderTextBlock(msg2, 6); + m2.setPos(COL_3 - m2.width() / 2f, ttl.top()); + PixelScene.align(m2); + add(m2); + + return m2.bottom() + GAP + 1; + + } + + private float addMessage(String text, int color, float bottom){ + RenderedTextBlock message = PixelScene.renderTextBlock(6); + message.text(text, WIDTH); + message.setPos(0, bottom + GAP); + message.hardlight(color); + add(message); + + return message.bottom(); + } + +}