v2.0.0: implemented the flurry of blows ability

This commit is contained in:
Evan Debenham
2023-02-27 15:53:12 -05:00
parent 3f5f95cf28
commit 6acf20336e
5 changed files with 67 additions and 21 deletions

View File

@@ -274,20 +274,18 @@ actors.buffs.monkenergy.name=energy
actors.buffs.monkenergy.desc=As she defeats enemies, the monk gains energy that she can use on a variety of abilities. Most enemies grant 1 energy when defeated, and the Monk does not lose energy over time.\n\nCurrent energy: %1$d/%2$d.
actors.buffs.monkenergy.desc_cooldown=The monk has recently used an ability, and must wait before using another.\n\nCurrent cooldown: %d turns.
actors.buffs.monkenergy$monkability$flurry.name=flurry of blows
actors.buffs.monkenergy$monkability$flurry.desc=An instant strike that deals %1$d-%2$d damage and ignores armor. This ability has no cooldown if the Monk just attacked normally or with a weapon ability.
actors.buffs.monkenergy$monkability$flurry.desc=Two instant strikes that deal %1$d-%2$d damage and ignore armor. This ability has no cooldown if the Monk just successfully attacked an enemy.
actors.buffs.monkenergy$monkability$focus.name=focus
actors.buffs.monkenergy$monkability$focus.desc=The monk takes a turn to focus, letting her parry the next physical attack made against her within 30 turns.
actors.buffs.monkenergy$monkability$focus$focusbuff.name=focused
actors.buffs.monkenergy$monkability$focus$focusbuff.desc=The monk is focused on her surroundings, anticipating the next physical attack made against her. While focused, she is garunteed to parry the next incoming physical attack.\n\nTurns remaining: %s.
actors.buffs.monkenergy$monkability$dash.name=dash
actors.buffs.monkenergy$monkability$dash.prompt=Choose a location
actors.buffs.monkenergy$monkability$dash.too_far=That location is too far away.
actors.buffs.monkenergy$monkability$dash.blocked=There is something blocking that location.
actors.buffs.monkenergy$monkability$dash.desc=An instant dash up to 2 tiles away. This ability can go over hazards, but not through enemies or walls.
actors.buffs.monkenergy$monkability$dash.prompt=Choose a Location
actors.buffs.monkenergy$monkability$dash.desc=An instant dash up to 3 tiles away. This ability can go over hazards, but not through enemies or walls.
actors.buffs.monkenergy$monkability$dragonkick.name=dragon kick
actors.buffs.monkenergy$monkability$dragonkick.desc=A devastating kick that deals X-Y damage and ignores armor. The force of the kick is so strong that the target is knocked away and paralyzed for 10 turns.
actors.buffs.monkenergy$monkability$dragonkick.desc=A devastating kick that deals %1$d-%2$d damage and ignores armor. The kick also knocks the target away and paralyzes them for 5 turns.
actors.buffs.monkenergy$monkability$meditate.name=meditate
actors.buffs.monkenergy$monkability$meditate.desc=The Monk focuses energy into her body for 5 turns. This clears most negative effects and restores charge to her magical items.
actors.buffs.monkenergy$monkability$meditate.desc=The Monk focuses energy into her body for 5 turns. This clears most negative effects and grants her 10 turns of wand and artifact recharging.
actors.buffs.ooze.name=caustic ooze
actors.buffs.ooze.heromsg=Caustic ooze eats your flesh. Wash it away!

View File

@@ -335,6 +335,13 @@ public abstract class Char extends Actor {
&& !Dungeon.level.adjacent(h.pos, enemy.pos)){
dr = 0;
}
if (h.buff(MonkEnergy.MonkAbility.UnarmedAbilityTracker.class) != null){
dr = 0;
} else if (h.subClass == HeroSubClass.MONK) {
//3 turns with standard attack delay
Buff.prolong(h, MonkEnergy.MonkAbility.JustHitTracker.class, 4f);
}
}
//we use a float here briefly so that we don't have to constantly round while

View File

