v2.0.0: implemented the focus and dash monk abilities

This commit is contained in:
Evan Debenham
2023-02-26 23:23:15 -05:00
parent 8d5dab76d4
commit 3f5f95cf28
5 changed files with 143 additions and 53 deletions

View File

@@ -274,11 +274,16 @@ 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 X-Y damage and ignores armor. This ability has no cooldown if the Monk just attacked normally or with a weapon ability.
actors.buffs.monkenergy$monkability$dash.name=dash
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$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$focus.name=focus
actors.buffs.monkenergy$monkability$focus.desc=The monk takes a turn to focus, letting them dodge the next physical attack made against them within 20 turns.
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$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$meditate.name=meditate
@@ -1181,7 +1186,7 @@ actors.mobs.monk.def_verb=blocked
actors.mobs.monk.parried=parried
actors.mobs.monk.desc=These monks are fanatics, who have devoted themselves to protecting their king through physical might. So great is their devotion that they have totally surrendered their minds to their king, and now roam the dwarvern city like mindless zombies.\n\nMonks rely solely on the art of hand-to-hand combat, and are able to use their unarmed fists both for offense and defense. When they become focused, monks will parry the next physical attack used against them, even if it was otherwise guaranteed to hit. Monks build focus more quickly while on the move, and more slowly when in direct combat.
actors.mobs.monk$focus.name=focused
actors.mobs.monk$focus.desc=This monk is perfectly honed in on their target, and seem to be anticipating their moves before they make them.\n\nWhile focused, the next physical attack made against their character is guaranteed to miss, no matter what circumstances there are. Parrying this attack will spend the monk's focus, and they will need to build it up again to parry another attack. Monks build focus more quickly while they are moving.
actors.mobs.monk$focus.desc=This monk is perfectly honed in on their target, and seem to be anticipating their moves before they make them.\n\nWhile focused, the next physical attack made against this character is guaranteed to miss, no matter what circumstances there are. Parrying this attack will spend the monk's focus, and they will need to build it up again to parry another attack. Monks build focus more quickly while they are moving.
actors.mobs.piranha.name=giant piranha
actors.mobs.piranha.desc=These carnivorous fish are not natural inhabitants of underground pools. They were bred specifically to protect flooded treasure vaults.

View File

