v0.8.1: balance/design improvements to assassin and sniper:

- sniper's mark now lasts 4 turns, up from 2
- further increased distance-based damage scaling on sniper shot
- assassin preparation now maxes at 11 turns, and becomes stronger faster
- preparation now executes enemies below a health threshold, rather than dealing more damage at low enemy HP.
This commit is contained in:
Evan Debenham
2020-06-08 17:53:58 -04:00
parent 093671ed3b
commit fa4d0e908d
5 changed files with 44 additions and 53 deletions

View File

@@ -232,9 +232,7 @@ actors.buffs.poison.desc=Poison works its way through the body, slowly impairing
actors.buffs.preparation.name=Preparation
actors.buffs.preparation.desc=The Assassin is waiting patiently, preparing to strike from the shadows.
actors.buffs.preparation.desc_dmg=His next attack will do _%d%% bonus damage._
actors.buffs.preparation.desc_dmg_scale=His next attack will do _%d%%-%d%% bonus damage,_ depending on how injured the target is.
actors.buffs.preparation.desc_dmg_instakill=His next attack will _instantly kill_ any non-boss enemy!\n\nOtherwise it will do _%d%%-%d%% bonus damage,_ depending on how injured the target is.
actors.buffs.preparation.desc_dmg=His next attack will deal _%1$d%% bonus damage_, and will execute regular enemies below _%2$d%% health_, or bosses below _%3$d%% health_.
actors.buffs.preparation.desc_dmg_likely=The attack will also be more likely to deal a larger amount of damage.
actors.buffs.preparation.desc_blink=He is able to blink towards an enemy before striking them, with a max distance of _%d._
actors.buffs.preparation.desc_invis_time=The Assassin has been invisible for _%d turns._
@@ -242,6 +240,7 @@ actors.buffs.preparation.desc_invis_next=His attack will become stronger at _%d
actors.buffs.preparation.prompt=Select a target to attack!\nMax blink distance: %d
actors.buffs.preparation.no_target=There's nothing to attack there.
actors.buffs.preparation.out_of_reach=That target is out of reach.
actors.buffs.preparation.assassinated=assassinated
actors.buffs.prismaticguard.name=Prismatic Guard
actors.buffs.prismaticguard.desc=You are being guarded by a prismatic image which is currently inactive. When enemies are present the prismatic image will spring to action and protect you!\n\nWhile inactive, the prismatic image will steadily recover from any damage it has taken.\n\nCurrent HP: %d/%d.

View File

