v0.8.2c: big refactor to cursed wand effects, fixes various minor bugs

This commit is contained in:
Evan Debenham 2020-08-23 16:45:20 -04:00
parent 0a6127b843
commit 076b26302c
3 changed files with 181 additions and 243 deletions

View File

@ -44,7 +44,6 @@ import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ElementalSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.ElementalSprite;
import com.watabou.noosa.audio.Sample; import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Bundle; import com.watabou.utils.Bundle;
import com.watabou.utils.Callback;
import com.watabou.utils.Random; import com.watabou.utils.Random;
import java.util.ArrayList; import java.util.ArrayList;
@ -305,22 +304,12 @@ public abstract class Elemental extends Mob {
@Override @Override
protected void meleeProc( Char enemy, int damage ) { protected void meleeProc( Char enemy, int damage ) {
CursedWand.cursedZap( null, this, new Ballistica( pos, enemy.pos, Ballistica.MAGIC_BOLT ), new Callback() { CursedWand.cursedEffect(null, this, enemy);
@Override
public void call() {
next();
}
} );
} }
@Override @Override
protected void rangedProc( Char enemy ) { protected void rangedProc( Char enemy ) {
CursedWand.cursedZap( null, this, new Ballistica( pos, enemy.pos, Ballistica.MAGIC_BOLT ), new Callback() { CursedWand.cursedEffect(null, this, enemy);
@Override
public void call() {
next();
}
} );
} }
} }

View File

