diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index 185f659c0..69ec856ca 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -132,14 +132,17 @@ items.armor.warriorarmor.desc=While this armor looks heavy, it allows the Warrio ###artifacts items.artifacts.alchemiststoolkit.name=alchemist's toolkit items.artifacts.alchemiststoolkit.ac_brew=BREW +items.artifacts.alchemiststoolkit.ac_energize=ENERGIZE items.artifacts.alchemiststoolkit.not_ready=Your toolkit has not finished warming up. items.artifacts.alchemiststoolkit.cursed=Your cursed toolkit prevents you from using alchemy! -items.artifacts.alchemiststoolkit.enemy_near=You cannot do that with enemies nearby. -items.artifacts.alchemiststoolkit.full=Your toolkit is at maximum energy! -items.artifacts.alchemiststoolkit.desc=This toolkit contains a number of reagents and herbs along with a small mixing vial, allowing for alchemy on-the-go. +items.artifacts.alchemiststoolkit.need_energy=You need at least 5 energy for that. +items.artifacts.alchemiststoolkit.energize_desc=Every 5 energy crystals will give the toolkit enough energy to gain a level, which increases the rate that the toolkit generates energy over time, and how quickly it warms up.\n\nHow much energy would you like to use? +items.artifacts.alchemiststoolkit.energize_1=5 energy: +1 level +items.artifacts.alchemiststoolkit.energize_all=%1$d energy: +%2$d levels +items.artifacts.alchemiststoolkit.desc=This toolkit contains a number of reagents and herbs along with a small mixing vial, allowing for alchemy on-the-go. The vial contains what looks like alchemical energy crystals in liquid form. items.artifacts.alchemiststoolkit.desc_cursed=The cursed toolkit has bound itself to your side, and refuses to let you use alchemy. -items.artifacts.alchemiststoolkit.desc_warming=The toolkit is currently warming up, and will be ready to use after you gain experience. -items.artifacts.alchemiststoolkit.desc_hint=The equipped toolkit is slowly generating alchemical energy as you gain experience. Perhaps it could be enhanced in an alchemy pot? +items.artifacts.alchemiststoolkit.desc_warming=The toolkit is currently warming up, and will be ready to use after a little while. +items.artifacts.alchemiststoolkit.desc_hint=The equipped toolkit is slowly generating alchemical energy as you gain experience. It looks like the mixture in the vial could be enhanced by adding energy crystals to it. items.artifacts.artifact.cannot_wear_two=You cannot wear two of the same artifact. items.artifacts.artifact.equip_cursed=The artifact painfully binds itself to you. diff --git a/core/src/main/assets/messages/scenes/scenes.properties b/core/src/main/assets/messages/scenes/scenes.properties index f63870c1a..22722c0c4 100644 --- a/core/src/main/assets/messages/scenes/scenes.properties +++ b/core/src/main/assets/messages/scenes/scenes.properties @@ -3,8 +3,7 @@ scenes.alchemyscene.title=Alchemy scenes.alchemyscene.text=Combine ingredients to create something new! scenes.alchemyscene.select=Select an item -scenes.alchemyscene.cost=Energy: %d -scenes.alchemyscene.energy=Energy: %d +scenes.alchemyscene.energy=Energy: scenes.amuletscene.exit=Let's call it a day scenes.amuletscene.stay=I'm not done yet diff --git a/core/src/main/assets/sprites/items.png b/core/src/main/assets/sprites/items.png index e447b072b..248dce3ab 100644 Binary files a/core/src/main/assets/sprites/items.png and b/core/src/main/assets/sprites/items.png differ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/blobs/Alchemy.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/blobs/Alchemy.java index a8123859f..ec044b9b6 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/blobs/Alchemy.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/blobs/Alchemy.java @@ -43,7 +43,7 @@ public class Alchemy extends Blob { off[cell] = cur[cell]; //for pre-v1.1.0 saves, drops 1/4 the pot's old energy contents in crystals - if (off[cell] >= 1){ + if (off[cell] >= 4){ int n; do { n = cell + PathFinder.NEIGHBOURS8[Random.Int( 8 )]; 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 061eed67e..38b4898bd 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 @@ -831,7 +831,8 @@ public class Hero extends Char { GLog.w( Messages.get(AlchemistsToolkit.class, "cursed")); return false; } - + + AlchemyScene.clearToolkit(); ShatteredPixelDungeon.switchScene(AlchemyScene.class); return false; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Recipe.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Recipe.java index 897a0f093..6c93321fe 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Recipe.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Recipe.java @@ -170,7 +170,6 @@ public abstract class Recipe { }; private static Recipe[] oneIngredientRecipes = new Recipe[]{ - new AlchemistsToolkit.upgradeKit(), new ExoticPotion.PotionToExotic(), new ExoticScroll.ScrollToExotic(), new Scroll.ScrollToStone(), @@ -250,10 +249,7 @@ public abstract class Recipe { } public static boolean usableInRecipe(Item item){ - return !item.cursed - && (!(item instanceof EquipableItem) - || (item instanceof AlchemistsToolkit && item.isIdentified()) - || item instanceof MissileWeapon); + return !item.cursed && (!(item instanceof EquipableItem) || item instanceof MissileWeapon); } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/AlchemistsToolkit.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/AlchemistsToolkit.java index 078761033..90927399f 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/AlchemistsToolkit.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/AlchemistsToolkit.java @@ -21,19 +21,23 @@ package com.shatteredpixel.shatteredpixeldungeon.items.artifacts; +import com.shatteredpixel.shatteredpixeldungeon.Assets; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; -import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.Recipe; import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfEnergy; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.scenes.AlchemyScene; +import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; +import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions; import com.watabou.noosa.Game; +import com.watabou.noosa.Image; +import com.watabou.noosa.audio.Sample; import com.watabou.utils.Bundle; -import com.watabou.utils.GameMath; import java.util.ArrayList; @@ -47,18 +51,22 @@ public class AlchemistsToolkit extends Artifact { charge = 0; partialCharge = 0; - chargeCap = 100; } public static final String AC_BREW = "BREW"; - - private boolean alchemyReady = false; + public static final String AC_ENERGIZE = "ENERGIZE"; + + private float warmUpDelay; @Override public ArrayList actions( Hero hero ) { ArrayList actions = super.actions( hero ); - if (isEquipped( hero ) && !cursed) + if (isEquipped( hero ) && !cursed) { actions.add(AC_BREW); + if (level() < levelCap) { + actions.add(AC_ENERGIZE); + } + } return actions; } @@ -68,17 +76,75 @@ public class AlchemistsToolkit extends Artifact { super.execute(hero, action); if (action.equals(AC_BREW)){ - if (!isEquipped(hero)) GLog.i( Messages.get(this, "need_to_equip") ); - else if (cursed) GLog.w( Messages.get(this, "cursed") ); - else if (!alchemyReady) GLog.i( Messages.get(this, "not_ready") ); - else if (hero.visibleEnemies() > hero.mindVisionEnemies.size()) GLog.i( Messages.get(this, "enemy_near") ); + if (!isEquipped(hero)) GLog.i( Messages.get(this, "need_to_equip") ); + else if (cursed) GLog.w( Messages.get(this, "cursed") ); + else if (warmUpDelay > 0) GLog.w( Messages.get(this, "not_ready") ); else { - - //TODO notify scene we're using a toolkit here instead - //AlchemyScene.setProvider(hero.buff(kitEnergy.class)); + AlchemyScene.assignToolkit(this); Game.switchScene(AlchemyScene.class); } + } else if (action.equals(AC_ENERGIZE)){ + if (!isEquipped(hero)) GLog.i( Messages.get(this, "need_to_equip") ); + else if (cursed) GLog.w( Messages.get(this, "cursed") ); + else if (Dungeon.energy < 5) GLog.w( Messages.get(this, "need_energy") ); + else { + + final int maxLevels = Math.min(levelCap - level(), Dungeon.energy/5); + + String[] options; + if (maxLevels > 1){ + options = new String[]{ Messages.get(this, "energize_1"), Messages.get(this, "energize_all", 5*maxLevels, maxLevels)}; + } else { + options = new String[]{ Messages.get(this, "energize_1")}; + } + + GameScene.show(new WndOptions(new ItemSprite(image), + Messages.titleCase(name()), + Messages.get(this, "energize_desc"), + options){ + @Override + protected void onSelect(int index) { + super.onSelect(index); + + if (index == 0){ + Dungeon.energy -= 5; + Sample.INSTANCE.play(Assets.Sounds.DRINK); + Sample.INSTANCE.playDelayed(Assets.Sounds.PUFF, 0.5f); + Dungeon.hero.sprite.operate(Dungeon.hero.pos); + upgrade(); + } else if (index == 1){ + Dungeon.energy -= 5*maxLevels; + Sample.INSTANCE.play(Assets.Sounds.DRINK); + Sample.INSTANCE.playDelayed(Assets.Sounds.PUFF, 0.5f); + Dungeon.hero.sprite.operate(Dungeon.hero.pos); + upgrade(maxLevels); + } + + } + + @Override + protected boolean hasIcon(int index) { + return true; + } + + @Override + protected Image getIcon(int index) { + return new ItemSprite(ItemSpriteSheet.ENERGY); + } + }); + } + } + + updateQuickslot(); + } + + @Override + public String status() { + if (isEquipped(Dungeon.hero) && warmUpDelay > 0){ + return Messages.format( "%d%%", 100 - (int)warmUpDelay ); + } else { + return super.status(); } } @@ -98,34 +164,15 @@ public class AlchemistsToolkit extends Artifact { } } } - - public void absorbEnergy( int energy ){ - - exp += energy; - while (exp >= 10 && level() < levelCap){ - upgrade(); - exp -= 10; - } - if (level() == levelCap){ - partialCharge += exp; - energy -= exp; - exp = 0; - } - - partialCharge += energy/3f; - while (partialCharge >= 1){ - - partialCharge -= 1; - charge++; - - if (charge >= chargeCap){ - charge = chargeCap; - partialCharge = 0; - break; - } - } - updateQuickslot(); - + + public int availableEnergy(){ + return charge; + } + + public int consumeEnergy(int amount){ + int result = amount - charge; + charge = Math.max(0, charge - amount); + return Math.max(0, result); } @Override @@ -133,9 +180,9 @@ public class AlchemistsToolkit extends Artifact { String result = Messages.get(this, "desc"); if (isEquipped(Dungeon.hero)) { - if (cursed) result += "\n\n" + Messages.get(this, "desc_cursed"); - else if (!alchemyReady) result += "\n\n" + Messages.get(this, "desc_warming"); - else result += "\n\n" + Messages.get(this, "desc_hint"); + if (cursed) result += "\n\n" + Messages.get(this, "desc_cursed"); + else if (warmUpDelay > 0) result += "\n\n" + Messages.get(this, "desc_warming"); + else result += "\n\n" + Messages.get(this, "desc_hint"); } return result; @@ -144,101 +191,67 @@ public class AlchemistsToolkit extends Artifact { @Override public boolean doEquip(Hero hero) { if (super.doEquip(hero)){ - alchemyReady = false; + warmUpDelay = 101f; return true; } else { return false; } } - private static final String READY = "ready"; + private static final String WARM_UP = "warm_up"; @Override public void storeInBundle(Bundle bundle) { super.storeInBundle(bundle); - bundle.put(READY, alchemyReady); + bundle.put(WARM_UP, warmUpDelay); } @Override public void restoreFromBundle(Bundle bundle) { super.restoreFromBundle(bundle); - alchemyReady = bundle.getBoolean(READY); + warmUpDelay = bundle.getFloat(WARM_UP); } public class kitEnergy extends ArtifactBuff { - - public void gainCharge(float levelPortion) { - alchemyReady = true; - - if (cursed) return; - - if (charge < chargeCap) { - - //generates 2 energy every hero level, +0.1 energy per toolkit level - //to a max of 12 energy per hero level - //This means that energy absorbed into the kit is recovered in 6.67 hero levels (as 33% of input energy is kept) - //exp towards toolkit levels is included here - float effectiveLevel = GameMath.gate(0, level() + exp/10f, 10); - float chargeGain = (2 + (1f * effectiveLevel)) * levelPortion; - chargeGain *= RingOfEnergy.artifactChargeMultiplier(target); - partialCharge += chargeGain; - - //charge is in increments of 1/10 max hunger value. - while (partialCharge >= 1) { - charge++; - partialCharge -= 1; - - if (charge == chargeCap){ - GLog.p( Messages.get(AlchemistsToolkit.class, "full") ); - partialCharge = 0; - } - - updateQuickslot(); + + @Override + public boolean act() { + + if (warmUpDelay > 0){ + if (level() == 10){ + warmUpDelay = 0; + } else if (warmUpDelay == 101){ + warmUpDelay = 100f; + } else { + float turnsToWarmUp = (int) Math.pow(10 - level(), 2); + warmUpDelay -= 100 / turnsToWarmUp; } - } else - partialCharge = 0; + updateQuickslot(); + } + + spend(TICK); + return true; } - } + public void gainCharge(float levelPortion) { + if (cursed) return; - //TODO this isn't working with new energy yet, atm it's just sucking up all energy possible - public static class upgradeKit extends Recipe { - - @Override - public boolean testIngredients(ArrayList ingredients) { - return ingredients.get(0) instanceof AlchemistsToolkit; - } - - private static int lastCost; - - @Override - public int cost(ArrayList ingredients) { - return lastCost = Math.max(1, Dungeon.energy); - } - - @Override - public Item brew(ArrayList ingredients) { - AlchemistsToolkit existing = (AlchemistsToolkit) ingredients.get(0); - - existing.absorbEnergy(lastCost); - - return existing; - } - - @Override - public Item sampleOutput(ArrayList ingredients) { - AlchemistsToolkit sample = new AlchemistsToolkit(); - sample.identify(); - - AlchemistsToolkit existing = (AlchemistsToolkit) ingredients.get(0); - - sample.charge = existing.charge; - sample.partialCharge = existing.partialCharge; - sample.exp = existing.exp; - sample.level(existing.level()); - sample.absorbEnergy(Dungeon.energy); - return sample; + //generates 2 energy every hero level, +0.1 energy per toolkit level + //to a max of 12 energy per hero level + //This means that energy absorbed into the kit is recovered in 5 hero levels + float chargeGain = (2 + level()) * levelPortion; + chargeGain *= RingOfEnergy.artifactChargeMultiplier(target); + partialCharge += chargeGain; + + //charge is in increments of 1 energy. + while (partialCharge >= 1) { + charge++; + partialCharge -= 1; + + updateQuickslot(); + } } + } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AlchemyScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AlchemyScene.java index afda6278f..78994e01f 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AlchemyScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/AlchemyScene.java @@ -292,8 +292,13 @@ public class AlchemyScene extends PixelScene { }; btnGuide.setRect(0, 0, 20, 20); add(btnGuide); - - energyLeft = PixelScene.renderTextBlock(Messages.get(AlchemyScene.class, "energy", Dungeon.energy), 9); + + String energyText = Messages.get(AlchemyScene.class, "energy") + " " + Dungeon.energy; + if (toolkit != null){ + energyText += "+" + toolkit.availableEnergy(); + } + + energyLeft = PixelScene.renderTextBlock(energyText, 9); energyLeft.setPos( (Camera.main.width - energyLeft.width())/2, Camera.main.height - 8 - energyLeft.height() @@ -301,7 +306,7 @@ public class AlchemyScene extends PixelScene { energyLeft.hardlight(0x44CCFF); add(energyLeft); - energyIcon = Icons.get(Icons.ENERGY); + energyIcon = new ItemSprite( toolkit != null ? ItemSpriteSheet.ARTIFACT_TOOLKIT : ItemSpriteSheet.ENERGY); energyIcon.x = energyLeft.left() - energyIcon.width(); energyIcon.y = energyLeft.top() - (energyIcon.height() - energyLeft.height())/2; align(energyIcon); @@ -313,7 +318,8 @@ public class AlchemyScene extends PixelScene { WndEnergizeItem.openItemSelector(); } }; - energyAdd.setRect(energyLeft.right(), energyIcon.y, 16, 16); + energyAdd.setRect(energyLeft.right(), energyLeft.top() - (16 - energyLeft.height())/2, 16, 16); + align(energyAdd); add(energyAdd); energyCost = PixelScene.renderTextBlock(6); @@ -403,15 +409,20 @@ public class AlchemyScene extends PixelScene { output.item(recipe.sampleOutput(ingredients)); output.visible = true; - energyCost.text( Messages.get(AlchemyScene.class, "cost", cost) ); + energyCost.text( Messages.get(AlchemyScene.class, "energy") + " " + cost ); energyCost.setPos( btnCombine.left() + (btnCombine.width() - energyCost.width())/2, btnCombine.top() - energyCost.height() ); energyCost.visible = (cost > 0); - - if (cost <= Dungeon.energy) { + + int availableEnergy = Dungeon.energy; + if (toolkit != null){ + availableEnergy += toolkit.availableEnergy(); + } + + if (cost <= availableEnergy) { btnCombine.enable(true); energyCost.hardlight(0x44CCFF); } else { @@ -435,18 +446,27 @@ public class AlchemyScene extends PixelScene { Item result = null; if (recipe != null){ - Dungeon.energy -= recipe.cost(ingredients); - energyLeft.text(Messages.get(AlchemyScene.class, "energy", Dungeon.energy)); + int cost = recipe.cost(ingredients); + if (toolkit != null){ + cost = toolkit.consumeEnergy(cost); + } + Dungeon.energy -= cost; + + String energyText = Messages.get(AlchemyScene.class, "energy") + " " + Dungeon.energy; + if (toolkit != null){ + energyText += "+" + toolkit.availableEnergy(); + } + energyLeft.text(energyText); energyLeft.setPos( (Camera.main.width - energyLeft.width())/2, Camera.main.height - 8 - energyLeft.height() ); energyIcon.x = energyLeft.left() - energyIcon.width(); - energyIcon.y = energyLeft.top() - (energyIcon.height() - energyLeft.height())/2; align(energyIcon); - energyAdd.setRect(energyLeft.right(), energyIcon.y, 16, 16); + energyAdd.setPos(energyLeft.right(), energyAdd.top()); + align(energyAdd); result = recipe.brew(ingredients); } @@ -548,17 +568,21 @@ public class AlchemyScene extends PixelScene { } public void createEnergy(){ - energyLeft.text(Messages.get(AlchemyScene.class, "energy", Dungeon.energy)); + String energyText = Messages.get(AlchemyScene.class, "energy") + " " + Dungeon.energy; + if (toolkit != null){ + energyText += "+" + toolkit.availableEnergy(); + } + energyLeft.text(energyText); energyLeft.setPos( (Camera.main.width - energyLeft.width())/2, Camera.main.height - 8 - energyLeft.height() ); energyIcon.x = energyLeft.left() - energyIcon.width(); - energyIcon.y = energyLeft.top() - (energyIcon.height() - energyLeft.height())/2; align(energyIcon); - energyAdd.setRect(energyLeft.right(), energyIcon.y, 16, 16); + energyAdd.setPos(energyLeft.right(), energyAdd.top()); + align(energyAdd); bubbleEmitter.start(Speck.factory( Speck.BUBBLE ), 0.01f, 100 ); sparkEmitter.burst(SparkParticle.FACTORY, 20); @@ -626,6 +650,14 @@ public class AlchemyScene extends PixelScene { } } - //TODO add code here for the toolkit's energy + private static AlchemistsToolkit toolkit; + + public static void assignToolkit( AlchemistsToolkit toolkit ){ + AlchemyScene.toolkit = toolkit; + } + + public static void clearToolkit(){ + AlchemyScene.toolkit = null; + } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndOptions.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndOptions.java index 15d9599ac..d705b9cb6 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndOptions.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndOptions.java @@ -92,6 +92,7 @@ public class WndOptions extends Window { onSelect( index ); } }; + if (hasIcon(i)) btn.icon(getIcon(i)); btn.enable(enabled(i)); add( btn ); @@ -121,9 +122,17 @@ public class WndOptions extends Window { protected void onSelect( int index ) {} - protected boolean hasInfo( int index) { + protected boolean hasInfo( int index ) { return false; } protected void onInfo( int index ) {} + + protected boolean hasIcon( int index ) { + return false; + } + + protected Image getIcon( int index ) { + return null; + } }