diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index 7e13de0ba..66161f18e 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -348,9 +348,20 @@ items.artifacts.lloydsbeacon.desc=Lloyd's beacon is an intricate magical device items.artifacts.lloydsbeacon.desc_set=This beacon was set somewhere on the level %d of Pixel Dungeon. items.artifacts.masterthievesarmband.name=master thieves' armband +items.artifacts.masterthievesarmband.ac_steal=STEAL +items.artifacts.masterthievesarmband.no_charge=Your armband does not have enough charge. +items.artifacts.masterthievesarmband.cursed=You can't use a cursed armband. +items.artifacts.masterthievesarmband.full=Your armband is fully charged! +items.artifacts.masterthievesarmband.prompt=Choose an enemy to target +items.artifacts.masterthievesarmband.no_target=You must target an enemy that is adjacent to you +items.artifacts.masterthievesarmband.steal_shopkeeper=You can't steal from shopkeepers directly. +items.artifacts.masterthievesarmband.no_steal=That enemy has nothing to steal. +items.artifacts.masterthievesarmband.stole_item=You stole an item: %s. +items.artifacts.masterthievesarmband.failed_steal=You didn't manage to steal anything. +items.artifacts.masterthievesarmband.level_up=Your armband grows stronger! items.artifacts.masterthievesarmband.desc=This purple velvet armband bears the mark of a master thief. This doesn't belong to you, but it probably didn't belong to its last user either. items.artifacts.masterthievesarmband.desc_cursed=The cursed armband is bound to your wrist, and somehow your pouch of gold seems to be slowly getting lighter... -items.artifacts.masterthievesarmband.desc_worn=With the armband around your wrist, every piece of gold you find makes you desire other people's property more. Perhaps it wouldn't be too hard to steal from a shop... +items.artifacts.masterthievesarmband.desc_worn=You feel power building in the armband on your wrist as you gain experience. You can use this power to steal from shops, or to rob enemies!\n\nRobbing enemies happens instantly, briefly disorients them, and gives you a chance to get whatever loot they have! This power is more effective against unaware enemies. Robbing the same enemy multiple times does not give more chances at loot. items.artifacts.sandalsofnature.name=sandals of nature items.artifacts.sandalsofnature.name_1=shoes of nature diff --git a/core/src/main/assets/messages/windows/windows.properties b/core/src/main/assets/messages/windows/windows.properties index 245fe70d6..c4dcc0458 100644 --- a/core/src/main/assets/messages/windows/windows.properties +++ b/core/src/main/assets/messages/windows/windows.properties @@ -237,7 +237,7 @@ windows.wndsupportprompt.intro=Hello, I hope you're enjoying Shattered Pixel Dun windows.wndsupportprompt.close=Close windows.wndtradeitem.buy=Buy for %dg -windows.wndtradeitem.steal=Steal with %d%% chance +windows.wndtradeitem.steal=Steal with %1$d%% chance\nCharges used: %2$d windows.wndtradeitem.sell=Sell for %dg windows.wndtradeitem.sell_1=Sell 1 for %dg windows.wndtradeitem.sell_all=Sell all for %dg diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java index 0a30c2623..38e949bfc 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java @@ -81,6 +81,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.CloakOfShadows; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.DriedRose; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.EtherealChains; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HornOfPlenty; +import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.MasterThievesArmband; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.TalismanOfForesight; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.TimekeepersHourglass; import com.shatteredpixel.shatteredpixeldungeon.items.bags.MagicalHolster; @@ -1520,6 +1521,9 @@ public class Hero extends Char { AlchemistsToolkit.kitEnergy kit = buff(AlchemistsToolkit.kitEnergy.class); if (kit != null) kit.gainCharge(percent); + + MasterThievesArmband.Thievery armband = buff(MasterThievesArmband.Thievery.class); + if (armband != null) armband.gainCharge(percent); Berserk berserk = buff(Berserk.class); if (berserk != null) berserk.recover(percent); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Acidic.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Acidic.java index 2d0c98e6f..ae33f53c8 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Acidic.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Acidic.java @@ -54,7 +54,7 @@ public class Acidic extends Scorpio { } @Override - protected Item createLoot() { + public Item createLoot() { return new PotionOfExperience(); } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/ArmoredBrute.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/ArmoredBrute.java index 1ad126add..02a72bedf 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/ArmoredBrute.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/ArmoredBrute.java @@ -58,7 +58,7 @@ public class ArmoredBrute extends Brute { } @Override - protected Item createLoot () { + public Item createLoot() { if (Random.Int( 4 ) == 0) { return new PlateArmor().random(); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Bat.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Bat.java index 76ae1d59f..9bc9dc657 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Bat.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Bat.java @@ -44,7 +44,7 @@ public class Bat extends Mob { flying = true; loot = new PotionOfHealing(); - lootChance = 0.1667f; //by default, see rollToDropLoot() + lootChance = 0.1667f; //by default, see lootChance() } @Override @@ -76,13 +76,12 @@ public class Bat extends Mob { } @Override - public void rollToDropLoot() { - lootChance *= ((7f - Dungeon.LimitedDrops.BAT_HP.count) / 7f); - super.rollToDropLoot(); + public float lootChance(){ + return super.lootChance() * ((7f - Dungeon.LimitedDrops.BAT_HP.count) / 7f); } @Override - protected Item createLoot(){ + public Item createLoot(){ Dungeon.LimitedDrops.BAT_HP.count++; return super.createLoot(); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DM200.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DM200.java index c878eeb66..45aa30249 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DM200.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DM200.java @@ -45,7 +45,7 @@ public class DM200 extends Mob { maxLvl = 17; loot = Random.oneOf(Generator.Category.WEAPON, Generator.Category.ARMOR); - lootChance = 0.125f; //initially, see rollToDropLoot + lootChance = 0.125f; //initially, see lootChance() properties.add(Property.INORGANIC); properties.add(Property.LARGE); @@ -69,14 +69,13 @@ public class DM200 extends Mob { } @Override - public void rollToDropLoot() { + public float lootChance(){ //each drop makes future drops 1/2 as likely // so loot chance looks like: 1/8, 1/16, 1/32, 1/64, etc. - lootChance *= Math.pow(1/2f, Dungeon.LimitedDrops.DM200_EQUIP.count); - super.rollToDropLoot(); + return super.lootChance() * (float)Math.pow(1/2f, Dungeon.LimitedDrops.DM200_EQUIP.count); } - protected Item createLoot() { + public Item createLoot() { Dungeon.LimitedDrops.DM200_EQUIP.count++; //uses probability tables for dwarf city if (loot == Generator.Category.WEAPON){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Eye.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Eye.java index b384e954f..d15c25745 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Eye.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Eye.java @@ -202,7 +202,7 @@ public class Eye extends Mob { //generates an average of 1 dew, 0.25 seeds, and 0.25 stones @Override - protected Item createLoot() { + public Item createLoot() { Item loot; switch(Random.Int(4)){ case 0: case 1: default: diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/GnollTrickster.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/GnollTrickster.java index 0310e4d14..87d14d158 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/GnollTrickster.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/GnollTrickster.java @@ -111,7 +111,7 @@ public class GnollTrickster extends Gnoll { } @Override - protected Item createLoot() { + public Item createLoot() { MissileWeapon drop = (MissileWeapon)super.createLoot(); //half quantity, rounded up drop.quantity((drop.quantity()+1)/2); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Golem.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Golem.java index 14b281aaf..2ace142d3 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Golem.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Golem.java @@ -47,7 +47,7 @@ public class Golem extends Mob { maxLvl = 22; loot = Random.oneOf(Generator.Category.WEAPON, Generator.Category.ARMOR); - lootChance = 0.125f; //initially, see rollToDropLoot + lootChance = 0.125f; //initially, see lootChance() properties.add(Property.INORGANIC); properties.add(Property.LARGE); @@ -72,16 +72,19 @@ public class Golem extends Mob { } @Override - public void rollToDropLoot() { - Imp.Quest.process( this ); - + public float lootChance() { //each drop makes future drops 1/2 as likely // so loot chance looks like: 1/8, 1/16, 1/32, 1/64, etc. - lootChance *= Math.pow(1/2f, Dungeon.LimitedDrops.GOLEM_EQUIP.count); + return super.lootChance() * (float)Math.pow(1/2f, Dungeon.LimitedDrops.GOLEM_EQUIP.count); + } + + @Override + public void rollToDropLoot() { + Imp.Quest.process( this ); super.rollToDropLoot(); } - protected Item createLoot() { + public Item createLoot() { Dungeon.LimitedDrops.GOLEM_EQUIP.count++; //uses probability tables for demon halls if (loot == Generator.Category.WEAPON){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Guard.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Guard.java index f7ca421fd..adea45c7e 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Guard.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Guard.java @@ -54,7 +54,7 @@ public class Guard extends Mob { maxLvl = 14; loot = Generator.Category.ARMOR; - lootChance = 0.2f; //by default, see rollToDropLoot() + lootChance = 0.2f; //by default, see lootChance() properties.add(Property.UNDEAD); @@ -137,15 +137,14 @@ public class Guard extends Mob { } @Override - public void rollToDropLoot() { + public float lootChance() { //each drop makes future drops 1/2 as likely // so loot chance looks like: 1/5, 1/10, 1/20, 1/40, etc. - lootChance *= Math.pow(1/2f, Dungeon.LimitedDrops.GUARD_ARM.count); - super.rollToDropLoot(); + return super.lootChance() * (float)Math.pow(1/2f, Dungeon.LimitedDrops.GUARD_ARM.count); } @Override - protected Item createLoot() { + public Item createLoot() { Dungeon.LimitedDrops.GUARD_ARM.count++; return super.createLoot(); } 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 b3042f83c..9defcac27 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 @@ -52,7 +52,7 @@ import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle import com.shatteredpixel.shatteredpixeldungeon.items.Generator; import com.shatteredpixel.shatteredpixeldungeon.items.Gold; import com.shatteredpixel.shatteredpixeldungeon.items.Item; -import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.DriedRose; +import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.MasterThievesArmband; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.TimekeepersHourglass; import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring; import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfWealth; @@ -631,10 +631,14 @@ public abstract class Mob extends Char { return damage; } - public boolean surprisedBy( Char enemy ){ + public final boolean surprisedBy( Char enemy ){ + return surprisedBy( enemy, true); + } + + public boolean surprisedBy( Char enemy, boolean attacking ){ return enemy == Dungeon.hero && (enemy.invisible > 0 || !enemySeen) - && ((Hero)enemy).canSurpriseAttack(); + && (!attacking || ((Hero)enemy).canSurpriseAttack()); } public void aggro( Char ch ) { @@ -725,17 +729,25 @@ public abstract class Mob extends Char { } } } + + public float lootChance(){ + float lootChance = this.lootChance; + + lootChance *= RingOfWealth.dropChanceMultiplier( Dungeon.hero ); + + return lootChance; + } public void rollToDropLoot(){ if (Dungeon.hero.lvl > maxLvl + 2) return; - - float lootChance = this.lootChance; - lootChance *= RingOfWealth.dropChanceMultiplier( Dungeon.hero ); - - if (Random.Float() < lootChance) { - Item loot = createLoot(); - if (loot != null) { - Dungeon.level.drop(loot, pos).sprite.drop(); + + MasterThievesArmband.StolenTracker stolen = buff(MasterThievesArmband.StolenTracker.class); + if (stolen == null || !stolen.itemWasStolen()) { + if (Random.Float() < lootChance()) { + Item loot = createLoot(); + if (loot != null) { + Dungeon.level.drop(loot, pos).sprite.drop(); + } } } @@ -777,7 +789,7 @@ public abstract class Mob extends Char { protected float lootChance = 0; @SuppressWarnings("unchecked") - protected Item createLoot() { + public Item createLoot() { Item item; if (loot instanceof Generator.Category) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Necromancer.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Necromancer.java index 0d6632fea..ae63a31c3 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Necromancer.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Necromancer.java @@ -28,7 +28,6 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Adrenaline; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AllyBuff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ChampionEnemy; -import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Corruption; import com.shatteredpixel.shatteredpixeldungeon.effects.Beam; import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing; import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; @@ -54,7 +53,7 @@ public class Necromancer extends Mob { maxLvl = 14; loot = new PotionOfHealing(); - lootChance = 0.2f; //see createloot + lootChance = 0.2f; //see lootChance() properties.add(Property.UNDEAD); @@ -84,13 +83,12 @@ public class Necromancer extends Mob { } @Override - public void rollToDropLoot() { - lootChance *= ((6f - Dungeon.LimitedDrops.NECRO_HP.count) / 6f); - super.rollToDropLoot(); + public float lootChance() { + return super.lootChance() * ((6f - Dungeon.LimitedDrops.NECRO_HP.count) / 6f); } @Override - protected Item createLoot(){ + public Item createLoot(){ Dungeon.LimitedDrops.NECRO_HP.count++; return super.createLoot(); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Piranha.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Piranha.java index 16cce1ea6..887c4bc76 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Piranha.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Piranha.java @@ -89,8 +89,8 @@ public class Piranha extends Mob { } @Override - public boolean surprisedBy(Char enemy) { - if (enemy == Dungeon.hero && ((Hero)enemy).canSurpriseAttack()){ + public boolean surprisedBy(Char enemy, boolean attacking) { + if (enemy == Dungeon.hero && (!attacking || ((Hero)enemy).canSurpriseAttack())){ if (fieldOfView == null || fieldOfView.length != Dungeon.level.length()){ fieldOfView = new boolean[Dungeon.level.length()]; Dungeon.level.updateFieldOfView( this, fieldOfView ); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Scorpio.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Scorpio.java index f289035bd..80dfc42d0 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Scorpio.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Scorpio.java @@ -103,7 +103,7 @@ public class Scorpio extends Mob { } @Override - protected Item createLoot() { + public Item createLoot() { Class loot; do{ loot = (Class) Random.oneOf(Generator.Category.POTION.classes); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Shaman.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Shaman.java index 0d28398ab..8ff73a476 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Shaman.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Shaman.java @@ -48,7 +48,7 @@ public abstract class Shaman extends Mob { maxLvl = 16; loot = Generator.Category.WAND; - lootChance = 0.03f; //initially, see rollToDropLoot + lootChance = 0.03f; //initially, see lootChance() } @Override @@ -72,15 +72,14 @@ public abstract class Shaman extends Mob { } @Override - public void rollToDropLoot() { + public float lootChance() { //each drop makes future drops 1/3 as likely // so loot chance looks like: 1/33, 1/100, 1/300, 1/900, etc. - lootChance *= Math.pow(1/3f, Dungeon.LimitedDrops.SHAMAN_WAND.count); - super.rollToDropLoot(); + return super.lootChance() * (float)Math.pow(1/3f, Dungeon.LimitedDrops.SHAMAN_WAND.count); } @Override - protected Item createLoot() { + public Item createLoot() { Dungeon.LimitedDrops.SHAMAN_WAND.count++; return super.createLoot(); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Skeleton.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Skeleton.java index c909a4a5a..d3171865b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Skeleton.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Skeleton.java @@ -46,7 +46,7 @@ public class Skeleton extends Mob { maxLvl = 10; loot = Generator.Category.WEAPON; - lootChance = 0.1667f; //by default, see rollToDropLoot() + lootChance = 0.1667f; //by default, see lootChance() properties.add(Property.UNDEAD); properties.add(Property.INORGANIC); @@ -88,15 +88,14 @@ public class Skeleton extends Mob { } @Override - public void rollToDropLoot() { + public float lootChance() { //each drop makes future drops 1/2 as likely // so loot chance looks like: 1/6, 1/12, 1/24, 1/48, etc. - lootChance *= Math.pow(1/2f, Dungeon.LimitedDrops.SKELE_WEP.count); - super.rollToDropLoot(); + return super.lootChance() * (float)Math.pow(1/2f, Dungeon.LimitedDrops.SKELE_WEP.count); } @Override - protected Item createLoot() { + public Item createLoot() { Dungeon.LimitedDrops.SKELE_WEP.count++; return super.createLoot(); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Slime.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Slime.java index e904e16b7..46fabf40b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Slime.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Slime.java @@ -41,7 +41,7 @@ public class Slime extends Mob { EXP = 4; maxLvl = 9; - lootChance = 0.2f; //by default, see rollToDropLoot() + lootChance = 0.2f; //by default, see lootChance() } @Override @@ -64,15 +64,14 @@ public class Slime extends Mob { } @Override - public void rollToDropLoot() { + public float lootChance(){ //each drop makes future drops 1/3 as likely // so loot chance looks like: 1/5, 1/15, 1/45, 1/135, etc. - lootChance *= Math.pow(1/3f, Dungeon.LimitedDrops.SLIME_WEP.count); - super.rollToDropLoot(); + return super.lootChance() * (float)Math.pow(1/3f, Dungeon.LimitedDrops.SLIME_WEP.count); } @Override - protected Item createLoot() { + public Item createLoot() { Dungeon.LimitedDrops.SLIME_WEP.count++; Generator.Category c = Generator.Category.WEP_T2; MeleeWeapon w = (MeleeWeapon) Reflection.newInstance(c.classes[Random.chances(c.probs)]); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Succubus.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Succubus.java index c3e93ca73..505b08f1c 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Succubus.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Succubus.java @@ -163,7 +163,7 @@ public class Succubus extends Mob { } @Override - protected Item createLoot() { + public Item createLoot() { Class loot; do{ loot = (Class) Random.oneOf(Generator.Category.SCROLL.classes); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Swarm.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Swarm.java index 7137f6d7a..5fe460157 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Swarm.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Swarm.java @@ -28,7 +28,6 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AllyBuff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ChampionEnemy; -import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Corruption; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison; import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing; import com.shatteredpixel.shatteredpixeldungeon.items.Item; @@ -54,7 +53,7 @@ public class Swarm extends Mob { flying = true; loot = new PotionOfHealing(); - lootChance = 0.1667f; //by default, see rollToDropLoot() + lootChance = 0.1667f; //by default, see lootChance() } private static final float SPLIT_DELAY = 1f; @@ -137,16 +136,15 @@ public class Swarm extends Mob { } return clone; } - + @Override - public void rollToDropLoot() { + public float lootChance() { lootChance = 1f/(6 * (generation+1) ); - lootChance *= (5f - Dungeon.LimitedDrops.SWARM_HP.count) / 5f; - super.rollToDropLoot(); + return super.lootChance() * (5f - Dungeon.LimitedDrops.SWARM_HP.count) / 5f; } @Override - protected Item createLoot(){ + public Item createLoot(){ Dungeon.LimitedDrops.SWARM_HP.count++; return super.createLoot(); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Thief.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Thief.java index 376aecb8d..bee2cbb78 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Thief.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Thief.java @@ -24,7 +24,6 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.mobs; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AllyBuff; -import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Corruption; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Dread; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; @@ -55,7 +54,7 @@ public class Thief extends Mob { maxLvl = 11; loot = Random.oneOf(Generator.Category.RING, Generator.Category.ARTIFACT); - lootChance = 0.03f; //initially, see rollToDropLoot + lootChance = 0.03f; //initially, see lootChance() WANDERING = new Wandering(); FLEEING = new Fleeing(); @@ -92,7 +91,14 @@ public class Thief extends Mob { public float attackDelay() { return super.attackDelay()*0.5f; } - + + @Override + public float lootChance() { + //each drop makes future drops 1/3 as likely + // so loot chance looks like: 1/33, 1/100, 1/300, 1/900, etc. + return super.lootChance() * (float)Math.pow(1/3f, Dungeon.LimitedDrops.THEIF_MISC.count); + } + @Override public void rollToDropLoot() { if (item != null) { @@ -101,14 +107,11 @@ public class Thief extends Mob { if (item instanceof Honeypot.ShatteredPot) ((Honeypot.ShatteredPot)item).dropPot( this, pos ); item = null; } - //each drop makes future drops 1/3 as likely - // so loot chance looks like: 1/33, 1/100, 1/300, 1/900, etc. - lootChance *= Math.pow(1/3f, Dungeon.LimitedDrops.THEIF_MISC.count); super.rollToDropLoot(); } @Override - protected Item createLoot() { + public Item createLoot() { Dungeon.LimitedDrops.THEIF_MISC.count++; return super.createLoot(); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Warlock.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Warlock.java index 04c91a16b..88d28dd36 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Warlock.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Warlock.java @@ -134,7 +134,7 @@ public class Warlock extends Mob implements Callback { public Item createLoot(){ // 1/6 chance for healing, scaling to 0 over 8 drops - if (Random.Int(2) == 0 && Random.Int(8) > Dungeon.LimitedDrops.WARLOCK_HP.count ){ + if (Random.Int(3) == 0 && Random.Int(8) > Dungeon.LimitedDrops.WARLOCK_HP.count ){ Dungeon.LimitedDrops.WARLOCK_HP.count++; return new PotionOfHealing(); } else { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Gold.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Gold.java index 136b4e4e1..00c33bf46 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Gold.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Gold.java @@ -64,10 +64,6 @@ public class Gold extends Item { Statistics.goldCollected += quantity; Badges.validateGoldCollected(); - MasterThievesArmband.Thievery thievery = hero.buff(MasterThievesArmband.Thievery.class); - if (thievery != null) - thievery.collect(quantity); - GameScene.pickUp( this, pos ); hero.sprite.showStatus( CharSprite.NEUTRAL, TXT_VALUE, quantity ); hero.spendAndNext( TIME_TO_PICK_UP ); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/MasterThievesArmband.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/MasterThievesArmband.java index 7b3585aff..3cac91c8e 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/MasterThievesArmband.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/MasterThievesArmband.java @@ -21,14 +21,36 @@ package com.shatteredpixel.shatteredpixeldungeon.items.artifacts; +import com.shatteredpixel.shatteredpixeldungeon.Assets; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; +import com.shatteredpixel.shatteredpixeldungeon.actors.Char; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Blindness; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.CounterBuff; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Cripple; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Shopkeeper; +import com.shatteredpixel.shatteredpixeldungeon.effects.Surprise; +import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfEnergy; +import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.scenes.CellSelector; +import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; +import com.shatteredpixel.shatteredpixeldungeon.utils.BArray; +import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; +import com.watabou.noosa.audio.Sample; +import com.watabou.utils.Bundle; +import com.watabou.utils.Callback; +import com.watabou.utils.PathFinder; import com.watabou.utils.Random; +import java.util.ArrayList; + public class MasterThievesArmband extends Artifact { { @@ -37,9 +59,132 @@ public class MasterThievesArmband extends Artifact { levelCap = 10; charge = 0; + partialCharge = 0; + chargeCap = 5+level()/2; + + defaultAction = AC_STEAL; } - private int exp = 0; + public static final String AC_STEAL = "STEAL"; + + @Override + public ArrayList actions(Hero hero) { + ArrayList actions = super.actions(hero); + if (isEquipped(hero) && charge > 0 && !cursed) actions.add(AC_STEAL); + return actions; + } + + @Override + public void execute(Hero hero, String action) { + super.execute(hero, action); + if (action.equals(AC_STEAL)){ + + curUser = hero; + + if (!isEquipped( hero )) { + GLog.i( Messages.get(Artifact.class, "need_to_equip") ); + usesTargeting = false; + + } else if (charge < 1) { + GLog.i( Messages.get(this, "no_charge") ); + usesTargeting = false; + + } else if (cursed) { + GLog.w( Messages.get(this, "cursed") ); + usesTargeting = false; + + } else { + usesTargeting = true; + GameScene.selectCell(targeter); + } + + } + } + + private CellSelector.Listener targeter = new CellSelector.Listener(){ + + @Override + public void onSelect(Integer target) { + + if (target == null) { + return; + } else if (!Dungeon.level.adjacent(curUser.pos, target) || Actor.findChar(target) == null){ + GLog.w( Messages.get(MasterThievesArmband.class, "no_target") ); + } else { + Char ch = Actor.findChar(target); + if (ch instanceof Shopkeeper){ + GLog.w( Messages.get(MasterThievesArmband.class, "steal_shopkeeper") ); + } else if (ch.alignment != Char.Alignment.ENEMY){ + GLog.w( Messages.get(MasterThievesArmband.class, "no_target") ); + } else if (ch instanceof Mob) { + curUser.busy(); + curUser.sprite.attack(target, new Callback() { + @Override + public void call() { + Sample.INSTANCE.play(Assets.Sounds.HIT); + + boolean surprised = ((Mob) ch).surprisedBy(curUser, false); + float lootMultiplier = 1f + 0.1f*level(); + int debuffDuration = 3 + level()/2; + + if (surprised){ + lootMultiplier += 0.5f; + Surprise.hit(ch); + Sample.INSTANCE.play(Assets.Sounds.HIT_STRONG); + debuffDuration += 2; + exp += 2; + } + + float lootChance = ((Mob) ch).lootChance() * lootMultiplier; + if (ch.buff(StolenTracker.class) != null || lootChance == 0){ + GLog.w(Messages.get(MasterThievesArmband.class, "no_steal")); + } else if (Random.Float() <= lootChance){ + Item loot = ((Mob) ch).createLoot(); + if (loot.doPickUp(curUser)){ + //item collection happens instantly + curUser.spend(-TIME_TO_PICK_UP); + } else { + Dungeon.level.drop( loot, curUser.pos ).sprite.drop(); + } + GLog.i(Messages.get(MasterThievesArmband.class, "stole_item", loot.name())); + Buff.affect(ch, StolenTracker.class).setItemStolen(true); + } else { + GLog.i(Messages.get(MasterThievesArmband.class, "failed_steal")); + Buff.affect(ch, StolenTracker.class).setItemStolen(false); + } + + Buff.prolong(ch, Blindness.class, debuffDuration); + Buff.prolong(ch, Cripple.class, debuffDuration); + + charge--; + exp += 3; + Talent.onArtifactUsed(Dungeon.hero); + while (exp >= (10 + Math.round(3.33f * level())) && level() < levelCap) { + exp -= 10 + Math.round(3.33f * level()); + GLog.p(Messages.get(MasterThievesArmband.class, "level_up")); + upgrade(); + } + Item.updateQuickslot(); + curUser.next(); + } + }); + + } + } + + } + + @Override + public String prompt() { + return Messages.get(MasterThievesArmband.class, "prompt"); + } + }; + + //counter of 0 for attempt but no success, 1 for success + public static class StolenTracker extends CounterBuff { + public void setItemStolen(boolean stolen){ if (stolen) countUp(1); } + public boolean itemWasStolen(){ return count() > 0; } + } @Override protected ArtifactBuff passiveBuff() { @@ -48,8 +193,22 @@ public class MasterThievesArmband extends Artifact { @Override public void charge(Hero target, float amount) { - charge += Math.round(10*amount); - updateQuickslot(); + partialCharge += 0.1f * amount; + partialCharge = Math.min(partialCharge, chargeCap - charge); + while (partialCharge > 1f){ + charge++; + partialCharge--; + updateQuickslot(); + if (charge == chargeCap){ + GLog.p( Messages.get(MasterThievesArmband.class, "full") ); + } + } + } + + @Override + public Item upgrade() { + chargeCap = 5 + (level()+1)/2; + return super.upgrade(); } @Override @@ -63,68 +222,91 @@ public class MasterThievesArmband extends Artifact { desc += "\n\n" + Messages.get(this, "desc_worn"); } } - return desc; } - public class Thievery extends ArtifactBuff{ - public void collect(int gold){ - if (!cursed) { - charge += gold/2 * RingOfEnergy.artifactChargeMultiplier(target); - } + @Override + public void restoreFromBundle(Bundle bundle) { + super.restoreFromBundle(bundle); + //conversion for old armband on pre-1.2.0 saves + if (exp > Math.round(10 + level()*3.33f)){ + exp = 0; } + } + + public class Thievery extends ArtifactBuff { - @Override - public void detach() { - charge *= 0.95; - super.detach(); - } - @Override public boolean act() { - if (cursed) { - - if (Dungeon.gold > 0 && Random.Int(6) == 0){ - Dungeon.gold--; - } - - spend(TICK); - return true; - } else { - return super.act(); - } - } - - public boolean steal(int value){ - if (value <= charge){ - charge -= value; - exp += value; - } else { - float chance = stealChance(value); - if (Random.Float() > chance) - return false; - else { - if (chance <= 1) - charge = 0; - else - //removes the charge it took you to reach 100% - charge -= charge/chance; - exp += value; - } - } - Talent.onArtifactUsed(Dungeon.hero); - while(exp >= (250 + 50*level()) && level() < levelCap) { - exp -= (250 + 50*level()); - upgrade(); + if (cursed && Dungeon.gold > 0 && Random.Int(5) == 0){ + Dungeon.gold--; } + + spend(TICK); return true; } - public float stealChance(int value){ - //get lvl*50 gold or lvl*3.33% item value of free charge, whichever is less. - int chargeBonus = Math.min(level()*50, (value*level())/30); - return (((float)charge + chargeBonus)/value); + public void gainCharge(float levelPortion) { + if (cursed) return; + + if (charge < chargeCap){ + float chargeGain = 3f * levelPortion; + + partialCharge += chargeGain; + while (partialCharge > 1f){ + partialCharge--; + charge++; + updateQuickslot(); + + if (charge == chargeCap){ + GLog.p( Messages.get(MasterThievesArmband.class, "full") ); + partialCharge = 0; + } + } + + } else { + partialCharge = 0f; + } + } + + public boolean steal(Item item){ + int chargesUsed = chargesToUse(item); + float stealChance = stealChance(item); + if (Random.Float() > stealChance){ + return false; + } else { + charge -= chargesUsed; + exp += 4 * chargesUsed; + GLog.i(Messages.get(MasterThievesArmband.class, "stole_item", item.name())); + + Talent.onArtifactUsed(Dungeon.hero); + while (exp >= (10 + Math.round(3.33f * level())) && level() < levelCap) { + exp -= 10 + Math.round(3.33f * level()); + GLog.p(Messages.get(MasterThievesArmband.class, "level_up")); + upgrade(); + } + return true; + } + } + + public float stealChance(Item item){ + int chargesUsed = chargesToUse(item); + float val = chargesUsed * (10 + level()/2f); + return Math.min(1f, val/item.value()); + } + + public int chargesToUse(Item item){ + int value = item.value(); + float valUsing = 0; + int chargesUsed = 0; + while (valUsing < value && chargesUsed < charge){ + valUsing += 10 + level()/2f; + chargesUsed++; + } + return chargesUsed; } } + + } 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 74d2c914f..5f7ce409d 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTradeItem.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndTradeItem.java @@ -129,12 +129,13 @@ public class WndTradeItem extends WndInfoItem { pos = btnBuy.bottom(); final MasterThievesArmband.Thievery thievery = Dungeon.hero.buff(MasterThievesArmband.Thievery.class); - if (thievery != null && !thievery.isCursed()) { - final float chance = thievery.stealChance(price); - RedButton btnSteal = new RedButton(Messages.get(this, "steal", Math.min(100, (int) (chance * 100)))) { + if (thievery != null && !thievery.isCursed() && thievery.chargesToUse(item) > 0) { + final float chance = thievery.stealChance(item); + final int chargesToUse = thievery.chargesToUse(item); + RedButton btnSteal = new RedButton(Messages.get(this, "steal", Math.min(100, (int) (chance * 100)), chargesToUse), 6) { @Override protected void onClick() { - if (thievery.steal(price)) { + if (thievery.steal(item)) { Hero hero = Dungeon.hero; Item item = heap.pickUp(); hide();