diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index c1cc683ba..3a1f640b2 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -978,8 +978,10 @@ items.scrolls.exotic.scrollofsirenssong$enthralled.desc=This creature has been b items.scrolls.exotic.scrollofantimagic.name=scroll of anti-magic items.scrolls.exotic.scrollofantimagic.desc=The incantation on this scroll will surround you with a magical aura that temporarily blocks all magical effects, harmful or helpful. -items.scrolls.exotic.scrollofconfusion.name=scroll of confusion -items.scrolls.exotic.scrollofconfusion.desc=When read aloud, this scroll will unleash confusing magic on all targets in sight, blinding and disorienting them. +items.scrolls.exotic.scrollofchallenge.name=scroll of challenge +items.scrolls.exotic.scrollofchallenge.desc=When read aloud, this scroll will unleash a great roar that draws enemies to the user while simultaneously creating a small arena around them.\n\nAs long as the reader stays in this arena they will take 33% less damage from all sources (this is applied before other forms of damage reduction), and they will not lose satiety.\n\nThe size of the arena will scale with the size of the area the reader is in. It will be particularly small in some boss areas. +items.scrolls.exotic.scrollofchallenge$challengearena.name=Challenge Arena +items.scrolls.exotic.scrollofchallenge$challengearena.desc=A scroll of challenge has created an arena around you, denoted by a billowing red fog.\n\nWhile standing in the fog, your hunger will not increase and you will take 33%% less damage from all sources. If you have any other sources of damage reduction (such as armor), they will apply after the 33%% reduction.\n\nTurns remaining: %d. items.scrolls.exotic.scrollofdivination.name=scroll of divination items.scrolls.exotic.scrollofdivination.nothing_left=There is nothing left to identify! diff --git a/core/src/main/assets/sprites/item_icons.png b/core/src/main/assets/sprites/item_icons.png index 337642ab2..a1a06b59f 100644 Binary files a/core/src/main/assets/sprites/item_icons.png and b/core/src/main/assets/sprites/item_icons.png differ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java index 59d7fa517..8733b58d0 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java @@ -52,6 +52,9 @@ public class ShatteredPixelDungeon extends Game { com.watabou.utils.Bundle.addAlias( com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ScrollOfSirensSong.class, "com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ScrollOfAffection" ); + com.watabou.utils.Bundle.addAlias( + com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ScrollOfChallenge.class, + "com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ScrollOfConfusion" ); //v1.0.0 com.watabou.utils.Bundle.addAlias( diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java index bb28de603..20522edf8 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java @@ -79,6 +79,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.potions.exotic.PotionOfCle import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfElements; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfRetribution; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTeleportation; +import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ScrollOfChallenge; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ScrollOfPsionicBlast; import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfAggression; import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfFireblast; @@ -357,6 +358,10 @@ public abstract class Char extends Actor { if (endure != null){ dmg = endure.adjustDamageTaken(dmg); } + + if (enemy.buff(ScrollOfChallenge.ChallengeArena.class) != null){ + dmg *= 0.67f; + } int effectiveDamage = enemy.defenseProc( this, dmg ); effectiveDamage = Math.max( effectiveDamage - dr, 0 ); @@ -590,9 +595,15 @@ public abstract class Char extends Actor { dmg *= 1.25f; } Endure.EndureTracker endure = buff(Endure.EndureTracker.class); - //reduce damage here if it isn't coming from a chacter (if it is we already reduced it) - if (endure != null && !(src instanceof Char)){ - dmg = endure.adjustDamageTaken(dmg); + if (!(src instanceof Char)){ + //reduce damage here if it isn't coming from a character (if it is we already reduced it) + if (endure != null){ + dmg = endure.adjustDamageTaken(dmg); + } + //the same also applies to challenge scroll damage reduction + if (buff(ScrollOfChallenge.ChallengeArena.class) != null){ + dmg *= 0.67f; + } } Class srcClass = src.getClass(); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Hunger.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Hunger.java index 2fcddef90..8fbc00609 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Hunger.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Hunger.java @@ -27,6 +27,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.Artifact; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HornOfPlenty; import com.shatteredpixel.shatteredpixeldungeon.items.journal.Guidebook; +import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ScrollOfChallenge; import com.shatteredpixel.shatteredpixeldungeon.journal.Document; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; @@ -64,7 +65,9 @@ public class Hunger extends Buff implements Hero.Doom { @Override public boolean act() { - if (Dungeon.level.locked || target.buff(WellFed.class) != null){ + if (Dungeon.level.locked + || target.buff(WellFed.class) != null + || target.buff(ScrollOfChallenge.ChallengeArena.class) != null){ spend(STEP); return true; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/effects/particles/ChallengeParticle.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/effects/particles/ChallengeParticle.java new file mode 100644 index 000000000..e6aa8074a --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/effects/particles/ChallengeParticle.java @@ -0,0 +1,47 @@ +package com.shatteredpixel.shatteredpixeldungeon.effects.particles; + +import com.watabou.noosa.particles.Emitter; +import com.watabou.noosa.particles.PixelParticle; +import com.watabou.utils.Random; + +public class ChallengeParticle extends PixelParticle.Shrinking { + + public static final Emitter.Factory FACTORY = new Emitter.Factory() { + @Override + public void emit( Emitter emitter, int index, float x, float y ) { + ((ChallengeParticle)emitter.recycle( ChallengeParticle.class )).reset( x, y ); + } + @Override + public boolean lightMode() { + return false; + } + }; + + public ChallengeParticle() { + super(); + + lifespan = 0.6f; + + color( 0xFF0000 ); + } + + public void reset( float x, float y){ + revive(); + + this.x = x; + this.y = y; + + left = lifespan; + size = 8; + + speed.set( Random.Float( -8, +8 ), Random.Float( -16, -32 ) ); + } + + @Override + public void update() { + super.update(); + + am = 1 - left / lifespan; + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ExoticScroll.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ExoticScroll.java index d11667ff9..d97f11c33 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ExoticScroll.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ExoticScroll.java @@ -60,8 +60,8 @@ public abstract class ExoticScroll extends Scroll { regToExo.put(ScrollOfLullaby.class, ScrollOfSirensSong.class); exoToReg.put(ScrollOfSirensSong.class, ScrollOfLullaby.class); - regToExo.put(ScrollOfRage.class, ScrollOfConfusion.class); - exoToReg.put(ScrollOfConfusion.class, ScrollOfRage.class); + regToExo.put(ScrollOfRage.class, ScrollOfChallenge.class); + exoToReg.put(ScrollOfChallenge.class, ScrollOfRage.class); regToExo.put(ScrollOfTerror.class, ScrollOfDread.class); exoToReg.put(ScrollOfDread.class, ScrollOfTerror.class); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ScrollOfChallenge.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ScrollOfChallenge.java new file mode 100644 index 000000000..c6b6eb55f --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ScrollOfChallenge.java @@ -0,0 +1,210 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2021 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.items.scrolls.exotic; + +import com.shatteredpixel.shatteredpixeldungeon.Assets; +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; +import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; +import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ChallengeParticle; +import com.shatteredpixel.shatteredpixeldungeon.levels.RegularLevel; +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room; +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.connection.ConnectionRoom; +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.StandardRoom; +import com.shatteredpixel.shatteredpixeldungeon.mechanics.ShadowCaster; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; +import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator; +import com.shatteredpixel.shatteredpixeldungeon.utils.BArray; +import com.watabou.noosa.Image; +import com.watabou.noosa.audio.Sample; +import com.watabou.noosa.particles.Emitter; +import com.watabou.utils.Bundle; +import com.watabou.utils.PathFinder; +import com.watabou.utils.Point; + +import java.util.ArrayList; +import java.util.Arrays; + +public class ScrollOfChallenge extends ExoticScroll { + + { + icon = ItemSpriteSheet.Icons.SCROLL_CHALLENGE; + } + + @Override + public void doRead() { + for (Mob mob : Dungeon.level.mobs.toArray( new Mob[0] )) { + mob.beckon( curUser.pos ); + } + + Buff.affect(curUser, ChallengeArena.class).setup(curUser.pos); + + identify(); + + curUser.sprite.centerEmitter().start( Speck.factory( Speck.SCREAM ), 0.3f, 3 ); + Sample.INSTANCE.play( Assets.Sounds.CHALLENGE ); + + readAnimation(); + } + + + public static class ChallengeArena extends Buff { + + private ArrayList arenaPositions = new ArrayList<>(); + private ArrayList arenaEmitters = new ArrayList<>(); + + private static final float DURATION = 100; + int left = 0; + + { + type = buffType.POSITIVE; + } + + @Override + public int icon() { + return BuffIndicator.ARMOR; + } + + @Override + public void tintIcon(Image icon) { + icon.hardlight(1f, 0f, 0f); + } + + @Override + public float iconFadePercent() { + return Math.max(0, (DURATION - left) / DURATION); + } + + @Override + public String toString() { + return Messages.get(this, "name"); + } + + @Override + public String desc() { + return Messages.get(this, "desc", left); + } + + public void setup(int pos){ + + int dist; + if (Dungeon.depth == 5 || Dungeon.depth == 10 || Dungeon.depth == 20){ + dist = 1; //smaller boss arenas + } else { + + boolean[] visibleCells = new boolean[Dungeon.level.length()]; + Point c = Dungeon.level.cellToPoint(pos); + ShadowCaster.castShadow(c.x, c.y, visibleCells, Dungeon.level.losBlocking, 8); + int count=0; + for (boolean b : visibleCells){ + if (b) count++; + } + + if (count < 30){ + dist = 1; + } else if (count >= 100) { + dist = 3; + } else { + dist = 2; + } + } + + PathFinder.buildDistanceMap( pos, BArray.or( Dungeon.level.passable, Dungeon.level.avoid, null ), dist ); + for (int i = 0; i < PathFinder.distance.length; i++) { + if (PathFinder.distance[i] < Integer.MAX_VALUE && !arenaPositions.contains(i)) { + arenaPositions.add(i); + } + } + if (target != null) { + fx(false); + fx(true); + } + + left = (int) DURATION; + + } + + @Override + public boolean act() { + + if (!arenaPositions.contains(target.pos)){ + detach(); + } + + left--; + BuffIndicator.refreshHero(); + if (left <= 0){ + detach(); + } + + spend(TICK); + return true; + } + + @Override + public void fx(boolean on) { + if (on){ + for (int i : arenaPositions){ + Emitter e = CellEmitter.get(i); + e.pour(ChallengeParticle.FACTORY, 0.05f); + arenaEmitters.add(e); + } + } else { + for (Emitter e : arenaEmitters){ + e.on = false; + } + arenaEmitters.clear(); + } + } + + private static final String ARENA_POSITIONS = "arena_positions"; + private static final String LEFT = "left"; + + @Override + public void storeInBundle(Bundle bundle) { + super.storeInBundle(bundle); + + int[] values = new int[arenaPositions.size()]; + for (int i = 0; i < values.length; i ++) + values[i] = arenaPositions.get(i); + bundle.put(ARENA_POSITIONS, values); + + bundle.put(LEFT, left); + } + + @Override + public void restoreFromBundle(Bundle bundle) { + super.restoreFromBundle(bundle); + + int[] values = bundle.getIntArray( ARENA_POSITIONS ); + for (int value : values) { + arenaPositions.add(value); + } + + left = bundle.getInt(LEFT); + } + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ScrollOfConfusion.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ScrollOfConfusion.java deleted file mode 100644 index 5c186f02d..000000000 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ScrollOfConfusion.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Pixel Dungeon - * Copyright (C) 2012-2015 Oleg Dolya - * - * Shattered Pixel Dungeon - * Copyright (C) 2014-2021 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.items.scrolls.exotic; - -import com.shatteredpixel.shatteredpixeldungeon.Assets; -import com.shatteredpixel.shatteredpixeldungeon.Dungeon; -import com.shatteredpixel.shatteredpixeldungeon.actors.Char; -import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Blindness; -import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; -import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vertigo; -import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; -import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; -import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; -import com.watabou.noosa.audio.Sample; - -public class ScrollOfConfusion extends ExoticScroll { - - { - icon = ItemSpriteSheet.Icons.SCROLL_CONFUSION; - } - - @Override - public void doRead() { - for (Mob mob : Dungeon.level.mobs.toArray( new Mob[0] )) { - if (mob.alignment != Char.Alignment.ALLY && Dungeon.level.heroFOV[mob.pos]) { - Buff.prolong(mob, Vertigo.class, Vertigo.DURATION); - Buff.prolong(mob, Blindness.class, Blindness.DURATION); - } - } - - identify(); - - curUser.sprite.centerEmitter().start( Speck.factory( Speck.SCREAM ), 0.3f, 3 ); - Sample.INSTANCE.play( Assets.Sounds.READ ); - - readAnimation(); - } - -} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ItemSpriteSheet.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ItemSpriteSheet.java index e8969aa6a..b2064ecdd 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ItemSpriteSheet.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ItemSpriteSheet.java @@ -776,7 +776,7 @@ public class ItemSpriteSheet { public static final int SCROLL_PASSAGE = EXOTIC_SCROLLS+5; public static final int SCROLL_SIREN = EXOTIC_SCROLLS+6; public static final int SCROLL_FORESIGHT= EXOTIC_SCROLLS+7; - public static final int SCROLL_CONFUSION= EXOTIC_SCROLLS+8; + public static final int SCROLL_CHALLENGE= EXOTIC_SCROLLS+8; public static final int SCROLL_PSIBLAST = EXOTIC_SCROLLS+9; public static final int SCROLL_DREAD = EXOTIC_SCROLLS+10; public static final int SCROLL_POLYMORPH= EXOTIC_SCROLLS+11; @@ -789,7 +789,7 @@ public class ItemSpriteSheet { assignIconRect( SCROLL_PASSAGE, 5, 7 ); assignIconRect( SCROLL_SIREN, 7, 6 ); assignIconRect( SCROLL_FORESIGHT, 7, 5 ); - assignIconRect( SCROLL_CONFUSION, 7, 7 ); + assignIconRect( SCROLL_CHALLENGE, 7, 7 ); assignIconRect( SCROLL_PSIBLAST, 5, 6 ); assignIconRect( SCROLL_DREAD, 5, 7 ); assignIconRect( SCROLL_POLYMORPH, 7, 6 );