v0.9.3: implemented the spirit hawk ability

This commit is contained in:
Evan Debenham 2021-05-27 18:39:36 -04:00
parent 4bf8ac0383
commit e22b12c6d3
5 changed files with 279 additions and 14 deletions

View File

@ -387,13 +387,19 @@ actors.hero.abilities.huntress.spectralblades.name=spectral blades
actors.hero.abilities.huntress.spectralblades.short_desc=The Huntress throws _Spectral Blades_ at a target, inflicting damage depending on her currently equipped melee weapon.
actors.hero.abilities.huntress.spectralblades.desc=The Huntress throws out a spectral blade at an enemy, which acts like a ranged strike from her melee weapon. All of the normal effects of a melee weapon will activate, including enchantments.\n\nThis ability costs 25 charge.
actors.hero.abilities.huntress.naturespower.name=nature's power
actors.hero.abilities.huntress.naturespower$naturespowertracker.name=nature's power
actors.hero.abilities.huntress.naturespower$naturespowertracker.desc=The Huntress is briefly empowered, increasing her movement speed and attack speed with her bow.\n\nTurns remaining: %s.
actors.hero.abilities.huntress.naturespower.short_desc=The Huntress calls upon _Nature's Power_, increasing her movement speed and her bow's rate of fire for a short time.
actors.hero.abilities.huntress.naturespower.desc=The Huntress temporarily empowers herself and her bow with the power of nature! This lasts for 8 turns.\n\nDuring this time, the huntress moves at 2x speed, and attacks 33% faster with her bow. The bonus bow effects do not trigger if the sniper is using her special attacks.\n\nThis ability costs 35 charge.
actors.hero.abilities.huntress.naturespower$naturespowertracker.name=nature's power
actors.hero.abilities.huntress.naturespower$naturespowertracker.desc=The Huntress is briefly empowered, increasing her movement speed and attack speed with her bow.\n\nTurns remaining: %s.
actors.hero.abilities.huntress.spirithawk.name=spirit hawk
actors.hero.abilities.huntress.spirithawk.no_space=There is no free space near you.
actors.hero.abilities.huntress.spirithawk.short_desc=The Huntress summons a _Spirit Hawk_ familiar, which can help her scout locations and distract enemies.
actors.hero.abilities.huntress.spirithawk.desc=TODO
actors.hero.abilities.huntress.spirithawk.desc=The Huntress summons a spirit hawk familiar, which can be directed by using the ability again while it is summoned. The hawk lasts for 50 turns.\n\nThe hawk has minimal health and attacking power, but is fast, evasive, and accurate. It shares its entire field of vision with the huntress at all times. It is immune to all area-bound effects, such as fire and poison gas. It will not attack unless specifically directed to.\n\nSummoning the hawk costs 35 charge.
actors.hero.abilities.huntress.spirithawk$hawkally.name=spirit hawk
actors.hero.abilities.huntress.spirithawk$hawkally.direct_follow=Your hawk moves to follow you.
actors.hero.abilities.huntress.spirithawk$hawkally.direct_attack=Your hawk moves to attack!
actors.hero.abilities.huntress.spirithawk$hawkally.desc=A magical hawk, summoned by the Huntress.\n\nWhile it isn't much of a fighter its speed and vision make it excellent for scouting and distracting enemies.\n\nTurns remaining: %d.
actors.hero.abilities.huntress.spirithawk$hawkally.desc_dodges=Guaranteed dodges remaining: %d.
actors.hero.abilities.ratmogrify.name=ratmogrify
actors.hero.abilities.ratmogrify.cant_transform=You can't ratmogrify that!
@ -706,12 +712,12 @@ actors.hero.talent.natures_wrath.desc=_+1:_ While nature's power is active, shot
actors.hero.talent.wild_momentum.title=wild momentum
actors.hero.talent.wild_momentum.desc=_+1:_ Killing an enemy with the spirit bow prolongs the duration of nature's power by _1 turn_. The duration can be extended by a max of _2 turns_.\n\n_+2:_ Killing an enemy with the spirit bow prolongs the duration of nature's power by _2 turns_. The duration can be extended by a max of _4 turns_.\n\n_+3:_ Killing an enemy with the spirit bow prolongs the duration of nature's power by _3 turns_. The duration can be extended by a max of _6 turns_.\n\n_+4:_ Killing an enemy with the spirit bow prolongs the duration of nature's power by _4 turns_. The duration can be extended by a max of _8 turns_.
actors.hero.talent.huntress_3_1.title=TODO NAME
actors.hero.talent.huntress_3_1.desc=_+1:_ \n\n_+2:_ \n\n_+3:_ \n\n_+4:_
actors.hero.talent.huntress_3_2.title=TODO NAME
actors.hero.talent.huntress_3_2.desc=_+1:_ \n\n_+2:_ \n\n_+3:_ \n\n_+4:_
actors.hero.talent.huntress_3_3.title=TODO NAME
actors.hero.talent.huntress_3_3.desc=_+1:_ \n\n_+2:_ \n\n_+3:_ \n\n_+4:_
actors.hero.talent.eagle_eye.title=eagle eye
actors.hero.talent.eagle_eye.desc=_+1:_ The spirit hawk's vision range is increased to _7 tiles_ from 6.\n\n_+2:_ The spirit hawk's vision range is increased to _8 tiles_ from 6.\n\n_+3:_ The spirit hawk's vision range is increased to _8 tiles_ from 6, and it gets _2 tiles_ of mind vision.\n\n_+4:_ The spirit hawk's vision range is increased to _8 tiles_ from 6, and it gets _3 tiles_ of mind vision.
actors.hero.talent.go_for_the_eyes.title=go for the eyes
actors.hero.talent.go_for_the_eyes.desc=_+1:_ Attacks from the spirit hawk blind enemies for _2 turns_.\n\n_+2:_ Attacks from the spirit hawk blind enemies for _3 turns_.\n\n_+3:_ Attacks from the spirit hawk blind enemies for _4 turns_.\n\n_+4:_ Attacks from the spirit hawk blind enemies for _5 turns_.
actors.hero.talent.swift_spirit.title=swift spirit
actors.hero.talent.swift_spirit.desc=_+1:_ The spirit hawk's movement speed is increased to _2.5 tiles_ from 2, and it is guaranteed to dodge the first _1 attack_ made against it.\n\n_+2:_ The spirit hawk's movement speed is increased to _3 tiles_ from 2, and it is guaranteed to dodge the first _2 attacks_ made against it.\n\n_+3:_ The spirit hawk's movement speed is increased to _3.5 tiles_ from 2, and it is guaranteed to dodge the first _3 attacks_ made against it.\n\n_+4:_The spirit hawk's movement speed is increased to _4 tiles_ from 2, and it is guaranteed to dodge the first _4 attacks_ made against it.
#universal
actors.hero.talent.heroic_energy.title=heroic energy

