diff --git a/core/src/main/assets/messages/actors/actors.properties b/core/src/main/assets/messages/actors/actors.properties index 753da5d17..fc0f75eef 100644 --- a/core/src/main/assets/messages/actors/actors.properties +++ b/core/src/main/assets/messages/actors/actors.properties @@ -67,6 +67,22 @@ actors.buffs.arcanearmor.desc=A thin shield is surrounding you, blocking some of actors.buffs.artifactrecharge.name=Artifact Recharging actors.buffs.artifactrecharge.desc=Energy is coursing through you, increasing the rate your equipped artifacts charge.\n\nEach artifact is affected a little differently, but they will all gain charge much faster than normal.\n\nTurns remaining: %s. +actors.buffs.ascensionchallenge.name=Amulet's Curse +actors.buffs.ascensionchallenge.desc=Somehow Yog-Dzewa is maintaining influence on this world through the amulet, and is trying to stop your ascent!\n\nYog's power is making the dungeon much more dangerous, increasing the number of enemies and making them stronger! This power will keep building if left unchecked, it can be weakened by defeating enemies that has been enhanced by Yog's power. +actors.buffs.ascensionchallenge.desc_clear=The dark energy radiating from the amulet has been weakened as much as possible for now. +actors.buffs.ascensionchallenge.desc_beckon=The amulet is currently _calling out to distant enemies,_ alerting them to your position. +actors.buffs.ascensionchallenge.desc_haste=The amulet is currently _hastening distant enemies,_ letting them reach you faster! +actors.buffs.ascensionchallenge.desc_slow=The amulet is currently _slowing you down,_ and negating speed boosting effects! +actors.buffs.ascensionchallenge.desc_damage=The energy from the amulet has gotten so strong that it's _directly harming you!_ +actors.buffs.ascensionchallenge.beckon=The amulet begins calling out to distant enemies. +actors.buffs.ascensionchallenge.haste=The amulet begins hastening distant enemies! +actors.buffs.ascensionchallenge.slow=The amulet starts to feel like a lead weight in your inventory! +actors.buffs.ascensionchallenge.damage=The amulet begins radiating dark energy. It burns! +actors.buffs.ascensionchallenge.weaken=You can feel the amulet's curse weaken slightly. +actors.buffs.ascensionchallenge.break=You take a moment to catch your breath and feel your wounds begin to close! +actors.buffs.ascensionchallenge.almost=You feel Yog's grip on the amulet begin to weaken, you're almost there! +actors.buffs.ascensionchallenge.on_kill=The dark energy consumes you... + actors.buffs.barkskin.name=Barkskin actors.buffs.barkskin.desc=Your skin is hardened, it feels rough and solid like bark.\n\nThe hardened skin increases your effective armor, allowing you to better defend against physical attack.\n\nYour armor is currently increased by: 0-%d.\nTurns until barkskin weakens: %s. @@ -812,6 +828,7 @@ actors.mobs.npcs.imp.desc=Imps are lesser demons. They are notable for neither t actors.mobs.npcs.impshopkeeper.name=ambitious imp actors.mobs.npcs.impshopkeeper.greetings=Hello, %s! +actors.mobs.npcs.impshopkeeper.greetings_ascent=What did you do %s? Shop quickly, I've got to get out of here! actors.mobs.npcs.impshopkeeper.thief=I thought I could trust you! actors.mobs.npcs.impshopkeeper.desc=Imps are lesser demons. They are notable for neither their strength nor their magic talent. But they are quite smart and sociable, and many of imps prefer to live and do business among non-demons. diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index 7a9eae394..5dacc19e7 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -1705,8 +1705,13 @@ items.weapon.weapon$enchantment.enchant=enchantment ###misc items items.amulet.name=amulet of yendor items.amulet.ac_end=END THE GAME -items.amulet.rankings_desc=Obtained the Amulet of Yendor -items.amulet.desc=The Amulet of Yendor is the most powerful artifact known to human or dwarf kind. Its shimmering crystals contain incredible and wondrous power.\n\nThe origins of the amulet are unknown. History says that the King of Dwarves boasted that he had discovered the artifact shortly before the Dwarven civilization cut off all contact. How did he find it? How did Yog-Dzewa take it from him? Questions for another time perhaps, all that matters now is the amulet is yours! +items.amulet.desc=The Amulet of Yendor is the most powerful artifact known to human or dwarf kind. Its shimmering crystals contain incredible and wondrous power. +items.amulet.desc_origins=The origins of the amulet are unknown. History says that the King of Dwarves boasted that he had discovered the artifact shortly before the Dwarven civilization cut off all contact. How did he find it? How did Yog-Dzewa take it from him? Questions for another time perhaps, all that matters now is the amulet is yours! +items.amulet.desc_ascent=The origins of the amulet are unknown, but clearly it has been heavily influenced by Yog-Dzewa's power. The dungeon seems to be bending to Yog's will as you move through it, making enemies much more plentiful and powerful! You're also no longer able to activate or discard the amulet, almost as if it is cursed. +items.amulet.ascent_title=Ascension +items.amulet.ascent_desc=You begin to feel Yog-Dzewa's great and terrible power radiating from the amulet. Ascending back through the dungeon as a mortal might be more difficult than you thought!\n\nIf you continue ascending with the amulet the dungeon will become more dangerous, teleports between floors will be disabled, and you will only be able to win the game by ascending through the dungeon!\n\nYou can leave the amulet here for now if you want to ascend without starting this challenge, or activate the amulet before ascending to end the game normally. +items.amulet.ascent_yes=Continue! +items.amulet.ascent_no=Stop for Now items.ankh.name=ankh items.ankh.ac_bless=BLESS diff --git a/core/src/main/assets/messages/misc/misc.properties b/core/src/main/assets/messages/misc/misc.properties index cd2f91e3d..87b7dd118 100644 --- a/core/src/main/assets/messages/misc/misc.properties +++ b/core/src/main/assets/messages/misc/misc.properties @@ -197,3 +197,5 @@ challenges.stronger_bosses=Badder bosses challenges.stronger_bosses_desc=Bosses are much tougher with this challenge!\n\n_Goo:_ +20% health\n_-_ Healing in water ramps up, to 3/turn\n_-_ Pumps up in 1 turn instead of 2\n_Tengu:_ +25% health\n_-_ Phase 1: traps are much deadlier\n_-_ Phase 2: abilities are more frequent\n_DM-300:_ +33% health\n_-_ Pylons are tougher and 3 activate\n_-_ Abilities are more powerful and frequent\n_-_ DM-300 is faster when supercharged\n_-_ Exposed wires are twice as common\n_Dwarf King:_ +50% health\n_-_ Minions are stronger in all phases\n_-_ Phase 1: faster abilities and summons\n_-_ Phase 2: 2 more minions per round\n_-_ Phase 3: 2x health, faster summons\n_Yog-Dzewa:_\n_-_ Two fists are summoned at a time!\n_-_ +60% laser damage\n_-_ Stronger minions rankings$record.something=Killed by Something +rankings$record.won=Obtained the Amulet of Yendor +rankings$record.ascended=Ascended with the Amulet! \ No newline at end of file diff --git a/core/src/main/assets/messages/scenes/scenes.properties b/core/src/main/assets/messages/scenes/scenes.properties index 275b0dee7..bc0768d4b 100644 --- a/core/src/main/assets/messages/scenes/scenes.properties +++ b/core/src/main/assets/messages/scenes/scenes.properties @@ -7,7 +7,7 @@ scenes.alchemyscene.energy=Energy: scenes.amuletscene.exit=Let's call it a day scenes.amuletscene.stay=I'm not done yet -scenes.amuletscene.text=You finally hold it in your hands, the Amulet of Yendor! With the power of this amulet nothing will be able to stand in your way! You have conquered the dungeon and succeeded in your quest!\n\nOr, perhaps you're not ready yet? You can also decide to just hold onto the amulet, and stay a mere mortal a little longer... +scenes.amuletscene.text=You finally hold it in your hands, the Amulet of Yendor! With the power of this amulet nothing will be able to stand in your way! You have conquered the dungeon and succeeded in your quest!\n\nOr, perhaps you're not ready yet? You can also decide to just hold onto the amulet, stay a mere mortal a little longer, and perhaps leave the dungeon the old fashioned way... scenes.badgesscene.title=Your Badges diff --git a/core/src/main/assets/messages/windows/windows.properties b/core/src/main/assets/messages/windows/windows.properties index 452226591..0ff5190a7 100644 --- a/core/src/main/assets/messages/windows/windows.properties +++ b/core/src/main/assets/messages/windows/windows.properties @@ -161,6 +161,7 @@ windows.wndranking$statstab.score=Score windows.wndranking$statstab.str=Strength windows.wndranking$statstab.duration=Game Duration windows.wndranking$statstab.depth=Maximum Depth +windows.wndranking$statstab.ascent=Highest Ascent windows.wndranking$statstab.seed=Dungeon Seed windows.wndranking$statstab.enemies=Mobs Killed windows.wndranking$statstab.gold=Gold Collected diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java index 3bcf5317c..6c77550fb 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java @@ -431,6 +431,10 @@ public class Dungeon { hero.curAction = hero.lastAction = null; + if (hero.buff(AscensionChallenge.class) != null){ + hero.buff(AscensionChallenge.class).onLevelSwitch(); + } + observe(); try { saveAll(); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Rankings.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Rankings.java index 4c8aedcdb..3703c5e42 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Rankings.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Rankings.java @@ -75,7 +75,13 @@ public enum Rankings { rec.heroClass = Dungeon.hero.heroClass; rec.armorTier = Dungeon.hero.tier(); rec.herolevel = Dungeon.hero.lvl; - rec.depth = Dungeon.depth; + if (Statistics.highestAscent == 0){ + rec.depth = Statistics.deepestFloor; + rec.ascending = false; + } else { + rec.depth = Statistics.highestAscent; + rec.ascending = true; + } rec.score = calculateScore(); Badges.validateHighScore( rec.score ); @@ -346,6 +352,7 @@ public enum Rankings { private static final String TIER = "tier"; private static final String LEVEL = "level"; private static final String DEPTH = "depth"; + private static final String ASCEND = "ascending"; private static final String DATA = "gameData"; private static final String ID = "gameID"; @@ -356,7 +363,8 @@ public enum Rankings { public int armorTier; public int herolevel; public int depth; - + public boolean ascending; + public Bundle gameData; public String gameID; @@ -364,7 +372,13 @@ public enum Rankings { public int score; public String desc(){ - if (cause == null) { + if (win){ + if (ascending){ + return Messages.get(this, "ascended"); + } else { + return Messages.get(this, "won"); + } + } else if (cause == null) { return Messages.get(this, "something"); } else { String result = Messages.get(cause, "rankings_desc", (Messages.get(cause, "name"))); @@ -390,15 +404,15 @@ public enum Rankings { heroClass = bundle.getEnum( CLASS, HeroClass.class ); armorTier = bundle.getInt( TIER ); - + herolevel = bundle.getInt( LEVEL ); + depth = bundle.getInt( DEPTH ); + ascending = bundle.getBoolean( ASCEND ); + if (bundle.contains(DATA)) gameData = bundle.getBundle(DATA); if (bundle.contains(ID)) gameID = bundle.getString(ID); if (gameID == null) gameID = UUID.randomUUID().toString(); - depth = bundle.getInt( DEPTH ); - herolevel = bundle.getInt( LEVEL ); - } @Override @@ -413,7 +427,8 @@ public enum Rankings { bundle.put( TIER, armorTier ); bundle.put( LEVEL, herolevel ); bundle.put( DEPTH, depth ); - + bundle.put( ASCEND, ascending ); + if (gameData != null) bundle.put( DATA, gameData ); bundle.put( ID, gameID ); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Statistics.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Statistics.java index 8b98591dc..932585761 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Statistics.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Statistics.java @@ -28,6 +28,7 @@ public class Statistics { public static int goldCollected; public static int deepestFloor; + public static int highestAscent; public static int enemiesSlain; public static int foodEaten; public static int itemsCrafted; @@ -70,6 +71,7 @@ public class Statistics { goldCollected = 0; deepestFloor = 0; + highestAscent = 0; enemiesSlain = 0; foodEaten = 0; itemsCrafted = 0; @@ -108,6 +110,7 @@ public class Statistics { private static final String GOLD = "score"; private static final String DEEPEST = "maxDepth"; + private static final String HIGHEST = "maxAscent"; private static final String SLAIN = "enemiesSlain"; private static final String FOOD = "foodEaten"; private static final String ALCHEMY = "potionsCooked"; @@ -145,6 +148,7 @@ public class Statistics { public static void storeInBundle( Bundle bundle ) { bundle.put( GOLD, goldCollected ); bundle.put( DEEPEST, deepestFloor ); + bundle.put( HIGHEST, highestAscent ); bundle.put( SLAIN, enemiesSlain ); bundle.put( FOOD, foodEaten ); bundle.put( ALCHEMY, itemsCrafted ); @@ -187,6 +191,7 @@ public class Statistics { public static void restoreFromBundle( Bundle bundle ) { goldCollected = bundle.getInt( GOLD ); deepestFloor = bundle.getInt( DEEPEST ); + highestAscent = bundle.getInt( HIGHEST ); enemiesSlain = bundle.getInt( SLAIN ); foodEaten = bundle.getInt( FOOD ); itemsCrafted = bundle.getInt( ALCHEMY ); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/AllyBuff.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/AllyBuff.java index 2c1785cd6..c81984c43 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/AllyBuff.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/AllyBuff.java @@ -63,6 +63,7 @@ public abstract class AllyBuff extends Buff{ } else { hero.earnExp(0, enemy.getClass()); } + if (droppingLoot) AscensionChallenge.processEnemyKill(enemy); } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/AscensionChallenge.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/AscensionChallenge.java index 81085ddc8..920ac4083 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/AscensionChallenge.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/AscensionChallenge.java @@ -21,19 +21,22 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.buffs; +import com.shatteredpixel.shatteredpixeldungeon.Badges; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.Statistics; import com.shatteredpixel.shatteredpixeldungeon.actors.Char; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.Ratmogrify; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.*; +import com.shatteredpixel.shatteredpixeldungeon.items.Amulet; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator; +import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; import com.watabou.noosa.Image; +import com.watabou.utils.Bundle; import java.util.HashMap; -public class AscensionChallenge extends Buff{ - - { - revivePersists = true; - } +public class AscensionChallenge extends Buff { private static HashMap, Float> modifiers = new HashMap<>(); static { @@ -73,6 +76,10 @@ public class AscensionChallenge extends Buff{ return 1; } + if (ch instanceof Ratmogrify.TransmogRat){ + ch = ((Ratmogrify.TransmogRat) ch).getOriginal(); + } + for (Class cls : modifiers.keySet()){ if (ch.getClass().isAssignableFrom(cls)){ return modifiers.get(cls); @@ -82,16 +89,83 @@ public class AscensionChallenge extends Buff{ return 1; } + //mobs get constantly beckoned to the hero at 2.5+ stacks + public static void beckonEnemies(){ + if (Dungeon.hero.buff(AscensionChallenge.class) != null + && Dungeon.hero.buff(AscensionChallenge.class).stacks >= 2.5f){ + for (Mob m : Dungeon.level.mobs){ + if (m.alignment == Char.Alignment.ENEMY) m.beckon(Dungeon.hero.pos); + } + } + } + + //mobs move at 2x speed when not hunting/fleeing at 5 stacks or higher + public static float enemySpeedModifier(Mob m){ + if (Dungeon.hero.buff(AscensionChallenge.class) != null + && m.alignment == Char.Alignment.ENEMY + && Dungeon.hero.buff(AscensionChallenge.class).stacks >= 5f + && m.state != m.HUNTING && m.state != m.FLEEING){ + return 2; + } + + return 1; + } + + //hero speed is halved and capped at 1x at 7.5+ stacks + public static float modifyHeroSpeed(float speed){ + if (Dungeon.hero.buff(AscensionChallenge.class) != null + && Dungeon.hero.buff(AscensionChallenge.class).stacks >= 7.5f){ + return Math.min(speed/2f, 1f); + } + + return speed; + } + + public static void processEnemyKill(Char enemy){ + AscensionChallenge chal = Dungeon.hero.buff(AscensionChallenge.class); + if (chal == null) return; + + if (enemy instanceof Ratmogrify.TransmogRat){ + enemy = ((Ratmogrify.TransmogRat) enemy).getOriginal(); + } + + //only enemies that are boosted count + boolean found = false; + for (Class cls : modifiers.keySet()){ + if (enemy.getClass().isAssignableFrom(cls)){ + found = true; + break; + } + } + if (!found) return; + + float oldStacks = chal.stacks; + if (enemy instanceof Ghoul || enemy instanceof RipperDemon){ + chal.stacks -= 0.5f; + } else { + chal.stacks -= 1; + } + chal.stacks = Math.max(0, chal.stacks); + if (chal.stacks < 10f && (int)(chal.stacks/2.5) != (int)(oldStacks/2.5f)){ + GLog.p(Messages.get(AscensionChallenge.class, "weaken")); + } + BuffIndicator.refreshHero(); + } + //used for internal calculations like corruption, not actual exp gain public static int AscensionExp(Mob m){ if (Dungeon.hero.buff(AscensionChallenge.class) == null){ return m.EXP; } + if (m instanceof Ratmogrify.TransmogRat){ + m = ((Ratmogrify.TransmogRat) m).getOriginal(); + } + if (m instanceof RipperDemon){ return 10; //reduced due to their numbers } else if (m instanceof Ghoul){ - return 7; //half of 14 + return 7; //half of 13, rounded up } else { for (Class cls : modifiers.keySet()){ if (m.getClass().isAssignableFrom(cls)){ @@ -102,6 +176,77 @@ public class AscensionChallenge extends Buff{ return m.EXP; } + { + revivePersists = true; + } + + private float stacks = 0; + private float damageInc = 0; + + public void onLevelSwitch(){ + if (Dungeon.depth < Statistics.highestAscent){ + Statistics.highestAscent = Dungeon.depth; + if (Dungeon.bossLevel()){ + Dungeon.hero.buff(Hunger.class).satisfy(Hunger.STARVING); + Buff.affect(Dungeon.hero, Healing.class).setHeal(Dungeon.hero.HT, 0, 20); + toSay = Messages.get(this, "break"); + } else { + stacks += 2.5f; + + if (Dungeon.depth == 1){ + toSay = Messages.get(this, "almost"); + } else if (stacks >= 10f){ + toSay = Messages.get(this, "damage"); + } else if (stacks >= 7.5f){ + toSay = Messages.get(this, "slow"); + } else if (stacks >= 5f){ + toSay = Messages.get(this, "haste"); + } else if (stacks >= 2.5f){ + toSay = Messages.get(this, "beckon"); + } + } + } + } + + private String toSay; + + public void saySwitch(){ + if (toSay != null){ + if (Dungeon.bossLevel() || Dungeon.depth == 1){ + GLog.p(toSay); + } else { + GLog.n(toSay); + } + toSay = null; + } + } + + @Override + public boolean act() { + + beckonEnemies(); + + //hero starts progressively taking damage over time at 10+ stacks + if (stacks >= 10 && !Dungeon.bossLevel()){ + damageInc += (stacks-5)/5f; + if (damageInc >= 1){ + target.damage((int)damageInc, this); + damageInc -= (int)damageInc; + + if (target == Dungeon.hero && !target.isAlive()){ + Badges.validateDeathFromFriendlyMagic(); + GLog.n(Messages.get(this, "on_kill")); + Dungeon.fail(Amulet.class); + } + } + } else { + damageInc = 0; + } + + spend(TICK); + return true; + } + @Override public int icon() { return BuffIndicator.AMULET; @@ -109,8 +254,58 @@ public class AscensionChallenge extends Buff{ @Override public void tintIcon(Image icon) { - icon.hardlight(1, 1, 0); + if (stacks < 2.5f){ + icon.hardlight(0.5f, 1, 0); + } else if (stacks < 5) { + icon.hardlight(1, 1, 0); + } else if (stacks < 7.5f){ + icon.hardlight(1, 0.67f, 0); + } else if (stacks < 10){ + icon.hardlight(1, 0.33f, 0); + } else { + icon.hardlight(1, 0, 0); + } } + @Override + public String toString() { + return Messages.get(this, "name"); + } + @Override + public String desc() { + String desc = Messages.get(this, "desc"); + desc += "\n"; + if (stacks < 2.5f){ + + desc += "\n" + Messages.get(this, "desc_clear"); + + } else { + + if (stacks >= 2.5f) desc += "\n" + Messages.get(this, "desc_beckon"); + if (stacks >= 5.0f) desc += "\n" + Messages.get(this, "desc_haste"); + if (stacks >= 7.5f) desc += "\n" + Messages.get(this, "desc_slow"); + if (stacks >= 10.0f) desc += "\n" + Messages.get(this, "desc_damage"); + + } + + return desc; + } + + public static final String STACKS = "enemy_stacks"; + public static final String DAMAGE = "damage_inc"; + + @Override + public void storeInBundle(Bundle bundle) { + super.storeInBundle(bundle); + bundle.put(STACKS, stacks); + bundle.put(DAMAGE, damageInc); + } + + @Override + public void restoreFromBundle(Bundle bundle) { + super.restoreFromBundle(bundle); + stacks = bundle.getFloat(STACKS); + damageInc = bundle.getFloat(DAMAGE); + } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java index 715fb7fae..470752af4 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java @@ -33,6 +33,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AdrenalineSurge; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AnkhInvulnerability; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AscensionChallenge; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Awareness; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Barkskin; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Berserk; @@ -130,6 +131,8 @@ import com.shatteredpixel.shatteredpixeldungeon.scenes.InterlevelScene; import com.shatteredpixel.shatteredpixeldungeon.scenes.SurfaceScene; import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.HeroSprite; +import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite; +import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; import com.shatteredpixel.shatteredpixeldungeon.ui.AttackIndicator; import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator; import com.shatteredpixel.shatteredpixeldungeon.ui.QuickSlotButton; @@ -137,6 +140,7 @@ import com.shatteredpixel.shatteredpixeldungeon.ui.StatusPane; import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; import com.shatteredpixel.shatteredpixeldungeon.windows.WndHero; import com.shatteredpixel.shatteredpixeldungeon.windows.WndMessage; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions; import com.shatteredpixel.shatteredpixeldungeon.windows.WndResurrect; import com.shatteredpixel.shatteredpixeldungeon.windows.WndTradeItem; import com.watabou.noosa.Camera; @@ -566,6 +570,8 @@ public class Hero extends Char { if (natStrength != null){ speed *= (2f + 0.25f*pointsInTalent(Talent.GROWING_POWER)); } + + speed = AscensionChallenge.modifyHeroSpeed(speed); return speed; @@ -1028,6 +1034,34 @@ public class Hero extends Char { Game.switchScene( SurfaceScene.class ); } + } else if (transition.type == LevelTransition.Type.REGULAR_ENTRANCE + && Dungeon.depth == 25 + //ascension challenge only works on runs started on v1.3+ + && Dungeon.initialVersion > ShatteredPixelDungeon.v1_2_3 + && belongings.getItem(Amulet.class) != null + && buff(AscensionChallenge.class) == null) { + + Game.runOnRenderThread(new Callback() { + @Override + public void call() { + GameScene.show( new WndOptions( new ItemSprite(ItemSpriteSheet.AMULET), + Messages.get(Amulet.class, "ascent_title"), + Messages.get(Amulet.class, "ascent_desc"), + Messages.get(Amulet.class, "ascent_yes"), + Messages.get(Amulet.class, "ascent_no")){ + @Override + protected void onSelect(int index) { + if (index == 0){ + Buff.affect(Hero.this, AscensionChallenge.class); + Statistics.highestAscent = 25; + actTransition(action); + } + } + } ); + } + }); + ready(); + } else { curAction = null; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/Ratmogrify.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/Ratmogrify.java index 18111f968..327488c19 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/Ratmogrify.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/Ratmogrify.java @@ -190,6 +190,10 @@ public class Ratmogrify extends ArmorAbility { } + public Mob getOriginal(){ + return original; + } + private float timeLeft = 6f; @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java index 43f83c48f..798a76ed1 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java @@ -31,6 +31,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Adrenaline; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AllyBuff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AscensionChallenge; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ChampionEnemy; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Charm; @@ -643,6 +644,11 @@ public abstract class Mob extends Char { return damage; } + @Override + public float speed() { + return super.speed() * AscensionChallenge.enemySpeedModifier(this); + } + public final boolean surprisedBy( Char enemy ){ return surprisedBy( enemy, true); } @@ -691,6 +697,8 @@ public abstract class Mob extends Char { Statistics.enemiesSlain++; Badges.validateMonstersSlain(); Statistics.qualifiedForNoKilling = false; + + AscensionChallenge.processEnemyKill(this); int exp = Dungeon.hero.lvl <= maxLvl ? EXP : 0; if (exp > 0) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/ImpShopkeeper.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/ImpShopkeeper.java index c6c4d0d3f..636261a24 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/ImpShopkeeper.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/ImpShopkeeper.java @@ -22,6 +22,7 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AscensionChallenge; import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ElmoParticle; @@ -41,7 +42,11 @@ public class ImpShopkeeper extends Shopkeeper { protected boolean act() { if (!seenBefore && Dungeon.level.heroFOV[pos]) { - yell( Messages.get(this, "greetings", Dungeon.hero.name() ) ); + if (Dungeon.hero.buff(AscensionChallenge.class) == null) { + yell(Messages.get(this, "greetings", Dungeon.hero.name())); + } else { + yell(Messages.get(this, "greetings_ascent", Dungeon.hero.name())); + } seenBefore = true; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/Shopkeeper.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/Shopkeeper.java index 3dba2f142..feeaaf96a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/Shopkeeper.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/Shopkeeper.java @@ -22,6 +22,7 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.Statistics; import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AscensionChallenge; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; @@ -54,7 +55,7 @@ public class Shopkeeper extends NPC { Notes.add(Notes.Landmark.SHOP); } - if (Dungeon.depth < 20 && Dungeon.hero.buff(AscensionChallenge.class) != null){ + if (Statistics.highestAscent < 20 && Dungeon.hero.buff(AscensionChallenge.class) != null){ flee(); return true; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Amulet.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Amulet.java index 05cc477d9..df8b5709d 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Amulet.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Amulet.java @@ -29,6 +29,7 @@ import com.shatteredpixel.shatteredpixeldungeon.Statistics; import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AscensionChallenge; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.scenes.AmuletScene; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; import com.watabou.noosa.Game; @@ -124,4 +125,16 @@ public class Amulet extends Item { return false; } + @Override + public String desc() { + String desc = super.desc(); + + if (Dungeon.hero.buff(AscensionChallenge.class) == null){ + desc += "\n\n" + Messages.get(this, "desc_origins"); + } else { + desc += "\n\n" + Messages.get(this, "desc_ascent"); + } + + return desc; + } } 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 f4da7d360..1cbdc6754 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java @@ -582,13 +582,13 @@ public abstract class Level implements Bundlable { } public int mobCount(){ - int count = 0; + float count = 0; for (Mob mob : Dungeon.level.mobs.toArray(new Mob[0])){ if (mob.alignment == Char.Alignment.ENEMY && !mob.properties().contains(Char.Property.MINIBOSS)) { count += mob.spawningWeight(); } } - return count; + return Math.round(count); } public Mob findMob( int pos ){ @@ -647,10 +647,12 @@ public abstract class Level implements Bundlable { public float respawnCooldown(){ if (Statistics.amuletObtained){ - if (Dungeon.level.mobCount() <= 2){ - return TIME_TO_RESPAWN / 10f; + if (Dungeon.depth == 1){ + //very fast spawns on floor 1! 0/2/4/6/8/10, etc. + return Dungeon.level.mobCount() * (TIME_TO_RESPAWN / 25f); } else { - return TIME_TO_RESPAWN / 2f; + //respawn time is 0/6/13/19/25/25, etc. + return Math.round(Math.min(Dungeon.level.mobCount() * (TIME_TO_RESPAWN / 8f), TIME_TO_RESPAWN / 2f)); } } else if (Dungeon.level.feeling == Feeling.DARK){ return 2*TIME_TO_RESPAWN/3f; @@ -667,9 +669,6 @@ public abstract class Level implements Bundlable { mob.pos = Dungeon.level.randomRespawnCell( mob ); if (Dungeon.hero.isAlive() && mob.pos != -1 && PathFinder.distance[mob.pos] >= disLimit) { GameScene.add( mob ); - if (Statistics.amuletObtained) { - mob.beckon( Dungeon.hero.pos ); - } if (!mob.buffs(ChampionEnemy.class).isEmpty()){ GLog.w(Messages.get(ChampionEnemy.class, "warn")); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/RegularLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/RegularLevel.java index 5644f3469..a72df7837 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/RegularLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/RegularLevel.java @@ -23,6 +23,7 @@ package com.shatteredpixel.shatteredpixeldungeon.levels; import com.shatteredpixel.shatteredpixeldungeon.Bones; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.Statistics; import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob; @@ -186,7 +187,10 @@ public abstract class RegularLevel extends Level { @Override public int mobLimit() { - if (Dungeon.depth <= 1) return 0; + if (Dungeon.depth <= 1){ + if (!Statistics.amuletObtained) return 0; + else return 10; + } int mobs = 3 + Dungeon.depth % 5 + Random.Int(3); if (feeling == Feeling.LARGE){ 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 df6a0a10f..f08bd3378 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java @@ -34,6 +34,7 @@ import com.shatteredpixel.shatteredpixeldungeon.Statistics; import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AscensionChallenge; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ChampionEnemy; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; @@ -285,9 +286,6 @@ public class GameScene extends PixelScene { for (Mob mob : Dungeon.level.mobs) { addMobSprite( mob ); - if (Statistics.amuletObtained) { - mob.beckon( Dungeon.hero.pos ); - } } raisedTerrain = new RaisedTerrainTilemap(); @@ -564,6 +562,10 @@ public class GameScene extends PixelScene { } } + if (Dungeon.hero.buff(AscensionChallenge.class) != null){ + Dungeon.hero.buff(AscensionChallenge.class).saySwitch(); + } + InterlevelScene.mode = InterlevelScene.Mode.NONE; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/RankingsScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/RankingsScene.java index 39d68e86b..2868542e1 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/RankingsScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/RankingsScene.java @@ -210,6 +210,11 @@ public class RankingsScene extends PixelScene { add(depth); } + if (rec.ascending){ + shield.view( ItemSpriteSheet.AMULET, null ); + shield.hardlight(0.4f, 0.4f, 0.7f); + } + } if (rec.herolevel != 0){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndRanking.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndRanking.java index db797bc34..27cca1176 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndRanking.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndRanking.java @@ -132,12 +132,16 @@ public class WndRanking extends WndTabbed { Icons[] icons = {Icons.RANKINGS, Icons.TALENT, Icons.BACKPACK_LRG, Icons.BADGES, Icons.CHALLENGE_ON}; Group[] pages = - {new StatsTab(), new TalentsTab(), new ItemsTab(), new BadgesTab(), new ChallengesTab()}; + {new StatsTab(), new TalentsTab(), new ItemsTab(), new BadgesTab(), null}; + + if (Dungeon.challenges != 0) pages[4] = new ChallengesTab(); for (int i=0; i < pages.length; i++) { - if (i == 4 && Dungeon.challenges == 0) break; - + if (pages[i] == null) { + break; + } + add( pages[i] ); Tab tab = new RankingTab( icons[i], pages[i] ); @@ -183,7 +187,7 @@ public class WndRanking extends WndTabbed { title.setRect( 0, 0, WIDTH, 0 ); add( title ); - float pos = title.bottom() + GAP + 2; + float pos = title.bottom() + GAP + 1; NumberFormat num = NumberFormat.getInstance(Locale.US); pos = statSlot( this, Messages.get(this, "score"), num.format( Statistics.totalScore ), pos ); @@ -205,7 +209,11 @@ public class WndRanking extends WndTabbed { else if (strBonus < 0) pos = statSlot(this, Messages.get(this, "str"), Dungeon.hero.STR + " - " + -strBonus, pos ); else pos = statSlot(this, Messages.get(this, "str"), Integer.toString(Dungeon.hero.STR), pos); pos = statSlot( this, Messages.get(this, "duration"), num.format( (int)Statistics.duration ), pos ); - pos = statSlot( this, Messages.get(this, "depth"), num.format( Statistics.deepestFloor ), pos ); + if (Statistics.highestAscent == 0) { + pos = statSlot(this, Messages.get(this, "depth"), num.format(Statistics.deepestFloor), pos); + } else { + pos = statSlot(this, Messages.get(this, "ascent"), num.format(Statistics.highestAscent), pos); + } if (Dungeon.seed != -1) { pos = statSlot(this, Messages.get(this, "seed"), DungeonSeed.convertToCode(Dungeon.seed), pos); } else {