diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index 6de67b9b0..c7ce911d3 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -857,7 +857,7 @@ items.rings.ringoftenacity.desc=When worn, this ring will allow the wearer to re items.rings.ringofwealth.name=ring of wealth items.rings.ringofwealth.stats=When worn, this ring will increase your luck by _%s%%._ items.rings.ringofwealth.typical_stats=When worn, this ring will typically increase your luck by _%s%%._ -items.rings.ringofwealth.desc=It's not clear what this ring does exactly, good luck may influence the life of an adventurer in many subtle ways. Naturally a cursed ring would give bad luck. +items.rings.ringofwealth.desc=This ring acts like a magnet for treasure, increasing the likelihood that enemies and containers will be carrying valuable items. A cursed ring will instead reduce your chance of receiving loot. diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java index c5442d0e8..9dcb789b5 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java @@ -674,12 +674,7 @@ public abstract class Mob extends Char { ArrayList bonus = RingOfWealth.tryForBonusDrop(Dungeon.hero, rolls); if (bonus != null && !bonus.isEmpty()) { for (Item b : bonus) Dungeon.level.drop(b, pos).sprite.drop(); - if (RingOfWealth.latestDropWasRare){ - new Flare(8, 48).color(0xAA00FF, true).show(sprite, 3f); - RingOfWealth.latestDropWasRare = false; - } else { - new Flare(8, 24).color(0xFFFFFF, true).show(sprite, 3f); - } + RingOfWealth.showFlareForBonusDrop(sprite); } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Heap.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Heap.java index b9acae7ae..01f8fc246 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Heap.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Heap.java @@ -112,12 +112,7 @@ public class Heap implements Bundlable { ArrayList bonus = RingOfWealth.tryForBonusDrop(hero, 1); if (bonus != null && !bonus.isEmpty()) { items.addAll(0, bonus); - if (RingOfWealth.latestDropWasRare){ - new Flare(8, 48).color(0xAA00FF, true).show(sprite, 2f); - RingOfWealth.latestDropWasRare = false; - } else { - new Flare(8, 24).color(0xFFFFFF, true).show(sprite, 2f); - } + RingOfWealth.showFlareForBonusDrop(sprite); } sprite.link(); sprite.drop(); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/rings/RingOfWealth.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/rings/RingOfWealth.java index 86d7be0fa..c4ac1fdef 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/rings/RingOfWealth.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/rings/RingOfWealth.java @@ -24,20 +24,27 @@ package com.shatteredpixel.shatteredpixeldungeon.items.rings; import com.shatteredpixel.shatteredpixeldungeon.Challenges; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.actors.Char; +import com.shatteredpixel.shatteredpixeldungeon.effects.Flare; import com.shatteredpixel.shatteredpixeldungeon.items.Generator; import com.shatteredpixel.shatteredpixeldungeon.items.Gold; +import com.shatteredpixel.shatteredpixeldungeon.items.Honeypot; import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor; +import com.shatteredpixel.shatteredpixeldungeon.items.bombs.Bomb; import com.shatteredpixel.shatteredpixeldungeon.items.potions.AlchemicalCatalyst; import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfExperience; +import com.shatteredpixel.shatteredpixeldungeon.items.potions.exotic.ExoticPotion; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTransmutation; +import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ExoticScroll; import com.shatteredpixel.shatteredpixeldungeon.items.spells.ArcaneCatalyst; import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfEnchantment; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; +import com.watabou.noosa.Visual; import com.watabou.utils.Bundle; import com.watabou.utils.Random; +import com.watabou.utils.Reflection; import java.text.DecimalFormat; import java.util.ArrayList; @@ -51,14 +58,12 @@ public class RingOfWealth extends Ring { private float triesToDrop = Float.MIN_VALUE; private int dropsToRare = Integer.MIN_VALUE; - - public static boolean latestDropWasRare = false; public String statsInfo() { if (isIdentified()){ - return Messages.get(this, "stats", new DecimalFormat("#.##").format(100f * (Math.pow(1.2f, soloBuffedBonus()) - 1f))); + return Messages.get(this, "stats", new DecimalFormat("#.##").format(100f * (Math.pow(1.25f, soloBuffedBonus()) - 1f))); } else { - return Messages.get(this, "typical_stats", new DecimalFormat("#.##").format(20f)); + return Messages.get(this, "typical_stats", new DecimalFormat("#.##").format(25f)); } } @@ -85,161 +90,196 @@ public class RingOfWealth extends Ring { } public static float dropChanceMultiplier( Char target ){ - return (float)Math.pow(1.2, getBuffedBonus(target, Wealth.class)); + return (float)Math.pow(1.25, getBuffedBonus(target, Wealth.class)); } public static ArrayList tryForBonusDrop(Char target, int tries ){ - if (getBuffedBonus(target, Wealth.class) <= 0) return null; + int bonus = getBuffedBonus(target, Wealth.class); + + if (bonus <= 0) return null; HashSet buffs = target.buffs(Wealth.class); float triesToDrop = Float.MIN_VALUE; - int dropsToRare = Integer.MIN_VALUE; + int dropsToEquip = Integer.MIN_VALUE; //find the largest count (if they aren't synced yet) for (Wealth w : buffs){ if (w.triesToDrop() > triesToDrop){ triesToDrop = w.triesToDrop(); - dropsToRare = w.dropsToRare(); + dropsToEquip = w.dropsToRare(); } } //reset (if needed), decrement, and store counts if (triesToDrop == Float.MIN_VALUE) { - triesToDrop = Random.NormalIntRange(0, 50); - dropsToRare = Random.NormalIntRange(5, 10); + triesToDrop = Random.NormalIntRange(0, 30); + dropsToEquip = Random.NormalIntRange(5, 10); } //now handle reward logic ArrayList drops = new ArrayList<>(); - triesToDrop -= dropProgression(target, tries); + triesToDrop -= tries; while ( triesToDrop <= 0 ){ - if (dropsToRare <= 0){ + if (dropsToEquip <= 0){ Item i; do { - i = genRareDrop(); + i = genEquipmentDrop(bonus - 1); } while (Challenges.isItemBlocked(i)); drops.add(i); - latestDropWasRare = true; - dropsToRare = Random.NormalIntRange(5, 10); + dropsToEquip = Random.NormalIntRange(5, 10); } else { Item i; do { - i = genStandardDrop(); + i = genConsumableDrop(bonus - 1); } while (Challenges.isItemBlocked(i)); drops.add(i); - dropsToRare--; + dropsToEquip--; } - triesToDrop += Random.NormalIntRange(0, 50); + triesToDrop += Random.NormalIntRange(0, 30); } //store values back into rings for (Wealth w : buffs){ w.triesToDrop(triesToDrop); - w.dropsToRare(dropsToRare); + w.dropsToRare(dropsToEquip); } return drops; } - - public static Item genStandardDrop(){ - float roll = Random.Float(); - if (roll < 0.3f){ //30% chance - Item result = new Gold().random(); - result.quantity(Math.round(result.quantity() * Random.NormalFloat(0.33f, 1f))); - return result; - } else if (roll < 0.7f){ //40% chance - return genBasicConsumable(); - } else if (roll < 0.9f){ //20% chance - return genExoticConsumable(); - } else { //10% chance - if (Random.Int(3) != 0){ - Weapon weapon = Generator.randomWeapon(); - weapon.enchant(null); - weapon.cursed = false; - weapon.cursedKnown = true; - weapon.level(0); - return weapon; - } else { - Armor armor = Generator.randomArmor(); - armor.inscribe(null); - armor.cursed = false; - armor.cursedKnown = true; - armor.level(0); - return armor; - } - } - } - - private static Item genBasicConsumable(){ - float roll = Random.Float(); - if (roll < 0.4f){ //40% chance - return Generator.random(Generator.Category.STONE); - } else if (roll < 0.7f){ //30% chance - return Generator.random(Generator.Category.POTION); - } else { //30% chance - return Generator.random(Generator.Category.SCROLL); - } - } - - private static Item genExoticConsumable(){ - float roll = Random.Float(); - if (roll < 0.3f){ //30% chance - return Generator.random(Generator.Category.POTION); - } else if (roll < 0.6f) { //30% chance - return Generator.random(Generator.Category.SCROLL); - } else { //40% chance - return Random.Int(2) == 0 ? new AlchemicalCatalyst() : new ArcaneCatalyst(); - } - } - - public static Item genRareDrop(){ - float roll = Random.Float(); - if (roll < 0.3f){ //30% chance - Item result = new Gold().random(); - result.quantity(Math.round(result.quantity() * Random.NormalFloat(3f, 6f))); - return result; - } else if (roll < 0.7f){ //40% chance - return genHighValueConsumable(); - } else if (roll < 0.9f){ //20% chance - Item result = Random.Int(2) == 0 ? Generator.random(Generator.Category.ARTIFACT) : Generator.random(Generator.Category.RING); - result.cursed = false; - result.cursedKnown = true; - return result; - } else { //10% chance - if (Random.Int(3) != 0){ - Weapon weapon = Generator.randomWeapon((Dungeon.depth / 5) + 1); - weapon.upgrade(1); - weapon.enchant(Weapon.Enchantment.random()); - weapon.cursed = false; - weapon.cursedKnown = true; - return weapon; - } else { - Armor armor = Generator.randomArmor((Dungeon.depth / 5) + 1); - armor.upgrade(); - armor.inscribe(Armor.Glyph.random()); - armor.cursed = false; - armor.cursedKnown = true; - return armor; - } - } - } - - private static Item genHighValueConsumable(){ - switch( Random.Int(4) ){ //25% chance each - case 0: default: - return new StoneOfEnchantment(); + + //used for visuals + // 1/2/3 used for low/mid/high tier consumables + // 3 used for +0-1 equips, 4 used for +2 or higher equips + private static int latestDropTier = 0; + + public static void showFlareForBonusDrop( Visual vis ){ + switch (latestDropTier){ + default: + break; //do nothing case 1: - return new StoneOfEnchantment().quantity(2); + new Flare(6, 20).color(0x00FF00, true).show(vis, 3f); + break; + case 2: + new Flare(6, 24).color(0x00AAFF, true).show(vis, 3.33f); + break; + case 3: + new Flare(6, 28).color(0xAA00FF, true).show(vis, 3.67f); + break; + case 4: + new Flare(6, 32).color(0xFFAA00, true).show(vis, 4f); + break; + } + latestDropTier = 0; + } + + public static Item genConsumableDrop(int level) { + float roll = Random.Float(); + //60% chance - 4% per level. Starting from +15: 0% + if (roll < (0.6f - 0.04f * level)) { + latestDropTier = 1; + return genLowValueConsumable(); + //30% chance + 2% per level. Starting from +15: 60%-2%*lvl + } else if (roll < (0.9f - 0.02f * level)) { + latestDropTier = 2; + return genMidValueConsumable(); + //10% chance + 2% per level. Starting from +15: 40%+2%*lvl + } else { + latestDropTier = 3; + return genHighValueConsumable(); + } + } + + private static Item genLowValueConsumable(){ + switch (Random.Int(4)){ + case 0: default: + Item i = new Gold().random(); + return i.quantity(i.quantity()/2); + case 1: + return Generator.random(Generator.Category.STONE); + case 2: + return Generator.random(Generator.Category.POTION); + case 3: + return Generator.random(Generator.Category.SCROLL); + } + } + + private static Item genMidValueConsumable(){ + switch (Random.Int(6)){ + case 0: default: + Item i = genLowValueConsumable(); + return i.quantity(i.quantity()*2); + case 1: + i = Generator.randomUsingDefaults(Generator.Category.POTION); + return Reflection.newInstance(ExoticPotion.regToExo.get(i.getClass())); + case 2: + i = Generator.randomUsingDefaults(Generator.Category.SCROLL); + return Reflection.newInstance(ExoticScroll.regToExo.get(i.getClass())); + case 3: + return Random.Int(2) == 0 ? new ArcaneCatalyst() : new AlchemicalCatalyst(); + case 4: + return new Bomb(); + case 5: + return new Honeypot(); + } + } + + private static Item genHighValueConsumable(){ + switch (Random.Int(4)){ + case 0: default: + Item i = genLowValueConsumable(); + if (i instanceof Bomb){ + return new Bomb.DoubleBomb(); + } else { + return i.quantity(i.quantity() * 2); + } + case 1: + return new StoneOfEnchantment(); case 2: return new PotionOfExperience(); case 3: return new ScrollOfTransmutation(); } } - - private static float dropProgression( Char target, int tries ){ - return tries * (float)Math.pow(1.2f, getBuffedBonus(target, Wealth.class) ); + + private static Item genEquipmentDrop( int level ){ + Item result; + //each upgrade increases depth used for calculating drops by 1 + int floorset = (Dungeon.depth + level)/5; + switch (Random.Int(5)){ + default: case 0: case 1: + Weapon w = Generator.randomWeapon(floorset); + if (!w.hasGoodEnchant() && Random.Int(10-level) == 0) w.enchant(); + else if (w.hasCurseEnchant()) w.enchant(null); + result = w; + break; + case 2: + Armor a = Generator.randomArmor(floorset); + if (!a.hasGoodGlyph() && Random.Int(10-level) == 0) a.inscribe(); + else if (a.hasCurseGlyph()) a.inscribe(null); + result = a; + break; + case 3: + result = Generator.random(Generator.Category.RING); + break; + case 4: + result = Generator.random(Generator.Category.ARTIFACT); + break; + } + //minimum level of sqrt(ringLvl) + if (result.isUpgradable()){ + if (result.level() < Math.floor(Math.sqrt(level))){ + result.level((int)Math.floor(Math.sqrt(level))); + } + } + result.cursed = false; + result.cursedKnown = true; + if (result.level() >= 2) { + latestDropTier = 4; + } else { + latestDropTier = 3; + } + return result; } public class Wealth extends RingBuff {