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.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

View File

@ -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

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.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;
@ -1521,6 +1522,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);

View File

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

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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){

View File

@ -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:

View File

@ -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);

View File

@ -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){

View File

@ -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();
}

View File

@ -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 ) {
@ -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(){
if (Dungeon.hero.lvl > maxLvl + 2) return;
float lootChance = this.lootChance;
lootChance *= RingOfWealth.dropChanceMultiplier( Dungeon.hero );
if (Random.Float() < lootChance) {
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();
}
}
}
//ring of wealth logic
if (Ring.getBuffedBonus(Dungeon.hero, RingOfWealth.Wealth.class) > 0) {
@ -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) {

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.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();
}

View File

@ -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 );

View File

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

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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)]);

View File

@ -163,7 +163,7 @@ public class Succubus extends Mob {
}
@Override
protected Item createLoot() {
public Item createLoot() {
Class<?extends Scroll> loot;
do{
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.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;
@ -139,14 +138,13 @@ public class Swarm extends Mob {
}
@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();
}

View File

@ -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();
@ -93,6 +92,13 @@ public class Thief extends Mob {
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();
}

View File

@ -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 {

View File

@ -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 );

View File

@ -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<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
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);
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
@ -64,67 +223,90 @@ public class MasterThievesArmband extends Artifact {
}
}
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;
}
}
@Override
public void detach() {
charge *= 0.95;
super.detach();
}
public class Thievery extends ArtifactBuff {
@Override
public boolean act() {
if (cursed) {
if (Dungeon.gold > 0 && Random.Int(6) == 0){
if (cursed && Dungeon.gold > 0 && Random.Int(5) == 0){
Dungeon.gold--;
}
spend(TICK);
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 {
float chance = stealChance(value);
if (Random.Float() > chance)
partialCharge = 0f;
}
}
public boolean steal(Item item){
int chargesUsed = chargesToUse(item);
float stealChance = stealChance(item);
if (Random.Float() > stealChance){
return false;
else {
if (chance <= 1)
charge = 0;
else
//removes the charge it took you to reach 100%
charge -= charge/chance;
exp += value;
}
}
} else {
charge -= chargesUsed;
exp += 4 * chargesUsed;
GLog.i(Messages.get(MasterThievesArmband.class, "stole_item", item.name()));
Talent.onArtifactUsed(Dungeon.hero);
while(exp >= (250 + 50*level()) && level() < levelCap) {
exp -= (250 + 50*level());
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(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 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;
}
}
}

View File

@ -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();