From 076b26302cb714c6f343d59574e7db7fbba640c1 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Sun, 23 Aug 2020 16:45:20 -0400 Subject: [PATCH] v0.8.2c: big refactor to cursed wand effects, fixes various minor bugs --- .../actors/mobs/Elemental.java | 15 +- .../items/wands/CursedWand.java | 397 ++++++++---------- .../sprites/ElementalSprite.java | 12 +- 3 files changed, 181 insertions(+), 243 deletions(-) diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Elemental.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Elemental.java index 810210f82..e3d2ebf40 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Elemental.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Elemental.java @@ -44,7 +44,6 @@ import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.ElementalSprite; import com.watabou.noosa.audio.Sample; import com.watabou.utils.Bundle; -import com.watabou.utils.Callback; import com.watabou.utils.Random; import java.util.ArrayList; @@ -305,22 +304,12 @@ public abstract class Elemental extends Mob { @Override protected void meleeProc( Char enemy, int damage ) { - CursedWand.cursedZap( null, this, new Ballistica( pos, enemy.pos, Ballistica.MAGIC_BOLT ), new Callback() { - @Override - public void call() { - next(); - } - } ); + CursedWand.cursedEffect(null, this, enemy); } @Override protected void rangedProc( Char enemy ) { - CursedWand.cursedZap( null, this, new Ballistica( pos, enemy.pos, Ballistica.MAGIC_BOLT ), new Callback() { - @Override - public void call() { - next(); - } - } ); + CursedWand.cursedEffect(null, this, enemy); } } 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 f7a8d645e..1ec915538 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 @@ -22,6 +22,7 @@ package com.shatteredpixel.shatteredpixeldungeon.items.wands; import com.shatteredpixel.shatteredpixeldungeon.Assets; +import com.shatteredpixel.shatteredpixeldungeon.Challenges; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; @@ -69,6 +70,7 @@ import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions; 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.Random; import java.io.IOException; @@ -78,189 +80,154 @@ import java.util.ArrayList; public class CursedWand { private static float COMMON_CHANCE = 0.6f; - private static float UNCOMMON_CHANCE = 0.3f; + private static float UNCOMMON_CHANCE = 10.3f; private static float RARE_CHANCE = 0.09f; private static float VERY_RARE_CHANCE = 0.01f; public static void cursedZap(final Item origin, final Char user, final Ballistica bolt, final Callback afterZap){ + + cursedFX(user, bolt, new Callback() { + @Override + public void call() { + if (cursedEffect(origin, user, bolt.collisionPos)){ + if (afterZap != null) afterZap.call(); + } + } + }); + } + + public static boolean cursedEffect(final Item origin, final Char user, final Char target){ + return cursedEffect(origin, user, target.pos); + } + + public static boolean cursedEffect(final Item origin, final Char user, final int targetPos){ switch (Random.chances(new float[]{COMMON_CHANCE, UNCOMMON_CHANCE, RARE_CHANCE, VERY_RARE_CHANCE})){ - case 0: - default: - commonEffect(origin, user, bolt, afterZap); - break; + case 0: default: + return commonEffect(origin, user, targetPos); case 1: - uncommonEffect(origin, user, bolt, afterZap); - break; + return uncommonEffect(origin, user, targetPos); case 2: - rareEffect(origin, user, bolt, afterZap); - break; + return rareEffect(origin, user, targetPos); case 3: - veryRareEffect(origin, user, bolt, afterZap); - break; + return veryRareEffect(origin, user, targetPos); } } - private static void commonEffect(final Item origin, final Char user, final Ballistica bolt, final Callback afterZap){ + private static boolean commonEffect(final Item origin, final Char user, final int targetPos){ switch(Random.Int(4)){ //anti-entropy - case 0: - cursedFX(user, bolt, new Callback() { - public void call() { - Char target = Actor.findChar(bolt.collisionPos); - switch (Random.Int(2)){ - case 0: - if (target != null) - Buff.affect(target, Burning.class).reignite(target); - Buff.affect(user, Frost.class, Frost.DURATION); - break; - case 1: - Buff.affect(user, Burning.class).reignite(user); - if (target != null) - Buff.affect(target, Frost.class, Frost.DURATION); - break; - } - afterZap.call(); - } - }); - break; + case 0: default: + Char target = Actor.findChar(targetPos); + if (Random.Int(2) == 0) { + if (target != null) Buff.affect(target, Burning.class).reignite(target); + Buff.affect(user, Frost.class, Frost.DURATION); + } else { + Buff.affect(user, Burning.class).reignite(user); + if (target != null) Buff.affect(target, Frost.class, Frost.DURATION); + } + return true; //spawns some regrowth case 1: - cursedFX(user, bolt, new Callback() { - public void call() { - GameScene.add( Blob.seed(bolt.collisionPos, 30, Regrowth.class)); - afterZap.call(); - } - }); - break; + GameScene.add( Blob.seed(targetPos, 30, Regrowth.class)); + return true; //random teleportation case 2: - switch(Random.Int(2)){ - case 0: + if(Random.Int(2) == 0) { + if (user != null && !user.properties().contains(Char.Property.IMMOVABLE)) { ScrollOfTeleportation.teleportChar(user); - afterZap.call(); - break; - case 1: - cursedFX(user, bolt, new Callback() { - public void call() { - Char ch = Actor.findChar( bolt.collisionPos ); - if (ch != null && !ch.properties().contains(Char.Property.IMMOVABLE)) { - ScrollOfTeleportation.teleportChar(user); - } - afterZap.call(); - } - }); - break; + } else { + return cursedEffect(origin, user, targetPos); + } + } else { + Char ch = Actor.findChar( targetPos ); + if (ch != null && !ch.properties().contains(Char.Property.IMMOVABLE)) { + ScrollOfTeleportation.teleportChar(ch); + } else { + return cursedEffect(origin, user, targetPos); + } } - break; + return true; //random gas at location case 3: - cursedFX(user, bolt, new Callback() { - public void call() { - switch (Random.Int(3)) { - case 0: - GameScene.add( Blob.seed( bolt.collisionPos, 800, ConfusionGas.class ) ); - break; - case 1: - GameScene.add( Blob.seed( bolt.collisionPos, 500, ToxicGas.class ) ); - break; - case 2: - GameScene.add( Blob.seed( bolt.collisionPos, 200, ParalyticGas.class ) ); - break; - } - Sample.INSTANCE.play( Assets.Sounds.GAS ); - afterZap.call(); - } - }); - break; + Sample.INSTANCE.play( Assets.Sounds.GAS ); + switch (Random.Int(3)) { + case 0: default: + GameScene.add( Blob.seed( targetPos, 800, ConfusionGas.class ) ); + return true; + case 1: + GameScene.add( Blob.seed( targetPos, 500, ToxicGas.class ) ); + return true; + case 2: + GameScene.add( Blob.seed( targetPos, 200, ParalyticGas.class ) ); + return true; + } } } - private static void uncommonEffect(final Item origin, final Char user, final Ballistica bolt, final Callback afterZap){ + private static boolean uncommonEffect(final Item origin, final Char user, final int targetPos){ switch(Random.Int(4)){ //Random plant - case 0: - cursedFX(user, bolt, new Callback() { - public void call() { - int pos = bolt.collisionPos; - //place the plant infront of an enemy so they walk into it. - if (Actor.findChar(pos) != null && bolt.dist > 1) { - pos = bolt.path.get(bolt.dist - 1); - } + case 0: default: + int pos = targetPos; - if (pos == Terrain.EMPTY || - pos == Terrain.EMBERS || - pos == Terrain.EMPTY_DECO || - pos == Terrain.GRASS || - pos == Terrain.HIGH_GRASS || - pos == Terrain.FURROWED_GRASS) { - Dungeon.level.plant((Plant.Seed) Generator.randomUsingDefaults(Generator.Category.SEED), pos); - } - afterZap.call(); - } - }); - break; + if (Dungeon.level.map[pos] != Terrain.ALCHEMY + && !Dungeon.level.pit[pos] + && Dungeon.level.traps.get(pos) == null + && !Dungeon.isChallenged(Challenges.NO_HERBALISM)) { + Dungeon.level.plant((Plant.Seed) Generator.randomUsingDefaults(Generator.Category.SEED), pos); + } else { + return cursedEffect(origin, user, targetPos); + } + + return true; //Health transfer case 1: - final Char target = Actor.findChar( bolt.collisionPos ); + final Char target = Actor.findChar( targetPos ); if (target != null) { - cursedFX(user, bolt, new Callback() { - public void call() { - int damage = Dungeon.depth * 2; - Char toHeal, toDamage; - - switch (Random.Int(2)) { - case 0: default: - toHeal = user; - toDamage = target; - break; - case 1: - toHeal = target; - toDamage = user; - break; - } - toHeal.HP = Math.min(toHeal.HT, toHeal.HP + damage); - toHeal.sprite.emitter().burst(Speck.factory(Speck.HEALING), 3); - toDamage.damage(damage, origin == null ? toHeal : origin); - toDamage.sprite.emitter().start(ShadowParticle.UP, 0.05f, 10); - - if (toDamage == Dungeon.hero){ - Sample.INSTANCE.play(Assets.Sounds.CURSED); - if (!toDamage.isAlive()) { - if (origin != null) { - Dungeon.fail( origin.getClass() ); - GLog.n( Messages.get( CursedWand.class, "ondeath", origin.name() ) ); - } else { - Dungeon.fail( toHeal.getClass() ); - } - } + int damage = Dungeon.depth * 2; + Char toHeal, toDamage; + + if (Random.Int(2) == 0){ + toHeal = user; + toDamage = target; + } else { + toHeal = target; + toDamage = user; + } + toHeal.HP = Math.min(toHeal.HT, toHeal.HP + damage); + toHeal.sprite.emitter().burst(Speck.factory(Speck.HEALING), 3); + toDamage.damage(damage, origin == null ? toHeal : origin); + toDamage.sprite.emitter().start(ShadowParticle.UP, 0.05f, 10); + + if (toDamage == Dungeon.hero){ + Sample.INSTANCE.play(Assets.Sounds.CURSED); + if (!toDamage.isAlive()) { + if (origin != null) { + Dungeon.fail( origin.getClass() ); + GLog.n( Messages.get( CursedWand.class, "ondeath", origin.name() ) ); } else { - Sample.INSTANCE.play(Assets.Sounds.BURNING); + Dungeon.fail( toHeal.getClass() ); } - afterZap.call(); } - }); + } else { + Sample.INSTANCE.play(Assets.Sounds.BURNING); + } } else { - GLog.i(Messages.get(CursedWand.class, "nothing")); - afterZap.call(); + return cursedEffect(origin, user, targetPos); } - break; + return true; //Bomb explosion case 2: - cursedFX(user, bolt, new Callback() { - public void call() { - new Bomb().explode(bolt.collisionPos); - afterZap.call(); - } - }); - break; + new Bomb().explode(targetPos); + return true; //shock and recharge case 3: @@ -268,53 +235,45 @@ public class CursedWand { Buff.prolong(user, Recharging.class, Recharging.DURATION); ScrollOfRecharging.charge(user); SpellSprite.show(user, SpellSprite.CHARGE); - afterZap.call(); - break; + return true; } } - private static void rareEffect(final Item origin, final Char user, final Ballistica bolt, final Callback afterZap){ + private static boolean rareEffect(final Item origin, final Char user, final int targetPos){ switch(Random.Int(4)){ //sheep transformation - case 0: - if (user != Dungeon.hero){ - cursedZap(origin, user, bolt, afterZap); - return; - } - - cursedFX(user, bolt, new Callback() { - public void call() { - Char ch = Actor.findChar( bolt.collisionPos ); + case 0: default: - if (ch != null && ch != user - && !ch.properties().contains(Char.Property.BOSS) - && !ch.properties().contains(Char.Property.MINIBOSS)){ - Sheep sheep = new Sheep(); - sheep.lifespan = 10; - sheep.pos = ch.pos; - ch.destroy(); - ch.sprite.killAndErase(); - Dungeon.level.mobs.remove(ch); - TargetHealthIndicator.instance.target(null); - GameScene.add(sheep); - CellEmitter.get(sheep.pos).burst(Speck.factory(Speck.WOOL), 4); - Sample.INSTANCE.play(Assets.Sounds.PUFF); - Sample.INSTANCE.play(Assets.Sounds.SHEEP); - } else { - GLog.i(Messages.get(CursedWand.class, "nothing")); - } - afterZap.call(); - } - }); - break; + Char ch = Actor.findChar( targetPos ); + if (ch != null && !(ch instanceof Hero) + && !ch.properties().contains(Char.Property.BOSS) + && !ch.properties().contains(Char.Property.MINIBOSS)){ + Sheep sheep = new Sheep(); + sheep.lifespan = 10; + sheep.pos = ch.pos; + ch.destroy(); + ch.sprite.killAndErase(); + Dungeon.level.mobs.remove(ch); + TargetHealthIndicator.instance.target(null); + GameScene.add(sheep); + CellEmitter.get(sheep.pos).burst(Speck.factory(Speck.WOOL), 4); + Sample.INSTANCE.play(Assets.Sounds.PUFF); + Sample.INSTANCE.play(Assets.Sounds.SHEEP); + } else { + return cursedEffect(origin, user, targetPos); + } + return true; //curses! case 1: - if (user instanceof Hero) CursingTrap.curse( (Hero) user ); - afterZap.call(); - break; + if (user instanceof Hero) { + CursingTrap.curse( (Hero) user ); + } else { + return cursedEffect(origin, user, targetPos); + } + return true; //inter-level teleportation case 2: @@ -340,22 +299,20 @@ public class CursedWand { ScrollOfTeleportation.teleportChar(user); } - afterZap.call(); - break; + return true; //summon monsters case 3: - new SummoningTrap().set( user.pos ).activate(); - afterZap.call(); - break; + new SummoningTrap().set( targetPos ).activate(); + return true; } } - private static void veryRareEffect(final Item origin, final Char user, final Ballistica bolt, final Callback afterZap){ + private static boolean veryRareEffect(final Item origin, final Char user, final int targetPos){ switch(Random.Int(4)){ //great forest fire! - case 0: + case 0: default: for (int i = 0; i < Dungeon.level.length(); i++){ GameScene.add( Blob.seed(i, 15, Regrowth.class)); } @@ -366,51 +323,52 @@ public class CursedWand { Sample.INSTANCE.play(Assets.Sounds.TELEPORT); GLog.p(Messages.get(CursedWand.class, "grass")); GLog.w(Messages.get(CursedWand.class, "fire")); - afterZap.call(); - break; + return true; //golden mimic case 1: - cursedFX(user, bolt, new Callback() { - public void call() { - Mimic mimic = Mimic.spawnAt(bolt.collisionPos, new ArrayList(), GoldenMimic.class); - if (mimic != null) { - mimic.stopHiding(); - mimic.alignment = Char.Alignment.ENEMY; - Item reward; - do { - reward = Generator.random(Random.oneOf(Generator.Category.WEAPON, Generator.Category.ARMOR, - Generator.Category.RING, Generator.Category.WAND)); - } while (reward.level() < 1); - //play vfx/sfx manually as mimic isn't in the scene yet - Sample.INSTANCE.play(Assets.Sounds.MIMIC, 1, 0.85f); - CellEmitter.get(mimic.pos).burst(Speck.factory(Speck.STAR), 10); - mimic.items.clear(); - mimic.items.add(reward); - GameScene.add(mimic); - } else { - GLog.i(Messages.get(CursedWand.class, "nothing")); + + Char ch = Actor.findChar(targetPos); + int spawnCell = targetPos; + if (ch != null){ + ArrayList candidates = new ArrayList(); + for (int n : PathFinder.NEIGHBOURS8) { + int cell = targetPos + n; + if (Dungeon.level.passable[cell] && Actor.findChar( cell ) == null) { + candidates.add( cell ); } - - afterZap.call(); } - }); - break; + if (!candidates.isEmpty()){ + spawnCell = Random.element(candidates); + } else { + return cursedEffect(origin, user, targetPos); + } + } + + Mimic mimic = Mimic.spawnAt(spawnCell, new ArrayList(), GoldenMimic.class); + mimic.stopHiding(); + mimic.alignment = Char.Alignment.ENEMY; + Item reward; + do { + reward = Generator.random(Random.oneOf(Generator.Category.WEAPON, Generator.Category.ARMOR, + Generator.Category.RING, Generator.Category.WAND)); + } while (reward.level() < 1); + //play vfx/sfx manually as mimic isn't in the scene yet + Sample.INSTANCE.play(Assets.Sounds.MIMIC, 1, 0.85f); + CellEmitter.get(mimic.pos).burst(Speck.factory(Speck.STAR), 10); + mimic.items.clear(); + mimic.items.add(reward); + GameScene.add(mimic); + return true; //crashes the game, yes, really. case 2: - if (user != Dungeon.hero){ - cursedZap(origin, user, bolt, afterZap); - return; - } - try { Dungeon.saveAll(); if(Messages.lang() != Languages.ENGLISH){ //Don't bother doing this joke to none-english speakers, I doubt it would translate. - GLog.i(Messages.get(CursedWand.class, "nothing")); - afterZap.call(); + return cursedEffect(origin, user, targetPos); } else { GameScene.show( new WndOptions("CURSED WAND ERROR", "this application will now self-destruct", "abort", "retry", "fail") { @@ -426,21 +384,19 @@ public class CursedWand { } } ); + return false; } } catch(IOException e){ ShatteredPixelDungeon.reportException(e); - //oookay maybe don't kill the game if the save failed. - GLog.i(Messages.get(CursedWand.class, "nothing")); - afterZap.call(); + //maybe don't kill the game if the save failed. + return cursedEffect(origin, user, targetPos); } - break; //random transmogrification case 3: //skips this effect if there is no item to transmogrify if (origin == null || user != Dungeon.hero || !Dungeon.hero.belongings.contains(origin)){ - cursedZap(origin, user, bolt, afterZap); - return; + return cursedEffect(origin, user, targetPos); } origin.detach(Dungeon.hero.belongings.backpack); Item result; @@ -456,8 +412,7 @@ public class CursedWand { GLog.w( Messages.get(CursedWand.class, "transmogrify_other") ); } Dungeon.level.drop(result, user.pos).sprite.drop(); - afterZap.call(); - break; + return true; } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ElementalSprite.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ElementalSprite.java index 54a859d02..d6d123f75 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ElementalSprite.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ElementalSprite.java @@ -234,15 +234,9 @@ public abstract class ElementalSprite extends MobSprite { } public static class Chaos extends ElementalSprite { - - //no bolt, overrides zap instead - @Override - public void zap( int cell ) { - turnTo( ch.pos , cell ); - play( zap ); - - ((Elemental)ch).onZapComplete(); - Sample.INSTANCE.play( Assets.Sounds.ZAP ); + + { + boltType = MagicMissile.RAINBOW; } @Override