v0.7.1: implemented sniper special attacks

This commit is contained in:
Evan Debenham 2018-11-30 23:12:22 -05:00
parent 9f8b66cdf0
commit 584fabd667
6 changed files with 170 additions and 24 deletions

View File

@ -21,16 +21,37 @@
package com.shatteredpixel.shatteredpixeldungeon.actors.buffs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.SpiritBow;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.ui.ActionIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.QuickSlotButton;
import com.watabou.noosa.Image;
import com.watabou.utils.Bundle;
public class SnipersMark extends FlavourBuff {
public class SnipersMark extends FlavourBuff implements ActionIndicator.Action {
public int object = 0;
private static final String OBJECT = "object";
@Override
public boolean attachTo(Char target) {
ActionIndicator.setAction(this);
return super.attachTo(target);
}
@Override
public void detach() {
super.detach();
ActionIndicator.clearAction(this);
}
@Override
public void storeInBundle( Bundle bundle ) {
super.storeInBundle( bundle );
@ -58,4 +79,28 @@ public class SnipersMark extends FlavourBuff {
public String desc() {
return Messages.get(this, "desc");
}
@Override
public Image getIcon() {
return new ItemSprite(ItemSpriteSheet.SPIRIT_BOW, null);
}
@Override
public void doAction() {
SpiritBow bow = Dungeon.hero.belongings.getItem(SpiritBow.class);
if (bow == null) return;
SpiritBow.SpiritArrow arrow = bow.knockArrow();
if (arrow == null) return;
Char ch = (Char) Actor.findById(object);
if (ch == null) return;
bow.sniperSpecial = true;
arrow.cast(Dungeon.hero, QuickSlotButton.autoAim(ch, arrow));
detach();
}
}

View File

@ -90,6 +90,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfTenacity;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.Scroll;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfMagicMapping;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.SpiritBow;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Flail;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
@ -920,15 +921,30 @@ public class Hero extends Char {
}
@Override
public int attackProc( Char enemy, int damage ) {
public int attackProc( final Char enemy, int damage ) {
KindOfWeapon wep = belongings.weapon;
if (wep != null) damage = wep.proc( this, enemy, damage );
switch (subClass) {
case SNIPER:
if (wep instanceof MissileWeapon) {
Buff.prolong( this, SnipersMark.class, attackDelay() ).object = enemy.id();
if (wep instanceof MissileWeapon && !(wep instanceof SpiritBow.SpiritArrow)) {
final float delay = attackDelay();
Actor.add(new Actor() {
{
actPriority = VFX_PRIO;
}
@Override
protected boolean act() {
if (enemy.isAlive()) {
Buff.prolong(Hero.this, SnipersMark.class, delay).object = enemy.id();
}
Actor.remove(this);
return true;
}
});
}
break;
default:

View File

@ -21,16 +21,22 @@
package com.shatteredpixel.shatteredpixeldungeon.items.weapon;
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.effects.Splash;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfFuror;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.CellSelector;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.sprites.MissileSprite;
import com.shatteredpixel.shatteredpixeldungeon.ui.QuickSlotButton;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Callback;
import com.watabou.utils.Random;
import java.util.ArrayList;
@ -49,6 +55,8 @@ public class SpiritBow extends Weapon {
bones = false;
}
public boolean sniperSpecial = false;
@Override
public ArrayList<String> actions(Hero hero) {
ArrayList<String> actions = super.actions(hero);
@ -125,9 +133,24 @@ public class SpiritBow extends Weapon {
return 6 + Dungeon.hero.lvl/3;
}
private int targetPos;
@Override
public int damageRoll(Char owner) {
int damage = augment.damageFactor(super.damageRoll(owner));
if (sniperSpecial){
switch (augment){
case NONE:
damage = Math.round(damage * 0.667f);
break;
case SPEED:
damage = Math.round(damage * 0.5f);
break;
case DAMAGE:
int distance = Dungeon.level.distance(owner.pos, targetPos) - 1;
damage = Math.round(damage * (1f + 0.1f * distance));
}
}
if (owner instanceof Hero) {
int exStr = ((Hero)owner).STR() - STRReq();
@ -139,6 +162,22 @@ public class SpiritBow extends Weapon {
return damage;
}
@Override
public float speedFactor(Char owner) {
if (sniperSpecial){
switch (augment){
case NONE: default:
return 0f;
case SPEED:
return 1f * RingOfFuror.attackDelayMultiplier(owner);
case DAMAGE:
return 2f * RingOfFuror.attackDelayMultiplier(owner);
}
} else {
return super.speedFactor(owner);
}
}
@Override
public int level() {
//need to check if hero is null for loading an upgraded bow from pre-0.7.0
@ -161,6 +200,10 @@ public class SpiritBow extends Weapon {
return false;
}
public SpiritArrow knockArrow(){
return new SpiritArrow();
}
public class SpiritArrow extends MissileWeapon {
{
@ -202,6 +245,57 @@ public class SpiritBow extends Weapon {
if (!curUser.shoot( enemy, this )) {
Splash.at(cell, 0xCC99FFFF, 1);
}
if (sniperSpecial && SpiritBow.this.augment != Augment.SPEED) sniperSpecial = false;
}
}
int flurryCount = -1;
@Override
public void cast(final Hero user, final int dst) {
final int cell = throwPos( user, dst );
SpiritBow.this.targetPos = cell;
if (sniperSpecial && SpiritBow.this.augment == Augment.SPEED){
if (flurryCount == -1) flurryCount = 3;
final Char enemy = Actor.findChar( cell );
QuickSlotButton.target(enemy);
final boolean last = flurryCount == 1;
user.busy();
Sample.INSTANCE.play( Assets.SND_MISS, 0.6f, 0.6f, 1.5f );
((MissileSprite) user.sprite.parent.recycle(MissileSprite.class)).
reset(user.sprite,
cell,
this,
new Callback() {
@Override
public void call() {
if (enemy.isAlive()) onThrow(cell);
if (last) {
user.spendAndNext(castDelay(user, dst));
sniperSpecial = false;
flurryCount = -1;
}
}
});
user.sprite.zap(cell, new Callback() {
@Override
public void call() {
flurryCount--;
if (flurryCount > 0){
cast(user, dst);
}
}
});
} else {
super.cast(user, dst);
}
}
}
@ -210,7 +304,7 @@ public class SpiritBow extends Weapon {
@Override
public void onSelect( Integer target ) {
if (target != null) {
new SpiritArrow().cast(curUser, target);
knockArrow().cast(curUser, target);
}
}
@Override

View File

@ -26,7 +26,6 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.PinCushion;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.SnipersMark;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
@ -176,20 +175,7 @@ abstract public class MissileWeapon extends Weapon {
@Override
public float castDelay(Char user, int dst) {
float delay = speedFactor( user );
Char enemy = Actor.findChar(dst);
if (enemy != null) {
SnipersMark mark = user.buff( SnipersMark.class );
if (mark != null) {
if (mark.object == enemy.id()) {
delay *= 0.5f;
}
}
}
return delay;
return speedFactor( user );
}
protected void rangedHit( Char enemy, int cell ){

View File

@ -233,6 +233,11 @@ public class CharSprite extends MovieClip implements Tweener.Listener, MovieClip
play( zap );
}
public void zap( int cell, Callback callback ) {
animCallback = callback;
zap( cell );
}
public void turnTo( int from, int to ) {
int fx = from % Dungeon.level.width();
int tx = to % Dungeon.level.width();

View File

@ -245,7 +245,7 @@ actors.buffs.slow.name=Slowed
actors.buffs.slow.desc=Slowing magic affects the target's rate of time, to them everything is moving super-fast.\n\nA slowed character performs all actions in twice the amount of time they would normally take.\n\nTurns of slow remaining: %s.
actors.buffs.snipersmark.name=Sniper's mark
actors.buffs.snipersmark.desc=The sniper is honed in on a nearby target, gaining increased attack speed and armor penetration while attacking it.\n\nThe sniper will remain honed in until she switches targets, stops attacking, or the target dies.
actors.buffs.snipersmark.desc=The Sniper is honed in on the target she most recently attacked. She is able to perform a special attack with her bow which will vary based on how the bow is augmented.\n\nAn unaugmented bow will fire a _snapshot,_ which deals reduced damage but does not take any time to fire.\n\nA bow augmented for speed with fire a _volley_ of three arrows. Each arrow will deal reduced damage, but can still activate enchantment. This volley takes 1 turn to shoot.\n\nA bow augmented for damage will fire a _sniper shot._ This shot deals bonus damage based on distance from the target, and takes 2 turns to fire.
actors.buffs.soulmark.name=Soul Marked
actors.buffs.soulmark.desc=The warlock has tapped into the soul of this creature. He will heal and satisfy his hunger as it takes physical damage.\n\nTurns of soul mark remaining: %s.
@ -333,7 +333,7 @@ actors.hero.herosubclass.assassin_desc=While invisible the _Assassin_ prepares a
actors.hero.herosubclass.freerunner=freerunner
actors.hero.herosubclass.freerunner_desc=The _Freerunner_ builds momentum as he runs. Momentum increases his movement speed and evasion, but it quickly fades when he isn't moving.
actors.hero.herosubclass.sniper=sniper
actors.hero.herosubclass.sniper_desc=The _Sniper_ is a master of ranged combat, with increased vision and bonus accuracy and armor piercing based on attack distance. After striking with a thrown weapon, she can follow up with a special attack from her bow.
actors.hero.herosubclass.sniper_desc=The _Sniper_ is a master of ranged combat. She gains increased vision, her ranged attacks pierce armor, and her ranged attacks gain bonus accuracy based on attack distance. After striking with a thrown weapon, she can follow up with a special attack from her bow.
actors.hero.herosubclass.warden=warden
actors.hero.herosubclass.warden_desc=The _Warden_ has a strong connection to nature which allows her to see through tall grass and command furrowed grass to sprout around plants she grows. Plants she tramples will also give bonus effects, tipped darts gain 2x durability, and grass will give her an armor boost when she stands in it.