v1.2.0: reworked master thieves' armband

This commit is contained in:
Evan Debenham 2022-03-15 17:22:30 -04:00
parent 2bcacbaa5d
commit 1f45564487
25 changed files with 342 additions and 140 deletions

View File

@ -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.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.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=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_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=sandals of nature
items.artifacts.sandalsofnature.name_1=shoes of nature items.artifacts.sandalsofnature.name_1=shoes of nature

View File

@ -237,7 +237,7 @@ windows.wndsupportprompt.intro=Hello, I hope you're enjoying Shattered Pixel Dun
windows.wndsupportprompt.close=Close windows.wndsupportprompt.close=Close
windows.wndtradeitem.buy=Buy for %dg 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=Sell for %dg
windows.wndtradeitem.sell_1=Sell 1 for %dg windows.wndtradeitem.sell_1=Sell 1 for %dg
windows.wndtradeitem.sell_all=Sell all for %dg windows.wndtradeitem.sell_all=Sell all for %dg

View File

@ -81,6 +81,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.CloakOfShadows;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.DriedRose; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.DriedRose;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.EtherealChains; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.EtherealChains;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HornOfPlenty; 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.TalismanOfForesight;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.TimekeepersHourglass; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.TimekeepersHourglass;
import com.shatteredpixel.shatteredpixeldungeon.items.bags.MagicalHolster; import com.shatteredpixel.shatteredpixeldungeon.items.bags.MagicalHolster;
@ -1521,6 +1522,9 @@ public class Hero extends Char {
AlchemistsToolkit.kitEnergy kit = buff(AlchemistsToolkit.kitEnergy.class); AlchemistsToolkit.kitEnergy kit = buff(AlchemistsToolkit.kitEnergy.class);
if (kit != null) kit.gainCharge(percent); if (kit != null) kit.gainCharge(percent);
MasterThievesArmband.Thievery armband = buff(MasterThievesArmband.Thievery.class);
if (armband != null) armband.gainCharge(percent);
Berserk berserk = buff(Berserk.class); Berserk berserk = buff(Berserk.class);
if (berserk != null) berserk.recover(percent); if (berserk != null) berserk.recover(percent);

View File

@ -54,7 +54,7 @@ public class Acidic extends Scorpio {
} }
@Override @Override
protected Item createLoot() { public Item createLoot() {
return new PotionOfExperience(); return new PotionOfExperience();
} }
} }

View File

@ -58,7 +58,7 @@ public class ArmoredBrute extends Brute {
} }
@Override @Override
protected Item createLoot () { public Item createLoot() {
if (Random.Int( 4 ) == 0) { if (Random.Int( 4 ) == 0) {
return new PlateArmor().random(); return new PlateArmor().random();
} }

View File

@ -44,7 +44,7 @@ public class Bat extends Mob {
flying = true; flying = true;
loot = new PotionOfHealing(); loot = new PotionOfHealing();
lootChance = 0.1667f; //by default, see rollToDropLoot() lootChance = 0.1667f; //by default, see lootChance()
} }
@Override @Override
@ -76,13 +76,12 @@ public class Bat extends Mob {
} }
@Override @Override
public void rollToDropLoot() { public float lootChance(){
lootChance *= ((7f - Dungeon.LimitedDrops.BAT_HP.count) / 7f); return super.lootChance() * ((7f - Dungeon.LimitedDrops.BAT_HP.count) / 7f);
super.rollToDropLoot();
} }
@Override @Override
protected Item createLoot(){ public Item createLoot(){
Dungeon.LimitedDrops.BAT_HP.count++; Dungeon.LimitedDrops.BAT_HP.count++;
return super.createLoot(); return super.createLoot();
} }

View File