@@ -54,6 +54,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.LifeLink;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.LostInventory;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MagicalSleep;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Momentum;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MonkEnergy;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Ooze;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison;
@@ -323,7 +324,7 @@ public abstract class Char extends Actor {
return false;
} else if (hit( this, enemy, accMulti )) {
} else if (hit( this, enemy, accMulti, false )) {
int dr = Math.round(enemy.drRoll() * AscensionChallenge.statModifier(enemy));
@@ -488,10 +489,10 @@ public abstract class Char extends Actor {
public static int INFINITE_EVASION = 1_000_000;
final public static boolean hit( Char attacker, Char defender, boolean magic ) {
return hit(attacker, defender, magic ? 2f : 1f);
return hit(attacker, defender, magic ? 2f : 1f, magic);
}
public static boolean hit( Char attacker, Char defender, float accMulti ) {
public static boolean hit( Char attacker, Char defender, float accMulti, boolean magic ) {
float acuStat = attacker.attackSkill( defender );
float defStat = defender.defenseSkill( attacker );
@@ -500,6 +501,10 @@ public abstract class Char extends Actor {
acuStat = INFINITE_ACCURACY;
}
if (defender.buff(MonkEnergy.MonkAbility.Focus.FocusBuff.class) != null && !magic){
defStat = INFINITE_EVASION;
}
//if accuracy or evasion are large enough, treat them as infinite.
//note that infinite evasion beats infinite accuracy
if (defStat >= INFINITE_EVASION){

View File

@@ -23,21 +23,26 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
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.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.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.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.HeroIcon;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndMonkAbilities;
import com.watabou.noosa.Image;
import com.watabou.noosa.audio.Sample;
@@ -141,6 +146,7 @@ public class MonkEnergy extends Buff implements ActionIndicator.Action {
if (energy > 0 && cooldown == 0){
ActionIndicator.setAction(this);
}
BuffIndicator.refreshHero();
}
//10 at base, 20 at level 30
@@ -148,6 +154,14 @@ public class MonkEnergy extends Buff implements ActionIndicator.Action {
return Math.max(10, 5 + Dungeon.hero.lvl/2);
}
public void abilityUsed( MonkAbility abil ){
energy -= abil.energyCost();
cooldown = abil.cooldown();
if (cooldown > 0 || energy < 1){
ActionIndicator.clearAction(this);
}
}
@Override
public String actionName() {
return "TODO";
@@ -167,8 +181,8 @@ public class MonkEnergy extends Buff implements ActionIndicator.Action {
public static MonkAbility[] abilities = new MonkAbility[]{
new Flurry(),
new Dash(),
new Focus(),
new Dash(),
new DragonKick(),
new Meditate()
};
@@ -203,31 +217,15 @@ public class MonkEnergy extends Buff implements ActionIndicator.Action {
}
@Override
public String targetingPrompt() {
return "choose a location";
}
@Override
public void doAbility(Hero hero, Integer target) {
//TODO
}
}
public static class Dash extends MonkAbility {
@Override
public int energyCost() {
return 2;
}
@Override
public int cooldown() {
return 3; //extra turn as no time is spend dashing
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));
}
@Override
public String targetingPrompt() {
return "choose a location";
return "choose a target";
}
@Override
@@ -236,10 +234,103 @@ public class MonkEnergy extends Buff implements ActionIndicator.Action {
return;
}
//TODO check conditions
//TODO check for target viability
//TODO add a buff that forces melee only (and no RoF!)
//check for can attack
//clear buff if can't
//do attack logic
}
}
public static class Focus extends MonkAbility {
@Override
public int energyCost() {
return 2;
}
@Override
public int cooldown() {
return 4;
}
@Override
public void doAbility(Hero hero, Integer target) {
Buff.prolong(hero, FocusBuff.class, 30f);
Buff.affect(hero, MonkEnergy.class).abilityUsed(this);
hero.spendAndNext(1f);
}
public static class FocusBuff extends FlavourBuff {
{
type = buffType.POSITIVE;
announced = true;
}
@Override
public int icon() {
return BuffIndicator.MIND_VISION;
}
@Override
public void tintIcon(Image icon) {
icon.hardlight(0.25f, 1.5f, 1f);
}
@Override
public float iconFadePercent() {
return Math.max(0, (30 - visualcooldown()) / 30);
}
}
}
public static class Dash extends MonkAbility {
@Override
public int energyCost() {
return 3;
}
@Override
public int cooldown() {
return 3; //1 less turn as no time is spent dashing
}
@Override
public String targetingPrompt() {
return Messages.get(this, "prompt");
}
@Override
public void doAbility(Hero hero, Integer target) {
if (target == null || target == -1){
return;
}
if (Dungeon.level.distance(hero.pos, target) > 3){
GLog.w(Messages.get(this, "too_far"));
return;
}
Ballistica dash = new Ballistica(hero.pos, target, Ballistica.PROJECTILE);
if (!dash.collisionPos.equals(target)
|| Actor.findChar(target) != null
|| (Dungeon.level.solid[target] && !Dungeon.level.passable[target])){
GLog.w(Messages.get(this, "blocked"));
return;
}
hero.busy();
Sample.INSTANCE.play(Assets.Sounds.MISS);
hero.sprite.emitter().start(Speck.factory(Speck.JET), 0.01f, Math.round(4 + 2*Dungeon.level.trueDistance(hero.pos, target)));
hero.sprite.jump(hero.pos, target, 0, 0.1f, new Callback() {
@Override
public void call() {
@@ -252,30 +343,10 @@ public class MonkEnergy extends Buff implements ActionIndicator.Action {
}
});
//TODO decrement energy
Buff.affect(hero, MonkEnergy.class).abilityUsed(this);
}
}
public static class Focus extends MonkAbility {
@Override
public int energyCost() {
return 3;
}
@Override
public int cooldown() {
return 3;
}
@Override
public void doAbility(Hero hero, Integer target) {
//TODO
}
}
public static class DragonKick extends MonkAbility {
@Override

View File

@@ -54,6 +54,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Levitation;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.LostInventory;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MindVision;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Momentum;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MonkEnergy;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.PhysicalEmpower;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Recharging;
@@ -540,6 +541,14 @@ public class Hero extends Char {
return Messages.get(Monk.class, "parried");
}
if (buff(MonkEnergy.MonkAbility.Focus.FocusBuff.class) != null){
buff(MonkEnergy.MonkAbility.Focus.FocusBuff.class).detach();
if (sprite != null && sprite.visible) {
Sample.INSTANCE.play(Assets.Sounds.HIT_PARRY, 1, Random.Float(0.96f, 1.05f));
}
return Messages.get(Monk.class, "parried");
}
if (buff(RoundShield.GuardTracker.class) != null){
buff(RoundShield.GuardTracker.class).detach();
Sample.INSTANCE.play(Assets.Sounds.HIT_PARRY, 1, Random.Float(0.96f, 1.05f));

View File

@@ -176,7 +176,7 @@ public class RipperDemon extends Mob {
public void call() {
if (leapVictim != null && alignment != leapVictim.alignment){
if (hit(RipperDemon.this, leapVictim, Char.INFINITE_ACCURACY)) {
if (hit(RipperDemon.this, leapVictim, Char.INFINITE_ACCURACY, false)) {
Buff.affect(leapVictim, Bleeding.class).set(0.75f * damageRoll());
leapVictim.sprite.flash();
Sample.INSTANCE.play(Assets.Sounds.HIT);