View File

@ -30,6 +30,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MindVision;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.RevealedArea;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.huntress.SpiritHawk;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Blacksmith;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Ghost;
@ -43,6 +44,8 @@ import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.TalismanOfForesi
import com.shatteredpixel.shatteredpixeldungeon.items.potions.Potion;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.Scroll;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfRegrowth;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfWarding;
import com.shatteredpixel.shatteredpixeldungeon.journal.Notes;
import com.shatteredpixel.shatteredpixeldungeon.levels.CavesLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.CityLevel;
@ -764,6 +767,33 @@ public class Dungeon {
GameScene.updateFog(a.pos, 2);
}
for (Char ch : Actor.chars()){
if (ch instanceof WandOfWarding.Ward
|| ch instanceof WandOfRegrowth.Lotus
|| ch instanceof SpiritHawk.HawkAlly){
x = ch.pos % level.width();
y = ch.pos / level.width();
//left, right, top, bottom
dist = ch.viewDistance+1;
l = Math.max( 0, x - dist );
r = Math.min( x + dist, level.width() - 1 );
t = Math.max( 0, y - dist );
b = Math.min( y + dist, level.height() - 1 );
width = r - l + 1;
height = b - t + 1;
pos = l + t * level.width();
for (int i = t; i <= b; i++) {
BArray.or( level.visited, level.heroFOV, pos, width, level.visited );
pos+=level.width();
}
GameScene.updateFog(ch.pos, dist);
}
}
GameScene.afterObserve();
}

