diff --git a/core/src/main/assets/effects/text_icons.png b/core/src/main/assets/effects/text_icons.png index e7714a2b1..abcdafc64 100644 Binary files a/core/src/main/assets/effects/text_icons.png and b/core/src/main/assets/effects/text_icons.png differ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java index 28963c78d..efe37ec6d 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java @@ -590,19 +590,19 @@ public abstract class Char extends Actor { } else { if (enemy.sprite != null){ - if (tuftDodged){ + if (hitMissIcon != -1){ //dooking is a playful sound Ferrets can make, like low pitched chirping // I doubt this will translate, so it's only in English - if (Messages.lang() == Languages.ENGLISH && Random.Int(10) == 0) { - enemy.sprite.showStatusWithIcon(CharSprite.NEUTRAL, "dooked", FloatingText.TUFT); + if (hitMissIcon == FloatingText.MISS_TUFT && Messages.lang() == Languages.ENGLISH && Random.Int(10) == 0) { + enemy.sprite.showStatusWithIcon(CharSprite.NEUTRAL, "dooked", hitMissIcon); } else { - enemy.sprite.showStatusWithIcon(CharSprite.NEUTRAL, enemy.defenseVerb(), FloatingText.TUFT); + enemy.sprite.showStatusWithIcon(CharSprite.NEUTRAL, enemy.defenseVerb(), hitMissIcon); } + hitMissIcon = -1; } else { enemy.sprite.showStatus(CharSprite.NEUTRAL, enemy.defenseVerb()); } } - tuftDodged = false; if (visibleFight) { //TODO enemy.defenseSound? currently miss plays for monks/crab even when they parry Sample.INSTANCE.play(Assets.Sounds.MISS); @@ -611,6 +611,7 @@ public abstract class Char extends Actor { return false; } + } public static int INFINITE_ACCURACY = 1_000_000; @@ -640,8 +641,10 @@ public abstract class Char extends Actor { //if accuracy or evasion are large enough, treat them as infinite. //note that infinite evasion beats infinite accuracy if (defStat >= INFINITE_EVASION){ + hitMissIcon = FloatingText.getMissReasonIcon(attacker, acuStat, defender, INFINITE_EVASION); return false; } else if (acuStat >= INFINITE_ACCURACY){ + hitMissIcon = FloatingText.getHitReasonIcon(attacker, INFINITE_ACCURACY, defender, defStat); return true; } @@ -675,17 +678,18 @@ public abstract class Char extends Actor { // + 3%/5% defRoll *= 1.01f + 0.02f*Dungeon.hero.pointsInTalent(Talent.BLESS); } - - if (defRoll < acuRoll && (defRoll*FerretTuft.evasionMultiplier()) >= acuRoll){ - tuftDodged = true; - } defRoll *= FerretTuft.evasionMultiplier(); - return acuRoll >= defRoll; + if (acuRoll >= defRoll){ + hitMissIcon = FloatingText.getHitReasonIcon(attacker, acuRoll, defender, defRoll); + return true; + } else { + hitMissIcon = FloatingText.getMissReasonIcon(attacker, acuRoll, defender, defRoll); + return false; + } } - //TODO this is messy and hacky atm, should consider standardizing this so we can have many 'dodge reasons' - private static boolean tuftDodged = false; + private static int hitMissIcon = -1; public int attackSkill( Char target ) { return 0; @@ -1011,6 +1015,12 @@ public abstract class Char extends Actor { if (src instanceof Corruption) icon = FloatingText.CORRUPTION; if (src instanceof AscensionChallenge) icon = FloatingText.AMULET; + if ((icon == FloatingText.PHYS_DMG || icon == FloatingText.PHYS_DMG_NO_BLOCK) && hitMissIcon != -1){ + if (icon == FloatingText.PHYS_DMG_NO_BLOCK) hitMissIcon += 18; //extra row + icon = hitMissIcon; + } + hitMissIcon = -1; + sprite.showStatusWithIcon(CharSprite.NEGATIVE, Integer.toString(dmg + shielded), icon); } 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 e4a51a178..2cabd4cb1 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 @@ -484,6 +484,20 @@ public class Hero extends Char { return hit; } + @Override + public boolean attack(Char enemy, float dmgMulti, float dmgBonus, float accMulti) { + boolean result = super.attack(enemy, dmgMulti, dmgBonus, accMulti); + if (!(belongings.attackingWeapon() instanceof MissileWeapon)){ + if (buff(Talent.PreciseAssaultTracker.class) != null){ + buff(Talent.PreciseAssaultTracker.class).detach(); + } else if (buff(Talent.LiquidAgilACCTracker.class) != null + && buff(Talent.LiquidAgilACCTracker.class).uses <= 0){ + buff(Talent.LiquidAgilACCTracker.class).detach(); + } + } + return result; + } + @Override public int attackSkill( Char target ) { KindOfWeapon wep = belongings.attackingWeapon(); @@ -517,17 +531,17 @@ public class Hero extends Char { case 3: accuracy *= Float.POSITIVE_INFINITY; break; } - buff(Talent.PreciseAssaultTracker.class).detach(); } else if (buff(Talent.LiquidAgilACCTracker.class) != null){ // 3x/inf. ACC, depending on talent level accuracy *= pointsInTalent(Talent.LIQUID_AGILITY) == 2 ? Float.POSITIVE_INFINITY : 3f; Talent.LiquidAgilACCTracker buff = buff(Talent.LiquidAgilACCTracker.class); buff.uses--; - if (buff.uses <= 0) { - buff.detach(); - } } } + } else { + if (buff(Momentum.class) != null && buff(Momentum.class).freerunning()){ + accuracy *= 1f + pointsInTalent(Talent.PROJECTILE_MOMENTUM)/3f; + } } if (buff(Scimitar.SwordDance.class) != null){ @@ -535,9 +549,9 @@ public class Hero extends Char { } if (!RingOfForce.fightingUnarmed(this)) { - return (int)(attackSkill * accuracy * wep.accuracyFactor( this, target )); + return Math.max(1, Math.round(attackSkill * accuracy * wep.accuracyFactor( this, target ))); } else { - return (int)(attackSkill * accuracy); + return Math.max(1, Math.round(attackSkill * accuracy)); } } @@ -579,7 +593,7 @@ public class Hero extends Char { evasion = belongings.armor().evasionFactor(this, evasion); } - return Math.round(evasion); + return Math.max(1, Math.round(evasion)); } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Statue.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Statue.java index ede942e3a..fbedf8f3a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Statue.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Statue.java @@ -69,6 +69,10 @@ public class Statue extends Mob { weapon.cursed = false; weapon.enchant( Enchantment.random() ); } + + public Weapon weapon(){ + return weapon; + } private static final String WEAPON = "weapon"; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/effects/FloatingText.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/effects/FloatingText.java index 88e11186c..13808e77b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/effects/FloatingText.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/effects/FloatingText.java @@ -22,6 +22,31 @@ package com.shatteredpixel.shatteredpixeldungeon.effects; import com.shatteredpixel.shatteredpixeldungeon.Assets; +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.Char; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Bless; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ChampionEnemy; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Daze; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Hex; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Momentum; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.GuidingLight; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.ArmoredStatue; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Statue; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.MirrorImage; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.PrismaticImage; +import com.shatteredpixel.shatteredpixeldungeon.items.KindOfWeapon; +import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor; +import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.DriedRose; +import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfAccuracy; +import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfEvasion; +import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.FerretTuft; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Quarterstaff; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Scimitar; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene; import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap; @@ -35,14 +60,18 @@ import com.watabou.utils.Callback; import com.watabou.utils.SparseArray; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; public class FloatingText extends RenderedTextBlock { private static final float LIFESPAN = 1f; private static final float DISTANCE = DungeonTilemap.SIZE; - public static final int ICON_SIZE = 7; - public static TextureFilm iconFilm = new TextureFilm( Assets.Effects.TEXT_ICONS, ICON_SIZE, ICON_SIZE ); + public static final int ICON_WIDTH = 7; + public static final int ICON_HEIGHT = 8; + public static TextureFilm iconFilm = new TextureFilm( Assets.Effects.TEXT_ICONS, ICON_WIDTH, ICON_HEIGHT ); public static int NO_ICON = -1; @@ -77,8 +106,34 @@ public class FloatingText extends RenderedTextBlock { public static int GOLD = 23; public static int ENERGY = 24; - //dodge icons - public static int TUFT = 26; + //hit reason icons + public static int HIT_WEP = 36; + public static int HIT_ARM = 37; + public static int HIT_BLS = 38; + public static int HIT_HEX = 39; + public static int HIT_DAZE = 40; + public static int HIT_ACC = 41; + public static int HIT_EVA = 42; + public static int HIT_LIQ = 43; + public static int HIT_DANCE = 44; + public static int HIT_SUPR = 45; + public static int HIT_PRES = 46; + public static int HIT_MOMEN = 47; + + //extra row for hit icons that are armor-piercing + + //miss reason icons + public static int MISS_WEP = 72; + public static int MISS_ARM = 73; + public static int MISS_BLS = 74; + public static int MISS_HEX = 75; + public static int MISS_DAZE = 76; + public static int MISS_ACC = 77; + public static int MISS_EVA = 78; + public static int MISS_LIQ = 79; + public static int MISS_DEF = 80; + public static int MISS_TUFT = 81; + public static int MISS_RUN = 82; private Image icon; private boolean iconLeft; @@ -247,4 +302,201 @@ public class FloatingText extends RenderedTextBlock { stack.add(txt); } } + + // *** These are for custom icons that show impact of effects on top of base acc/eve *** + // This logic is honestly a mess. Rather than tracking these modifiers as we + // perform acc/eva calculation, we check and 'unwind' the modifiers from the final rolls. + // This results in a lot of duplicated logic/numbers and awkward workarounds. + // It does work though... mostly. + + public static int getHitReasonIcon(Char attacker, float accRoll, Char defender, float defRoll){ + HashMap hitReasons = new HashMap<>(); + + //go through some garunteed hit interactions first + if (defRoll == 0 && defender.buff(GuidingLight.Illuminated.class) != null){ + return HIT_BLS; + } + if (accRoll == Char.INFINITE_ACCURACY && attacker.invisible > 0){ + return HIT_SUPR; + } + if (defRoll == 0 && defender instanceof Mob && ((Mob) defender).surprisedBy(attacker)){ + return HIT_SUPR; + } + if (accRoll == Char.INFINITE_ACCURACY + && attacker.buff(Talent.PreciseAssaultTracker.class) != null){ + return HIT_PRES; + } + if (accRoll == Char.INFINITE_ACCURACY + && attacker.buff(Talent.LiquidAgilACCTracker.class) != null){ + return HIT_LIQ; + } + + KindOfWeapon wep = null; + if (attacker instanceof Hero) wep = ((Hero) attacker).belongings.attackingWeapon(); + if (attacker instanceof MirrorImage) wep = Dungeon.hero.belongings.weapon(); + if (attacker instanceof Statue) wep = ((Statue)attacker).weapon(); + if (attacker instanceof DriedRose.GhostHero) wep = ((DriedRose.GhostHero)attacker).weapon(); + + Armor arm = null; + if (defender instanceof Hero) arm = ((Hero) defender).belongings.armor(); + if (defender instanceof PrismaticImage) arm = Dungeon.hero.belongings.armor(); + if (defender instanceof ArmoredStatue) arm = ((ArmoredStatue)defender).armor(); + if (defender instanceof DriedRose.GhostHero) arm = ((DriedRose.GhostHero)defender).armor(); + + //accuracy boosts (always > 1) + if (wep != null && wep.accuracyFactor(attacker, defender) > 1){ + hitReasons.put( HIT_WEP, wep.accuracyFactor(attacker, defender)); + } + float blessBoost = 1; //a few different sources contribute to this icon + if (attacker.buff(ChampionEnemy.class) != null + && attacker.buff(ChampionEnemy.class).evasionAndAccuracyFactor() > 1){ + blessBoost *= attacker.buff(ChampionEnemy.class).evasionAndAccuracyFactor(); + } + if (attacker.buff(Bless.class) != null) blessBoost *= 1.25f; + if (Dungeon.hero.heroClass != HeroClass.CLERIC + && Dungeon.hero.hasTalent(Talent.BLESS) + && attacker.alignment == Char.Alignment.ALLY){ + // + 3%/5% + blessBoost *= 1.01f + 0.02f*Dungeon.hero.pointsInTalent(Talent.BLESS); + } + if (blessBoost > 1f) hitReasons.put(HIT_BLS, blessBoost); + if (RingOfAccuracy.accuracyMultiplier(attacker) > 1) hitReasons.put(HIT_ACC, RingOfAccuracy.accuracyMultiplier(attacker)); + if (attacker.buff(Scimitar.SwordDance.class) != null) hitReasons.put(HIT_DANCE, 1.5f); + if (!(wep instanceof MissileWeapon)) { + if (attacker.buff(Talent.PreciseAssaultTracker.class) != null){ + hitReasons.put(HIT_PRES, Dungeon.hero.pointsInTalent(Talent.PRECISE_ASSAULT) == 2 ? 5f : 2f); + } else if (attacker.buff(Talent.LiquidAgilACCTracker.class) != null) { + hitReasons.put(HIT_LIQ, 3f); + } + } else { + if (attacker.buff(Momentum.class) != null + && attacker.buff(Momentum.class).freerunning() + && ((Hero)attacker).hasTalent(Talent.PROJECTILE_MOMENTUM)) { + hitReasons.put(HIT_MOMEN, 1f + ((Hero) attacker).pointsInTalent(Talent.PROJECTILE_MOMENTUM) / 3f); + } + } + + //evasion reductions (always < 1) + if (defender.buff(Hex.class) != null) hitReasons.put(HIT_HEX, 0.8f); + if (defender.buff(Daze.class) != null) hitReasons.put(HIT_DAZE, 0.5f); + if (RingOfEvasion.evasionMultiplier(defender) < 1) hitReasons.put(HIT_EVA, RingOfEvasion.evasionMultiplier(defender)); + if (arm != null && arm.evasionFactor(defender, 100) < 100) { + //we express armor's normally flat evasion boost as a %, yes this is very awkward + Armor.testingNoArmDefSkill = true; + int baseDef = defender.defenseSkill(attacker); + Armor.testingNoArmDefSkill = false; + hitReasons.put(HIT_ARM, defender.defenseSkill(attacker)/(float)baseDef); + } + //hero specifically gets 1/2 eva when stunned, for mobs its a garunteed hit + if (defender.paralysed > 0) { + if (defender instanceof Hero) hitReasons.put(HIT_SUPR, 0.5f); + else return HIT_SUPR; + } + + //sort from largest modifier to smallest one + ArrayList sortedReasons = new ArrayList<>(hitReasons.keySet()); + Collections.sort(sortedReasons, new Comparator() { + @Override + public int compare(Integer a, Integer b) { + float a1 = hitReasons.get(a) >= 1f ? hitReasons.get(a) : 1 / hitReasons.get(a); + float b1 = hitReasons.get(b) >= 1f ? hitReasons.get(b) : 1 / hitReasons.get(b); + return (int)Math.signum(b1 - a1); + } + }); + + for (Integer reason : sortedReasons){ + if (hitReasons.get(reason) >= 1f) { + accRoll /= hitReasons.get(reason); + } else { + defRoll /= hitReasons.get(reason); + } + if (accRoll < defRoll){ + return reason; + } + } + return -1; + } + + public static int getMissReasonIcon(Char attacker, float accRoll, Char defender, float defRoll){ + HashMap missReasons = new HashMap<>(); + + //some guaranteed dodged first + if (defRoll == Char.INFINITE_EVASION && defender.buff(Talent.LiquidAgilEVATracker.class) != null){ + return MISS_LIQ; + } + + KindOfWeapon wep = null; + if (attacker instanceof Hero) wep = ((Hero) attacker).belongings.attackingWeapon(); + if (attacker instanceof MirrorImage) wep = Dungeon.hero.belongings.weapon(); + if (attacker instanceof Statue) wep = ((Statue)attacker).weapon(); + if (attacker instanceof DriedRose.GhostHero) wep = ((DriedRose.GhostHero)attacker).weapon(); + + Armor arm = null; + if (defender instanceof Hero) arm = ((Hero) defender).belongings.armor(); + if (defender instanceof PrismaticImage) arm = Dungeon.hero.belongings.armor(); + if (defender instanceof ArmoredStatue) arm = ((ArmoredStatue)defender).armor(); + if (defender instanceof DriedRose.GhostHero) arm = ((DriedRose.GhostHero)defender).armor(); + + //evasion boosts (always > 1) + float blessBoost = 1; //a few different sources contribute to this icon + if (defender.buff(ChampionEnemy.class) != null + && defender.buff(ChampionEnemy.class).evasionAndAccuracyFactor() > 1){ + blessBoost *= defender.buff(ChampionEnemy.class).evasionAndAccuracyFactor(); + } + if (defender.buff(Bless.class) != null) blessBoost *= 1.25f; + if (Dungeon.hero.heroClass != HeroClass.CLERIC + && Dungeon.hero.hasTalent(Talent.BLESS) + && defender.alignment == Char.Alignment.ALLY){ + // + 3%/5% + blessBoost *= 1.01f + 0.02f*Dungeon.hero.pointsInTalent(Talent.BLESS); + } + if (blessBoost > 1f) missReasons.put(MISS_BLS, blessBoost); + if (FerretTuft.evasionMultiplier() > 1) missReasons.put(MISS_TUFT, FerretTuft.evasionMultiplier()); + if (RingOfEvasion.evasionMultiplier(defender) > 1) missReasons.put(MISS_EVA, RingOfEvasion.evasionMultiplier(defender)); + if (defender.buff(Quarterstaff.DefensiveStance.class) != null) missReasons.put(MISS_DEF, 3f); + if (arm != null && arm.evasionFactor(defender, 100) > 100) { + //we express armor's normally flat evasion boost as a %, yes this is very awkward + Armor.testingNoArmDefSkill = true; + int baseDef = defender.defenseSkill(attacker); + Armor.testingNoArmDefSkill = false; + if (defender.buff(Momentum.class) != null){ + //this is cheating a little, as evasion aug gets wrapped into this too + missReasons.put(MISS_RUN, defender.defenseSkill(attacker) / (float) baseDef); + } else { + missReasons.put(MISS_ARM, defender.defenseSkill(attacker) / (float) baseDef); + } + } + if (defender.buff(Talent.LiquidAgilEVATracker.class) != null) missReasons.put(MISS_LIQ, 3f); + + //accuracy reductions (always < 1) + if (wep != null && wep.accuracyFactor(attacker, defender) < 1){ + missReasons.put( MISS_WEP, wep.accuracyFactor(attacker, defender)); + } + if (attacker.buff(Hex.class) != null) missReasons.put(MISS_HEX, 0.8f); + if (attacker.buff(Daze.class) != null) missReasons.put(MISS_DAZE, 0.5f); + if (RingOfAccuracy.accuracyMultiplier(attacker) < 1) missReasons.put(MISS_ACC, RingOfAccuracy.accuracyMultiplier(attacker)); + + //sort from largest modifier to smallest one + ArrayList sortedReasons = new ArrayList<>(missReasons.keySet()); + Collections.sort(sortedReasons, new Comparator() { + @Override + public int compare(Integer a, Integer b) { + float a1 = missReasons.get(a) >= 1f ? missReasons.get(a) : 1 / missReasons.get(a); + float b1 = missReasons.get(b) >= 1f ? missReasons.get(b) : 1 / missReasons.get(b); + return (int)Math.signum(b1 - a1); + } + }); + + for (Integer reason : sortedReasons){ + if (missReasons.get(reason) >= 1f) { + defRoll /= missReasons.get(reason); + } else { + accRoll /= missReasons.get(reason); + } + if (defRoll < accRoll){ + return reason; + } + } + return -1; + } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/Armor.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/Armor.java index ad3507a63..06c271bb1 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/Armor.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/Armor.java @@ -403,8 +403,13 @@ public class Armor extends EquipableItem { return lvl; } } + + //This exists so we can test what a char's base evasion would be without armor affecting it + //more ugly static vars yaaay~ + public static boolean testingNoArmDefSkill = false; public float evasionFactor( Char owner, float evasion ){ + if (testingNoArmDefSkill) return evasion; if (hasGlyph(Stone.class, owner) && !Stone.testingEvasion()){ return 0; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/DriedRose.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/DriedRose.java index 09cc3be00..bf0d5e7ed 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/DriedRose.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/DriedRose.java @@ -588,6 +588,16 @@ public class DriedRose extends Artifact { HT = 20 + 8*rose.level(); } + public Weapon weapon(){ + if (rose != null) return rose.weapon; + else return null; + } + + public Armor armor(){ + if (rose != null) return rose.armor; + else return null; + } + @Override protected boolean act() { updateRose(); @@ -611,8 +621,8 @@ public class DriedRose extends Artifact { //same accuracy as the hero. int acc = Dungeon.hero.lvl + 9; - if (rose != null && rose.weapon != null){ - acc *= rose.weapon.accuracyFactor( this, target ); + if (weapon() != null){ + acc *= weapon().accuracyFactor( this, target ); } return acc; @@ -621,22 +631,22 @@ public class DriedRose extends Artifact { @Override public float attackDelay() { float delay = super.attackDelay(); - if (rose != null && rose.weapon != null){ - delay *= rose.weapon.delayFactor(this); + if (weapon() != null){ + delay *= weapon().delayFactor(this); } return delay; } @Override protected boolean canAttack(Char enemy) { - return super.canAttack(enemy) || (rose != null && rose.weapon != null && rose.weapon.canReach(this, enemy.pos)); + return super.canAttack(enemy) || (weapon() != null && weapon().canReach(this, enemy.pos)); } @Override public int damageRoll() { int dmg = 0; - if (rose != null && rose.weapon != null){ - dmg += rose.weapon.damageRoll(this); + if (weapon() != null){ + dmg += weapon().damageRoll(this); } else { dmg += Random.NormalIntRange(0, 5); } @@ -647,13 +657,12 @@ public class DriedRose extends Artifact { @Override public int attackProc(Char enemy, int damage) { damage = super.attackProc(enemy, damage); - if (rose != null) { - if (rose.weapon != null) { - damage = rose.weapon.proc(this, enemy, damage); - if (!enemy.isAlive() && enemy == Dungeon.hero) { - Dungeon.fail(this); - GLog.n(Messages.capitalize(Messages.get(Char.class, "kill", name()))); - } + + if (weapon() != null) { + damage = weapon().proc(this, enemy, damage); + if (!enemy.isAlive() && enemy == Dungeon.hero) { + Dungeon.fail(this); + GLog.n(Messages.capitalize(Messages.get(Char.class, "kill", name()))); } } @@ -662,8 +671,8 @@ public class DriedRose extends Artifact { @Override public int defenseProc(Char enemy, int damage) { - if (rose != null && rose.armor != null) { - damage = rose.armor.proc( enemy, this, damage ); + if (armor() != null) { + damage = armor().proc( enemy, this, damage ); } return super.defenseProc(enemy, damage); } @@ -694,8 +703,8 @@ public class DriedRose extends Artifact { public int defenseSkill(Char enemy) { int defense = super.defenseSkill(enemy); - if (defense != 0 && rose != null && rose.armor != null ){ - defense = Math.round(rose.armor.evasionFactor( this, defense )); + if (defense != 0 && armor() != null ){ + defense = Math.round(armor().evasionFactor( this, defense )); } return defense; @@ -704,19 +713,19 @@ public class DriedRose extends Artifact { @Override public int drRoll() { int dr = super.drRoll(); - if (rose != null && rose.armor != null){ - dr += Random.NormalIntRange( rose.armor.DRMin(), rose.armor.DRMax()); + if (armor() != null){ + dr += Random.NormalIntRange( armor().DRMin(), armor().DRMax()); } - if (rose != null && rose.weapon != null){ - dr += Random.NormalIntRange( 0, rose.weapon.defenseFactor( this )); + if (weapon() != null){ + dr += Random.NormalIntRange( 0, weapon().defenseFactor( this )); } return dr; } @Override public int glyphLevel(Class cls) { - if (rose != null && rose.armor != null && rose.armor.hasGlyph(cls, this)){ - return Math.max(super.glyphLevel(cls), rose.armor.buffedLvl()); + if (armor() != null && armor().hasGlyph(cls, this)){ + return Math.max(super.glyphLevel(cls), armor().buffedLvl()); } else { return super.glyphLevel(cls); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/trinkets/FerretTuft.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/trinkets/FerretTuft.java index 977794fe2..b5a7e9e80 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/trinkets/FerretTuft.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/trinkets/FerretTuft.java @@ -24,6 +24,7 @@ package com.shatteredpixel.shatteredpixeldungeon.items.trinkets; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; +//🍋‍🟩 public class FerretTuft extends Trinket { { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/missiles/MissileWeapon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/missiles/MissileWeapon.java index e35a21f72..2e3e20ee9 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/missiles/MissileWeapon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/missiles/MissileWeapon.java @@ -197,9 +197,6 @@ abstract public class MissileWeapon extends Weapon { @Override public float accuracyFactor(Char owner, Char target) { float accFactor = super.accuracyFactor(owner, target); - if (owner instanceof Hero && owner.buff(Momentum.class) != null && owner.buff(Momentum.class).freerunning()){ - accFactor *= 1f + ((Hero) owner).pointsInTalent(Talent.PROJECTILE_MOMENTUM)/3f; - } accFactor *= adjacentAccFactor(owner, target);