@@ -29,17 +29,18 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Ghoul;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Monk;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.RipperDemon;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Wraith;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.YogDzewa;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MeleeWeapon;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.Door;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.ui.ActionIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.AttackIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.HeroIcon;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
@@ -204,6 +205,9 @@ public class MonkEnergy extends Buff implements ActionIndicator.Action {
public abstract void doAbility(Hero hero, Integer target );
public static class UnarmedAbilityTracker extends FlavourBuff{};
public static class JustHitTracker extends FlavourBuff{};
public static class Flurry extends MonkAbility {
@Override
@@ -213,19 +217,18 @@ public class MonkEnergy extends Buff implements ActionIndicator.Action {
@Override
public int cooldown() {
return 3;
return Dungeon.hero.buff(JustHitTracker.class) != null ? 0 : 3;
}
@Override
public String desc() {
//double hero unarmed damage
//TODO maybe two hits at regular unarmed damage instead?
return Messages.get(this, "desc", 2, 2*(Dungeon.hero.STR()-8));
//hero unarmed damage
return Messages.get(this, "desc", 1, Dungeon.hero.STR()-8);
}
@Override
public String targetingPrompt() {
return "choose a target";
return Messages.get(MeleeWeapon.class, "prompt");
}
@Override
@@ -234,15 +237,49 @@ public class MonkEnergy extends Buff implements ActionIndicator.Action {
return;
}
//TODO check for target viability
Char enemy = Actor.findChar(target);
if (enemy == null || enemy == hero || hero.isCharmedBy(enemy) || !Dungeon.level.heroFOV[target]) {
GLog.w(Messages.get(MeleeWeapon.class, "ability_no_target"));
return;
}
//TODO add a buff that forces melee only (and no RoF!)
UnarmedAbilityTracker tracker = Buff.affect(hero, UnarmedAbilityTracker.class);
if (!hero.canAttack(enemy)){
GLog.w(Messages.get(MeleeWeapon.class, "ability_bad_position"));
tracker.detach();
return;
}
//check for can attack
hero.sprite.attack(enemy.pos, new Callback() {
@Override
public void call() {
AttackIndicator.target(enemy);
hero.attack(enemy, 1, 0, Char.INFINITE_ACCURACY);
//clear buff if can't
if (enemy.isAlive()){
hero.sprite.attack(enemy.pos, new Callback() {
@Override
public void call() {
hero.attack(enemy, 1, 0, Char.INFINITE_ACCURACY);
Invisibility.dispel();
tracker.detach();
//do attack logic
Buff.affect(hero, MonkEnergy.class).abilityUsed(Flurry.this);
if (hero.buff(JustHitTracker.class) != null) {
hero.buff(JustHitTracker.class).detach();
}
}
});
} else {
Invisibility.dispel();
tracker.detach();
Buff.affect(hero, MonkEnergy.class).abilityUsed(Flurry.this);
if (hero.buff(JustHitTracker.class) != null) {
hero.buff(JustHitTracker.class).detach();
}
}
}
});
}
}
@@ -315,7 +352,7 @@ public class MonkEnergy extends Buff implements ActionIndicator.Action {
}
if (Dungeon.level.distance(hero.pos, target) > 3){
GLog.w(Messages.get(this, "too_far"));
GLog.w(Messages.get(MeleeWeapon.class, "ability_no_target"));
return;
}
@@ -324,7 +361,7 @@ public class MonkEnergy extends Buff implements ActionIndicator.Action {
if (!dash.collisionPos.equals(target)
|| Actor.findChar(target) != null
|| (Dungeon.level.solid[target] && !Dungeon.level.passable[target])){
GLog.w(Messages.get(this, "blocked"));
GLog.w(Messages.get(MeleeWeapon.class, "ability_bad_position"));
return;
}

View File

@@ -25,6 +25,7 @@ import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.GamesInProgress;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.LostInventory;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MonkEnergy;
import com.shatteredpixel.shatteredpixeldungeon.items.EquipableItem;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.KindOfWeapon;
@@ -98,6 +99,7 @@ public class Belongings implements Iterable<Item> {
public KindOfWeapon attackingWeapon(){
if (thrownWeapon != null) return thrownWeapon;
if (abilityWeapon != null) return abilityWeapon;
if (owner.buff(MonkEnergy.MonkAbility.UnarmedAbilityTracker.class) != null) return null;
return weapon();
}

View File

@@ -24,6 +24,7 @@ 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.buffs.MonkEnergy;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MeleeWeapon;
@@ -77,7 +78,8 @@ public class RingOfForce extends Ring {
}
public static int damageRoll( Hero hero ){
if (hero.buff(Force.class) != null) {
if (hero.buff(Force.class) != null
&& hero.buff(MonkEnergy.MonkAbility.UnarmedAbilityTracker.class) == null) {
int level = getBuffedBonus(hero, Force.class);
float tier = tier(hero.STR());
return Random.NormalIntRange(min(level, tier), max(level, tier));