@@ -265,7 +265,7 @@ public abstract class Char extends Actor {
int dmg;
Preparation prep = buff(Preparation.class);
if (prep != null){
dmg = prep.damageRoll(this, enemy);
dmg = prep.damageRoll(this);
} else {
dmg = damageRoll();
}
@@ -298,6 +298,11 @@ public abstract class Char extends Actor {
if (buff(FrostImbue.class) != null)
buff(FrostImbue.class).proc(enemy);
if (prep != null && prep.canKO(enemy)){
enemy.die(this);
enemy.sprite.showStatus(CharSprite.NEGATIVE, Messages.get(Preparation.class, "assassinated"));
}
enemy.sprite.bloodBurstA( sprite.center(), effectiveDamage );
enemy.sprite.flash();

View File

@@ -54,36 +54,37 @@ public class Preparation extends Buff implements ActionIndicator.Action {
}
public enum AttackLevel{
LVL_1( 1, 0.1f, 0.0f, 1, 0),
LVL_2( 3, 0.2f, 0.0f, 1, 1),
LVL_3( 6, 0.3f, 0.0f, 2, 3),
LVL_4( 11, 0.4f, 0.6f, 2, 5),
LVL_5( 16, 0.5f, 1.0f, 3, 7);
LVL_1( 1, 0.15f, 0.05f, 1, 1),
LVL_2( 3, 0.30f, 0.15f, 1, 3),
LVL_3( 6, 0.45f, 0.30f, 2, 5),
LVL_4( 11, 0.60f, 0.50f, 3, 7);
final int turnsReq;
final float baseDmgBonus, missingHPBonus;
final float baseDmgBonus, KOThreshold;
final int damageRolls, blinkDistance;
AttackLevel( int turns, float base, float missing, int rolls, int dist){
AttackLevel( int turns, float base, float threshold, int rolls, int dist){
turnsReq = turns;
baseDmgBonus = base; missingHPBonus = missing;
damageRolls =rolls; blinkDistance = dist;
baseDmgBonus = base; KOThreshold = threshold;
damageRolls = rolls; blinkDistance = dist;
}
public boolean canInstakill(Char defender){
return this == LVL_5
&& !defender.properties().contains(Char.Property.MINIBOSS)
&& !defender.properties().contains(Char.Property.BOSS);
public boolean canKO(Char defender){
if (defender.properties().contains(Char.Property.MINIBOSS)
|| defender.properties().contains(Char.Property.BOSS)){
return (defender.HP/(float)defender.HT) <= (KOThreshold/5f);
} else {
return (defender.HP/(float)defender.HT) <= KOThreshold;
}
}
public int damageRoll( Char attacker, Char defender){
public int damageRoll( Char attacker ){
int dmg = attacker.damageRoll();
for( int i = 1; i < damageRolls; i++){
int newDmg = attacker.damageRoll();
if (newDmg > dmg) dmg = newDmg;
}
float defenderHPPercent = 1f - (defender.HP / (float)defender.HT);
return Math.round(dmg * (1f + baseDmgBonus + (missingHPBonus * defenderHPPercent)));
return Math.round(dmg * (1f + baseDmgBonus));
}
public static AttackLevel getLvl(int turnsInvis){
@@ -120,16 +121,12 @@ public class Preparation extends Buff implements ActionIndicator.Action {
ActionIndicator.clearAction(this);
}
public int damageRoll(Char attacker, Char defender ){
AttackLevel lvl = AttackLevel.getLvl(turnsInvis);
if (lvl.canInstakill(defender)){
int dmg = lvl.damageRoll(attacker, defender);
defender.damage( Math.max(defender.HT, dmg), attacker );
//even though the defender is dead, other effects should still proc (enchants, etc.)
return Math.max( defender.HT, dmg);
} else {
return lvl.damageRoll(attacker, defender);
}
public int damageRoll( Char attacker ){
return AttackLevel.getLvl(turnsInvis).damageRoll(attacker);
}
public boolean canKO( Char defender ){
return AttackLevel.getLvl(turnsInvis).canKO(defender);
}
@Override
@@ -141,18 +138,15 @@ public class Preparation extends Buff implements ActionIndicator.Action {
public void tintIcon(Image icon) {
switch (AttackLevel.getLvl(turnsInvis)){
case LVL_1:
icon.hardlight(1f, 1f, 1f);
break;
case LVL_2:
icon.hardlight(0f, 1f, 0f);
break;
case LVL_3:
case LVL_2:
icon.hardlight(1f, 1f, 0f);
break;
case LVL_4:
case LVL_3:
icon.hardlight(1f, 0.6f, 0f);
break;
case LVL_5:
case LVL_4:
icon.hardlight(1f, 0f, 0f);
break;
}
@@ -160,7 +154,7 @@ public class Preparation extends Buff implements ActionIndicator.Action {
@Override
public float iconFadePercent() {
if (AttackLevel.getLvl(turnsInvis) == AttackLevel.LVL_5){
if (AttackLevel.getLvl(turnsInvis) == AttackLevel.LVL_4){
return 0;
} else {
float turnsForCur = AttackLevel.getLvl(turnsInvis).turnsReq;
@@ -181,18 +175,11 @@ public class Preparation extends Buff implements ActionIndicator.Action {
String desc = Messages.get(this, "desc");
AttackLevel lvl = AttackLevel.getLvl(turnsInvis);
if (lvl.canInstakill(new Rat())){
desc += "\n\n" + Messages.get(this, "desc_dmg_instakill",
(int)(lvl.baseDmgBonus*100),
(int)(lvl.baseDmgBonus*100 + lvl.missingHPBonus*100));
} else if (lvl.missingHPBonus > 0){
desc += "\n\n" + Messages.get(this, "desc_dmg_scale",
(int)(lvl.baseDmgBonus*100),
(int)(lvl.baseDmgBonus*100 + lvl.missingHPBonus*100));
} else {
desc += "\n\n" + Messages.get(this, "desc_dmg", (int)(lvl.baseDmgBonus*100));
}
desc += "\n\n" + Messages.get(this, "desc_dmg",
(int)(lvl.baseDmgBonus*100),
(int)(lvl.KOThreshold*100),
(int)(lvl.KOThreshold*20));
if (lvl.damageRolls > 1){
desc += " " + Messages.get(this, "desc_dmg_likely");

View File

@@ -41,7 +41,7 @@ public class SnipersMark extends FlavourBuff implements ActionIndicator.Action {
private static final String OBJECT = "object";
public static final float DURATION = 2f;
public static final float DURATION = 4f;
{
type = buffType.POSITIVE;

View File

@@ -166,10 +166,10 @@ public class SpiritBow extends Weapon {
damage = Math.round(damage * 0.5f);
break;
case DAMAGE:
//as distance increases so does damage, capping at 2.5x:
//1.20x|1.32x|1.45x|1.59x|1.76x|1.93x|2.13x|2.34x|2.50x
//as distance increases so does damage, capping at 3x:
//1.20x|1.35x|1.52x|1.71x|1.92x|2.16x|2.43x|2.74x|3.00x
int distance = Dungeon.level.distance(owner.pos, targetPos) - 1;
float multiplier = Math.min(2.5f, 1.2f * (float)Math.pow(1.1f, distance));
float multiplier = Math.min(2.5f, 1.2f * (float)Math.pow(1.125f, distance));
damage = Math.round(damage * multiplier);
break;
}