View File

@ -136,8 +136,8 @@ public enum Talent {
FAN_OF_BLADES(113, 4), PROJECTING_BLADES(114, 4), SPIRIT_BLADES(115, 4),
//Natures Power T4
GROWING_POWER(116, 4), NATURES_WRATH(117, 4), WILD_MOMENTUM(118, 4),
//??? T4
HUNTRESS_3_1(119, 4), HUNTRESS_3_2(120, 4), HUNTRESS_3_3(121, 4),
//Spirit Hawk T4
EAGLE_EYE(119, 4), GO_FOR_THE_EYES(120, 4), SWIFT_SPIRIT(121, 4),
//universal T4
HEROIC_ENERGY(123, 4),

View File

@ -21,20 +21,234 @@
package com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.huntress;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Electricity;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Freezing;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Bleeding;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Blindness;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.BlobImmunity;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.ArmorAbility;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.DirectableAlly;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.ClassArmor;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.BatSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.utils.Bundle;
import com.watabou.utils.GameMath;
import com.watabou.utils.PathFinder;
import com.watabou.utils.Random;
import java.util.ArrayList;
public class SpiritHawk extends ArmorAbility {
@Override
public String targetingPrompt() {
if (getHawk() == null) {
return super.targetingPrompt();
} else {
return Messages.get(this, "prompt");
}
}
{
baseChargeUse = 35f;
}
@Override
public float chargeUse(Hero hero) {
if (getHawk() == null) {
return super.chargeUse(hero);
} else {
return 0;
}
}
@Override
protected void activate(ClassArmor armor, Hero hero, Integer target) {
//TODO
HawkAlly ally = getHawk();
if (ally != null){
if (target == null){
return;
} else {
ally.directTocell(target);
}
} else {
ArrayList<Integer> spawnPoints = new ArrayList<>();
for (int i = 0; i < PathFinder.NEIGHBOURS8.length; i++) {
int p = hero.pos + PathFinder.NEIGHBOURS8[i];
if (Actor.findChar(p) == null && (Dungeon.level.passable[p] || Dungeon.level.avoid[p])) {
spawnPoints.add(p);
}
}
if (!spawnPoints.isEmpty()){
ally = new HawkAlly();
ally.pos = Random.element(spawnPoints);
GameScene.add(ally);
Dungeon.level.occupyCell(ally);
Dungeon.observe();
armor.charge -= chargeUse(hero);
armor.updateQuickslot();
Invisibility.dispel();
hero.spendAndNext(Actor.TICK);
} else {
GLog.w(Messages.get(this, "no_space"));
}
}
}
@Override
public Talent[] talents() {
return new Talent[]{Talent.HUNTRESS_3_1, Talent.HUNTRESS_3_2, Talent.HUNTRESS_3_3, Talent.HEROIC_ENERGY};
return new Talent[]{Talent.EAGLE_EYE, Talent.GO_FOR_THE_EYES, Talent.SWIFT_SPIRIT, Talent.HEROIC_ENERGY};
}
private static HawkAlly getHawk(){
for (Char ch : Actor.chars()){
if (ch instanceof HawkAlly){
return (HawkAlly) ch;
}
}
return null;
}
public static class HawkAlly extends DirectableAlly {
{
spriteClass = HawkSprite.class;
HP = HT = 10;
defenseSkill = 50;
flying = true;
viewDistance = (int)GameMath.gate(6, 6+Dungeon.hero.pointsInTalent(Talent.EAGLE_EYE), 8);
attacksAutomatically = false;
immunities.addAll(new BlobImmunity().immunities());
}
@Override
public int attackSkill(Char target) {
return 50;
}
private int dodgesUsed = 0;
private float timeRemaining = 50f;
@Override
public int defenseSkill(Char enemy) {
if (dodgesUsed < Dungeon.hero.pointsInTalent(Talent.SWIFT_SPIRIT)){
dodgesUsed++;
return Char.INFINITE_EVASION;
}
return super.defenseSkill(enemy);
}
@Override
public int damageRoll() {
return Random.NormalIntRange(5, 10);
}
@Override
public int attackProc(Char enemy, int damage) {
damage = super.attackProc( enemy, damage );
if (Dungeon.hero.hasTalent(Talent.GO_FOR_THE_EYES)) {
Buff.prolong( enemy, Blindness.class, 1 + Dungeon.hero.pointsInTalent(Talent.GO_FOR_THE_EYES) );
}
return damage;
}
@Override
protected boolean act() {
viewDistance = (int)GameMath.gate(6, 6+Dungeon.hero.pointsInTalent(Talent.EAGLE_EYE), 8);
boolean result = super.act();
Dungeon.level.updateFieldOfView( this, fieldOfView );
GameScene.updateFog(pos, viewDistance+(int)Math.ceil(speed()));
return result;
}
@Override
public float speed() {
return 2f + Dungeon.hero.pointsInTalent(Talent.SWIFT_SPIRIT)/2f;
}
@Override
protected void spend(float time) {
super.spend(time);
timeRemaining -= time;
if (timeRemaining <= 0){
die(null);
}
}
@Override
public void destroy() {
super.destroy();
Dungeon.observe();
GameScene.updateFog();
}
@Override
public void followHero() {
GLog.i(Messages.get(this, "direct_follow"));
super.followHero();
}
@Override
public void targetChar(Char ch) {
GLog.i(Messages.get(this, "direct_attack"));
super.targetChar(ch);
}
@Override
public String description() {
String message = Messages.get(this, "desc", (int)timeRemaining);
if (dodgesUsed < Dungeon.hero.pointsInTalent(Talent.SWIFT_SPIRIT)){
message += "\n" + Messages.get(this, "desc_dodges", (Dungeon.hero.pointsInTalent(Talent.SWIFT_SPIRIT) - dodgesUsed));
}
return message;
}
private static final String DODGES_USED = "dodges_used";
private static final String TIME_REMAINING = "time_remaining";
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
bundle.put(DODGES_USED, dodgesUsed);
bundle.put(TIME_REMAINING, timeRemaining);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
dodgesUsed = bundle.getInt(DODGES_USED);
timeRemaining = bundle.getFloat(TIME_REMAINING);
}
}
//TODO real sprite
public static class HawkSprite extends BatSprite {
@Override
public void resetColor() {
super.resetColor();
tint(0, 1f, 1f, 1f);
}
}
}