@ -22,6 +22,7 @@
package com.shatteredpixel.shatteredpixeldungeon.items.wands; package com.shatteredpixel.shatteredpixeldungeon.items.wands;
import com.shatteredpixel.shatteredpixeldungeon.Assets; import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Challenges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; 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.Game;
import com.watabou.noosa.audio.Sample; import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Callback; import com.watabou.utils.Callback;
import com.watabou.utils.PathFinder;
import com.watabou.utils.Random; import com.watabou.utils.Random;
import java.io.IOException; import java.io.IOException;
@ -78,189 +80,154 @@ import java.util.ArrayList;
public class CursedWand { public class CursedWand {
private static float COMMON_CHANCE = 0.6f; 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 RARE_CHANCE = 0.09f;
private static float VERY_RARE_CHANCE = 0.01f; private static float VERY_RARE_CHANCE = 0.01f;
public static void cursedZap(final Item origin, final Char user, final Ballistica bolt, final Callback afterZap){ 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})){ switch (Random.chances(new float[]{COMMON_CHANCE, UNCOMMON_CHANCE, RARE_CHANCE, VERY_RARE_CHANCE})){
case 0: case 0: default:
default: return commonEffect(origin, user, targetPos);
commonEffect(origin, user, bolt, afterZap);
break;
case 1: case 1:
uncommonEffect(origin, user, bolt, afterZap); return uncommonEffect(origin, user, targetPos);
break;
case 2: case 2:
rareEffect(origin, user, bolt, afterZap); return rareEffect(origin, user, targetPos);
break;
case 3: case 3:
veryRareEffect(origin, user, bolt, afterZap); return veryRareEffect(origin, user, targetPos);
break;
} }
} }
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)){ switch(Random.Int(4)){
//anti-entropy //anti-entropy
case 0: case 0: default:
cursedFX(user, bolt, new Callback() { Char target = Actor.findChar(targetPos);
public void call() { if (Random.Int(2) == 0) {
Char target = Actor.findChar(bolt.collisionPos); if (target != null) Buff.affect(target, Burning.class).reignite(target);
switch (Random.Int(2)){ Buff.affect(user, Frost.class, Frost.DURATION);
case 0: } else {
if (target != null) Buff.affect(user, Burning.class).reignite(user);
Buff.affect(target, Burning.class).reignite(target); if (target != null) Buff.affect(target, Frost.class, Frost.DURATION);
Buff.affect(user, Frost.class, Frost.DURATION); }
break; return true;
case 1:
Buff.affect(user, Burning.class).reignite(user);
if (target != null)
Buff.affect(target, Frost.class, Frost.DURATION);
break;
}
afterZap.call();
}
});
break;
//spawns some regrowth //spawns some regrowth
case 1: case 1:
cursedFX(user, bolt, new Callback() { GameScene.add( Blob.seed(targetPos, 30, Regrowth.class));
public void call() { return true;
GameScene.add( Blob.seed(bolt.collisionPos, 30, Regrowth.class));
afterZap.call();
}
});
break;
//random teleportation //random teleportation
case 2: case 2:
switch(Random.Int(2)){ if(Random.Int(2) == 0) {
case 0: if (user != null && !user.properties().contains(Char.Property.IMMOVABLE)) {
ScrollOfTeleportation.teleportChar(user); ScrollOfTeleportation.teleportChar(user);
afterZap.call(); } else {
break; return cursedEffect(origin, user, targetPos);
case 1: }
cursedFX(user, bolt, new Callback() { } else {
public void call() { Char ch = Actor.findChar( targetPos );
Char ch = Actor.findChar( bolt.collisionPos ); if (ch != null && !ch.properties().contains(Char.Property.IMMOVABLE)) {
if (ch != null && !ch.properties().contains(Char.Property.IMMOVABLE)) { ScrollOfTeleportation.teleportChar(ch);
ScrollOfTeleportation.teleportChar(user); } else {
} return cursedEffect(origin, user, targetPos);
afterZap.call(); }
}
});
break;
} }
break; return true;
//random gas at location //random gas at location
case 3: case 3:
cursedFX(user, bolt, new Callback() { Sample.INSTANCE.play( Assets.Sounds.GAS );
public void call() { switch (Random.Int(3)) {
switch (Random.Int(3)) { case 0: default:
case 0: GameScene.add( Blob.seed( targetPos, 800, ConfusionGas.class ) );
GameScene.add( Blob.seed( bolt.collisionPos, 800, ConfusionGas.class ) ); return true;
break; case 1:
case 1: GameScene.add( Blob.seed( targetPos, 500, ToxicGas.class ) );
GameScene.add( Blob.seed( bolt.collisionPos, 500, ToxicGas.class ) ); return true;
break; case 2:
case 2: GameScene.add( Blob.seed( targetPos, 200, ParalyticGas.class ) );
GameScene.add( Blob.seed( bolt.collisionPos, 200, ParalyticGas.class ) ); return true;
break; }
}
Sample.INSTANCE.play( Assets.Sounds.GAS );
afterZap.call();
}
});
break;
} }
} }
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)){ switch(Random.Int(4)){
//Random plant //Random plant
case 0: case 0: default:
cursedFX(user, bolt, new Callback() { int pos = targetPos;
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);
}
if (pos == Terrain.EMPTY || if (Dungeon.level.map[pos] != Terrain.ALCHEMY
pos == Terrain.EMBERS || && !Dungeon.level.pit[pos]
pos == Terrain.EMPTY_DECO || && Dungeon.level.traps.get(pos) == null
pos == Terrain.GRASS || && !Dungeon.isChallenged(Challenges.NO_HERBALISM)) {
pos == Terrain.HIGH_GRASS || Dungeon.level.plant((Plant.Seed) Generator.randomUsingDefaults(Generator.Category.SEED), pos);
pos == Terrain.FURROWED_GRASS) { } else {
Dungeon.level.plant((Plant.Seed) Generator.randomUsingDefaults(Generator.Category.SEED), pos); return cursedEffect(origin, user, targetPos);
} }
afterZap.call();
} return true;
});
break;
//Health transfer //Health transfer
case 1: case 1:
final Char target = Actor.findChar( bolt.collisionPos ); final Char target = Actor.findChar( targetPos );
if (target != null) { if (target != null) {
cursedFX(user, bolt, new Callback() { int damage = Dungeon.depth * 2;
public void call() { Char toHeal, toDamage;
int damage = Dungeon.depth * 2;
Char toHeal, toDamage; if (Random.Int(2) == 0){
toHeal = user;
switch (Random.Int(2)) { toDamage = target;
case 0: default: } else {
toHeal = user; toHeal = target;
toDamage = target; toDamage = user;
break; }
case 1: toHeal.HP = Math.min(toHeal.HT, toHeal.HP + damage);
toHeal = target; toHeal.sprite.emitter().burst(Speck.factory(Speck.HEALING), 3);
toDamage = user; toDamage.damage(damage, origin == null ? toHeal : origin);
break; toDamage.sprite.emitter().start(ShadowParticle.UP, 0.05f, 10);
}
toHeal.HP = Math.min(toHeal.HT, toHeal.HP + damage); if (toDamage == Dungeon.hero){
toHeal.sprite.emitter().burst(Speck.factory(Speck.HEALING), 3); Sample.INSTANCE.play(Assets.Sounds.CURSED);
toDamage.damage(damage, origin == null ? toHeal : origin); if (!toDamage.isAlive()) {
toDamage.sprite.emitter().start(ShadowParticle.UP, 0.05f, 10); if (origin != null) {
Dungeon.fail( origin.getClass() );
if (toDamage == Dungeon.hero){ GLog.n( Messages.get( CursedWand.class, "ondeath", origin.name() ) );
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() );
}
}
} else { } else {
Sample.INSTANCE.play(Assets.Sounds.BURNING); Dungeon.fail( toHeal.getClass() );
} }
afterZap.call();
} }
}); } else {
Sample.INSTANCE.play(Assets.Sounds.BURNING);
}
} else { } else {
GLog.i(Messages.get(CursedWand.class, "nothing")); return cursedEffect(origin, user, targetPos);
afterZap.call();
} }
break; return true;
//Bomb explosion //Bomb explosion
case 2: case 2:
cursedFX(user, bolt, new Callback() { new Bomb().explode(targetPos);
public void call() { return true;
new Bomb().explode(bolt.collisionPos);
afterZap.call();
}
});
break;
//shock and recharge //shock and recharge
case 3: case 3:
@ -268,53 +235,45 @@ public class CursedWand {
Buff.prolong(user, Recharging.class, Recharging.DURATION); Buff.prolong(user, Recharging.class, Recharging.DURATION);
ScrollOfRecharging.charge(user); ScrollOfRecharging.charge(user);
SpellSprite.show(user, SpellSprite.CHARGE); SpellSprite.show(user, SpellSprite.CHARGE);
afterZap.call(); return true;
break;
} }
} }
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)){ switch(Random.Int(4)){
//sheep transformation //sheep transformation
case 0: case 0: default:
if (user != Dungeon.hero){
cursedZap(origin, user, bolt, afterZap);
return;
}
cursedFX(user, bolt, new Callback() {
public void call() {
Char ch = Actor.findChar( bolt.collisionPos );
if (ch != null && ch != user Char ch = Actor.findChar( targetPos );
&& !ch.properties().contains(Char.Property.BOSS) if (ch != null && !(ch instanceof Hero)
&& !ch.properties().contains(Char.Property.MINIBOSS)){ && !ch.properties().contains(Char.Property.BOSS)
Sheep sheep = new Sheep(); && !ch.properties().contains(Char.Property.MINIBOSS)){
sheep.lifespan = 10; Sheep sheep = new Sheep();
sheep.pos = ch.pos; sheep.lifespan = 10;
ch.destroy(); sheep.pos = ch.pos;
ch.sprite.killAndErase(); ch.destroy();
Dungeon.level.mobs.remove(ch); ch.sprite.killAndErase();
TargetHealthIndicator.instance.target(null); Dungeon.level.mobs.remove(ch);
GameScene.add(sheep); TargetHealthIndicator.instance.target(null);
CellEmitter.get(sheep.pos).burst(Speck.factory(Speck.WOOL), 4); GameScene.add(sheep);
Sample.INSTANCE.play(Assets.Sounds.PUFF); CellEmitter.get(sheep.pos).burst(Speck.factory(Speck.WOOL), 4);
Sample.INSTANCE.play(Assets.Sounds.SHEEP); Sample.INSTANCE.play(Assets.Sounds.PUFF);
} else { Sample.INSTANCE.play(Assets.Sounds.SHEEP);
GLog.i(Messages.get(CursedWand.class, "nothing")); } else {
} return cursedEffect(origin, user, targetPos);
afterZap.call(); }
} return true;
});
break;
//curses! //curses!
case 1: case 1:
if (user instanceof Hero) CursingTrap.curse( (Hero) user ); if (user instanceof Hero) {
afterZap.call(); CursingTrap.curse( (Hero) user );
break; } else {
return cursedEffect(origin, user, targetPos);
}
return true;
//inter-level teleportation //inter-level teleportation
case 2: case 2:
@ -340,22 +299,20 @@ public class CursedWand {
ScrollOfTeleportation.teleportChar(user); ScrollOfTeleportation.teleportChar(user);
} }
afterZap.call(); return true;
break;
//summon monsters //summon monsters
case 3: case 3:
new SummoningTrap().set( user.pos ).activate(); new SummoningTrap().set( targetPos ).activate();
afterZap.call(); return true;
break;
} }
} }
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)){ switch(Random.Int(4)){
//great forest fire! //great forest fire!
case 0: case 0: default:
for (int i = 0; i < Dungeon.level.length(); i++){ for (int i = 0; i < Dungeon.level.length(); i++){
GameScene.add( Blob.seed(i, 15, Regrowth.class)); GameScene.add( Blob.seed(i, 15, Regrowth.class));
} }
@ -366,51 +323,52 @@ public class CursedWand {
Sample.INSTANCE.play(Assets.Sounds.TELEPORT); Sample.INSTANCE.play(Assets.Sounds.TELEPORT);
GLog.p(Messages.get(CursedWand.class, "grass")); GLog.p(Messages.get(CursedWand.class, "grass"));
GLog.w(Messages.get(CursedWand.class, "fire")); GLog.w(Messages.get(CursedWand.class, "fire"));
afterZap.call(); return true;
break;
//golden mimic //golden mimic
case 1: case 1:
cursedFX(user, bolt, new Callback() {
public void call() { Char ch = Actor.findChar(targetPos);
Mimic mimic = Mimic.spawnAt(bolt.collisionPos, new ArrayList<Item>(), GoldenMimic.class); int spawnCell = targetPos;
if (mimic != null) { if (ch != null){
mimic.stopHiding(); ArrayList<Integer> candidates = new ArrayList<Integer>();
mimic.alignment = Char.Alignment.ENEMY; for (int n : PathFinder.NEIGHBOURS8) {
Item reward; int cell = targetPos + n;
do { if (Dungeon.level.passable[cell] && Actor.findChar( cell ) == null) {
reward = Generator.random(Random.oneOf(Generator.Category.WEAPON, Generator.Category.ARMOR, candidates.add( cell );
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"));
} }
afterZap.call();
} }
}); if (!candidates.isEmpty()){
break; spawnCell = Random.element(candidates);
} else {
return cursedEffect(origin, user, targetPos);
}
}
Mimic mimic = Mimic.spawnAt(spawnCell, new ArrayList<Item>(), 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. //crashes the game, yes, really.
case 2: case 2:
if (user != Dungeon.hero){
cursedZap(origin, user, bolt, afterZap);
return;
}
try { try {
Dungeon.saveAll(); Dungeon.saveAll();
if(Messages.lang() != Languages.ENGLISH){ if(Messages.lang() != Languages.ENGLISH){
//Don't bother doing this joke to none-english speakers, I doubt it would translate. //Don't bother doing this joke to none-english speakers, I doubt it would translate.
GLog.i(Messages.get(CursedWand.class, "nothing")); return cursedEffect(origin, user, targetPos);
afterZap.call();
} else { } else {
GameScene.show( GameScene.show(
new WndOptions("CURSED WAND ERROR", "this application will now self-destruct", "abort", "retry", "fail") { 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){ } catch(IOException e){
ShatteredPixelDungeon.reportException(e); ShatteredPixelDungeon.reportException(e);
//oookay maybe don't kill the game if the save failed. //maybe don't kill the game if the save failed.
GLog.i(Messages.get(CursedWand.class, "nothing")); return cursedEffect(origin, user, targetPos);
afterZap.call();
} }
break;
//random transmogrification //random transmogrification
case 3: case 3:
//skips this effect if there is no item to transmogrify //skips this effect if there is no item to transmogrify
if (origin == null || user != Dungeon.hero || !Dungeon.hero.belongings.contains(origin)){ if (origin == null || user != Dungeon.hero || !Dungeon.hero.belongings.contains(origin)){
cursedZap(origin, user, bolt, afterZap); return cursedEffect(origin, user, targetPos);
return;
} }
origin.detach(Dungeon.hero.belongings.backpack); origin.detach(Dungeon.hero.belongings.backpack);
Item result; Item result;
@ -456,8 +412,7 @@ public class CursedWand {
GLog.w( Messages.get(CursedWand.class, "transmogrify_other") ); GLog.w( Messages.get(CursedWand.class, "transmogrify_other") );
} }
Dungeon.level.drop(result, user.pos).sprite.drop(); Dungeon.level.drop(result, user.pos).sprite.drop();
afterZap.call(); return true;
break;
} }
} }

View File

@ -234,15 +234,9 @@ public abstract class ElementalSprite extends MobSprite {
} }
public static class Chaos extends ElementalSprite { public static class Chaos extends ElementalSprite {
//no bolt, overrides zap instead {
@Override boltType = MagicMissile.RAINBOW;
public void zap( int cell ) {
turnTo( ch.pos , cell );
play( zap );
((Elemental)ch).onZapComplete();
Sample.INSTANCE.play( Assets.Sounds.ZAP );
} }
@Override @Override