@ -45,7 +45,7 @@ public class DM200 extends Mob {
maxLvl = 17; maxLvl = 17;
loot = Random.oneOf(Generator.Category.WEAPON, Generator.Category.ARMOR); 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.INORGANIC);
properties.add(Property.LARGE); properties.add(Property.LARGE);
@ -69,14 +69,13 @@ public class DM200 extends Mob {
} }
@Override @Override
public void rollToDropLoot() { public float lootChance(){
//each drop makes future drops 1/2 as likely //each drop makes future drops 1/2 as likely
// so loot chance looks like: 1/8, 1/16, 1/32, 1/64, etc. // so loot chance looks like: 1/8, 1/16, 1/32, 1/64, etc.
lootChance *= Math.pow(1/2f, Dungeon.LimitedDrops.DM200_EQUIP.count); return super.lootChance() * (float)Math.pow(1/2f, Dungeon.LimitedDrops.DM200_EQUIP.count);
super.rollToDropLoot();
} }
protected Item createLoot() { public Item createLoot() {
Dungeon.LimitedDrops.DM200_EQUIP.count++; Dungeon.LimitedDrops.DM200_EQUIP.count++;
//uses probability tables for dwarf city //uses probability tables for dwarf city
if (loot == Generator.Category.WEAPON){ if (loot == Generator.Category.WEAPON){

View File

@ -202,7 +202,7 @@ public class Eye extends Mob {
//generates an average of 1 dew, 0.25 seeds, and 0.25 stones //generates an average of 1 dew, 0.25 seeds, and 0.25 stones
@Override @Override
protected Item createLoot() { public Item createLoot() {
Item loot; Item loot;
switch(Random.Int(4)){ switch(Random.Int(4)){
case 0: case 1: default: case 0: case 1: default:

View File

@ -111,7 +111,7 @@ public class GnollTrickster extends Gnoll {
} }
@Override @Override
protected Item createLoot() { public Item createLoot() {
MissileWeapon drop = (MissileWeapon)super.createLoot(); MissileWeapon drop = (MissileWeapon)super.createLoot();
//half quantity, rounded up //half quantity, rounded up
drop.quantity((drop.quantity()+1)/2); drop.quantity((drop.quantity()+1)/2);

View File

@ -47,7 +47,7 @@ public class Golem extends Mob {
maxLvl = 22; maxLvl = 22;
loot = Random.oneOf(Generator.Category.WEAPON, Generator.Category.ARMOR); 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.INORGANIC);
properties.add(Property.LARGE); properties.add(Property.LARGE);
@ -72,16 +72,19 @@ public class Golem extends Mob {
} }
@Override @Override
public void rollToDropLoot() { public float lootChance() {
Imp.Quest.process( this );
//each drop makes future drops 1/2 as likely //each drop makes future drops 1/2 as likely
// so loot chance looks like: 1/8, 1/16, 1/32, 1/64, etc. // 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(); super.rollToDropLoot();
} }
protected Item createLoot() { public Item createLoot() {
Dungeon.LimitedDrops.GOLEM_EQUIP.count++; Dungeon.LimitedDrops.GOLEM_EQUIP.count++;
//uses probability tables for demon halls //uses probability tables for demon halls
if (loot == Generator.Category.WEAPON){ if (loot == Generator.Category.WEAPON){

View File

@ -54,7 +54,7 @@ public class Guard extends Mob {
maxLvl = 14; maxLvl = 14;
loot = Generator.Category.ARMOR; loot = Generator.Category.ARMOR;
lootChance = 0.2f; //by default, see rollToDropLoot() lootChance = 0.2f; //by default, see lootChance()
properties.add(Property.UNDEAD); properties.add(Property.UNDEAD);
@ -137,15 +137,14 @@ public class Guard extends Mob {
} }
@Override @Override
public void rollToDropLoot() { public float lootChance() {
//each drop makes future drops 1/2 as likely //each drop makes future drops 1/2 as likely
// so loot chance looks like: 1/5, 1/10, 1/20, 1/40, etc. // so loot chance looks like: 1/5, 1/10, 1/20, 1/40, etc.
lootChance *= Math.pow(1/2f, Dungeon.LimitedDrops.GUARD_ARM.count); return super.lootChance() * (float)Math.pow(1/2f, Dungeon.LimitedDrops.GUARD_ARM.count);
super.rollToDropLoot();
} }
@Override @Override
protected Item createLoot() { public Item createLoot() {
Dungeon.LimitedDrops.GUARD_ARM.count++; Dungeon.LimitedDrops.GUARD_ARM.count++;
return super.createLoot(); return super.createLoot();
} }

View File

@ -52,7 +52,7 @@ import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle
import com.shatteredpixel.shatteredpixeldungeon.items.Generator; import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Gold; import com.shatteredpixel.shatteredpixeldungeon.items.Gold;
import com.shatteredpixel.shatteredpixeldungeon.items.Item; 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.artifacts.TimekeepersHourglass;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring; import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfWealth; import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfWealth;
@ -631,10 +631,14 @@ public abstract class Mob extends Char {
return damage; 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 return enemy == Dungeon.hero
&& (enemy.invisible > 0 || !enemySeen) && (enemy.invisible > 0 || !enemySeen)
&& ((Hero)enemy).canSurpriseAttack(); && (!attacking || ((Hero)enemy).canSurpriseAttack());
} }
public void aggro( Char ch ) { public void aggro( Char ch ) {
@ -726,18 +730,26 @@ public abstract class Mob extends Char {
} }
} }
public float lootChance(){
float lootChance = this.lootChance;
lootChance *= RingOfWealth.dropChanceMultiplier( Dungeon.hero );
return lootChance;
}
public void rollToDropLoot(){ public void rollToDropLoot(){
if (Dungeon.hero.lvl > maxLvl + 2) return; if (Dungeon.hero.lvl > maxLvl + 2) return;
float lootChance = this.lootChance; MasterThievesArmband.StolenTracker stolen = buff(MasterThievesArmband.StolenTracker.class);
lootChance *= RingOfWealth.dropChanceMultiplier( Dungeon.hero ); if (stolen == null || !stolen.itemWasStolen()) {
if (Random.Float() < lootChance()) {
if (Random.Float() < lootChance) {
Item loot = createLoot(); Item loot = createLoot();
if (loot != null) { if (loot != null) {
Dungeon.level.drop(loot, pos).sprite.drop(); Dungeon.level.drop(loot, pos).sprite.drop();
} }
} }
}
//ring of wealth logic //ring of wealth logic
if (Ring.getBuffedBonus(Dungeon.hero, RingOfWealth.Wealth.class) > 0) { if (Ring.getBuffedBonus(Dungeon.hero, RingOfWealth.Wealth.class) > 0) {
@ -777,7 +789,7 @@ public abstract class Mob extends Char {
protected float lootChance = 0; protected float lootChance = 0;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected Item createLoot() { public Item createLoot() {
Item item; Item item;
if (loot instanceof Generator.Category) { if (loot instanceof Generator.Category) {

View File

@ -28,7 +28,6 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Adrenaline;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AllyBuff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AllyBuff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ChampionEnemy; 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.Beam;
import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing; import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
@ -54,7 +53,7 @@ public class Necromancer extends Mob {
maxLvl = 14; maxLvl = 14;
loot = new PotionOfHealing(); loot = new PotionOfHealing();
lootChance = 0.2f; //see createloot lootChance = 0.2f; //see lootChance()
properties.add(Property.UNDEAD); properties.add(Property.UNDEAD);
@ -84,13 +83,12 @@ public class Necromancer extends Mob {
} }
@Override @Override
public void rollToDropLoot() { public float lootChance() {
lootChance *= ((6f - Dungeon.LimitedDrops.NECRO_HP.count) / 6f); return super.lootChance() * ((6f - Dungeon.LimitedDrops.NECRO_HP.count) / 6f);
super.rollToDropLoot();
} }
@Override @Override
protected Item createLoot(){ public Item createLoot(){
Dungeon.LimitedDrops.NECRO_HP.count++; Dungeon.LimitedDrops.NECRO_HP.count++;
return super.createLoot(); return super.createLoot();
} }

View File

@ -89,8 +89,8 @@ public class Piranha extends Mob {
} }
@Override @Override
public boolean surprisedBy(Char enemy) { public boolean surprisedBy(Char enemy, boolean attacking) {
if (enemy == Dungeon.hero && ((Hero)enemy).canSurpriseAttack()){ if (enemy == Dungeon.hero && (!attacking || ((Hero)enemy).canSurpriseAttack())){
if (fieldOfView == null || fieldOfView.length != Dungeon.level.length()){ if (fieldOfView == null || fieldOfView.length != Dungeon.level.length()){
fieldOfView = new boolean[Dungeon.level.length()]; fieldOfView = new boolean[Dungeon.level.length()];
Dungeon.level.updateFieldOfView( this, fieldOfView ); Dungeon.level.updateFieldOfView( this, fieldOfView );

View File

@ -103,7 +103,7 @@ public class Scorpio extends Mob {
} }
@Override @Override
protected Item createLoot() { public Item createLoot() {
Class<?extends Potion> loot; Class<?extends Potion> loot;
do{ do{
loot = (Class<? extends Potion>) Random.oneOf(Generator.Category.POTION.classes); loot = (Class<? extends Potion>) Random.oneOf(Generator.Category.POTION.classes);

View File

@ -48,7 +48,7 @@ public abstract class Shaman extends Mob {
maxLvl = 16; maxLvl = 16;
loot = Generator.Category.WAND; loot = Generator.Category.WAND;
lootChance = 0.03f; //initially, see rollToDropLoot lootChance = 0.03f; //initially, see lootChance()
} }
@Override @Override
@ -72,15 +72,14 @@ public abstract class Shaman extends Mob {
} }
@Override @Override
public void rollToDropLoot() { public float lootChance() {
//each drop makes future drops 1/3 as likely //each drop makes future drops 1/3 as likely
// so loot chance looks like: 1/33, 1/100, 1/300, 1/900, etc. // so loot chance looks like: 1/33, 1/100, 1/300, 1/900, etc.
lootChance *= Math.pow(1/3f, Dungeon.LimitedDrops.SHAMAN_WAND.count); return super.lootChance() * (float)Math.pow(1/3f, Dungeon.LimitedDrops.SHAMAN_WAND.count);
super.rollToDropLoot();
} }
@Override @Override
protected Item createLoot() { public Item createLoot() {
Dungeon.LimitedDrops.SHAMAN_WAND.count++; Dungeon.LimitedDrops.SHAMAN_WAND.count++;
return super.createLoot(); return super.createLoot();
} }

View File

@ -46,7 +46,7 @@ public class Skeleton extends Mob {
maxLvl = 10; maxLvl = 10;
loot = Generator.Category.WEAPON; 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.UNDEAD);
properties.add(Property.INORGANIC); properties.add(Property.INORGANIC);
@ -88,15 +88,14 @@ public class Skeleton extends Mob {
} }
@Override @Override
public void rollToDropLoot() { public float lootChance() {
//each drop makes future drops 1/2 as likely //each drop makes future drops 1/2 as likely
// so loot chance looks like: 1/6, 1/12, 1/24, 1/48, etc. // so loot chance looks like: 1/6, 1/12, 1/24, 1/48, etc.
lootChance *= Math.pow(1/2f, Dungeon.LimitedDrops.SKELE_WEP.count); return super.lootChance() * (float)Math.pow(1/2f, Dungeon.LimitedDrops.SKELE_WEP.count);
super.rollToDropLoot();
} }
@Override @Override
protected Item createLoot() { public Item createLoot() {
Dungeon.LimitedDrops.SKELE_WEP.count++; Dungeon.LimitedDrops.SKELE_WEP.count++;
return super.createLoot(); return super.createLoot();
} }

View File

@ -41,7 +41,7 @@ public class Slime extends Mob {
EXP = 4; EXP = 4;
maxLvl = 9; maxLvl = 9;
lootChance = 0.2f; //by default, see rollToDropLoot() lootChance = 0.2f; //by default, see lootChance()
} }
@Override @Override
@ -64,15 +64,14 @@ public class Slime extends Mob {
} }
@Override @Override
public void rollToDropLoot() { public float lootChance(){
//each drop makes future drops 1/3 as likely //each drop makes future drops 1/3 as likely
// so loot chance looks like: 1/5, 1/15, 1/45, 1/135, etc. // so loot chance looks like: 1/5, 1/15, 1/45, 1/135, etc.
lootChance *= Math.pow(1/3f, Dungeon.LimitedDrops.SLIME_WEP.count); return super.lootChance() * (float)Math.pow(1/3f, Dungeon.LimitedDrops.SLIME_WEP.count);
super.rollToDropLoot();
} }
@Override @Override
protected Item createLoot() { public Item createLoot() {
Dungeon.LimitedDrops.SLIME_WEP.count++; Dungeon.LimitedDrops.SLIME_WEP.count++;
Generator.Category c = Generator.Category.WEP_T2; Generator.Category c = Generator.Category.WEP_T2;
MeleeWeapon w = (MeleeWeapon) Reflection.newInstance(c.classes[Random.chances(c.probs)]); MeleeWeapon w = (MeleeWeapon) Reflection.newInstance(c.classes[Random.chances(c.probs)]);

View File

@ -163,7 +163,7 @@ public class Succubus extends Mob {
} }
@Override @Override
protected Item createLoot() { public Item createLoot() {
Class<?extends Scroll> loot; Class<?extends Scroll> loot;
do{ do{
loot = (Class<? extends Scroll>) Random.oneOf(Generator.Category.SCROLL.classes); loot = (Class<? extends Scroll>) Random.oneOf(Generator.Category.SCROLL.classes);

View File

@ -28,7 +28,6 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AllyBuff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ChampionEnemy; 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.actors.buffs.Poison;
import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing; import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing;
import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.Item;
@ -54,7 +53,7 @@ public class Swarm extends Mob {
flying = true; flying = true;
loot = new PotionOfHealing(); loot = new PotionOfHealing();
lootChance = 0.1667f; //by default, see rollToDropLoot() lootChance = 0.1667f; //by default, see lootChance()
} }
private static final float SPLIT_DELAY = 1f; private static final float SPLIT_DELAY = 1f;
@ -139,14 +138,13 @@ public class Swarm extends Mob {
} }
@Override @Override
public void rollToDropLoot() { public float lootChance() {
lootChance = 1f/(6 * (generation+1) ); lootChance = 1f/(6 * (generation+1) );
lootChance *= (5f - Dungeon.LimitedDrops.SWARM_HP.count) / 5f; return super.lootChance() * (5f - Dungeon.LimitedDrops.SWARM_HP.count) / 5f;
super.rollToDropLoot();
} }
@Override @Override
protected Item createLoot(){ public Item createLoot(){
Dungeon.LimitedDrops.SWARM_HP.count++; Dungeon.LimitedDrops.SWARM_HP.count++;
return super.createLoot(); return super.createLoot();
} }

View File

@ -24,7 +24,6 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AllyBuff; 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.Dread;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
@ -55,7 +54,7 @@ public class Thief extends Mob {
maxLvl = 11; maxLvl = 11;
loot = Random.oneOf(Generator.Category.RING, Generator.Category.ARTIFACT); loot = Random.oneOf(Generator.Category.RING, Generator.Category.ARTIFACT);
lootChance = 0.03f; //initially, see rollToDropLoot lootChance = 0.03f; //initially, see lootChance()
WANDERING = new Wandering(); WANDERING = new Wandering();
FLEEING = new Fleeing(); FLEEING = new Fleeing();
@ -93,6 +92,13 @@ public class Thief extends Mob {
return super.attackDelay()*0.5f; 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 @Override
public void rollToDropLoot() { public void rollToDropLoot() {
if (item != null) { if (item != null) {
@ -101,14 +107,11 @@ public class Thief extends Mob {
if (item instanceof Honeypot.ShatteredPot) ((Honeypot.ShatteredPot)item).dropPot( this, pos ); if (item instanceof Honeypot.ShatteredPot) ((Honeypot.ShatteredPot)item).dropPot( this, pos );
item = null; 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(); super.rollToDropLoot();
} }
@Override @Override
protected Item createLoot() { public Item createLoot() {
Dungeon.LimitedDrops.THEIF_MISC.count++; Dungeon.LimitedDrops.THEIF_MISC.count++;
return super.createLoot(); return super.createLoot();
} }

View File

@ -134,7 +134,7 @@ public class Warlock extends Mob implements Callback {
public Item createLoot(){ public Item createLoot(){
// 1/6 chance for healing, scaling to 0 over 8 drops // 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++; Dungeon.LimitedDrops.WARLOCK_HP.count++;
return new PotionOfHealing(); return new PotionOfHealing();
} else { } else {

View File

@ -64,10 +64,6 @@ public class Gold extends Item {
Statistics.goldCollected += quantity; Statistics.goldCollected += quantity;
Badges.validateGoldCollected(); Badges.validateGoldCollected();
MasterThievesArmband.Thievery thievery = hero.buff(MasterThievesArmband.Thievery.class);
if (thievery != null)
thievery.collect(quantity);
GameScene.pickUp( this, pos ); GameScene.pickUp( this, pos );
hero.sprite.showStatus( CharSprite.NEUTRAL, TXT_VALUE, quantity ); hero.sprite.showStatus( CharSprite.NEUTRAL, TXT_VALUE, quantity );
hero.spendAndNext( TIME_TO_PICK_UP ); hero.spendAndNext( TIME_TO_PICK_UP );

View File

@ -21,14 +21,36 @@
package com.shatteredpixel.shatteredpixeldungeon.items.artifacts; package com.shatteredpixel.shatteredpixeldungeon.items.artifacts;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; 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.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; 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.items.rings.RingOfEnergy;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; 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.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 com.watabou.utils.Random;
import java.util.ArrayList;
public class MasterThievesArmband extends Artifact { public class MasterThievesArmband extends Artifact {
{ {
@ -37,9 +59,132 @@ public class MasterThievesArmband extends Artifact {
levelCap = 10; levelCap = 10;
charge = 0; 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<String> actions(Hero hero) {
ArrayList<String> 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 @Override
protected ArtifactBuff passiveBuff() { protected ArtifactBuff passiveBuff() {
@ -48,8 +193,22 @@ public class MasterThievesArmband extends Artifact {
@Override @Override
public void charge(Hero target, float amount) { public void charge(Hero target, float amount) {
charge += Math.round(10*amount); partialCharge += 0.1f * amount;
partialCharge = Math.min(partialCharge, chargeCap - charge);
while (partialCharge > 1f){
charge++;
partialCharge--;
updateQuickslot(); updateQuickslot();
if (charge == chargeCap){
GLog.p( Messages.get(MasterThievesArmband.class, "full") );
}
}
}
@Override
public Item upgrade() {
chargeCap = 5 + (level()+1)/2;
return super.upgrade();
} }
@Override @Override
@ -64,67 +223,90 @@ public class MasterThievesArmband extends Artifact {
} }
} }
return desc; return desc;
} }
public class Thievery extends ArtifactBuff{ @Override
public void collect(int gold){ public void restoreFromBundle(Bundle bundle) {
if (!cursed) { super.restoreFromBundle(bundle);
charge += gold/2 * RingOfEnergy.artifactChargeMultiplier(target); //conversion for old armband on pre-1.2.0 saves
if (exp > Math.round(10 + level()*3.33f)){
exp = 0;
} }
} }
@Override public class Thievery extends ArtifactBuff {
public void detach() {
charge *= 0.95;
super.detach();
}
@Override @Override
public boolean act() { public boolean act() {
if (cursed) { if (cursed && Dungeon.gold > 0 && Random.Int(5) == 0){
if (Dungeon.gold > 0 && Random.Int(6) == 0){
Dungeon.gold--; Dungeon.gold--;
} }
spend(TICK); spend(TICK);
return true; return true;
} else { }
return super.act();
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;
} }
} }
public boolean steal(int value){
if (value <= charge){
charge -= value;
exp += value;
} else { } else {
float chance = stealChance(value); partialCharge = 0f;
if (Random.Float() > chance) }
}
public boolean steal(Item item){
int chargesUsed = chargesToUse(item);
float stealChance = stealChance(item);
if (Random.Float() > stealChance){
return false; return false;
else { } else {
if (chance <= 1) charge -= chargesUsed;
charge = 0; exp += 4 * chargesUsed;
else GLog.i(Messages.get(MasterThievesArmband.class, "stole_item", item.name()));
//removes the charge it took you to reach 100%
charge -= charge/chance;
exp += value;
}
}
Talent.onArtifactUsed(Dungeon.hero); Talent.onArtifactUsed(Dungeon.hero);
while(exp >= (250 + 50*level()) && level() < levelCap) { while (exp >= (10 + Math.round(3.33f * level())) && level() < levelCap) {
exp -= (250 + 50*level()); exp -= 10 + Math.round(3.33f * level());
GLog.p(Messages.get(MasterThievesArmband.class, "level_up"));
upgrade(); upgrade();
} }
return true; return true;
} }
}
public float stealChance(int value){ public float stealChance(Item item){
//get lvl*50 gold or lvl*3.33% item value of free charge, whichever is less. int chargesUsed = chargesToUse(item);
int chargeBonus = Math.min(level()*50, (value*level())/30); float val = chargesUsed * (10 + level()/2f);
return (((float)charge + chargeBonus)/value); 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;
} }
} }
} }

View File

@ -129,12 +129,13 @@ public class WndTradeItem extends WndInfoItem {
pos = btnBuy.bottom(); pos = btnBuy.bottom();
final MasterThievesArmband.Thievery thievery = Dungeon.hero.buff(MasterThievesArmband.Thievery.class); final MasterThievesArmband.Thievery thievery = Dungeon.hero.buff(MasterThievesArmband.Thievery.class);
if (thievery != null && !thievery.isCursed()) { if (thievery != null && !thievery.isCursed() && thievery.chargesToUse(item) > 0) {
final float chance = thievery.stealChance(price); final float chance = thievery.stealChance(item);
RedButton btnSteal = new RedButton(Messages.get(this, "steal", Math.min(100, (int) (chance * 100)))) { final int chargesToUse = thievery.chargesToUse(item);
RedButton btnSteal = new RedButton(Messages.get(this, "steal", Math.min(100, (int) (chance * 100)), chargesToUse), 6) {
@Override @Override
protected void onClick() { protected void onClick() {
if (thievery.steal(price)) { if (thievery.steal(item)) {
Hero hero = Dungeon.hero; Hero hero = Dungeon.hero;
Item item = heap.pickUp(); Item item = heap.pickUp();
hide(); hide();