diff --git a/assets/effects.png b/assets/effects.png index 8de92a775..b3277d234 100644 Binary files a/assets/effects.png and b/assets/effects.png differ diff --git a/src/com/shatteredpixel/shatteredpixeldungeon/effects/Beam.java b/src/com/shatteredpixel/shatteredpixeldungeon/effects/Beam.java index b7dd5b9bb..3d9f77278 100644 --- a/src/com/shatteredpixel/shatteredpixeldungeon/effects/Beam.java +++ b/src/com/shatteredpixel/shatteredpixeldungeon/effects/Beam.java @@ -64,6 +64,12 @@ public class Beam extends Image { super(s, e, Effects.Type.LIGHT_RAY, 1f); } } + + public static class HealthRay extends Beam{ + public HealthRay(PointF s, PointF e){ + super(s, e, Effects.Type.HEALTH_RAY, 0.75f); + } + } @Override public void update() { diff --git a/src/com/shatteredpixel/shatteredpixeldungeon/effects/Effects.java b/src/com/shatteredpixel/shatteredpixeldungeon/effects/Effects.java index 10bbafdd4..638d62ea3 100644 --- a/src/com/shatteredpixel/shatteredpixeldungeon/effects/Effects.java +++ b/src/com/shatteredpixel/shatteredpixeldungeon/effects/Effects.java @@ -28,7 +28,8 @@ public class Effects { WOUND, EXCLAMATION, DEATH_RAY, - LIGHT_RAY + LIGHT_RAY, + HEALTH_RAY }; public static Image get( Type type ) { @@ -52,6 +53,9 @@ public class Effects { case LIGHT_RAY: icon.frame(icon.texture.uvRect(16, 23, 32, 31)); break; + case HEALTH_RAY: + icon.frame(icon.texture.uvRect(16, 30, 32, 38)); + break; } return icon; } diff --git a/src/com/shatteredpixel/shatteredpixeldungeon/effects/particles/BloodParticle.java b/src/com/shatteredpixel/shatteredpixeldungeon/effects/particles/BloodParticle.java index f3018e53a..16eed351e 100644 --- a/src/com/shatteredpixel/shatteredpixeldungeon/effects/particles/BloodParticle.java +++ b/src/com/shatteredpixel/shatteredpixeldungeon/effects/particles/BloodParticle.java @@ -20,6 +20,8 @@ package com.shatteredpixel.shatteredpixeldungeon.effects.particles; import com.watabou.noosa.particles.Emitter; import com.watabou.noosa.particles.PixelParticle; import com.watabou.noosa.particles.Emitter.Factory; +import com.watabou.utils.PointF; +import com.watabou.utils.Random; public class BloodParticle extends PixelParticle.Shrinking { @@ -29,6 +31,17 @@ public class BloodParticle extends PixelParticle.Shrinking { ((BloodParticle)emitter.recycle( BloodParticle.class )).reset( x, y ); } }; + + public static final Emitter.Factory BURST = new Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((BloodParticle)emitter.recycle( BloodParticle.class )).resetBurst( x, y ); + } + @Override + public boolean lightMode() { + return true; + } + }; public BloodParticle() { super(); @@ -50,6 +63,18 @@ public class BloodParticle extends PixelParticle.Shrinking { size = 4; speed.set( 0 ); } + + public void resetBurst( float x, float y ) { + revive(); + + this.x = x; + this.y = y; + + speed.polar( Random.Float(PointF.PI2), Random.Float( 16, 32 ) ); + size = 5; + + left = 0.5f; + } @Override public void update() { diff --git a/src/com/shatteredpixel/shatteredpixeldungeon/items/Generator.java b/src/com/shatteredpixel/shatteredpixeldungeon/items/Generator.java index fd2dfd51f..6220d84f1 100644 --- a/src/com/shatteredpixel/shatteredpixeldungeon/items/Generator.java +++ b/src/com/shatteredpixel/shatteredpixeldungeon/items/Generator.java @@ -127,10 +127,10 @@ public class Generator { WandOfFrost.class, WandOfPrismaticLight.class, //WandOfWarding.class, - //WandOfTransfusion.class, + WandOfTransfusion.class, //WandOfCorruption.class, WandOfRegrowth.class }; - Category.WAND.probs = new float[]{ 4, 4, 4, 4, 4, 3, /*3,*/ 3, 3, /*3, 3, 3,*/ 3 }; + Category.WAND.probs = new float[]{ 4, 4, 4, 4, 4, 3, /*3,*/ 3, 3, /*3,*/ 3, /*3,*/ 3 }; Category.WEAPON.classes = new Class[]{ Dagger.class, diff --git a/src/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfTransfusion.java b/src/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfTransfusion.java new file mode 100644 index 000000000..77130126a --- /dev/null +++ b/src/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfTransfusion.java @@ -0,0 +1,219 @@ +package com.shatteredpixel.shatteredpixeldungeon.items.wands; + +import com.shatteredpixel.shatteredpixeldungeon.Assets; +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.DungeonTilemap; +import com.shatteredpixel.shatteredpixeldungeon.ResultDescriptions; +import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; +import com.shatteredpixel.shatteredpixeldungeon.actors.Char; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Charm; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.*; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Ghost; +import com.shatteredpixel.shatteredpixeldungeon.effects.Beam; +import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; +import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.BloodParticle; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.LeafParticle; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle; +import com.shatteredpixel.shatteredpixeldungeon.items.Generator; +import com.shatteredpixel.shatteredpixeldungeon.items.Heap; +import com.shatteredpixel.shatteredpixeldungeon.items.Item; +import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MagesStaff; +import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain; +import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica; +import com.shatteredpixel.shatteredpixeldungeon.plants.Plant; +import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; +import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite; +import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; +import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; +import com.shatteredpixel.shatteredpixeldungeon.utils.Utils; +import com.watabou.noosa.audio.Sample; +import com.watabou.utils.Callback; +import com.watabou.utils.Random; + +import java.util.Arrays; +import java.util.HashSet; + +/** + * Created by debenhame on 13/05/2015. + */ +public class WandOfTransfusion extends Wand { + + { + name = "Wand of Transfusion"; + image = ItemSpriteSheet.WAND_TRANSFUSION; + + collisionProperties = Ballistica.PROJECTILE; + } + + private boolean freeCharge = false; + + //FIXME: this is sloppy + private static HashSet undeadMobs = new HashSet(Arrays.asList( + //Any Location + Wraith.class, + //Sewers + Ghost.FetidRat.class, + //Prison + Skeleton.class, + //City + Warlock.class, Monk.class, Senior.class, + King.class, King.Undead.class, + //Halls + Succubus.class, + Yog.RottingFist.class + )); + + @Override + protected void onZap(Ballistica beam) { + + for (int c : beam.subPath(0, beam.dist)) + CellEmitter.center(c).burst( BloodParticle.BURST, 1 ); + + int cell = beam.collisionPos; + + Char ch = Actor.findChar(cell); + Heap heap = Dungeon.level.heaps.get(cell); + + //this wand does a bunch of different things depending on what it targets. + + //if we find a character.. + if (ch != null && ch instanceof Mob){ + + //heals an ally, or charmed/corrupted enemy + //TODO: add corruption here + if (((Mob) ch).ally || ch.buff(Charm.class) != null){ + + int missingHP = ch.HT - ch.HP; + //heals 30%+3%*lvl missing HP. + int healing = (int)Math.ceil((missingHP * (0.30f+(0.03f*level)))); + ch.HP += healing; + ch.sprite.emitter().burst(Speck.factory(Speck.HEALING), 1 + level / 2); + ch.sprite.showStatus(CharSprite.POSITIVE, "+%dHP", healing); + + //harms the undead + } else if (undeadMobs.contains(ch.getClass())){ + + //deals 30%+5%*lvl total HP. + int damage = (int) Math.ceil(ch.HT*(0.3f+(0.05f*level))); + ch.damage(damage, this); + ch.sprite.emitter().start(ShadowParticle.UP, 0.05f, 10 + level); + Sample.INSTANCE.play(Assets.SND_BURNING); + + //charms an enemy + } else { + + float duration = 5+level; + Buff.affect(ch, Charm.class, Charm.durationFactor(ch) * duration).object = curUser.id(); + + duration *= Random.Float(0.75f, 1f); + Buff.affect(curUser, Charm.class, Charm.durationFactor(ch) * duration).object = curUser.id(); + + ch.sprite.centerEmitter().start( Speck.factory( Speck.HEART ), 0.2f, 5 ); + curUser.sprite.centerEmitter().start( Speck.factory( Speck.HEART ), 0.2f, 5 ); + + } + + + //if we find an item... + } else if (heap != null && heap.type == Heap.Type.HEAP){ + Item item = heap.peek(); + + //30% + 10%*lvl chance to uncurse the item and reset it to base level if degraded. + if (item != null && Random.Float() <= 0.3f+level*0.1f){ + if (item.cursed){ + item.cursed = false; + CellEmitter.get(cell).start( ShadowParticle.UP, 0.05f, 10 ); + Sample.INSTANCE.play(Assets.SND_BURNING); + } + + int lvldiffFromBase = item.level - (item instanceof Ring ? 1 : 0); + if (lvldiffFromBase < 0){ + item.upgrade(-lvldiffFromBase); + CellEmitter.get(cell).start(Speck.factory(Speck.UP), 0.2f, 3); + Sample.INSTANCE.play(Assets.SND_EVOKE); + } + } + + //if we find some trampled grass... + } else if (Dungeon.level.map[cell] == Terrain.GRASS) { + + //regrow one grass tile, suuuuuper useful... + Dungeon.level.set(cell, Terrain.HIGH_GRASS); + GameScene.updateMap(cell); + CellEmitter.get( cell ).burst(LeafParticle.LEVEL_SPECIFIC, 4); + + //If we find embers... + } else if (Dungeon.level.map[cell] == Terrain.EMBERS) { + + //30% + 3%*lvl chance to grow a random plant, or just regrow grass. + if (Random.Float() <= 0.3f+level*0.03f) { + Dungeon.level.plant((Plant.Seed) Generator.random(Generator.Category.SEED), cell); + CellEmitter.get( cell ).burst(LeafParticle.LEVEL_SPECIFIC, 8); + GameScene.updateMap(cell); + } else{ + Dungeon.level.set(cell, Terrain.HIGH_GRASS); + GameScene.updateMap(cell); + CellEmitter.get( cell ).burst(LeafParticle.LEVEL_SPECIFIC, 4); + } + + } else + return; //don't damage the hero if we can't find a target; + + if (!freeCharge) { + damageHero(); + } else { + freeCharge = false; + } + } + + + //this wand costs health too + private void damageHero(){ + // 15% of max hp + int damage = (int)Math.ceil(curUser.HT*0.15f); + curUser.damage(damage, this); + + if (!curUser.isAlive()){ + Dungeon.fail( Utils.format(ResultDescriptions.ITEM, name) ); + GLog.n("You killed yourself with your own Wand of Transfusion..."); + } + } + + @Override + protected int initialCharges() { + return 1; + } + + @Override + public void onHit(MagesStaff staff, Char attacker, Char defender, int damage) { + // lvl 0 - 10% + // lvl 1 - 18% + // lvl 2 - 25% + if (Random.Int( level + 10 ) >= 9){ + //grants a free use of the staff + freeCharge = true; + GLog.p("Your staff is charged with the life energy of your enemy!"); + attacker.sprite.emitter().burst(BloodParticle.BURST, 20); + } + } + + @Override + protected void fx(Ballistica beam, Callback callback) { + curUser.sprite.parent.add( + new Beam.HealthRay(curUser.sprite.center(), DungeonTilemap.tileCenterToWorld(beam.collisionPos))); + callback.call(); + } + + @Override + public String desc() { + return "A fairly plainly shaped wand, it stands out due to its magenta hue and pitch black gem at the tip.\n" + + "\n" + + "This wand will take some of your life energy and blast it at a target. This effect is very versatile: " + + "allies will be healed, enemies will be temporarily charmed, and hostile undead will take considerable damage. " + + "The life force effect can also be potent at dispelling curses as well. " + + "The life energy drain is significant though, using this wand will deal damage to you in addition to consuming charges."; + } +}