View File

@ -44,6 +44,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Shadows;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.huntress.SpiritHawk;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Bestiary;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
@ -1120,6 +1121,18 @@ public abstract class Level implements Bundlable {
}
}
if (c instanceof SpiritHawk.HawkAlly && Dungeon.hero.pointsInTalent(Talent.EAGLE_EYE) >= 3){
int range = 1+(Dungeon.hero.pointsInTalent(Talent.EAGLE_EYE)-2);
for (Mob mob : mobs) {
int p = mob.pos;
if (!fieldOfView[p] && distance(c.pos, p) <= range) {
for (int i : PathFinder.NEIGHBOURS9) {
fieldOfView[mob.pos + i] = true;
}
}
}
}
//Currently only the hero can get mind vision or awareness
if (c.isAlive() && c == Dungeon.hero) {
@ -1171,7 +1184,9 @@ public abstract class Level implements Bundlable {
}
for (Mob m : mobs){
if (m instanceof WandOfWarding.Ward || m instanceof WandOfRegrowth.Lotus){
if (m instanceof WandOfWarding.Ward
|| m instanceof WandOfRegrowth.Lotus
|| m instanceof SpiritHawk.HawkAlly){
if (m.fieldOfView == null || m.fieldOfView.length != length()){
m.fieldOfView = new boolean[length()];
Dungeon.level.updateFieldOfView( m, m.fieldOfView );