v0.7.1: implemented sniper special attacks
This commit is contained in:
parent
9f8b66cdf0
commit
584fabd667
|
@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ){
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user