v1.1.0: reworked alchemist's toolkit based on new energy system

This commit is contained in:
Evan Debenham 2021-11-17 21:40:35 -05:00
parent e52fa36cb4
commit ec0a241716
9 changed files with 199 additions and 146 deletions

View File

@ -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.

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -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 )];

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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<String> actions( Hero hero ) {
ArrayList<String> 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<Item> ingredients) {
return ingredients.get(0) instanceof AlchemistsToolkit;
}
private static int lastCost;
@Override
public int cost(ArrayList<Item> ingredients) {
return lastCost = Math.max(1, Dungeon.energy);
}
@Override
public Item brew(ArrayList<Item> ingredients) {
AlchemistsToolkit existing = (AlchemistsToolkit) ingredients.get(0);
existing.absorbEnergy(lastCost);
return existing;
}
@Override
public Item sampleOutput(ArrayList<Item> 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();
}
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}