v2.5.0: complete refactor of cursed wand effect logic

This commit is contained in:
Evan Debenham
2024-08-12 17:12:10 -04:00
parent 70053d933e
commit 70df007bcf
4 changed files with 605 additions and 376 deletions
@@ -552,12 +552,38 @@ public abstract class Elemental extends Mob {
@Override @Override
protected void meleeProc( Char enemy, int damage ) { protected void meleeProc( Char enemy, int damage ) {
CursedWand.cursedEffect(null, this, enemy); Ballistica aim = new Ballistica(pos, enemy.pos, Ballistica.STOP_TARGET);
//TODO shortcutting the fx seems fine for now but may cause problems with new cursed effects
//of course, not shortcutting it means actor ordering issues =S
CursedWand.randomValidEffect(null, this, aim, false).effect(null, this, aim, false);
}
@Override
protected void zap() {
spend( 1f );
Invisibility.dispel(this);
Char enemy = this.enemy;
//skips accuracy check, always hits
rangedProc( enemy );
rangedCooldown = Random.NormalIntRange( 3, 5 );
}
@Override
public void onZapComplete() {
zap();
//next(); triggers after wand effect
} }
@Override @Override
protected void rangedProc( Char enemy ) { protected void rangedProc( Char enemy ) {
CursedWand.cursedEffect(null, this, enemy); CursedWand.cursedZap(null, this, new Ballistica(pos, enemy.pos, Ballistica.STOP_TARGET), new Callback() {
@Override
public void call() {
next();
}
});
} }
} }
@@ -59,13 +59,17 @@ public class ScrollOfMirrorImage extends Scroll {
readAnimation(); readAnimation();
} }
//returns the number of images spawned
public static int spawnImages( Hero hero, int nImages ){ public static int spawnImages( Hero hero, int nImages ){
return spawnImages( hero, hero.pos, nImages);
}
//returns the number of images spawned
public static int spawnImages( Hero hero, int pos, int nImages ){
ArrayList<Integer> respawnPoints = new ArrayList<>(); ArrayList<Integer> respawnPoints = new ArrayList<>();
for (int i = 0; i < PathFinder.NEIGHBOURS8.length; i++) { for (int i = 0; i < PathFinder.NEIGHBOURS9.length; i++) {
int p = hero.pos + PathFinder.NEIGHBOURS8[i]; int p = pos + PathFinder.NEIGHBOURS9[i];
if (Actor.findChar( p ) == null && Dungeon.level.passable[p]) { if (Actor.findChar( p ) == null && Dungeon.level.passable[p]) {
respawnPoints.add( p ); respawnPoints.add( p );
} }
@@ -88,54 +88,106 @@ import java.util.ArrayList;
//helper class to contain all the cursed wand zapping logic, so the main wand class doesn't get huge. //helper class to contain all the cursed wand zapping logic, so the main wand class doesn't get huge.
public class CursedWand { public class CursedWand {
private static float COMMON_CHANCE = 0.6f;
private static float UNCOMMON_CHANCE = 0.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){ public static void cursedZap(final Item origin, final Char user, final Ballistica bolt, final Callback afterZap){
cursedFX(user, bolt, new Callback() { boolean positiveOnly = user == Dungeon.hero && Random.Float() < WondrousResin.positiveCurseEffectChance();
CursedEffect effect = randomValidEffect(origin, user, bolt, positiveOnly);
//CursedEffect effect = new InterFloorTeleport();
effect.FX(origin, user, bolt, new Callback() {
@Override @Override
public void call() { public void call() {
if (cursedEffect(origin, user, bolt.collisionPos)){ effect.effect(origin, user, bolt, positiveOnly);
if (afterZap != null) afterZap.call(); afterZap.call();
}
} }
}); });
} }
public static void tryForWandProc( Char target, Item origin ){ public static void tryForWandProc( Char target, Item origin ){
if (target != null && origin instanceof Wand){ if (target != null && target != Dungeon.hero && origin instanceof Wand){
Wand.wandProc(target, origin.buffedLvl(), 1); Wand.wandProc(target, origin.buffedLvl(), 1);
} }
} }
public static boolean cursedEffect(final Item origin, final Char user, final Char target){ //*** Cursed Effects ***
return cursedEffect(origin, user, target.pos);
public static abstract class CursedEffect {
public boolean valid(Item origin, Char user, Ballistica bolt, boolean positiveOnly){
return true;
} }
public static boolean cursedEffect(final Item origin, final Char user, final int targetPos){ public void FX(final Item origin, final Char user, final Ballistica bolt, final Callback callback){
switch (Random.chances(new float[]{COMMON_CHANCE, UNCOMMON_CHANCE, RARE_CHANCE, VERY_RARE_CHANCE})){ MagicMissile.boltFromChar(user.sprite.parent,
MagicMissile.RAINBOW,
user.sprite,
bolt.collisionPos,
callback);
Sample.INSTANCE.play( Assets.Sounds.ZAP );
}
public abstract boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly);
}
// common/uncommon/rare/v.rare have a 60/30/9/1% chance respectively
private static float[] EFFECT_CAT_CHANCES = new float[]{60, 30, 9, 1};
public static CursedEffect randomEffect(){
switch (Random.chances(EFFECT_CAT_CHANCES)){
case 0: default: case 0: default:
return commonEffect(origin, user, targetPos); return randomCommonEffect();
case 1: case 1:
return uncommonEffect(origin, user, targetPos); return randomUncommonEffect();
case 2: case 2:
return rareEffect(origin, user, targetPos); return randomRareEffect();
case 3: case 3:
return veryRareEffect(origin, user, targetPos); return randomVeryRareEffect();
} }
} }
private static boolean commonEffect(final Item origin, final Char user, final int targetPos){ public static CursedEffect randomValidEffect(Item origin, Char user, Ballistica bolt, boolean positiveOnly){
boolean positiveOnly = user == Dungeon.hero && Random.Float() < WondrousResin.positiveCurseEffectChance(); switch (Random.chances(EFFECT_CAT_CHANCES)){
switch(Random.Int(4)){
//anti-entropy
//doesn't affect caster if positive only
case 0: default: case 0: default:
Char target = Actor.findChar(targetPos); return randomValidCommonEffect(origin, user, bolt, positiveOnly);
case 1:
return randomValidUncommonEffect(origin, user, bolt, positiveOnly);
case 2:
return randomValidRareEffect(origin, user, bolt, positiveOnly);
case 3:
return randomValidVeryRareEffect(origin, user, bolt, positiveOnly);
}
}
//**********************
//*** Common Effects ***
//**********************
private static ArrayList<CursedEffect> COMMON_EFFECTS = new ArrayList<>();
static {
COMMON_EFFECTS.add(new BurnAndFreeze());
COMMON_EFFECTS.add(new SpawnRegrowth());
COMMON_EFFECTS.add(new RandomTeleport());
COMMON_EFFECTS.add(new RandomGas());
}
public static CursedEffect randomCommonEffect(){
return Random.element(COMMON_EFFECTS);
}
public static CursedEffect randomValidCommonEffect(Item origin, Char user, Ballistica bolt, boolean positiveOnly){
CursedEffect effect;
do {
effect = Random.element(COMMON_EFFECTS);
} while (!effect.valid(origin, user, bolt, positiveOnly));
return effect;
}
public static class BurnAndFreeze extends CursedEffect {
@Override
public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
Char target = Actor.findChar(bolt.collisionPos);
//doesn't affect caster if positive only
if (Random.Int(2) == 0) { if (Random.Int(2) == 0) {
if (target != null) Buff.affect(target, Burning.class).reignite(target); if (target != null) Buff.affect(target, Burning.class).reignite(target);
if (!positiveOnly) Buff.affect(user, Frost.class, Frost.DURATION); if (!positiveOnly) Buff.affect(user, Frost.class, Frost.DURATION);
@@ -145,80 +197,140 @@ public class CursedWand {
} }
tryForWandProc(target, origin); tryForWandProc(target, origin);
return true; return true;
}
}
//spawns some regrowth public static class SpawnRegrowth extends CursedEffect {
case 1: @Override
GameScene.add( Blob.seed(targetPos, 30, Regrowth.class)); public boolean effect(Item origin, Char user, Ballistica bolt, boolean postiveOnly) {
tryForWandProc(Actor.findChar(targetPos), origin); if (Actor.findChar(bolt.collisionPos) == null){
Dungeon.level.pressCell(bolt.collisionPos);
}
GameScene.add( Blob.seed(bolt.collisionPos, 30, Regrowth.class));
tryForWandProc(Actor.findChar(bolt.collisionPos), origin);
return true; return true;
}
}
//random teleportation public static class RandomTeleport extends CursedEffect {
//can only teleport enemy if positive only @Override
case 2: public boolean valid(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
if(!positiveOnly && Random.Int(2) == 0) { Char target = Actor.findChar(bolt.collisionPos);
if (user != null && !user.properties().contains(Char.Property.IMMOVABLE)) { if (positiveOnly && (target == null || Char.hasProp(target, Char.Property.IMMOVABLE))){
return false;
}
return true;
}
//might be nice to have no fx if self teleports?
@Override
public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
Char target = Actor.findChar( bolt.collisionPos );
//can only teleport target if positive only
if (target != null && !Char.hasProp(target, Char.Property.IMMOVABLE) && (positiveOnly || Random.Int(2) == 0)){
ScrollOfTeleportation.teleportChar(target);
tryForWandProc(target, origin);
return true;
} else {
if (positiveOnly || user == null || Char.hasProp(user, Char.Property.IMMOVABLE)){
return false;
} else {
ScrollOfTeleportation.teleportChar(user); ScrollOfTeleportation.teleportChar(user);
} else {
return cursedEffect(origin, user, targetPos);
}
} else {
Char ch = Actor.findChar( targetPos );
if (ch != null && !ch.properties().contains(Char.Property.IMMOVABLE)) {
ScrollOfTeleportation.teleportChar(ch);
tryForWandProc(ch, origin);
} else {
return cursedEffect(origin, user, targetPos);
}
}
return true; return true;
}
}
}
}
//random gas at location public static class RandomGas extends CursedEffect {
case 3: @Override
public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
Sample.INSTANCE.play( Assets.Sounds.GAS ); Sample.INSTANCE.play( Assets.Sounds.GAS );
tryForWandProc(Actor.findChar(targetPos), origin); tryForWandProc(Actor.findChar(bolt.collisionPos), origin);
if (Actor.findChar(bolt.collisionPos) == null){
Dungeon.level.pressCell(bolt.collisionPos);
}
switch (Random.Int(3)) { switch (Random.Int(3)) {
case 0: default: case 0: default:
GameScene.add( Blob.seed( targetPos, 800, ConfusionGas.class ) ); GameScene.add( Blob.seed( bolt.collisionPos, 800, ConfusionGas.class ) );
return true; return true;
case 1: case 1:
GameScene.add( Blob.seed( targetPos, 500, ToxicGas.class ) ); GameScene.add( Blob.seed( bolt.collisionPos, 500, ToxicGas.class ) );
return true; return true;
case 2: case 2:
GameScene.add( Blob.seed( targetPos, 200, ParalyticGas.class ) ); GameScene.add( Blob.seed( bolt.collisionPos, 200, ParalyticGas.class ) );
return true; return true;
} }
} }
} }
private static boolean uncommonEffect(final Item origin, final Char user, final int targetPos){ //************************
boolean positiveOnly = user == Dungeon.hero && Random.Float() < WondrousResin.positiveCurseEffectChance(); //*** Uncommon Effects ***
switch(Random.Int(4)){ //************************
//Random plant private static ArrayList<CursedEffect> UNCOMMON_EFFECTS = new ArrayList<>();
case 0: default: static {
int pos = targetPos; UNCOMMON_EFFECTS.add(new RandomPlant());
UNCOMMON_EFFECTS.add(new HealthTransfer());
UNCOMMON_EFFECTS.add(new Explosion());
UNCOMMON_EFFECTS.add(new ShockAndRecharge());
}
public static CursedEffect randomUncommonEffect(){
return Random.element(UNCOMMON_EFFECTS);
}
public static CursedEffect randomValidUncommonEffect(Item origin, Char user, Ballistica bolt, boolean positiveOnly){
CursedEffect effect;
do {
effect = Random.element(UNCOMMON_EFFECTS);
} while (!effect.valid(origin, user, bolt, positiveOnly));
return effect;
}
public static class RandomPlant extends CursedEffect {
@Override
public boolean valid(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
int pos = bolt.collisionPos;
if (Dungeon.level.map[pos] != Terrain.ALCHEMY if (Dungeon.level.map[pos] != Terrain.ALCHEMY
&& !Dungeon.level.pit[pos] && !Dungeon.level.pit[pos]
&& Dungeon.level.traps.get(pos) == null && Dungeon.level.traps.get(pos) == null
&& !Dungeon.isChallenged(Challenges.NO_HERBALISM)) { && !Dungeon.isChallenged(Challenges.NO_HERBALISM)) {
Dungeon.level.plant((Plant.Seed) Generator.randomUsingDefaults(Generator.Category.SEED), pos); return true;
tryForWandProc(Actor.findChar(pos), origin);
} else { } else {
return cursedEffect(origin, user, targetPos); return false;
}
} }
@Override
public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
if (valid(origin, user, bolt, positiveOnly)) {
Dungeon.level.plant((Plant.Seed) Generator.randomUsingDefaults(Generator.Category.SEED), bolt.collisionPos);
return true; return true;
} else {
return false;
}
}
}
//Health transfer public static class HealthTransfer extends CursedEffect {
//can only harm enemy if positive only
case 1: @Override
final Char target = Actor.findChar( targetPos ); public boolean valid(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
return Actor.findChar( bolt.collisionPos ) != null;
}
@Override
public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
final Char target = Actor.findChar( bolt.collisionPos );
if (target != null) { if (target != null) {
int damage = Dungeon.scalingDepth() * 2; int damage = Dungeon.scalingDepth() * 2;
Char toHeal, toDamage; Char toHeal, toDamage;
//can only harm target if positive only
if (positiveOnly || Random.Int(2) == 0){ if (positiveOnly || Random.Int(2) == 0){
toHeal = user; toHeal = user;
toDamage = target; toDamage = target;
@@ -249,42 +361,78 @@ public class CursedWand {
Sample.INSTANCE.play(Assets.Sounds.BURNING); Sample.INSTANCE.play(Assets.Sounds.BURNING);
} }
tryForWandProc(target, origin); tryForWandProc(target, origin);
return true;
} else { } else {
return cursedEffect(origin, user, targetPos); return false;
}
}
} }
return true;
//Bomb explosion public static class Explosion extends CursedEffect {
case 2: @Override
new Bomb.ConjuredBomb().explode(targetPos); public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
tryForWandProc(Actor.findChar(targetPos), origin); new Bomb.ConjuredBomb().explode(bolt.collisionPos);
tryForWandProc(Actor.findChar(bolt.collisionPos), origin);
return true; return true;
}
}
//shock and recharge public static class ShockAndRecharge extends CursedEffect {
@Override
public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
//no shock if positive only //no shock if positive only
case 3:
if (!positiveOnly) new ShockingTrap().set( user.pos ).activate(); if (!positiveOnly) new ShockingTrap().set( user.pos ).activate();
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);
return true; return true;
} }
} }
private static boolean rareEffect(final Item origin, final Char user, final int targetPos){ //********************
boolean positiveOnly = user == Dungeon.hero && Random.Float() < WondrousResin.positiveCurseEffectChance(); //*** Rare Effects ***
switch(Random.Int(4)){ //********************
//sheep transformation private static ArrayList<CursedEffect> RARE_EFFECTS = new ArrayList<>();
case 0: default: static {
RARE_EFFECTS.add(new SheepPolymorph());
RARE_EFFECTS.add(new CurseEquipment());
RARE_EFFECTS.add(new InterFloorTeleport());
RARE_EFFECTS.add(new SummonMonsters());
}
Char ch = Actor.findChar( targetPos ); public static CursedEffect randomRareEffect(){
return Random.element(RARE_EFFECTS);
}
public static CursedEffect randomValidRareEffect(Item origin, Char user, Ballistica bolt, boolean positiveOnly){
CursedEffect effect;
do {
effect = Random.element(RARE_EFFECTS);
} while (!effect.valid(origin, user, bolt, positiveOnly));
return effect;
}
public static class SheepPolymorph extends CursedEffect {
@Override
public boolean valid(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
Char ch = Actor.findChar( bolt.collisionPos );
if (ch != null && !(ch instanceof Hero) if (ch != null && !(ch instanceof Hero)
//ignores bosses, questgivers, rat king, etc. //ignores bosses, questgivers, rat king, etc.
&& !ch.properties().contains(Char.Property.BOSS) && !ch.properties().contains(Char.Property.BOSS)
&& !ch.properties().contains(Char.Property.MINIBOSS) && !ch.properties().contains(Char.Property.MINIBOSS)
&& !(ch instanceof NPC && ch.alignment == Char.Alignment.NEUTRAL)){ && !(ch instanceof NPC && ch.alignment == Char.Alignment.NEUTRAL)){
return true;
} else {
return false;
}
}
@Override
public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
if (valid(origin, user, bolt, positiveOnly)){
Char ch = Actor.findChar( bolt.collisionPos );
Sheep sheep = new Sheep(); Sheep sheep = new Sheep();
sheep.lifespan = 10; sheep.lifespan = 10;
sheep.pos = ch.pos; sheep.pos = ch.pos;
@@ -297,31 +445,40 @@ public class CursedWand {
Sample.INSTANCE.play(Assets.Sounds.PUFF); Sample.INSTANCE.play(Assets.Sounds.PUFF);
Sample.INSTANCE.play(Assets.Sounds.SHEEP); Sample.INSTANCE.play(Assets.Sounds.SHEEP);
Dungeon.level.occupyCell(sheep); Dungeon.level.occupyCell(sheep);
} else {
return cursedEffect(origin, user, targetPos);
}
return true; return true;
} else {
return false;
}
}
}
//curses! public static class CurseEquipment extends CursedEffect {
//or hexes target if positive only
case 1: @Override
if (positiveOnly){ public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
ch = Actor.findChar( targetPos ); //hexes target if positive only or user isn't hero
if (positiveOnly || !(user instanceof Hero)){
Char ch = Actor.findChar( bolt.collisionPos );
if (ch != null){ if (ch != null){
Buff.affect(ch, Hex.class, Hex.DURATION); Buff.affect(ch, Hex.class, Hex.DURATION);
} }
return true; return true;
}
if (user instanceof Hero) {
CursingTrap.curse( (Hero) user );
} else { } else {
return cursedEffect(origin, user, targetPos); CursingTrap.curse( (Hero) user );
}
return true; return true;
}
}
}
//inter-level teleportation public static class InterFloorTeleport extends CursedEffect {
//of scroll of teleportation if positive only, or inter-floor teleport disallowed
case 2: @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) {
if (!positiveOnly && Dungeon.depth > 1 && Dungeon.interfloorTeleportAllowed() && user == Dungeon.hero) { if (!positiveOnly && Dungeon.depth > 1 && Dungeon.interfloorTeleportAllowed() && user == Dungeon.hero) {
//each depth has 1 more weight than the previous depth. //each depth has 1 more weight than the previous depth.
@@ -336,31 +493,56 @@ public class CursedWand {
InterlevelScene.returnPos = -1; InterlevelScene.returnPos = -1;
Game.switchScene(InterlevelScene.class); Game.switchScene(InterlevelScene.class);
//scroll of teleportation if positive only, or inter-floor teleport disallowed
} else { } else {
ScrollOfTeleportation.teleportChar(user); ScrollOfTeleportation.teleportChar(user);
} }
return true; return true;
}
}
//summon monsters public static class SummonMonsters extends CursedEffect {
//or mirror images if positive only
case 3: @Override
public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
//mirror images if positive only and user is hero
if (positiveOnly && user == Dungeon.hero){ if (positiveOnly && user == Dungeon.hero){
ScrollOfMirrorImage.spawnImages(Dungeon.hero, 2); ScrollOfMirrorImage.spawnImages(Dungeon.hero, bolt.collisionPos, 2);
} else { } else {
new SummoningTrap().set(targetPos).activate(); new SummoningTrap().set(bolt.collisionPos).activate();
} }
return true; return true;
} }
} }
private static boolean veryRareEffect(final Item origin, final Char user, final int targetPos){ //*************************
boolean positiveOnly = user == Dungeon.hero && Random.Float() < WondrousResin.positiveCurseEffectChance(); //*** Very Rare Effects ***
switch( Random.Int(4) ){ //*************************
//great forest fire! private static ArrayList<CursedEffect> VERY_RARE_EFFECTS = new ArrayList<>();
//only grass, no fire, if positive only static {
case 0: default: VERY_RARE_EFFECTS.add(new ForestFire());
VERY_RARE_EFFECTS.add(new SpawnGoldenMimic());
VERY_RARE_EFFECTS.add(new AbortRetryFail());
VERY_RARE_EFFECTS.add(new RandomTransmogrify());
}
public static CursedEffect randomVeryRareEffect(){
return Random.element(VERY_RARE_EFFECTS);
}
public static CursedEffect randomValidVeryRareEffect(Item origin, Char user, Ballistica bolt, boolean positiveOnly){
CursedEffect effect;
do {
effect = Random.element(VERY_RARE_EFFECTS);
} while (!effect.valid(origin, user, bolt, positiveOnly));
return effect;
}
public static class ForestFire extends CursedEffect {
@Override
public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
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));
} }
@@ -368,6 +550,7 @@ public class CursedWand {
new Flare(8, 32).color(0xFFFF66, true).show(user.sprite, 2f); new Flare(8, 32).color(0xFFFF66, true).show(user.sprite, 2f);
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"));
//only grass, no fire, if positive only
if (!positiveOnly) { if (!positiveOnly) {
GLog.w(Messages.get(CursedWand.class, "fire")); GLog.w(Messages.get(CursedWand.class, "fire"));
do { do {
@@ -375,17 +558,18 @@ public class CursedWand {
} while (Random.Int(5) != 0); } while (Random.Int(5) != 0);
} }
return true; return true;
}
}
//golden mimic public static class SpawnGoldenMimic extends CursedEffect {
//mimic is enthralled if positive only @Override
case 1: public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
Char ch = Actor.findChar(bolt.collisionPos);
Char ch = Actor.findChar(targetPos); int spawnCell = bolt.collisionPos;
int spawnCell = targetPos;
if (ch != null){ if (ch != null){
ArrayList<Integer> candidates = new ArrayList<Integer>(); ArrayList<Integer> candidates = new ArrayList<Integer>();
for (int n : PathFinder.NEIGHBOURS8) { for (int n : PathFinder.NEIGHBOURS8) {
int cell = targetPos + n; int cell = bolt.collisionPos + n;
if (Dungeon.level.passable[cell] && Actor.findChar( cell ) == null) { if (Dungeon.level.passable[cell] && Actor.findChar( cell ) == null) {
candidates.add( cell ); candidates.add( cell );
} }
@@ -393,7 +577,7 @@ public class CursedWand {
if (!candidates.isEmpty()){ if (!candidates.isEmpty()){
spawnCell = Random.element(candidates); spawnCell = Random.element(candidates);
} else { } else {
return cursedEffect(origin, user, targetPos); return false;
} }
} }
@@ -406,6 +590,7 @@ public class CursedWand {
mimic.items.clear(); mimic.items.clear();
GameScene.add(mimic); GameScene.add(mimic);
//mimic is enthralled, but also contains no extra reward, if positive only
if (positiveOnly){ if (positiveOnly){
Buff.affect(mimic, ScrollOfSirensSong.Enthralled.class); Buff.affect(mimic, ScrollOfSirensSong.Enthralled.class);
} else { } else {
@@ -420,15 +605,20 @@ public class CursedWand {
Dungeon.level.occupyCell(mimic); Dungeon.level.occupyCell(mimic);
return true; return true;
}
}
public static class AbortRetryFail extends CursedEffect {
@Override
public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
//appears to crash the game (actually just closes it) //appears to crash the game (actually just closes it)
case 2:
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.
return cursedEffect(origin, user, targetPos); //we still consider the effect valid here though as it's cosmetic anyway
return false;
} else { } else {
ShatteredPixelDungeon.runOnRenderThread( ShatteredPixelDungeon.runOnRenderThread(
new Callback() { new Callback() {
@@ -460,13 +650,28 @@ public class CursedWand {
} }
} catch(IOException e){ } catch(IOException e){
ShatteredPixelDungeon.reportException(e); ShatteredPixelDungeon.reportException(e);
//maybe don't kill the game if the save failed. //maybe don't kill the game if the save failed, just do nothing
return cursedEffect(origin, user, targetPos); return false;
}
}
} }
//random transmogrification public static class RandomTransmogrify extends CursedEffect {
//or triggers metamorph effect if positive only
case 3: @Override
public boolean valid(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
if (positiveOnly){
return true;
} else if (origin == null || user != Dungeon.hero || !Dungeon.hero.belongings.contains(origin)){
return false;
} else {
return true;
}
}
@Override
public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) {
//triggers metamorph effect if positive only
if (positiveOnly){ if (positiveOnly){
GameScene.show(new ScrollOfMetamorphosis.WndMetamorphChoose()); GameScene.show(new ScrollOfMetamorphosis.WndMetamorphChoose());
return true; return true;
@@ -474,7 +679,7 @@ public class CursedWand {
//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)){
return cursedEffect(origin, user, targetPos); return false;
} }
origin.detach(Dungeon.hero.belongings.backpack); origin.detach(Dungeon.hero.belongings.backpack);
Item result; Item result;
@@ -494,13 +699,4 @@ public class CursedWand {
} }
} }
private static void cursedFX(final Char user, final Ballistica bolt, final Callback callback){
MagicMissile.boltFromChar( user.sprite.parent,
MagicMissile.RAINBOW,
user.sprite,
bolt.collisionPos,
callback);
Sample.INSTANCE.play( Assets.Sounds.ZAP );
}
} }
@@ -232,8 +232,11 @@ public abstract class ElementalSprite extends MobSprite {
public static class Chaos extends ElementalSprite { public static class Chaos extends ElementalSprite {
{ @Override
boltType = MagicMissile.RAINBOW; public void zap(int cell) {
zap( cell, null ); //effectively super.super.zap
//relies on cursed wand for effects
((Elemental)ch).onZapComplete();
} }
@Override @Override