From 3c4e8a5f74e2245fddf0a8300e5e21e0724759e0 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Tue, 10 Mar 2020 15:51:10 -0400 Subject: [PATCH] v0.8.0: rough implementation for Yog's fists and Yog fight progression --- core/src/main/assets/yog_fists.png | Bin 1458 -> 3976 bytes .../actors/buffs/Bleeding.java | 4 + .../actors/buffs/Light.java | 4 + .../actors/mobs/YogDzewa.java | 206 ++++++- .../actors/mobs/YogFist.java | 522 ++++++++++++++++++ .../shatteredpixeldungeon/levels/Level.java | 4 +- .../levels/NewHallsBossLevel.java | 7 +- .../scenes/GameScene.java | 10 +- .../sprites/FistSprite.java | 130 ++++- .../messages/actors/actors.properties | 8 + 10 files changed, 872 insertions(+), 23 deletions(-) create mode 100644 core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogFist.java diff --git a/core/src/main/assets/yog_fists.png b/core/src/main/assets/yog_fists.png index 05af17b24cf33a1cae1ad2281e8bed9351e9878a..676e87621cb40ff9fb0b9dfc774628880c88789b 100644 GIT binary patch delta 3833 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.level.mobs.clone()) { + if (mob instanceof Larva || mob instanceof RipperDemon) { + mob.die( cause ); + } + } + + Dungeon.level.viewDistance = 4; + if (Dungeon.hero.buff(Light.class) == null){ + Dungeon.hero.viewDistance = Dungeon.level.viewDistance; + } + + GameScene.bossSlain(); + Dungeon.level.unseal(); + super.die( cause ); + + yell( Messages.get(this, "defeated") ); + } + + @Override + public void notice() { + super.notice(); + if (!BossHealthBar.isAssigned()) { + BossHealthBar.assignBoss(this); + yell(Messages.get(this, "notice")); + for (Char ch : Actor.chars()){ + if (ch instanceof DriedRose.GhostHero){ + GLog.n("\n"); + ((DriedRose.GhostHero) ch).sayBoss(); + } + } + } + } + + { + immunities.add( Grim.class ); + immunities.add( GrimTrap.class ); + immunities.add( Terror.class ); + immunities.add( Amok.class ); + immunities.add( Charm.class ); + immunities.add( Sleep.class ); + immunities.add( Burning.class ); + immunities.add( ToxicGas.class ); + immunities.add( ScrollOfRetribution.class ); + immunities.add( ScrollOfPsionicBlast.class ); + immunities.add( Vertigo.class ); + } + + @Override + public void restoreFromBundle(Bundle bundle) { + super.restoreFromBundle(bundle); + BossHealthBar.assignBoss(this); + } + + public static class Larva extends Mob { + + { + spriteClass = LarvaSprite.class; + + HP = HT = 25; + defenseSkill = 20; + + EXP = 5; + maxLvl = -2; + + state = HUNTING; + + properties.add(Property.DEMONIC); + } + + @Override + public int attackSkill( Char target ) { + return 30; + } + + @Override + public int damageRoll() { + return Random.NormalIntRange( 22, 30 ); + } + + @Override + public int drRoll() { + return Random.NormalIntRange(0, 8); + } + + } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogFist.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogFist.java new file mode 100644 index 000000000..5047bab42 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogFist.java @@ -0,0 +1,522 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2020 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.mobs; + +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; +import com.shatteredpixel.shatteredpixeldungeon.actors.Char; +import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob; +import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Fire; +import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.ToxicGas; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Bleeding; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Blindness; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Cripple; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Light; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Ooze; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots; +import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; +import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.LeafParticle; +import com.shatteredpixel.shatteredpixeldungeon.items.armor.glyphs.Viscosity; +import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTeleportation; +import com.shatteredpixel.shatteredpixeldungeon.levels.Level; +import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain; +import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; +import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite; +import com.shatteredpixel.shatteredpixeldungeon.sprites.FistSprite; +import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; +import com.watabou.utils.PathFinder; +import com.watabou.utils.Random; + +public abstract class YogFist extends Mob { + + { + HP = HT = 300; + defenseSkill = 25; + + //for doomed resistance + EXP = 25; + maxLvl = -2; + + state = WANDERING; + + properties.add(Property.BOSS); + properties.add(Property.DEMONIC); + } + + private float rangedCooldown; + protected boolean canRangedInMelee = true; + + protected void incrementRangedCooldown(){ + rangedCooldown += Random.NormalFloat(6, 9); + } + + @Override + protected boolean act() { + if (paralysed <= 0 && rangedCooldown > 0) rangedCooldown--; + return super.act(); + } + + @Override + protected boolean canAttack(Char enemy) { + if (rangedCooldown <= 0){ + return new Ballistica( pos, enemy.pos, Ballistica.MAGIC_BOLT).collisionPos == enemy.pos; + } else { + return super.canAttack(enemy); + } + } + + @Override + protected boolean doAttack( Char enemy ) { + + if (Dungeon.level.adjacent( pos, enemy.pos ) && (!canRangedInMelee || rangedCooldown > 0)) { + + return super.doAttack( enemy ); + + } else { + + incrementRangedCooldown(); + if (sprite != null && (sprite.visible || enemy.sprite.visible)) { + sprite.zap( enemy.pos ); + return false; + } else { + zap(); + return true; + } + } + } + + protected abstract void zap(); + + public void onZapComplete(){ + zap(); + next(); + } + + @Override + public int attackSkill( Char target ) { + return 36; + } + + @Override + //TODO different for some fists perhaps? + public int damageRoll() { + return Random.NormalIntRange( 26, 32 ); + } + + @Override + public int drRoll() { + return Random.NormalIntRange(0, 15); + } + + public static class Burning extends YogFist { + + { + spriteClass = FistSprite.Burning.class; + + properties.add(Property.FIERY); + } + + @Override + public boolean act() { + + boolean result = super.act(); + + if (Dungeon.level.map[pos] == Terrain.WATER){ + Level.set( pos, Terrain.EMPTY); + GameScene.updateMap( pos ); + CellEmitter.get( pos ).burst( Speck.factory( Speck.STEAM ), 10 ); + } + + for (int i = 0; i < 2; i++){ + int cell = pos + PathFinder.NEIGHBOURS8[Random.Int(8)]; + if (Dungeon.level.map[cell] == Terrain.WATER){ + Level.set( cell, Terrain.EMPTY); + GameScene.updateMap( cell ); + CellEmitter.get( cell ).burst( Speck.factory( Speck.STEAM ), 10 ); + } + } + + for (int i : PathFinder.NEIGHBOURS9) { + int vol = Fire.volumeAt(pos+i, Fire.class); + if (vol < 4 && !Dungeon.level.water[pos + i] && !Dungeon.level.solid[pos + i]){ + GameScene.add( Blob.seed( pos + i, 4 - vol, Fire.class ) ); + } + } + + return result; + } + + @Override + protected void zap() { + spend( 1f ); + + if (hit( this, enemy, true )) { + + int dmg = damageRoll()/2; + //TODO damage source logic + enemy.damage( dmg, this ); + Buff.affect( enemy, com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning.class ).reignite( enemy ); + + if (!enemy.isAlive() && enemy == Dungeon.hero) { + Dungeon.fail( getClass() ); + GLog.n( Messages.get(Char.class, "kill", name()) ); + } + + } else { + + enemy.sprite.showStatus( CharSprite.NEUTRAL, enemy.defenseVerb() ); + } + + if (Dungeon.level.map[enemy.pos] == Terrain.WATER){ + Level.set( enemy.pos, Terrain.EMPTY); + GameScene.updateMap( enemy.pos ); + CellEmitter.get( enemy.pos ).burst( Speck.factory( Speck.STEAM ), 10 ); + } + + for (int i : PathFinder.NEIGHBOURS9){ + if (!Dungeon.level.water[enemy.pos+i] && !Dungeon.level.solid[enemy.pos+i]){ + int vol = Fire.volumeAt(enemy.pos+i, Fire.class); + if (vol < 4){ + GameScene.add( Blob.seed( enemy.pos + i, 4 - vol, Fire.class ) ); + } + } + } + + } + + } + + public static class Soiled extends YogFist { + + { + spriteClass = FistSprite.Soiled.class; + } + + @Override + public boolean act() { + + boolean result = super.act(); + + for (int i = 0; i < 2; i++){ + int cell = pos + PathFinder.NEIGHBOURS9[Random.Int(9)]; + if (Dungeon.level.map[cell] == Terrain.GRASS){ + Level.set( cell, Terrain.FURROWED_GRASS); + GameScene.updateMap( cell ); + CellEmitter.get( cell ).burst( LeafParticle.GENERAL, 10 ); + } + } + + Dungeon.observe(); + + for (int i : PathFinder.NEIGHBOURS9) { + int cell = pos + i; + if (canSpreadGrass(cell)){ + Level.set(pos+i, Terrain.GRASS); + GameScene.updateMap( pos + i ); + } + } + + return result; + } + + @Override + public void damage(int dmg, Object src) { + int grassCells = 0; + for (int i : PathFinder.NEIGHBOURS9) { + if (Dungeon.level.map[pos+i] == Terrain.FURROWED_GRASS + || Dungeon.level.map[pos+i] == Terrain.HIGH_GRASS){ + grassCells++; + } + } + if (grassCells > 0) dmg = Math.round(dmg * (9-grassCells)/9f); + + super.damage(dmg, src); + } + + @Override + protected void zap() { + spend( 1f ); + + if (hit( this, enemy, true )) { + + int dmg = damageRoll()/2; + //TODO damage source logic + enemy.damage( dmg, this ); + Buff.affect( enemy, Roots.class, 3f ); + + if (!enemy.isAlive() && enemy == Dungeon.hero) { + Dungeon.fail( getClass() ); + GLog.n( Messages.get(Char.class, "kill", name()) ); + } + + } else { + + enemy.sprite.showStatus( CharSprite.NEUTRAL, enemy.defenseVerb() ); + } + + for (int i : PathFinder.NEIGHBOURS9){ + int cell = enemy.pos + i; + if (canSpreadGrass(cell)){ + if (Random.Int(5) == 0){ + Level.set(cell, Terrain.FURROWED_GRASS); + GameScene.updateMap( cell ); + } else { + Level.set(cell, Terrain.GRASS); + GameScene.updateMap( cell ); + } + CellEmitter.get( cell ).burst( LeafParticle.GENERAL, 10 ); + } + } + Dungeon.observe(); + + } + + private boolean canSpreadGrass(int cell){ + int yogPos = Dungeon.level.exit + Dungeon.level.width()*3; + return Dungeon.level.distance(cell, yogPos) > 4 && !Dungeon.level.solid[cell] + && !(Dungeon.level.map[cell] == Terrain.FURROWED_GRASS || Dungeon.level.map[cell] == Terrain.HIGH_GRASS); + } + + } + + public static class Rotting extends YogFist { + + { + spriteClass = FistSprite.Rotting.class; + + properties.add(Property.ACIDIC); + } + + @Override + protected boolean act() { + //ensures toxic gas acts at the appropriate time when added + GameScene.add(Blob.seed(pos, 0, ToxicGas.class)); + + if (Dungeon.level.water[pos] && HP < HT) { + sprite.emitter().burst( Speck.factory(Speck.HEALING), 3 ); + HP += HT/50; + } + + return super.act(); + } + + @Override + public void damage(int dmg, Object src) { + if (!(src instanceof Bleeding)){ + Bleeding b = buff(Bleeding.class); + if (b == null){ + b = new Bleeding(); + } + b.announced = false; + b.set(dmg/3f); + b.attachTo(this); + sprite.showStatus(CharSprite.WARNING, b.toString() + " " + (int)b.level()); + } else{ + super.damage(dmg, src); + } + } + + @Override + protected void zap() { + spend( 1f ); + GameScene.add(Blob.seed(enemy.pos, 100, ToxicGas.class)); + } + + @Override + public int attackProc( Char enemy, int damage ) { + damage = super.attackProc( enemy, damage ); + + if (Random.Int( 2 ) == 0) { + Buff.affect( enemy, Ooze.class ).set( 20f ); + enemy.sprite.burst( 0xFF000000, 5 ); + } + + return damage; + } + + { + immunities.add(ToxicGas.class); + } + + } + + public static class Rusted extends YogFist { + + { + spriteClass = FistSprite.Rusted.class; + + properties.add(Property.LARGE); + properties.add(Property.INORGANIC); + } + + @Override + public void damage(int dmg, Object src) { + if (!(src instanceof Viscosity.DeferedDamage)){ + Buff.affect(this, Viscosity.DeferedDamage.class).prolong(dmg); + sprite.showStatus( CharSprite.WARNING, Messages.get(Viscosity.class, "deferred", dmg) ); + } else{ + super.damage(dmg, src); + } + } + + @Override + protected void zap() { + spend( 1f ); + Buff.affect(enemy, Cripple.class, 5f); + } + + } + + public static class Bright extends YogFist { + + { + spriteClass = FistSprite.Bright.class; + + properties.add(Property.ELECTRIC); + + canRangedInMelee = false; + } + + @Override + protected void incrementRangedCooldown() { + //ranged attack has no cooldown + } + + @Override + protected void zap() { + spend( 1f ); + + if (hit( this, enemy, true )) { + + int dmg = damageRoll()/2; + //TODO damage source logic + enemy.damage( dmg, this ); + Buff.prolong( enemy, Blindness.class, 5f ); + + if (!enemy.isAlive() && enemy == Dungeon.hero) { + Dungeon.fail( getClass() ); + GLog.n( Messages.get(Char.class, "kill", name()) ); + } + + } else { + + enemy.sprite.showStatus( CharSprite.NEUTRAL, enemy.defenseVerb() ); + } + + } + + @Override + public void damage(int dmg, Object src) { + int beforeHP = HP; + super.damage(dmg, src); + if (beforeHP > HT/2 && HP < HT/2){ + HP = HT/2; + Buff.prolong( enemy, Blindness.class, 50f ); + int i; + do { + i = Random.Int(Dungeon.level.length()); + } while (Dungeon.level.heroFOV[i] || Dungeon.level.solid[i] || Actor.findChar(i) != null); + ScrollOfTeleportation.appear(this, i); + state = WANDERING; + GameScene.flash(0xFFFFFF); + } else if (!isAlive()){ + Buff.prolong( enemy, Blindness.class, 50f ); + GameScene.flash(0xFFFFFF); + } + } + + } + + public static class Dark extends YogFist { + + { + spriteClass = FistSprite.Dark.class; + + canRangedInMelee = false; + } + + @Override + protected void incrementRangedCooldown() { + //ranged attack has no cooldown + } + + @Override + protected void zap() { + spend( 1f ); + + if (hit( this, enemy, true )) { + + int dmg = damageRoll()/2; + //TODO damage source logic + enemy.damage( dmg, this ); + + Light l = enemy.buff(Light.class); + if (l != null){ + l.weaken(50); + } + + if (!enemy.isAlive() && enemy == Dungeon.hero) { + Dungeon.fail( getClass() ); + GLog.n( Messages.get(Char.class, "kill", name()) ); + } + + } else { + + enemy.sprite.showStatus( CharSprite.NEUTRAL, enemy.defenseVerb() ); + } + + } + + @Override + public void damage(int dmg, Object src) { + int beforeHP = HP; + super.damage(dmg, src); + if (beforeHP > HT/2 && HP < HT/2){ + HP = HT/2; + Light l = Dungeon.hero.buff(Light.class); + if (l != null){ + l.detach(); + } + int i; + do { + i = Random.Int(Dungeon.level.length()); + } while (Dungeon.level.heroFOV[i] || Dungeon.level.solid[i] || Actor.findChar(i) != null); + ScrollOfTeleportation.appear(this, i); + state = WANDERING; + GameScene.flash(0, false); + } else if (!isAlive()){ + Light l = Dungeon.hero.buff(Light.class); + if (l != null){ + l.detach(); + } + GameScene.flash(0, false); + } + } + + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java index 750f51edd..d8f1b0a82 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java @@ -45,6 +45,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Bestiary; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.YogFist; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Sheep; import com.shatteredpixel.shatteredpixeldungeon.effects.particles.FlowParticle; import com.shatteredpixel.shatteredpixeldungeon.effects.particles.WindParticle; @@ -951,7 +952,8 @@ public abstract class Level implements Bundlable { if (sighted) { boolean[] blocking; - if (c instanceof Hero && ((Hero) c).subClass == HeroSubClass.WARDEN) { + if ((c instanceof Hero && ((Hero) c).subClass == HeroSubClass.WARDEN) + || c instanceof YogFist.Soiled) { blocking = Dungeon.level.losBlocking.clone(); for (int i = 0; i < blocking.length; i++){ if (blocking[i] && (Dungeon.level.map[i] == Terrain.HIGH_GRASS || Dungeon.level.map[i] == Terrain.FURROWED_GRASS)){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/NewHallsBossLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/NewHallsBossLevel.java index bd6866187..ab214c482 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/NewHallsBossLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/NewHallsBossLevel.java @@ -27,7 +27,6 @@ import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.YogDzewa; -import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Yog; import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; import com.shatteredpixel.shatteredpixeldungeon.effects.particles.FlameParticle; import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle; @@ -164,7 +163,7 @@ public class NewHallsBossLevel extends Level { if (item != null) { int pos; do { - pos = Random.IntRange( ROOM_LEFT, ROOM_RIGHT ) + Random.IntRange( ROOM_TOP + 1, ROOM_BOTTOM ) * width(); + pos = randomRespawnCell(null); } while (pos == entrance); drop( item, pos ).setHauntedIfCursed().type = Heap.Type.REMAINS; } @@ -201,10 +200,9 @@ public class NewHallsBossLevel extends Level { Dungeon.observe(); - Yog boss = new YogDzewa(); + YogDzewa boss = new YogDzewa(); boss.pos = exit + width*3; GameScene.add( boss ); - //boss.spawnFists(); } @Override @@ -218,7 +216,6 @@ public class NewHallsBossLevel extends Level { CellEmitter.get(exit-1).burst(ShadowParticle.UP, 25); CellEmitter.get(exit).burst(ShadowParticle.UP, 100); CellEmitter.get(exit+1).burst(ShadowParticle.UP, 25); - GameScene.flash(0); for( CustomTilemap t : customTiles){ if (t instanceof CenterPieceVisuals){ ((CenterPieceVisuals) t).updateState(); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java index 3543900e0..7fd57c035 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java @@ -930,11 +930,15 @@ public class GameScene extends PixelScene { } } } - + public static void flash( int color ) { - scene.fadeIn( 0xFF000000 | color, true ); + flash( color, true); } - + + public static void flash( int color, boolean lightmode ) { + scene.fadeIn( 0xFF000000 | color, lightmode ); + } + public static void gameOver() { Banner gameOver = new Banner( BannerSprites.get( BannerSprites.Type.GAME_OVER ) ); gameOver.show( 0x000000, 1f ); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/FistSprite.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/FistSprite.java index cc7bb07f2..22568da23 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/FistSprite.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/FistSprite.java @@ -24,9 +24,16 @@ package com.shatteredpixel.shatteredpixeldungeon.sprites; import com.shatteredpixel.shatteredpixeldungeon.Assets; import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Yog; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.YogFist; +import com.shatteredpixel.shatteredpixeldungeon.effects.Beam; import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile; +import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.CorrosionParticle; import com.shatteredpixel.shatteredpixeldungeon.effects.particles.FlameParticle; -import com.shatteredpixel.shatteredpixeldungeon.levels.SewerLevel; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.LeafParticle; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.SparkParticle; +import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap; import com.watabou.noosa.Camera; import com.watabou.noosa.TextureFilm; import com.watabou.noosa.audio.Sample; @@ -124,7 +131,12 @@ public abstract class FistSprite extends MobSprite { new Callback() { @Override public void call() { - ((Yog.BurningFist)ch).onZapComplete(); + //pre-0.8.0 saves + if (ch instanceof Yog.BurningFist){ + ((Yog.BurningFist)ch).onZapComplete(); + } else { + ((YogFist)ch).onZapComplete(); + } } } ); Sample.INSTANCE.play( Assets.SND_ZAP ); @@ -160,12 +172,35 @@ public abstract class FistSprite extends MobSprite { @Override public int blood() { - return 0xFFFFBB33; + return 0xFFFFDD34; } } - /// + public static class Soiled extends FistSprite { + + { + boltType = MagicMissile.FOLIAGE; + } + + @Override + protected int texOffset() { + return 10; + } + + @Override + protected Emitter createEmitter() { + Emitter emitter = emitter(); + emitter.pour( LeafParticle.GENERAL, 0.06f ); + return emitter; + } + + @Override + public int blood() { + return 0xFF7F5424; + } + + } public static class Rotting extends FistSprite { @@ -181,13 +216,96 @@ public abstract class FistSprite extends MobSprite { @Override protected Emitter createEmitter() { Emitter emitter = emitter(); - //emitter.pour( SewerLevel.WaterParticle.FACTORY, 0.06f ); + emitter.pour(Speck.factory(Speck.TOXIC), 0.25f ); return emitter; } @Override public int blood() { - return 0xFFFFBB33; + return 0xFFB8BBA1; + } + + } + + public static class Rusted extends FistSprite { + + { + boltType = MagicMissile.CORROSION; + } + + @Override + protected int texOffset() { + return 30; + } + + @Override + protected Emitter createEmitter() { + Emitter emitter = emitter(); + emitter.pour(CorrosionParticle.MISSILE, 0.06f ); + return emitter; + } + + @Override + public int blood() { + return 0xFF7F7F7F; + } + + } + + public static class Bright extends FistSprite { + + { + boltType = MagicMissile.RAINBOW; + } + + @Override + protected int texOffset() { + return 40; + } + + @Override + protected Emitter createEmitter() { + Emitter emitter = emitter(); + emitter.pour(SparkParticle.STATIC, 0.06f ); + return emitter; + } + + @Override + public void zap( int cell ) { + turnTo( ch.pos , cell ); + play( zap ); + + ((YogFist)ch).onZapComplete(); + parent.add( new Beam.LightRay(center(), DungeonTilemap.raisedTileCenterToWorld(cell))); + } + @Override + public int blood() { + return 0xFFFFFFFF; + } + + } + + public static class Dark extends FistSprite { + + { + boltType = MagicMissile.SHADOW; + } + + @Override + protected int texOffset() { + return 50; + } + + @Override + protected Emitter createEmitter() { + Emitter emitter = emitter(); + emitter.pour(ShadowParticle.MISSILE, 0.06f ); + return emitter; + } + + @Override + public int blood() { + return 0xFF4A2F53; } } diff --git a/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties b/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties index b586696d9..5f0517666 100644 --- a/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties +++ b/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties @@ -740,6 +740,14 @@ actors.mobs.yog$larva.name=god's larva actors.mobs.yog$larva.rankings_desc=Devoured by Yog-Dzewa actors.mobs.yog$larva.desc=Yog-Dzewa is an Old God, a powerful entity from the realms of chaos. A century ago, the ancient dwarves barely won the war against its army of demons, but were unable to kill the god itself. Instead, they then imprisoned it in the halls below their city, believing it to be too weak to rise ever again. +actors.mobs.yogdzewa.name=Yog-Dzewa +actors.mobs.yogdzewa.notice=I. SEE. YOU. +actors.mobs.yogdzewa.immune=IMMUNE +actors.mobs.yogdzewa.darkness=The shadows pull in closer. +actors.mobs.yogdzewa.defeated=... +actors.mobs.yogdzewa.rankings_desc=Devoured by Yog-Dzewa +actors.mobs.yogdzewa.desc=TODO + actors.char.kill=%s killed you...