v0.9.3: fully implemented the redesigned Elemental Blast ability

This commit is contained in:
Evan Debenham 2021-05-13 20:54:45 -04:00
parent 8eedc0d215
commit 8e84b01738
5 changed files with 287 additions and 45 deletions

View File

@ -540,10 +540,10 @@ actors.hero.talent.necromancers_minions.desc=_+1:_ When a soul marked enemy dies
actors.hero.talent.blast_radius.title=blast radius
actors.hero.talent.blast_radius.desc=_+1:_ Elemental blast's radius is increased to _5 tiles_, from 4.\n\n_+2:_ Elemental blast's radius is increased to _6 tiles_, from 4.\n\n_+3:_ Elemental blast's radius is increased to _7 tiles_, from 4.\n\n_+4:_ Elemental blast's radius is increased to _8 tiles_, from 4.
actors.hero.talent.elemental_blast_2.title=TODO NAME
actors.hero.talent.elemental_blast_2.desc=TODO DESC
actors.hero.talent.elemental_blast_3.title=TODO NAME
actors.hero.talent.elemental_blast_3.desc=TODO DESC
actors.hero.talent.elemental_power.title=elemental power
actors.hero.talent.elemental_power.desc=_+1:_ The power of elemental blast is increased by _15%_.\n\n_+2:_ The power of elemental blast is increased by _30%_.\n\n_+3:_ The power of elemental blast is increased by _45%_.\n\n_+4:_ The power of elemental blast is increased by _60%_.
actors.hero.talent.reactive_barrier.title=reactive barrier
actors.hero.talent.reactive_barrier.desc=_+1:_ The Mage gains _2 shielding_ for every character affected by elemental blast.\n\n_+2:_ The Mage gains _4 shielding_ for every character affected by elemental blast.\n\n_+3:_ The Mage gains _6 shielding_ for every character affected by elemental blast.\n\n_+4:_ The Mage gains _8 shielding_ for every character affected by elemental blast.
actors.hero.talent.mage_2_1.title=TODO NAME
actors.hero.talent.mage_2_1.desc=TODO DESC

View File

