From f5cba1a9ba560c43fa2f55b95acffb507a1f143f Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Thu, 18 Jun 2020 14:00:02 -0400 Subject: [PATCH] v0.8.1: reworked wand of regrowth! --- .../assets/messages/items/items.properties | 8 +- core/src/main/assets/sprites/lotus.png | Bin 0 -> 9232 bytes .../shatteredpixeldungeon/Assets.java | 1 + .../items/wands/WandOfRegrowth.java | 371 +++++++++++------- .../weapon/missiles/darts/TippedDart.java | 33 +- .../shatteredpixeldungeon/levels/Level.java | 30 +- .../plants/Blindweed.java | 1 + .../plants/Dreamfoil.java | 1 + .../plants/Earthroot.java | 1 + .../plants/Fadeleaf.java | 1 + .../plants/Firebloom.java | 1 + .../shatteredpixeldungeon/plants/Icecap.java | 1 + .../shatteredpixeldungeon/plants/Plant.java | 20 + .../plants/Rotberry.java | 1 + .../plants/Sorrowmoss.java | 1 + .../plants/Starflower.java | 1 + .../plants/Stormvine.java | 1 + .../plants/Sungrass.java | 1 + .../plants/Swiftthistle.java | 1 + .../sprites/LotusSprite.java | 106 +++++ 20 files changed, 414 insertions(+), 167 deletions(-) create mode 100644 core/src/main/assets/sprites/lotus.png create mode 100644 core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/LotusSprite.java diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index def777c40..c48de07d3 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -1206,11 +1206,13 @@ items.wands.wandofprismaticlight.stats_desc=This wand shoots rays of light which items.wands.wandofregrowth.name=wand of regrowth items.wands.wandofregrowth.staff_name=staff of regrowth items.wands.wandofregrowth.desc=This wand is made from a thin shaft of expertly carved wood. Somehow it is still alive and vibrant, bright green like a young tree's core. -items.wands.wandofregrowth.stats_desc=When used, this wand will consume all its charges to blast magical regrowth energy outward in a cone. This magic will cause grass, roots, and rare plants to spring to life. "When life ceases new life always begins to grow... The eternal cycle always remains!" +items.wands.wandofregrowth.stats_desc=When used, this wand will blast magical regrowth energy outward in a cone, causing grass, roots, and rare plants to spring to life. As this wand is upgraded it will consume more charges, the effect becomes significantly more powerful the more charges are consumed. Its next zap will consume _%1$d charges_. "When life ceases new life always begins to grow... The eternal cycle always remains!" items.wands.wandofregrowth$dewcatcher.name=Dewcatcher -items.wands.wandofregrowth$dewcatcher.desc=Dewcatchers camouflage as grass to avoid attention, but their bulges of collected dew give them away. +items.wands.wandofregrowth$dewcatcher.desc=Dewcatchers are wonderous plants that fill themselves with magical dew. They attempt to camouflage as grass to avoid attention, but their bulges of collected dew give them away. items.wands.wandofregrowth$seedpod.name=Seed Pod -items.wands.wandofregrowth$seedpod.desc=Seed Pods look pretty, but the seeds they carry are actually stolen from other plants they kill with their roots. +items.wands.wandofregrowth$seedpod.desc=Seed Pods are magical plants that produce seeds from other plan types, rather than any seeds of their own. They somehow manage to spread despite having no seed of their own. +items.wands.wandofregrowth$lotus.name=Golden Lotus +items.wands.wandofregrowth$lotus.desc=The golden lotus is a mystical plant that can only be conjured by a powerful wand of regrowth. Its aura enhances all plants and plant effects, but it burns through its energy and dies after a short time.\n\nThis lotus was produced by a _+%1$d_ wand of regrowth. All plants in its effect will _instantly trigger_ if they are planted on a character. Plants will also have a _%2$d%% chance_ to drop their seed, and tipped darts gain _%3$d%% extra durability_. items.wands.wandoftransfusion.name=wand of transfusion items.wands.wandoftransfusion.staff_name=staff of transfusion diff --git a/core/src/main/assets/sprites/lotus.png b/core/src/main/assets/sprites/lotus.png new file mode 100644 index 0000000000000000000000000000000000000000..c76b2e0a30e3761f608bb2a999ca8d3462928daa GIT binary patch literal 9232 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 affectedCells; - //the cells to trace growth particles to, for visual effects. - private HashSet visualCells; - private int direction = 0; private int totChrgUsed = 0; - + + ConeAOE cone; + int target; + + @Override + public boolean tryToZap(Hero owner, int target) { + if (super.tryToZap(owner, target)){ + this.target = target; + return true; + } else { + return false; + } + } + @Override protected void onZap( Ballistica bolt ) { - //ignore tiles which can't have anything grow in them. - for (Iterator i = affectedCells.iterator(); i.hasNext();) { - int c = Dungeon.level.map[i.next()]; - if (!(c == Terrain.EMPTY || - c == Terrain.EMBERS || - c == Terrain.EMPTY_DECO || - c == Terrain.GRASS || - c == Terrain.HIGH_GRASS || - c == Terrain.FURROWED_GRASS)) { - i.remove(); - } - } + ArrayList cells = new ArrayList<>(cone.cells); - float numPlants, numDews, numPods, numStars; - int overLimit = totChrgUsed - chargeLimit(Dungeon.hero.lvl); + float furrowedChance = overLimit > 0 ? (overLimit / (10f + Dungeon.hero.lvl)) : 0; int chrgUsed = chargesPerCast(); - //numbers greater than n*100% means n guaranteed plants, e.g. 210% = 2 plants w/10% chance for 3 plants. - numPlants = 0.2f + chrgUsed*chrgUsed*0.020f; //scales from 22% to 220% - numDews = 0.05f + chrgUsed*chrgUsed*0.016f; //scales from 6.6% to 165% - numPods = 0.02f + chrgUsed*chrgUsed*0.013f; //scales from 3.3% to 135% - numStars = (chrgUsed*chrgUsed*chrgUsed/5f)*0.005f; //scales from 0.1% to 100% - - if (overLimit > 0){ - numPlants -= overLimit*0.02f; - numDews -= overLimit*0.02f; - numPods -= overLimit*0.02f; - numStars -= overLimit*0.02f; - } - - placePlants(numPlants, numDews, numPods, numStars); + int grassToPlace = Math.round((3.5f+buffedLvl()/2f)*chrgUsed); - for (int i : affectedCells){ - int c = Dungeon.level.map[i]; - if (c == Terrain.EMPTY || - c == Terrain.EMBERS || - c == Terrain.EMPTY_DECO) { - Level.set( i, Terrain.GRASS ); - } - - Char ch = Actor.findChar(i); - if (ch != null){ - processSoulMark(ch, chargesPerCast()); - } - - if (Random.Int(50) < overLimit) { - if (Dungeon.level.map[i] == Terrain.GRASS) { - Level.set( i, Terrain.FURROWED_GRASS ); - GameScene.updateMap( i ); - } - GameScene.add(Blob.seed(i, 9, Regrowth.class)); + //ignore cells which can't have anything grow in them. + for (Iterator i = cells.iterator(); i.hasNext();) { + int cell = i.next(); + int terr = Dungeon.level.map[cell]; + if (!(terr == Terrain.EMPTY || + terr == Terrain.EMBERS || + terr == Terrain.EMPTY_DECO || + terr == Terrain.GRASS || + terr == Terrain.HIGH_GRASS || + terr == Terrain.FURROWED_GRASS)) { + i.remove(); } else { - GameScene.add(Blob.seed(i, 10, Regrowth.class)); + Level.set( cell, Terrain.GRASS ); + GameScene.updateMap( cell ); + Char ch = Actor.findChar(cell); + if (ch != null){ + processSoulMark(ch, chargesPerCast()); + Buff.prolong( ch, Roots.class, 4f * chrgUsed ); + } } - } - - totChrgUsed += chrgUsed; + + Random.shuffle(cells); + + if (chargesPerCast() >= 3){ + Lotus l = new Lotus(); + l.setLevel(buffedLvl()); + if (cells.contains(target)){ + cells.remove((Integer)target); + l.pos = target; + GameScene.add(l); + } else { + for (int i = bolt.path.size()-1; i >= 0; i--){ + if (cells.contains(bolt.path.get(i))){ + cells.remove((Integer)bolt.path.get(i)); + l.pos = bolt.path.get(i); + GameScene.add(l); + break; + } + } + } + } + + //places grass along center of cone + for (int cell : bolt.path){ + if (grassToPlace > 0 && cells.contains(cell)){ + if (Random.Float() > furrowedChance) { + Level.set(cell, Terrain.HIGH_GRASS); + } else { + Level.set(cell, Terrain.FURROWED_GRASS); + } + GameScene.updateMap( cell ); + grassToPlace--; + //moves cell to the back + cells.remove((Integer)cell); + cells.add(cell); + } + } + + if (!cells.isEmpty() && Random.Float() > furrowedChance && + (chrgUsed == 3 || (chrgUsed == 2 && Random.Int(2) == 0))){ + int cell = cells.remove(0); + Dungeon.level.plant( Random.Int(2) == 0 ? new Seedpod.Seed() : new Dewcatcher.Seed(), cell); + } + + if (!cells.isEmpty() && Random.Float() > furrowedChance && chrgUsed == 3){ + int cell = cells.remove(0); + Dungeon.level.plant((Plant.Seed) Generator.randomUsingDefaults(Generator.Category.SEED), cell); + } + + if (!cells.isEmpty() && Random.Float() > furrowedChance && + (chrgUsed >= 2 || (chrgUsed == 1 && Random.Int(2) == 0))){ + int cell = cells.remove(0); + Dungeon.level.plant((Plant.Seed) Generator.randomUsingDefaults(Generator.Category.SEED), cell); + } + + for (int cell : cells){ + if (grassToPlace <= 0 || bolt.path.contains(cell)) break; + + if (Random.Float() > furrowedChance) { + Level.set(cell, Terrain.HIGH_GRASS); + } else { + Level.set(cell, Terrain.FURROWED_GRASS); + } + GameScene.updateMap( cell ); + grassToPlace--; + } + + if (furrowedChance < 1f) { + totChrgUsed += chrgUsed; + } } - private int chargeLimit( int heroLevel ){ - if (level() >= 12){ + private int chargeLimit( int heroLvl ){ + if (level() >= 10){ return Integer.MAX_VALUE; } else { - //4 charges per hero level at +0, with another 2-4 each upgrade from +1 to +9. - //then +7 at +10, +16 at +11, and infinite at +12. - return Math.round(((4 + 2*level())*heroLevel) * (11f/12f + 1f/(12f-level()))); + //8 charges at base, plus: + //2/3.33/5/7/10/14/20/30/50/110/infinite charges per hero level, based on wand level + float lvl = buffedLvl(); + return Math.round(8 + heroLvl * (2+lvl) * (1f + (lvl/(10 - lvl)))); } } - private void spreadRegrowth(int cell, float strength){ - if (strength >= 0 && Dungeon.level.passable[cell]){ - affectedCells.add(cell); - if (strength >= 1.5f) { - spreadRegrowth(cell + PathFinder.CIRCLE8[left(direction)], strength - 1.5f); - spreadRegrowth(cell + PathFinder.CIRCLE8[direction], strength - 1.5f); - spreadRegrowth(cell + PathFinder.CIRCLE8[right(direction)], strength-1.5f); - } else { - visualCells.add(cell); - } - } else if (!Dungeon.level.passable[cell]) - visualCells.add(cell); - } - - private void placePlants(float numPlants, float numDews, float numPods, float numStars){ - Iterator cells = affectedCells.iterator(); - Level floor = Dungeon.level; - - while(cells.hasNext() && Random.Float() <= numPlants){ - Plant.Seed seed = (Plant.Seed) Generator.randomUsingDefaults(Generator.Category.SEED); - floor.plant(seed, cells.next()); - - numPlants --; - } - - while (cells.hasNext() && Random.Float() <= numDews){ - floor.plant(new Dewcatcher.Seed(), cells.next()); - numDews --; - } - - while (cells.hasNext() && Random.Float() <= numPods){ - floor.plant(new Seedpod.Seed(), cells.next()); - numPods --; - } - - while (cells.hasNext() && Random.Float() <= numStars){ - floor.plant(new Starflower.Seed(), cells.next()); - numStars --; - } - - } - - private int left(int direction){ - return direction == 0 ? 7 : direction-1; - } - - private int right(int direction){ - return direction == 7 ? 0 : direction+1; - } - @Override public void onHit(MagesStaff staff, Char attacker, Char defender, int damage) { new Blooming().proc(staff, attacker, defender, damage); @@ -197,62 +198,43 @@ public class WandOfRegrowth extends Wand { protected void fx( Ballistica bolt, Callback callback ) { - affectedCells = new HashSet<>(); - visualCells = new HashSet<>(); - - int maxDist = Math.round(1.2f + chargesPerCast()*.8f); + // 4/6/8 distance + int maxDist = 2 + 2*chargesPerCast(); int dist = Math.min(bolt.dist, maxDist); - for (int i = 0; i < PathFinder.CIRCLE8.length; i++){ - if (bolt.sourcePos+PathFinder.CIRCLE8[i] == bolt.path.get(1)){ - direction = i; - break; - } - } + cone = new ConeAOE( bolt.sourcePos, bolt.path.get(dist), + maxDist, + 20 + 10*chargesPerCast(), + collisionProperties | Ballistica.STOP_TARGET); - float strength = maxDist; - for (int c : bolt.subPath(1, dist)) { - strength--; //as we start at dist 1, not 0. - if (Dungeon.level.passable[c]) { - affectedCells.add(c); - spreadRegrowth(c + PathFinder.CIRCLE8[left(direction)], strength - 1); - spreadRegrowth(c + PathFinder.CIRCLE8[direction], strength - 1); - spreadRegrowth(c + PathFinder.CIRCLE8[right(direction)], strength - 1); - } else { - visualCells.add(c); - } - } - - //going to call this one manually - visualCells.remove(bolt.path.get(dist)); - - for (int cell : visualCells){ - //this way we only get the cells at the tip, much better performance. + //cast to cells at the tip, rather than all cells, better performance. + for (Ballistica ray : cone.rays){ ((MagicMissile)curUser.sprite.parent.recycle( MagicMissile.class )).reset( MagicMissile.FOLIAGE_CONE, curUser.sprite, - cell, + ray.path.get(ray.dist), null ); } + + //final zap at half distance, for timing of the actual wand effect MagicMissile.boltFromChar( curUser.sprite.parent, MagicMissile.FOLIAGE_CONE, curUser.sprite, bolt.path.get(dist/2), callback ); - Sample.INSTANCE.play( Assets.Sounds.ZAP ); } @Override - protected int initialCharges() { - return 1; + protected int chargesPerCast() { + //consumes 30% of current charges, rounded up, with a minimum of one. + return Math.max(1, (int)Math.ceil(curCharges*0.3f)); } @Override - //consumes all available charges, needs at least one. - protected int chargesPerCast() { - return Math.max(1, curCharges); + public String statsDesc() { + return Messages.get(this, "stats_desc", chargesPerCast()); } @Override @@ -354,4 +336,89 @@ public class WandOfRegrowth extends Wand { } + public static class Lotus extends NPC { + + { + alignment = Alignment.ALLY; + properties.add(Property.IMMOVABLE); + + spriteClass = LotusSprite.class; + + viewDistance = 1; + } + + private int wandLvl = 0; + + private void setLevel( int lvl ){ + wandLvl = lvl; + HP = HT = lvl*4; + } + + public boolean inRange(int pos){ + return Dungeon.level.trueDistance(this.pos, pos) <= wandLvl; + } + + public float seedPreservation(){ + return 0.25f + 0.05f*wandLvl; + } + + @Override + public boolean canInteract(Char c) { + return false; + } + + @Override + protected boolean act() { + super.act(); + throwItem(); + + if (--HP <= 0){ + destroy(); + sprite.die(); + } + + spend(TICK); + return true; + } + + @Override + public void damage( int dmg, Object src ) { + } + + @Override + public void add( Buff buff ) { + } + + @Override + public void destroy() { + super.destroy(); + Dungeon.observe(); + GameScene.updateFog(pos, viewDistance+1); + } + + @Override + public boolean isInvulnerable(Class effect) { + return true; + } + + @Override + public String description() { + return Messages.get(this, "desc", wandLvl, (int)(seedPreservation()*100), (int)(seedPreservation()*100) ); + } + + private static final String WAND_LVL = "wand_lvl"; + + @Override + public void storeInBundle(Bundle bundle) { + super.storeInBundle(bundle); + bundle.put(WAND_LVL, wandLvl); + } + + @Override + public void restoreFromBundle(Bundle bundle) { + super.restoreFromBundle(bundle); + wandLvl = bundle.getInt(WAND_LVL); + } + } + } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/missiles/darts/TippedDart.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/missiles/darts/TippedDart.java index 059838132..b613106df 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/missiles/darts/TippedDart.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/missiles/darts/TippedDart.java @@ -22,12 +22,14 @@ package com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.darts; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +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.PinCushion; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass; import com.shatteredpixel.shatteredpixeldungeon.items.Generator; +import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfRegrowth; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.plants.Blindweed; import com.shatteredpixel.shatteredpixeldungeon.plants.Dreamfoil; @@ -102,9 +104,10 @@ public abstract class TippedDart extends Dart { } //exact same damage as regular darts, despite being higher tier. - + @Override protected void rangedHit(Char enemy, int cell) { + targetPos = cell; super.rangedHit( enemy, cell); //need to spawn a dart @@ -121,7 +124,9 @@ public abstract class TippedDart extends Dart { Dungeon.level.drop( d, enemy.pos ).sprite.drop(); } } - + + private static int targetPos = -1; + @Override protected float durabilityPerUse() { float use = super.durabilityPerUse(); @@ -129,6 +134,30 @@ public abstract class TippedDart extends Dart { if (Dungeon.hero.subClass == HeroSubClass.WARDEN){ use /= 2f; } + + //checks both destination and source position + float lotusPreserve = 0f; + if (targetPos != -1){ + for (Char ch : Actor.chars()){ + if (ch instanceof WandOfRegrowth.Lotus){ + WandOfRegrowth.Lotus l = (WandOfRegrowth.Lotus) ch; + if (l.inRange(targetPos)){ + lotusPreserve = Math.max(lotusPreserve, l.seedPreservation()); + } + } + } + targetPos = -1; + } + int p = curUser == null ? Dungeon.hero.pos : curUser.pos; + for (Char ch : Actor.chars()){ + if (ch instanceof WandOfRegrowth.Lotus){ + WandOfRegrowth.Lotus l = (WandOfRegrowth.Lotus) ch; + if (l.inRange(p)){ + lotusPreserve = Math.max(lotusPreserve, l.seedPreservation()); + } + } + } + use *= (1f - lotusPreserve); return use; } 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 47b813419..9cbdd236b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java @@ -61,6 +61,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfStrength; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade; import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfEnchantment; import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfIntuition; +import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfRegrowth; import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfWarding; import com.shatteredpixel.shatteredpixeldungeon.levels.features.Chasm; import com.shatteredpixel.shatteredpixeldungeon.levels.features.Door; @@ -791,6 +792,15 @@ public abstract class Level implements Bundlable { plants.put( pos, plant ); GameScene.plantSeed( pos ); + + for (Char ch : Actor.chars()){ + if (ch instanceof WandOfRegrowth.Lotus + && ((WandOfRegrowth.Lotus) ch).inRange(pos) + && Actor.findChar(pos) != null){ + plant.trigger(); + return null; + } + } return plant; } @@ -1100,19 +1110,19 @@ public abstract class Level implements Bundlable { fieldOfView[p+i] = true; } - for (Mob ward : mobs){ - if (ward instanceof WandOfWarding.Ward){ - if (ward.fieldOfView == null || ward.fieldOfView.length != length()){ - ward.fieldOfView = new boolean[length()]; - Dungeon.level.updateFieldOfView( ward, ward.fieldOfView ); + for (Mob m : mobs){ + if (m instanceof WandOfWarding.Ward || m instanceof WandOfRegrowth.Lotus){ + if (m.fieldOfView == null || m.fieldOfView.length != length()){ + m.fieldOfView = new boolean[length()]; + Dungeon.level.updateFieldOfView( m, m.fieldOfView ); } - for (Mob m : mobs){ - if (ward.fieldOfView[m.pos] && !fieldOfView[m.pos] && - !Dungeon.hero.mindVisionEnemies.contains(m)){ - Dungeon.hero.mindVisionEnemies.add(m); + for (Mob m1 : mobs){ + if (m.fieldOfView[m1.pos] && !fieldOfView[m1.pos] && + !Dungeon.hero.mindVisionEnemies.contains(m1)){ + Dungeon.hero.mindVisionEnemies.add(m1); } } - BArray.or(fieldOfView, ward.fieldOfView, fieldOfView); + BArray.or(fieldOfView, m.fieldOfView, fieldOfView); } } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Blindweed.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Blindweed.java index 44f2be867..f77ae3f5b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Blindweed.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Blindweed.java @@ -39,6 +39,7 @@ public class Blindweed extends Plant { { image = 11; + seedClass = Seed.class; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Dreamfoil.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Dreamfoil.java index 5cee0069c..c96b00613 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Dreamfoil.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Dreamfoil.java @@ -37,6 +37,7 @@ public class Dreamfoil extends Plant { { image = 7; + seedClass = Seed.class; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Earthroot.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Earthroot.java index 5b650db40..841067157 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Earthroot.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Earthroot.java @@ -40,6 +40,7 @@ public class Earthroot extends Plant { { image = 8; + seedClass = Seed.class; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Fadeleaf.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Fadeleaf.java index 49d305667..90e647ca8 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Fadeleaf.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Fadeleaf.java @@ -41,6 +41,7 @@ public class Fadeleaf extends Plant { { image = 10; + seedClass = Seed.class; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Firebloom.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Firebloom.java index 95afd4515..e1e625ff3 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Firebloom.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Firebloom.java @@ -38,6 +38,7 @@ public class Firebloom extends Plant { { image = 1; + seedClass = Seed.class; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Icecap.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Icecap.java index 0146fc5af..9bc4d88b2 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Icecap.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Icecap.java @@ -37,6 +37,7 @@ public class Icecap extends Plant { { image = 4; + seedClass = Seed.class; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Plant.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Plant.java index 787101757..a4605d67a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Plant.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Plant.java @@ -31,6 +31,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass; import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; import com.shatteredpixel.shatteredpixeldungeon.effects.particles.LeafParticle; import com.shatteredpixel.shatteredpixeldungeon.items.Item; +import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfRegrowth; import com.shatteredpixel.shatteredpixeldungeon.levels.Level; import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; @@ -40,6 +41,7 @@ import com.watabou.noosa.audio.Sample; import com.watabou.utils.Bundlable; import com.watabou.utils.Bundle; import com.watabou.utils.PathFinder; +import com.watabou.utils.Random; import com.watabou.utils.Reflection; import java.util.ArrayList; @@ -51,6 +53,8 @@ public abstract class Plant implements Bundlable { public int image; public int pos; + protected Class seedClass; + public void trigger(){ Char ch = Actor.findChar(pos); @@ -71,6 +75,22 @@ public abstract class Plant implements Bundlable { if (Dungeon.level.heroFOV[pos]) { CellEmitter.get( pos ).burst( LeafParticle.GENERAL, 6 ); } + + float seedChance = 0f; + for (Char c : Actor.chars()){ + if (c instanceof WandOfRegrowth.Lotus){ + WandOfRegrowth.Lotus l = (WandOfRegrowth.Lotus) c; + if (l.inRange(pos)){ + seedChance = Math.max(seedChance, l.seedPreservation()); + } + } + } + + if (Random.Float() < seedChance){ + if (seedClass != null && seedClass != Rotberry.Seed.class) { + Dungeon.level.drop(Reflection.newInstance(seedClass), pos).sprite.drop(); + } + } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Rotberry.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Rotberry.java index e3741c9ca..7c523a6a0 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Rotberry.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Rotberry.java @@ -35,6 +35,7 @@ public class Rotberry extends Plant { { image = 0; + seedClass = Seed.class; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Sorrowmoss.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Sorrowmoss.java index 5648f0567..5d453ce2b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Sorrowmoss.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Sorrowmoss.java @@ -36,6 +36,7 @@ public class Sorrowmoss extends Plant { { image = 6; + seedClass = Seed.class; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Starflower.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Starflower.java index 53ed475d4..1d05ccb4c 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Starflower.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Starflower.java @@ -35,6 +35,7 @@ public class Starflower extends Plant { { image = 9; + seedClass = Seed.class; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Stormvine.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Stormvine.java index e328da00b..6f0913e50 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Stormvine.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Stormvine.java @@ -33,6 +33,7 @@ public class Stormvine extends Plant { { image = 5; + seedClass = Seed.class; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Sungrass.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Sungrass.java index 3df9b9239..f7d457d9d 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Sungrass.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Sungrass.java @@ -41,6 +41,7 @@ public class Sungrass extends Plant { { image = 3; + seedClass = Seed.class; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Swiftthistle.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Swiftthistle.java index 4886e3658..8e7a96aaa 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Swiftthistle.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/plants/Swiftthistle.java @@ -42,6 +42,7 @@ public class Swiftthistle extends Plant { { image = 2; + seedClass = Seed.class; } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/LotusSprite.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/LotusSprite.java new file mode 100644 index 000000000..eceb63bb2 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/LotusSprite.java @@ -0,0 +1,106 @@ +/* + * 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.sprites; + +import com.shatteredpixel.shatteredpixeldungeon.Assets; +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.Char; +import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.LeafParticle; +import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfRegrowth; +import com.watabou.noosa.MovieClip; +import com.watabou.noosa.TextureFilm; +import com.watabou.noosa.particles.Emitter; + +import java.util.ArrayList; + +//TODO implement on WIP sprite +public class LotusSprite extends MobSprite { + + private ArrayList grassVfx; + + public LotusSprite(){ + super(); + + perspectiveRaise = 0.2f; + + texture( Assets.Sprites.LOTUS ); + + TextureFilm frames = new TextureFilm( texture, 17, 14 ); + + idle = new MovieClip.Animation( 1, true ); + idle.frames( frames, 0 ); + + run = new MovieClip.Animation( 1, true ); + run.frames( frames, 0 ); + + attack = new MovieClip.Animation( 1, false ); + attack.frames( frames, 0 ); + + die = new MovieClip.Animation( 1, false ); + die.frames( frames, 0 ); + + play( idle ); + } + + @Override + public void link( Char ch ) { + super.link( ch ); + + renderShadow = false; + + if (grassVfx == null && ch instanceof WandOfRegrowth.Lotus){ + WandOfRegrowth.Lotus l = (WandOfRegrowth.Lotus) ch; + grassVfx = new ArrayList<>(); + for (int i = 0; i < Dungeon.level.length(); i++){ + if (!Dungeon.level.solid[i] && l.inRange(i)) { + Emitter e = CellEmitter.get(i); + e.pour(LeafParticle.LEVEL_SPECIFIC, 0.5f); + grassVfx.add(e); + } + } + } + } + + @Override + public void turnTo(int from, int to) { + //do nothing + } + + @Override + public void update() { + visible = true; + super.update(); + } + + @Override + public void die() { + super.die(); + + if (grassVfx != null){ + for (Emitter e : grassVfx){ + e.on = false; + } + grassVfx = null; + } + } +}