diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index 57598cf1b..5bc6c134b 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -1397,6 +1397,8 @@ items.wands.cursedwand.fire=You smell burning... items.wands.cursedwand.transmogrify_wand=Your wand transmogrifies into a different item! items.wands.cursedwand.transmogrify_other=Your item transmogrifies into something different! items.wands.cursedwand.disguise=Your appearance changes before your eyes! +items.wands.cursedwand.supernova=A blazing ball of energy starts to grow. You might want to run! +items.wands.cursedwand.supernova_positive=A blazing ball of energy starts to grow, but it feels safe somehow. items.wands.damagewand.upgrade_stat_name_1=Magic Damage diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/SuperNovaTracker.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/SuperNovaTracker.java new file mode 100644 index 000000000..6f8311b18 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/SuperNovaTracker.java @@ -0,0 +1,194 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2024 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.buffs; + +import com.shatteredpixel.shatteredpixeldungeon.Assets; +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; +import com.shatteredpixel.shatteredpixeldungeon.actors.Char; +import com.shatteredpixel.shatteredpixeldungeon.effects.FloatingText; +import com.shatteredpixel.shatteredpixeldungeon.effects.TargetedCell; +import com.shatteredpixel.shatteredpixeldungeon.items.bombs.Bomb; +import com.shatteredpixel.shatteredpixeldungeon.mechanics.ShadowCaster; +import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; +import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene; +import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite; +import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap; +import com.watabou.noosa.Game; +import com.watabou.noosa.Halo; +import com.watabou.noosa.audio.Sample; +import com.watabou.utils.Bundle; +import com.watabou.utils.Point; +import com.watabou.utils.PointF; + +public class SuperNovaTracker extends Buff { + + public int pos; + private int depth = Dungeon.depth; + private int branch = Dungeon.branch; + + private int turnsLeft = 10; + public boolean harmsAllies = true; + + private boolean[] fieldOfView; + private NovaVFX halo; + + private static final int DIST = 8; + + @Override + public boolean act() { + + if (branch != Dungeon.branch || depth != Dungeon.depth){ + spend(TICK); + return true; + } + + PointF p = DungeonTilemap.raisedTileCenterToWorld(pos); + if (fieldOfView == null){ + fieldOfView = new boolean[Dungeon.level.length()]; + } + if (halo == null){ + halo = new NovaVFX(); + halo.point(p.x, p.y); + halo.hardlight(1, 1, 0f); + GameScene.effect(halo); + } + + if (turnsLeft > 0){ + + FloatingText.show(p.x, p.y, pos, turnsLeft + "...", CharSprite.WARNING); + halo.radius(5 + 2*(10-turnsLeft)); + halo.alpha(1.25f - 0.075f*turnsLeft); + halo.point(p.x, p.y); + } + + Point c = Dungeon.level.cellToPoint(pos); + ShadowCaster.castShadow(c.x, c.y, Dungeon.level.width(), fieldOfView, Dungeon.level.solid, Math.min(DIST, 11-turnsLeft)); + + if (turnsLeft <= 0){ + detach(); + halo.killAndErase(); + + //if positive only, bombs do not harm allies + if (!harmsAllies) { + for (Char ch : Actor.chars()) { + if (ch.alignment == Char.Alignment.ALLY) { + Buff.affect(ch, NovaBombImmune.class, 0f); + } + } + } + + Sample.INSTANCE.play(Assets.Sounds.BLAST); + Sample.INSTANCE.playDelayed(Assets.Sounds.BLAST, 0.25f); + Sample.INSTANCE.playDelayed(Assets.Sounds.BLAST, 0.5f); + PixelScene.shake( 5, 2f ); + for (int i = 0; i < Dungeon.level.length(); i++){ + if (fieldOfView[i]){ + new Bomb.ConjuredBomb().explode(i); //yes, a bomb at every cell + //this means that something in the blast effectively takes: + //5.33x bomb dmg when fully inside + //4.33x when along straight edge + //2x when outside straight edge + Dungeon.level.destroy(i); + if (Actor.findChar(i) == Dungeon.hero){ + GameScene.flash(0x80FFFFFF); + } + } + } + GameScene.updateMap(); + + } else { + for (int i = 0; i < Dungeon.level.length(); i++){ + if (fieldOfView[i]){ + target.sprite.parent.add(new TargetedCell(i, 0xFF0000)); + } + } + } + + turnsLeft--; + spend(TICK); + return true; + + } + + public static class NovaBombImmune extends FlavourBuff{ + { + immunities.add(Bomb.ConjuredBomb.class); + } + } + + @Override + public void fx(boolean on) { + if (on && depth == Dungeon.depth && branch == Dungeon.branch + && (halo == null || halo.parent == null)){ + halo = new NovaVFX(); + PointF p = DungeonTilemap.raisedTileCenterToWorld(pos); + halo.hardlight(1, 1, 0f); + halo.radius(5 + 2*(10-turnsLeft)); + halo.alpha(1.25f - 0.075f*turnsLeft); + halo.point(p.x, p.y); + GameScene.effect(halo); + } + super.fx(on); + } + + public static final String POS = "pos"; + public static final String DEPTH = "depth"; + public static final String BRANCH = "branch"; + + public static final String LEFT = "left"; + public static final String HARMS_ALLIES = "harms_allies"; + + @Override + public void storeInBundle(Bundle bundle) { + super.storeInBundle(bundle); + bundle.put(POS, pos); + bundle.put(DEPTH, depth); + bundle.put(BRANCH, branch); + bundle.put(LEFT, turnsLeft); + bundle.put(HARMS_ALLIES, harmsAllies); + } + + @Override + public void restoreFromBundle(Bundle bundle) { + super.restoreFromBundle(bundle); + pos = bundle.getInt(POS); + depth = bundle.getInt(DEPTH); + branch = bundle.getInt(BRANCH); + turnsLeft = bundle.getInt(LEFT); + harmsAllies = bundle.getBoolean(HARMS_ALLIES); + } + + public class NovaVFX extends Halo { + + @Override + public void update() { + am = brightness + 0.1f*(float)Math.cos(20*Game.timeTotal); + scale.set((radius + (float)Math.cos(20*Game.timeTotal))/RADIUS); + PointF p = DungeonTilemap.raisedTileCenterToWorld(pos); + point(p.x, p.y); + super.update(); + } + + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/CursedWand.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/CursedWand.java index 7b5341678..85905723c 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/CursedWand.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/CursedWand.java @@ -42,6 +42,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.HeroDisguise; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Hex; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Recharging; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.SuperNovaTracker; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.GoldenMimic; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic; @@ -586,6 +587,7 @@ public class CursedWand { VERY_RARE_EFFECTS.add(new AbortRetryFail()); VERY_RARE_EFFECTS.add(new RandomTransmogrify()); VERY_RARE_EFFECTS.add(new HeroShapeShift()); + VERY_RARE_EFFECTS.add(new SuperNova()); } public static CursedEffect randomVeryRareEffect(){ @@ -759,7 +761,7 @@ public class CursedWand { } } - public static class HeroShapeShift extends CursedEffect{ + public static class HeroShapeShift extends CursedEffect { @Override public boolean valid(Item origin, Char user, Ballistica bolt, boolean positiveOnly) { @@ -781,4 +783,20 @@ public class CursedWand { } } + public static class SuperNova extends CursedEffect { + @Override + public boolean effect(Item origin, Char user, Ballistica bolt, boolean positiveOnly) { + SuperNovaTracker nova = Buff.append(Dungeon.hero, SuperNovaTracker.class); + nova.pos = bolt.collisionPos; + nova.harmsAllies = !positiveOnly; + if (positiveOnly){ + GLog.p(Messages.get(CursedWand.class, "supernova_positive")); + } else { + GLog.w(Messages.get(CursedWand.class, "supernova")); + } + + return true; + } + } + }