diff --git a/core/src/main/assets/interfaces/buffs.png b/core/src/main/assets/interfaces/buffs.png index 16fdaa823..3f1f00d9a 100644 Binary files a/core/src/main/assets/interfaces/buffs.png and b/core/src/main/assets/interfaces/buffs.png differ diff --git a/core/src/main/assets/interfaces/large_buffs.png b/core/src/main/assets/interfaces/large_buffs.png index a0d7ad46f..b9fe51c04 100644 Binary files a/core/src/main/assets/interfaces/large_buffs.png and b/core/src/main/assets/interfaces/large_buffs.png differ diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index 61fb71c56..33b6541b7 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -900,7 +900,11 @@ items.rings.ringofevasion.desc=This ring quickens the wearer's reactions, making items.rings.ringofforce.name=ring of force items.rings.ringofforce.stats=When unarmed, at your current strength, this ring will deal _%1$d-%2$d damage._ With a weapon equipped, this ring will increase damage by _%3$d._ items.rings.ringofforce.typical_stats=When unarmed, at your current strength, typically this ring will deal _%1$d-%2$d damage._ With a weapon equipped, typically this ring will increase damage by _%3$d._ +items.rings.ringofforce.ability_name=brawler's stance +items.rings.ringofforce.ability_desc=The Duelist can adopt a _brawler's stance_ with a ring of force. While in this stance the Duelist's regular attacks will use her ring of force even with a weapon equipped. The ring will also inherit the weapon's enchantment and augmentation. Each regular attack in this stance consumes 1/3 of a weapon charge. items.rings.ringofforce.desc=This ring enhances the force of the wearer's melee blows. This extra power is fairly weak when wielding weapons, but an unarmed attack will be made much stronger. A cursed ring will instead weaken the wearer's blows. +items.rings.ringofforce$brawlersstance.name=brawler's stance +items.rings.ringofforce$brawlersstance.desc=While in this stance the Duelist's regular attacks will use an equipped ring of force even when she has a weapon equipped. The attack will still use the weapon's augmentation and enchantment however.\n\nEach regular attack in this stance consumes 1/4 of a weapon charge. You currently have enough charge for %d attacks.\n\nThis stance can be toggled on or off by using a ring of force. items.rings.ringoffuror.name=ring of furor items.rings.ringoffuror.stats=When worn, this ring will increase the speed of your attacks by _%s%%._ @@ -1494,6 +1498,8 @@ items.weapon.melee.battleaxe.desc=The enormous steel head of this battle axe put items.weapon.melee.crossbow.name=crossbow items.weapon.melee.crossbow.stats_desc=This weapon enhances the damage of thrown darts when equipped, and will even grant its enchantment to them. +items.weapon.melee.crossbow.ability_name=heavy blow +items.weapon.melee.crossbow.ability_desc=The Duelist can peform a _..._ with a crossbow. This attack causes the next fired dart to always hit and apply on-hit effects to all characters in a 5x5 tile radius. items.weapon.melee.crossbow.desc=A fairly intricate weapon which shoots bolts at exceptional speeds. While it isn't designed for it, this crossbow's heft and sturdy construction make it a decent melee weapon as well. items.weapon.melee.dagger.name=dagger 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 27a611d99..42037aeb9 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 @@ -397,7 +397,7 @@ public class Hero extends Char { @Override public void hitSound(float pitch) { - if ( belongings.weapon() != null ){ + if (!RingOfForce.fightingUnarmed(this)) { belongings.weapon().hitSound(pitch); } else if (RingOfForce.getBuffedBonus(this, RingOfForce.Force.class) > 0) { //pitch deepens by 2.5% (additive) per point of strength, down to 75% @@ -474,7 +474,7 @@ public class Hero extends Char { } } - if (wep != null) { + if (!RingOfForce.fightingUnarmed(this)) { return (int)(attackSkill * accuracy * wep.accuracyFactor( this, target )); } else { return (int)(attackSkill * accuracy); @@ -573,7 +573,7 @@ public class Hero extends Char { KindOfWeapon wep = belongings.weapon(); int dmg; - if (wep != null) { + if (!RingOfForce.fightingUnarmed(this)) { dmg = wep.damageRoll( this ); if (heroClass != HeroClass.DUELIST @@ -589,6 +589,9 @@ public class Hero extends Char { if (!(wep instanceof MissileWeapon)) dmg += RingOfForce.armedDamageBonus(this); } else { dmg = RingOfForce.damageRoll(this); + if (RingOfForce.unarmedGetsWeaponEffects(this)){ + dmg = ((Weapon)belongings.weapon()).augment.damageFactor(dmg); + } } PhysicalEmpower emp = buff(PhysicalEmpower.class); @@ -644,6 +647,7 @@ public class Hero extends Char { @Override public boolean canSurpriseAttack(){ if (belongings.weapon() == null || !(belongings.weapon() instanceof Weapon)) return true; + if (RingOfForce.fightingUnarmed(this)) return true; if (STR() < ((Weapon)belongings.weapon()).STRReq()) return false; if (belongings.weapon() instanceof Flail) return false; @@ -675,7 +679,7 @@ public class Hero extends Char { return 0; } - if (belongings.weapon() != null) { + if (!RingOfForce.fightingUnarmed(this)) { return belongings.weapon().delayFactor( this ); @@ -683,7 +687,13 @@ public class Hero extends Char { //Normally putting furor speed on unarmed attacks would be unnecessary //But there's going to be that one guy who gets a furor+force ring combo //This is for that one guy, you shall get your fists of fury! - return 1f/RingOfFuror.attackSpeedMultiplier(this); + float delay = 1f/RingOfFuror.attackSpeedMultiplier(this); + + if (RingOfForce.unarmedGetsWeaponEffects(this)){ + delay = ((Weapon)belongings.weapon).augment.delayFactor(delay); + } + + return delay; } } @@ -1240,7 +1250,8 @@ public class Hero extends Char { @Override public int attackProc( final Char enemy, int damage ) { damage = super.attackProc( enemy, damage ); - + + //procs with weapon even in brawler's stance KindOfWeapon wep = belongings.weapon(); if (wep != null) damage = wep.proc( this, enemy, damage ); @@ -1948,6 +1959,18 @@ public class Hero extends Char { Buff.append( this, Sai.ComboStrikeTracker.class, Sai.ComboStrikeTracker.DURATION); } + RingOfForce.BrawlersStance brawlStance = buff(RingOfForce.BrawlersStance.class); + if (brawlStance != null && brawlStance.hitsLeft() > 0){ + MeleeWeapon.Charger charger = Buff.affect(this, MeleeWeapon.Charger.class); + charger.partialCharge -= 0.25f; + while (charger.partialCharge < 0) { + charger.charges--; + charger.partialCharge++; + } + BuffIndicator.refreshHero(); + Item.updateQuickslot(); + } + curAction = null; super.onAttackComplete(); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/rings/RingOfForce.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/rings/RingOfForce.java index 905f7fe78..517eedb7f 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/rings/RingOfForce.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/rings/RingOfForce.java @@ -23,11 +23,19 @@ package com.shatteredpixel.shatteredpixeldungeon.items.rings; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.actors.Char; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MeleeWeapon; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; +import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator; +import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; +import com.watabou.noosa.Image; import com.watabou.utils.Random; +import java.util.ArrayList; + public class RingOfForce extends Ring { { @@ -99,5 +107,148 @@ public class RingOfForce extends Ring { public class Force extends RingBuff { } + + //Duelist stuff + + public static String AC_ABILITY = "ABILITY"; + + @Override + public void activate(Char ch) { + super.activate(ch); + if (ch instanceof Hero && ((Hero) ch).heroClass == HeroClass.DUELIST){ + Buff.affect(ch, MeleeWeapon.Charger.class); + } + } + + @Override + public String defaultAction() { + if (Dungeon.hero != null && Dungeon.hero.heroClass == HeroClass.DUELIST){ + return AC_ABILITY; + } else { + return super.defaultAction(); + } + } + + @Override + public ArrayList actions(Hero hero) { + ArrayList actions = super.actions(hero); + if (isEquipped(hero) && hero.heroClass == HeroClass.DUELIST){ + actions.add(AC_ABILITY); + } + return actions; + } + + @Override + public String actionName(String action, Hero hero) { + if (action.equals(AC_ABILITY)){ + return Messages.upperCase(Messages.get(this, "ability_name")); + } else { + return super.actionName(action, hero); + } + } + + @Override + public void execute(Hero hero, String action) { + if (action.equals(AC_ABILITY)){ + if (hero.buff(BrawlersStance.class) != null){ + hero.buff(BrawlersStance.class).detach(); + } else if (!isEquipped(hero)) { + GLog.w(Messages.get(MeleeWeapon.class, "ability_need_equip")); + + } else if ((Buff.affect(hero, MeleeWeapon.Charger.class).charges + Buff.affect(hero, MeleeWeapon.Charger.class).partialCharge) < 0.333f){ + GLog.w(Messages.get(MeleeWeapon.class, "ability_no_charge")); + + } else { + Buff.affect(hero, BrawlersStance.class); + } + } else { + super.execute(hero, action); + } + } + + @Override + public String info() { + String info = super.info(); + + if (Dungeon.hero.heroClass == HeroClass.DUELIST){ + info += "\n\n" + Messages.get(this, "ability_desc"); + } + + return info; + } + + public static boolean fightingUnarmed( Hero hero ){ + if (hero.belongings.weapon() == null){ + return true; + } + if (hero.belongings.thrownWeapon != null || hero.belongings.abilityWeapon != null){ + return false; + } + BrawlersStance stance = hero.buff(BrawlersStance.class); + if (stance != null && stance.hitsLeft() > 0){ + return true; + } + return false; + } + + public static boolean unarmedGetsWeaponEffects( Hero hero ){ + if (hero.belongings.weapon() == null){ + return false; + } + BrawlersStance stance = hero.buff(BrawlersStance.class); + if (stance != null && stance.hitsLeft() > 0){ + return true; + } + return false; + } + + public static class BrawlersStance extends Buff { + + public static float HIT_CHARGE_USE = 0.25f; + + { + announced = true; + type = buffType.POSITIVE; + } + + public int hitsLeft(){ + MeleeWeapon.Charger charger = Buff.affect(target, MeleeWeapon.Charger.class); + float charges = charger.charges; + charges += charger.partialCharge; + + return (int)(charges/HIT_CHARGE_USE); + } + + @Override + public int icon() { + return BuffIndicator.DUEL_BRAWL; + } + + @Override + public void tintIcon(Image icon) { + if (hitsLeft() == 0){ + icon.brightness(0.25f); + } else { + icon.resetColor(); + } + } + + @Override + public float iconFadePercent() { + float usableCharges = hitsLeft()*HIT_CHARGE_USE; + + return 1f - (usableCharges / Buff.affect(target, MeleeWeapon.Charger.class).chargeCap()); + } + + @Override + public String iconTextDisplay() { + return Integer.toString(hitsLeft()); + } + + @Override + public String desc() { + return Messages.get(this, "desc", hitsLeft()); + } + } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/MeleeWeapon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/MeleeWeapon.java index 28f9218c7..866cc7cc2 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/MeleeWeapon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/MeleeWeapon.java @@ -351,13 +351,13 @@ public class MeleeWeapon extends Weapon { public static class Charger extends Buff implements ActionIndicator.Action { - private int charges = 3; - private float partialCharge; + public int charges = 3; + public float partialCharge; //offhand charge as well? //champion subclass - private int secondCharges = 0; - private float secondPartialCharge; + public int secondCharges = 3; + public float secondPartialCharge; @Override public boolean act() { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/BuffIndicator.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/BuffIndicator.java index 930b152d3..cb17212d4 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/BuffIndicator.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/BuffIndicator.java @@ -116,6 +116,7 @@ public class BuffIndicator extends Component { public static final int DUEL_SPIN = 62; public static final int DUEL_EVASIVE= 63; public static final int DUEL_DANCE = 64; + public static final int DUEL_BRAWL = 65; public static final int SIZE_SMALL = 7; public static final int SIZE_LARGE = 16;