@ -96,7 +96,7 @@ public enum Talent {
//Warlock T3
SOUL_EATER(46, 3), SOUL_SIPHON(47, 3), NECROMANCERS_MINIONS(48, 3),
//Elemental Blast T4
BLAST_RADIUS(49, 4), ELEMENTAL_BLAST_2(50, 4), ELEMENTAL_BLAST_3(51, 4),
BLAST_RADIUS(49, 4), ELEMENTAL_POWER(50, 4), REACTIVE_BARRIER(51, 4),
//??? T4
MAGE_2_1(52, 4), MAGE_2_2(53, 4), MAGE_2_3(54, 4),
//??? T4

View File

@ -25,16 +25,33 @@ 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.blobs.Blob;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Electricity;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Fire;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Freezing;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Barrier;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Blindness;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Charm;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Corrosion;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Frost;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Light;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Recharging;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots;
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.Mob;
import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.ClassArmor;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfMagicMapping;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfBlastWave;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfCorrosion;
@ -50,11 +67,17 @@ import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfRegrowth;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfTransfusion;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfWarding;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MagesStaff;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.ConeAOE;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Callback;
import com.watabou.utils.PathFinder;
import com.watabou.utils.Random;
import com.watabou.utils.Reflection;
import java.util.HashMap;
@ -77,6 +100,23 @@ public class ElementalBlast extends ArmorAbility {
effectTypes.put(WandOfRegrowth.class, MagicMissile.FOLIAGE_CONE);
}
private static final HashMap<Class<?extends Wand>, Float> damageFactors = new HashMap<>();
static {
damageFactors.put(WandOfMagicMissile.class, 0.5f);
damageFactors.put(WandOfLightning.class, 1f);
damageFactors.put(WandOfDisintegration.class, 1f);
damageFactors.put(WandOfFireblast.class, 1f);
damageFactors.put(WandOfCorrosion.class, 0f);
damageFactors.put(WandOfBlastWave.class, 0.67f);
damageFactors.put(WandOfLivingEarth.class, 0.5f);
damageFactors.put(WandOfFrost.class, 1f);
damageFactors.put(WandOfPrismaticLight.class, 0.67f);
damageFactors.put(WandOfWarding.class, 0f);
damageFactors.put(WandOfTransfusion.class, 0f);
damageFactors.put(WandOfCorruption.class, 0f);
damageFactors.put(WandOfRegrowth.class, 0f);
}
@Override
protected void activate(ClassArmor armor, Hero hero, Integer target) {
armor.charge -= chargeUse(hero);
@ -91,11 +131,6 @@ public class ElementalBlast extends ArmorAbility {
aim = new Ballistica(hero.pos, hero.pos + 1, Ballistica.WONT_STOP);
}
int aoeSize = 4 + hero.pointsInTalent(Talent.BLAST_RADIUS);
//TODO vary stopping based on wand? e.g. disint should absolutely ignore solid
ConeAOE aoe = new ConeAOE(aim, aoeSize, 360, Ballistica.STOP_SOLID | Ballistica.IGNORE_SOFT_SOLID | Ballistica.STOP_TARGET);
Class<? extends Wand> wandCls = null;
if (hero.belongings.getItem(MagesStaff.class) != null) {
wandCls = hero.belongings.getItem(MagesStaff.class).wandClass();
@ -106,6 +141,27 @@ public class ElementalBlast extends ArmorAbility {
return;
}
int aoeSize = 4 + hero.pointsInTalent(Talent.BLAST_RADIUS);
int projectileProps = Ballistica.STOP_SOLID | Ballistica.STOP_TARGET;
//### Special Projectile Properties ###
//*** Wand of Disintegration ***
if (wandCls == WandOfDisintegration.class){
projectileProps = Ballistica.STOP_TARGET;
//*** Wand of Fireblast ***
} else if (wandCls == WandOfFireblast.class){
projectileProps = projectileProps | Ballistica.IGNORE_SOFT_SOLID;
//*** Wand of Warding ***
} else if (wandCls == WandOfWarding.class){
projectileProps = Ballistica.STOP_TARGET;
}
ConeAOE aoe = new ConeAOE(aim, aoeSize, 360, projectileProps);
for (Ballistica ray : aoe.outerRays){
((MagicMissile)hero.sprite.parent.recycle( MagicMissile.class )).reset(
effectTypes.get(wandCls),
@ -115,7 +171,10 @@ public class ElementalBlast extends ArmorAbility {
);
}
final float effectMulti = 1f + 0.15f*hero.pointsInTalent(Talent.ELEMENTAL_POWER);
//cast a ray 2/3 the way, and do effects
Class<? extends Wand> finalWandCls = wandCls;
((MagicMissile)hero.sprite.parent.recycle( MagicMissile.class )).reset(
effectTypes.get(wandCls),
hero.sprite,
@ -124,17 +183,206 @@ public class ElementalBlast extends ArmorAbility {
@Override
public void call() {
int charsHit = 0;
Freezing freeze = (Freezing)Dungeon.level.blobs.get( Freezing.class );
Fire fire = (Fire)Dungeon.level.blobs.get( Fire.class );
for (int cell : aoe.cells) {
Char mob = Actor.findChar(cell);
//TODO different effect for each wand
if ( mob != null && mob.alignment != Char.Alignment.ALLY) {
Buff.affect( mob, Burning.class ).reignite( mob );
Buff.prolong( mob, Roots.class, Roots.DURATION );
mob.damage(Random.NormalIntRange(4, 16 + Dungeon.depth), new Burning());
//### Cell effects ###
//*** Wand of Lightning ***
if (finalWandCls == WandOfLightning.class){
if (Dungeon.level.water[cell]){
GameScene.add( Blob.seed( cell, 4, Electricity.class ) );
}
//*** Wand of Fireblast ***
} else if (finalWandCls == WandOfFireblast.class){
if (Dungeon.level.map[cell] == Terrain.DOOR){
Level.set(cell, Terrain.OPEN_DOOR);
GameScene.updateMap(cell);
}
if (freeze != null){
freeze.clear(cell);
}
if (Dungeon.level.flamable[cell]){
GameScene.add( Blob.seed( cell, 4, Fire.class ) );
}
//*** Wand of Frost ***
} else if (finalWandCls == WandOfFrost.class){
if (fire != null){
fire.clear(cell);
}
//*** Wand of Prismatic Light ***
} else if (finalWandCls == WandOfPrismaticLight.class){
for (int n : PathFinder.NEIGHBOURS9) {
int c = cell+n;
if (Dungeon.level.discoverable[c]) {
Dungeon.level.mapped[c] = true;
}
int terr = Dungeon.level.map[c];
if ((Terrain.flags[terr] & Terrain.SECRET) != 0) {
Dungeon.level.discover(c);
GameScene.discoverTile(c, terr);
ScrollOfMagicMapping.discover(c);
}
}
//TODO affect hero?
//*** Wand of Regrowth ***
} else if (finalWandCls == WandOfRegrowth.class){
//TODO: spend 3 charges worth of regrowth energy from staff?
int t = Dungeon.level.map[cell];
if (Random.Float() < 0.33f*effectMulti) {
if ((t == Terrain.EMPTY || t == Terrain.EMPTY_DECO || t == Terrain.EMBERS
|| t == Terrain.GRASS || t == Terrain.FURROWED_GRASS)
&& Dungeon.level.plants.get(cell) == null) {
Level.set(cell, Terrain.HIGH_GRASS);
GameScene.updateMap(cell);
}
}
}
//### Deal damage ###
Char mob = Actor.findChar(cell);
int damage = Math.round(Random.NormalIntRange(10, 20)
* effectMulti
* damageFactors.get(finalWandCls));
if (mob != null && damage > 0 && mob.alignment != Char.Alignment.ALLY){
mob.damage(damage, Reflection.newInstance(finalWandCls));
charsHit++;
}
//### Other Char Effects ###
if (mob != null && mob != hero){
//*** Wand of Lightning ***
if (finalWandCls == WandOfLightning.class){
if (mob.isAlive() && mob.alignment != Char.Alignment.ALLY) {
Buff.affect( mob, Paralysis.class, effectMulti*Paralysis.DURATION/2 );
}
//*** Wand of Fireblast ***
} else if (finalWandCls == WandOfFireblast.class){
if (mob.isAlive() && mob.alignment != Char.Alignment.ALLY) {
Buff.affect( mob, Burning.class ).reignite( mob );
}
//*** Wand of Corrosion ***
} else if (finalWandCls == WandOfCorrosion.class){
if (mob.isAlive() && mob.alignment != Char.Alignment.ALLY) {
Buff.affect( mob, Corrosion.class ).set(3, Math.round(6*effectMulti));
charsHit++;
}
//*** Wand of Blast Wave ***
} else if (finalWandCls == WandOfBlastWave.class){
if (mob.alignment != Char.Alignment.ALLY) {
Ballistica aim = new Ballistica(hero.pos, mob.pos, Ballistica.WONT_STOP);
int knockback = aoeSize + 1 - (int)Dungeon.level.trueDistance(hero.pos, mob.pos);
knockback *= effectMulti;
WandOfBlastWave.throwChar(mob,
new Ballistica(mob.pos, aim.collisionPos, Ballistica.MAGIC_BOLT),
knockback,
true);
}
//*** Wand of Frost ***
} else if (finalWandCls == WandOfFrost.class){
if (mob.isAlive() && mob.alignment != Char.Alignment.ALLY) {
Buff.affect( mob, Frost.class, effectMulti*Frost.DURATION );
}
//*** Wand of Prismatic Light ***
} else if (finalWandCls == WandOfPrismaticLight.class){
if (mob.isAlive() && mob.alignment != Char.Alignment.ALLY) {
Buff.prolong(mob, Blindness.class, effectMulti*Blindness.DURATION/2);
charsHit++;
}
//*** Wand of Warding ***
} else if (finalWandCls == WandOfWarding.class){
if (mob instanceof WandOfWarding.Ward){
((WandOfWarding.Ward) mob).wandHeal(0, effectMulti);
charsHit++;
}
//*** Wand of Transfusion ***
} else if (finalWandCls == WandOfTransfusion.class){
if(mob.alignment == Char.Alignment.ALLY || mob.buff(Charm.class) != null){
int healing = Math.round(10*effectMulti);
int shielding = (mob.HP + healing) - mob.HT;
if (shielding > 0){
healing -= shielding;
Buff.affect(mob, Barrier.class).setShield(shielding);
} else {
shielding = 0;
}
mob.HP += healing;
mob.sprite.emitter().burst(Speck.factory(Speck.HEALING), 4);
mob.sprite.showStatus(CharSprite.POSITIVE, "+%dHP", healing + shielding);
} else {
if (!mob.properties().contains(Char.Property.UNDEAD)) {
Charm charm = Buff.affect(mob, Charm.class, effectMulti*Charm.DURATION/2f);
charm.object = hero.id();
charm.ignoreHeroAllies = true;
mob.sprite.centerEmitter().start(Speck.factory(Speck.HEART), 0.2f, 3);
} else {
damage = Math.round(Random.NormalIntRange(10, 20) * effectMulti);
mob.damage(damage, Reflection.newInstance(finalWandCls));
mob.sprite.emitter().start(ShadowParticle.UP, 0.05f, 10);
}
}
charsHit++;
//*** Wand of Corruption ***
} else if (finalWandCls == WandOfCorruption.class){
if (mob.isAlive() && mob.alignment != Char.Alignment.ALLY) {
Buff.prolong(mob, Amok.class, effectMulti*5f);
charsHit++;
}
//*** Wand of Regrowth ***
} else if (finalWandCls == WandOfRegrowth.class){
if (mob.alignment != Char.Alignment.ALLY) {
Buff.prolong( mob, Roots.class, effectMulti*Roots.DURATION );
charsHit++;
}
}
}
}
//### Self-Effects ###
//*** Wand of Magic Missile ***
if (finalWandCls == WandOfMagicMissile.class) {
Buff.affect(hero, Recharging.class, effectMulti* Recharging.DURATION / 2f);
//*** Wand of Living Earth ***
} else if (finalWandCls == WandOfLivingEarth.class && charsHit > 0){
for (Mob m : Dungeon.level.mobs){
if (m instanceof WandOfLivingEarth.EarthGuardian){
((WandOfLivingEarth.EarthGuardian) m).setInfo(hero, 0, Math.round(effectMulti*charsHit*5));
m.sprite.centerEmitter().burst(MagicMissile.EarthParticle.ATTRACT, 8 + charsHit);
break;
}
}
//*** Wand of Prismatic Light ***
} else if (finalWandCls == WandOfPrismaticLight.class){
Buff.prolong( hero, Light.class, effectMulti*50f);
}
if (charsHit > 0 && hero.hasTalent(Talent.REACTIVE_BARRIER)){
Buff.affect(hero, Barrier.class).setShield(charsHit*2*hero.pointsInTalent(Talent.REACTIVE_BARRIER));
}
hero.spendAndNext(Actor.TICK);
}
@ -145,22 +393,12 @@ public class ElementalBlast extends ArmorAbility {
Invisibility.dispel();
hero.busy();
Sample.INSTANCE.play( Assets.Sounds.CHARGEUP );
Sample.INSTANCE.play( Assets.Sounds.CHARGEUP );
/*for (Mob mob : Dungeon.level.mobs.toArray(new Mob[0])) {
if (Dungeon.level.heroFOV[mob.pos]
&& mob.alignment != Char.Alignment.ALLY) {
Buff.affect( mob, Burning.class ).reignite( mob );
Buff.prolong( mob, Roots.class, Roots.DURATION );
mob.damage(Random.NormalIntRange(4, 16 + Dungeon.depth), new Burning());
}
}
*/
}
@Override
public Talent[] talents() {
return new Talent[]{Talent.BLAST_RADIUS, Talent.ELEMENTAL_BLAST_2, Talent.ELEMENTAL_BLAST_3, Talent.HEROIC_ENERGY};
return new Talent[]{Talent.BLAST_RADIUS, Talent.ELEMENTAL_POWER, Talent.REACTIVE_BARRIER, Talent.HEROIC_ENERGY};
}
}

View File

@ -303,7 +303,7 @@ public class WandOfLivingEarth extends DamageWand {
private int wandLevel = -1;
private void setInfo(Hero hero, int wandLevel, int healthToAdd){
public void setInfo(Hero hero, int wandLevel, int healthToAdd){
if (wandLevel > this.wandLevel) {
this.wandLevel = wandLevel;
HT = 16 + 8 * wandLevel;

View File

@ -232,7 +232,11 @@ public class WandOfWarding extends Wand {
}
private void wandHeal( int wandLevel ){
public void wandHeal( int wandLevel ){
wandHeal( wandLevel, 1f );
}
public void wandHeal( int wandLevel, float healFactor ){
if (this.wandLevel < wandLevel){
this.wandLevel = wandLevel;
}
@ -242,13 +246,13 @@ public class WandOfWarding extends Wand {
default:
return;
case 4:
heal = 9;
heal = Math.round(9 * healFactor);
break;
case 5:
heal = 12;
heal = Math.round(12 * healFactor);
break;
case 6:
heal = 16;
heal = Math.round(16 * healFactor);
break;
}