diff --git a/core/src/main/assets/messages/actors/actors.properties b/core/src/main/assets/messages/actors/actors.properties index 0b479eee3..b85c9644a 100644 --- a/core/src/main/assets/messages/actors/actors.properties +++ b/core/src/main/assets/messages/actors/actors.properties @@ -58,9 +58,6 @@ actors.buffs.adrenalinesurge.desc=A surge of great might, but sadly not permanen actors.buffs.amok.name=amok actors.buffs.amok.desc=Amok causes a state of great rage and confusion in its target.\n\nWhen a creature is amoked, they will attack whatever is near them, whether they be friend or foe.\n\nTurns of amok remaining: %s. -actors.buffs.ankhinvulnerability.name=invulnerable -actors.buffs.ankhinvulnerability.desc=Your blessed ankh has expended its energy, granting you some health and a brief period of invulnerability!\n\nTurns remaining: %s. - actors.buffs.arcanearmor.name=arcane armor actors.buffs.arcanearmor.desc=A thin shield is surrounding you, blocking some of the damage from magical attacks.\n\nYour magical armor is currently boosted by: 0-%d.\n\nTurns until arcane armor weakens: %s. @@ -253,6 +250,9 @@ actors.buffs.hunger.desc=\n\nHunger slowly increases as you spend time in the du actors.buffs.invisibility.name=invisible actors.buffs.invisibility.desc=You are completely blended into the surrounding terrain, making you impossible to see.\n\nWhile you are invisible enemies are unable to attack or follow you. Physical attacks and magical effects (such as scrolls and wands) will immediately cancel invisibility.\n\nTurns of invisibility remaining: %s. +actors.buffs.invulnerability.name=invulnerable +actors.buffs.invulnerability.desc=You are suffuse with great protective power, granting you a brief period of invulnerability!\n\nTurns remaining: %s. + actors.buffs.levitation.name=levitating actors.buffs.levitation.desc=A magical force is levitating you over the ground, making you feel weightless.\n\nWhile levitating you ignore all ground-based effects. Traps won't trigger, water won't put out fire, plants won't be trampled, roots will miss you, and you will hover right over pits. Be careful, as all these things can come into effect the second the levitation ends!\n\nTurns of levitation remaining: %s. diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index 5bc6c134b..9ac49ad6d 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -1392,6 +1392,8 @@ items.trinkets.trinket$placeholder.name=trinket ###wands items.wands.cursedwand.ondeath=You were killed by your own %s. items.wands.cursedwand.nothing=Nothing happens. +items.wands.cursedwand.mass_invuln=Brilliant light erupts from your wand! +items.wands.cursedwand.petrify=You suddenly freeze in place! items.wands.cursedwand.grass=Grass erupts around you! items.wands.cursedwand.fire=You smell burning... items.wands.cursedwand.transmogrify_wand=Your wand transmogrifies into a different item! diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java index 5f266bbb5..566b0d058 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java @@ -52,6 +52,9 @@ public class ShatteredPixelDungeon extends Game { com.watabou.utils.Bundle.addAlias( com.shatteredpixel.shatteredpixeldungeon.actors.mobs.MobSpawner.class, "com.shatteredpixel.shatteredpixeldungeon.levels.Level$Respawner" ); + com.watabou.utils.Bundle.addAlias( + com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invulnerability.class, + "com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AnkhInvulnerability" ); //pre-v2.4.0 com.watabou.utils.Bundle.addAlias( diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java index 7f4750ea7..ab3b7bbf4 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java @@ -30,6 +30,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Adrenaline; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AllyBuff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invulnerability; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ArcaneArmor; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AscensionChallenge; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Barkskin; @@ -1146,7 +1147,7 @@ public abstract class Char extends Actor { //similar to isImmune, but only factors in damage. //Is used in AI decision-making public boolean isInvulnerable( Class effect ){ - return buff(Challenge.SpectatorFreeze.class) != null; + return buff(Challenge.SpectatorFreeze.class) != null || buff(Invulnerability.class) != null; } protected HashSet properties = new HashSet<>(); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Burning.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Burning.java index c3469805b..0f4c9ebc3 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Burning.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Burning.java @@ -99,7 +99,9 @@ public class Burning extends Buff implements Hero.Doom { int damage = Random.NormalIntRange( 1, 3 + Dungeon.scalingDepth()/4 ); Buff.detach( target, Chill.class); - if (target instanceof Hero && target.buff(TimekeepersHourglass.timeStasis.class) == null) { + if (target instanceof Hero + && target.buff(TimekeepersHourglass.timeStasis.class) == null + && target.buff(TimeStasis.class) == null) { Hero hero = (Hero)target; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/AnkhInvulnerability.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Invulnerability.java similarity index 92% rename from core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/AnkhInvulnerability.java rename to core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Invulnerability.java index 7fa8f9c71..4b5196708 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/AnkhInvulnerability.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Invulnerability.java @@ -24,16 +24,18 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.buffs; import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator; -public class AnkhInvulnerability extends FlavourBuff { +public class Invulnerability extends FlavourBuff { { type = Buff.buffType.POSITIVE; + announced = true; } public static final float DURATION = 3f; @Override public void fx(boolean on) { + if (target.buff(ChampionEnemy.class) != null) return; if (on) target.sprite.aura( 0xFFFF00 ); else target.sprite.clearAura(); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/SuperNovaTracker.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/SuperNovaTracker.java index 6f8311b18..c6fe23221 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/SuperNovaTracker.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/SuperNovaTracker.java @@ -102,7 +102,7 @@ public class SuperNovaTracker extends Buff { Sample.INSTANCE.playDelayed(Assets.Sounds.BLAST, 0.5f); PixelScene.shake( 5, 2f ); for (int i = 0; i < Dungeon.level.length(); i++){ - if (fieldOfView[i]){ + if (fieldOfView[i] && !Dungeon.level.solid[i]){ new Bomb.ConjuredBomb().explode(i); //yes, a bomb at every cell //this means that something in the blast effectively takes: //5.33x bomb dmg when fully inside diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/TimeStasis.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/TimeStasis.java new file mode 100644 index 000000000..26495b029 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/TimeStasis.java @@ -0,0 +1,80 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2024 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.shatteredpixel.shatteredpixeldungeon.actors.buffs; + +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.Char; +import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite; + +//this is largely a copy-paste from timekeeper's hourglass with the artifact-specific code removed +public class TimeStasis extends FlavourBuff { + + { + type = Buff.buffType.POSITIVE; + actPriority = BUFF_PRIO-3; //acts after all other buffs, so they are prevented + } + + @Override + public boolean attachTo(Char target) { + + if (super.attachTo(target)) { + + target.invisible++; + target.paralysed++; + target.next(); + + if (Dungeon.hero != null) { + Dungeon.observe(); + } + + return true; + } else { + return false; + } + } + + @Override + protected void spend(float time) { + super.spend(time); + + //don't punish the player for going into stasis frequently + Hunger hunger = Buff.affect(target, Hunger.class); + if (hunger != null && !hunger.isStarving()) { + hunger.affectHunger(cooldown(), true); + } + } + + @Override + public void detach() { + if (target.invisible > 0) target.invisible--; + if (target.paralysed > 0) target.paralysed--; + super.detach(); + Dungeon.observe(); + } + + @Override + public void fx(boolean on) { + if (on) target.sprite.add( CharSprite.State.PARALYSED ); + else if (target.invisible == 0) target.sprite.remove( CharSprite.State.PARALYSED ); + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java index 2a5b8b6a5..a1ae6e04f 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java @@ -34,7 +34,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob; import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.SacrificialFire; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AdrenalineSurge; -import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AnkhInvulnerability; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invulnerability; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ArtifactRecharge; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AscensionChallenge; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Awareness; @@ -62,6 +62,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.PhysicalEmpower; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Recharging; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Regeneration; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.SnipersMark; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.TimeStasis; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vertigo; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.ArmorAbility; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.duelist.Challenge; @@ -1439,8 +1440,10 @@ public class Hero extends Char { @Override public void damage( int dmg, Object src ) { - if (buff(TimekeepersHourglass.timeStasis.class) != null) + if (buff(TimekeepersHourglass.timeStasis.class) != null + || buff(TimeStasis.class) != null) { return; + } //regular damage interrupt, triggers on any damage except specific mild DOT effects // unless the player recently hit 'continue moving', in which case this is ignored @@ -1920,7 +1923,8 @@ public class Hero extends Char { @Override public boolean add( Buff buff ) { - if (buff(TimekeepersHourglass.timeStasis.class) != null) { + if (buff(TimekeepersHourglass.timeStasis.class) != null + || buff(TimeStasis.class) != null) { return false; } @@ -1984,7 +1988,7 @@ public class Hero extends Char { this.HP = HT / 4; PotionOfHealing.cure(this); - Buff.prolong(this, AnkhInvulnerability.class, AnkhInvulnerability.DURATION); + Buff.prolong(this, Invulnerability.class, Invulnerability.DURATION); SpellSprite.show(this, SpellSprite.ANKH); GameScene.flash(0x80FFFF40); @@ -2253,11 +2257,6 @@ public class Hero extends Char { return super.isImmune(effect); } - @Override - public boolean isInvulnerable(Class effect) { - return super.isInvulnerable(effect) || buff(AnkhInvulnerability.class) != null; - } - public boolean search( boolean intentional ) { if (!isAlive()) return false; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/CursedWand.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/CursedWand.java index 85905723c..ade72a121 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/CursedWand.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/CursedWand.java @@ -35,14 +35,19 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Fire; import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ParalyticGas; import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Regrowth; import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invulnerability; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Bless; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Frost; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.HeroDisguise; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Hex; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Ooze; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Recharging; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.SuperNovaTracker; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.TimeStasis; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.GoldenMimic; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic; @@ -55,7 +60,11 @@ import com.shatteredpixel.shatteredpixeldungeon.effects.Lightning; import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile; import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; import com.shatteredpixel.shatteredpixeldungeon.effects.SpellSprite; +import com.shatteredpixel.shatteredpixeldungeon.effects.Splash; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.FlameParticle; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.PoisonParticle; import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.SparkParticle; import com.shatteredpixel.shatteredpixeldungeon.items.Generator; import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.bombs.Bomb; @@ -70,6 +79,8 @@ import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain; import com.shatteredpixel.shatteredpixeldungeon.levels.traps.CursingTrap; import com.shatteredpixel.shatteredpixeldungeon.levels.traps.SummoningTrap; import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica; +import com.shatteredpixel.shatteredpixeldungeon.mechanics.ConeAOE; +import com.shatteredpixel.shatteredpixeldungeon.mechanics.ShadowCaster; import com.shatteredpixel.shatteredpixeldungeon.messages.Languages; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.plants.Plant; @@ -85,6 +96,7 @@ import com.watabou.noosa.Game; import com.watabou.noosa.audio.Sample; import com.watabou.utils.Callback; import com.watabou.utils.PathFinder; +import com.watabou.utils.Point; import com.watabou.utils.Random; import java.io.IOException; @@ -459,6 +471,10 @@ public class CursedWand { RARE_EFFECTS.add(new CurseEquipment()); RARE_EFFECTS.add(new InterFloorTeleport()); RARE_EFFECTS.add(new SummonMonsters()); + RARE_EFFECTS.add(new FireBall()); + RARE_EFFECTS.add(new ConeOfColors()); + RARE_EFFECTS.add(new MassInvuln()); + RARE_EFFECTS.add(new Petrify()); } public static CursedEffect randomRareEffect(){ @@ -576,6 +592,204 @@ public class CursedWand { } } + public static class FireBall extends CursedEffect { + + @Override + public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) { + + Point c = Dungeon.level.cellToPoint(bolt.collisionPos); + boolean[] fieldOfView = new boolean[Dungeon.level.length()]; + ShadowCaster.castShadow(c.x, c.y, Dungeon.level.width(), fieldOfView, Dungeon.level.solid, 3); + + for (int i = 0; i < Dungeon.level.length(); i++){ + if (fieldOfView[i] && !Dungeon.level.solid[i]){ + //does not directly harm allies + if (positiveOnly && Actor.findChar(i) != null && Actor.findChar(i).alignment == Char.Alignment.ALLY){ + continue; + } + + CellEmitter.get(i).burst(FlameParticle.FACTORY, 10); + if (Actor.findChar(i) != null){ + Char ch = Actor.findChar(i); + Burning burning = Buff.affect(ch, Burning.class); + burning.reignite(ch); + int dmg = Random.NormalIntRange(5 + Dungeon.scalingDepth(), 10 + Dungeon.scalingDepth()*2); + ch.damage(dmg, burning); + } + if (Dungeon.level.flamable[i]){ + GameScene.add(Blob.seed(i, 4, Fire.class)); + } + + } + } + WandOfBlastWave.BlastWave.blast(bolt.collisionPos, 6); + Sample.INSTANCE.play(Assets.Sounds.BLAST); + Sample.INSTANCE.play(Assets.Sounds.BURNING); + + return false; + } + } + + public static class ConeOfColors extends CursedEffect { + + private ConeAOE cone = null; + + @Override + public void FX(Item origin, Char user, Ballistica bolt, Callback callback) { + + //need to re-do bolt as it should go through chars + bolt = new Ballistica(bolt.sourcePos, bolt.collisionPos, Ballistica.STOP_SOLID); + + cone = new ConeAOE( bolt, + 8, + 90, + Ballistica.STOP_SOLID); + + Ballistica longestRay = null; + for (Ballistica ray : cone.outerRays){ + if (longestRay == null || ray.dist > longestRay.dist){ + longestRay = ray; + } + ((MagicMissile)user.sprite.parent.recycle( MagicMissile.class )).reset( + MagicMissile.RAINBOW_CONE, + user.sprite, + ray.path.get(ray.dist), + null + ); + } + + //final zap at half distance of the longest ray, for timing of the actual effect + MagicMissile.boltFromChar( user.sprite.parent, + MagicMissile.RAINBOW_CONE, + user.sprite, + longestRay.path.get(longestRay.dist/2), + callback ); + Sample.INSTANCE.play( Assets.Sounds.ZAP ); + } + + @Override + public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) { + + ArrayList affectedChars = new ArrayList<>(); + if (cone == null && Actor.findChar(bolt.collisionPos) != null){ + //cone may be null in cases like chaos elemental melee + affectedChars.add(Actor.findChar(bolt.collisionPos)); + } else { + for (Integer cell : cone.cells){ + if (cell == user.pos) { + continue; + } + if (Actor.findChar(cell) != null){ + affectedChars.add(Actor.findChar(cell)); + } + } + } + + for (Char ch : affectedChars){ + //positive only does not harm allies + if (positiveOnly && ch.alignment == Char.Alignment.ALLY){ + continue; + } else { + + int dmg = Random.NormalIntRange(5 + Dungeon.scalingDepth(), 10 + Dungeon.scalingDepth()*2); + switch (Random.Int(5)){ + case 0: default: + Burning burning = Buff.affect(ch, Burning.class); + burning.reignite(ch); + ch.damage(dmg, burning); + ch.sprite.emitter().burst(FlameParticle.FACTORY, 20); + break; + case 1: + ch.damage(dmg, new Frost()); + if (ch.isAlive()) Buff.affect(ch, Frost.class, Frost.DURATION); + Splash.at( ch.sprite.center(), 0xFFB2D6FF, 20 ); + break; + case 2: + Poison poison = Buff.affect(ch, Poison.class); + poison.set(3 + Dungeon.scalingDepth() / 2); + ch.damage(dmg, poison); + ch.sprite.emitter().burst(PoisonParticle.SPLASH, 20); + break; + case 3: + Ooze ooze = Buff.affect(ch, Ooze.class); + ooze.set(Ooze.DURATION); + ch.damage(dmg, ooze); + Splash.at( ch.sprite.center(), 0x000000, 20 ); + break; + case 4: + ch.damage(dmg, new Electricity()); + if (ch.isAlive()) Buff.affect(ch, Paralysis.class, Paralysis.DURATION); + ch.sprite.emitter().burst(SparkParticle.FACTORY, 20); + break; + } + + if (ch == Dungeon.hero && !ch.isAlive()){ + if (user == Dungeon.hero && origin != null) { + Badges.validateDeathFromFriendlyMagic(); + Dungeon.fail( origin ); + GLog.n( Messages.get( CursedWand.class, "ondeath", origin.name() ) ); + } else { + Badges.validateDeathFromEnemyMagic(); + Dungeon.fail( user ); + } + } + + } + } + return true; + } + } + + public static class MassInvuln extends CursedEffect { + + @Override + public void FX(Item origin, Char user, Ballistica bolt, Callback callback) { + callback.call(); //no vfx + } + + @Override + public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) { + + for (Char ch : Actor.chars()){ + Buff.affect(ch, Invulnerability.class, 10f); + Buff.affect(ch, Bless.class, Bless.DURATION); + } + + new Flare(5, 48).color(0xFFFF00, true).show(user.sprite, 3f); + GameScene.flash(0x80FFFF40); + Sample.INSTANCE.play(Assets.Sounds.TELEPORT); + GLog.p(Messages.get(CursedWand.class, "mass_invuln")); + + return true; + } + + } + + public static class Petrify extends CursedEffect { + + @Override + public boolean valid(Item origin, Char user, Ballistica bolt, boolean positiveOnly) { + return user == Dungeon.hero; + } + + @Override + public void FX(Item origin, Char user, Ballistica bolt, Callback callback) { + callback.call(); //no vfx + } + + @Override + public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) { + + Buff.affect(user, TimeStasis.class, 100f); + Sample.INSTANCE.play(Assets.Sounds.TELEPORT); + + user.sprite.emitter().burst(Speck.factory(Speck.STEAM), 10); + GLog.w(Messages.get(CursedWand.class, "petrify")); + + return true; + } + } + //************************* //*** Very Rare Effects *** //************************* diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfBlastWave.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfBlastWave.java index 06fcc5997..68c0beb4f 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfBlastWave.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfBlastWave.java @@ -248,19 +248,21 @@ public class WandOfBlastWave extends DamageWand { private static final float TIME_TO_FADE = 0.2f; private float time; + private float size; public BlastWave(){ super(Effects.get(Effects.Type.RIPPLE)); origin.set(width / 2, height / 2); } - public void reset(int pos) { + public void reset(int pos, float size) { revive(); x = (pos % Dungeon.level.width()) * DungeonTilemap.SIZE + (DungeonTilemap.SIZE - width) / 2; y = (pos / Dungeon.level.width()) * DungeonTilemap.SIZE + (DungeonTilemap.SIZE - height) / 2; time = TIME_TO_FADE; + this.size = size; } @Override @@ -272,15 +274,19 @@ public class WandOfBlastWave extends DamageWand { } else { float p = time / TIME_TO_FADE; alpha(p); - scale.y = scale.x = (1-p)*3; + scale.y = scale.x = (1-p)*size; } } public static void blast(int pos) { + blast(pos, 3); + } + + public static void blast(int pos, float radius) { Group parent = Dungeon.hero.sprite.parent; BlastWave b = (BlastWave) parent.recycle(BlastWave.class); parent.bringToFront(b); - b.reset(pos); + b.reset(pos, radius); } }