v0.7.0: completely overhauled the alchemy guidebook interface

This commit is contained in:
Evan Debenham 2018-10-04 22:02:34 -04:00
parent 6e3097724a
commit e5867c8dd4
11 changed files with 634 additions and 93 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -58,6 +58,7 @@ import com.watabou.utils.Random;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
public class Bomb extends Item { public class Bomb extends Item {
@ -305,34 +306,38 @@ public class Bomb extends Item {
public static class EnhanceBomb extends Recipe { public static class EnhanceBomb extends Recipe {
private static final HashMap<Class<?extends Item>, Class<?extends Bomb>> validIngredients = new HashMap<>(); public static final LinkedHashMap<Class<?extends Item>, Class<?extends Bomb>> validIngredients = new LinkedHashMap<>();
static { static {
validIngredients.put(PotionOfLiquidFlame.class, Firebomb.class);
validIngredients.put(PotionOfFrost.class, FrostBomb.class); validIngredients.put(PotionOfFrost.class, FrostBomb.class);
validIngredients.put(PotionOfHealing.class, RegrowthBomb.class);
validIngredients.put(PotionOfInvisibility.class, Flashbang.class);
validIngredients.put(ScrollOfRecharging.class, ShockBomb.class);
validIngredients.put(ScrollOfRemoveCurse.class, HolyBomb.class);
validIngredients.put(ScrollOfMirrorImage.class, WoollyBomb.class); validIngredients.put(ScrollOfMirrorImage.class, WoollyBomb.class);
validIngredients.put(PotionOfLiquidFlame.class, Firebomb.class);
validIngredients.put(ScrollOfRage.class, Noisemaker.class); validIngredients.put(ScrollOfRage.class, Noisemaker.class);
validIngredients.put(PotionOfInvisibility.class, Flashbang.class);
validIngredients.put(ScrollOfRecharging.class, ShockBomb.class);
validIngredients.put(PotionOfHealing.class, RegrowthBomb.class);
validIngredients.put(ScrollOfRemoveCurse.class, HolyBomb.class);
validIngredients.put(GooBlob.class, ArcaneBomb.class); validIngredients.put(GooBlob.class, ArcaneBomb.class);
validIngredients.put(MetalShard.class, ShrapnelBomb.class); validIngredients.put(MetalShard.class, ShrapnelBomb.class);
} }
private static final HashMap<Class<?extends Bomb>, Integer> bombCosts = new HashMap<>(); private static final HashMap<Class<?extends Bomb>, Integer> bombCosts = new HashMap<>();
static { static {
bombCosts.put(Firebomb.class, 4);
bombCosts.put(FrostBomb.class, 3); bombCosts.put(FrostBomb.class, 3);
bombCosts.put(RegrowthBomb.class, 6);
bombCosts.put(Flashbang.class, 5);
bombCosts.put(ShockBomb.class, 5);
bombCosts.put(HolyBomb.class, 6);
bombCosts.put(WoollyBomb.class, 3); bombCosts.put(WoollyBomb.class, 3);
bombCosts.put(Firebomb.class, 4);
bombCosts.put(Noisemaker.class, 4); bombCosts.put(Noisemaker.class, 4);
bombCosts.put(Flashbang.class, 5);
bombCosts.put(ShockBomb.class, 5);
bombCosts.put(RegrowthBomb.class, 6);
bombCosts.put(HolyBomb.class, 6);
bombCosts.put(ArcaneBomb.class, 8); bombCosts.put(ArcaneBomb.class, 8);
bombCosts.put(ShrapnelBomb.class, 8); bombCosts.put(ShrapnelBomb.class, 8);
} }

View File

@ -76,7 +76,7 @@ public class MysteryMeat extends Food {
public static class PlaceHolder extends MysteryMeat { public static class PlaceHolder extends MysteryMeat {
{ {
image = ItemSpriteSheet.MEAT_HOLDER; image = ItemSpriteSheet.FOOD_HOLDER;
} }
@Override @Override

View File

@ -59,6 +59,10 @@ public enum Document {
return pages.containsKey(page) && pages.get(page); return pages.containsKey(page) && pages.get(page);
} }
public boolean hasPage( int pageIdx ){
return hasPage( pages.keySet().toArray(new String[0])[pageIdx] );
}
public boolean hasAnyPages(){ public boolean hasAnyPages(){
for (String p : pages.keySet()){ for (String p : pages.keySet()){
if (pages.get(p)) { if (pages.get(p)) {
@ -81,10 +85,18 @@ public enum Document {
return Messages.get( this, name() + "." + page + ".title"); return Messages.get( this, name() + "." + page + ".title");
} }
public String pageTitle( int pageIdx ){
return pageTitle( pages.keySet().toArray(new String[0])[pageIdx] );
}
public String pageBody( String page ){ public String pageBody( String page ){
return Messages.get( this, name() + "." + page + ".body"); return Messages.get( this, name() + "." + page + ".body");
} }
public String pageBody( int pageIdx ){
return pageBody( pages.keySet().toArray(new String[0])[pageIdx] );
}
public static final String GUIDE_INTRO_PAGE = "Intro"; public static final String GUIDE_INTRO_PAGE = "Intro";
public static final String GUIDE_SEARCH_PAGE = "Examining_and_Searching"; public static final String GUIDE_SEARCH_PAGE = "Examining_and_Searching";
@ -103,17 +115,13 @@ public enum Document {
ALCHEMY_GUIDE.pages.put("Potions", false); ALCHEMY_GUIDE.pages.put("Potions", false);
ALCHEMY_GUIDE.pages.put("Stones", false); ALCHEMY_GUIDE.pages.put("Stones", false);
ALCHEMY_GUIDE.pages.put("Darts", false); ALCHEMY_GUIDE.pages.put("Darts", false);
ALCHEMY_GUIDE.pages.put("Exotics", false); ALCHEMY_GUIDE.pages.put("Exotic_Potions", false);
ALCHEMY_GUIDE.pages.put("Energy", false); ALCHEMY_GUIDE.pages.put("Exotic_Scrolls", false);
ALCHEMY_GUIDE.pages.put("Food", false); ALCHEMY_GUIDE.pages.put("Energy_Food", false);
ALCHEMY_GUIDE.pages.put("Bombs", false); ALCHEMY_GUIDE.pages.put("Bombs", false);
ALCHEMY_GUIDE.pages.put("Combo_Brews", false); ALCHEMY_GUIDE.pages.put("Brews", false);
ALCHEMY_GUIDE.pages.put("Heal_Elixirs", false); ALCHEMY_GUIDE.pages.put("Elixirs", false);
ALCHEMY_GUIDE.pages.put("AOE_Brews", false); ALCHEMY_GUIDE.pages.put("Spells", false);
ALCHEMY_GUIDE.pages.put("Imbue_Elixirs", false);
ALCHEMY_GUIDE.pages.put("Tele_Spells", false);
ALCHEMY_GUIDE.pages.put("Item_Spells", false);
ALCHEMY_GUIDE.pages.put("Enviro_Spells", false);
} }
private static final String DOCUMENTS = "documents"; private static final String DOCUMENTS = "documents";

View File

@ -84,9 +84,8 @@ public class LaboratoryRoom extends SpecialRoom {
} }
} }
//drops a page every room for now //pages after 5 are always deeper than the sewers
//TODO make pages rarer as players get more, once more alchemy comes out if(!missingPages.isEmpty() && (missingPages.size() > 5 || Dungeon.depth > 5)){
if(!missingPages.isEmpty()){
AlchemyPage p = new AlchemyPage(); AlchemyPage p = new AlchemyPage();
p.page(missingPages.get(0)); p.page(missingPages.get(0));
int pos; int pos;

View File

@ -25,6 +25,7 @@ import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Badges; import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Chrome; import com.shatteredpixel.shatteredpixeldungeon.Chrome;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.SPDSettings;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Belongings; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Belongings;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
@ -32,7 +33,6 @@ import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.Recipe; import com.shatteredpixel.shatteredpixeldungeon.items.Recipe;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.AlchemistsToolkit; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.AlchemistsToolkit;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.darts.Dart; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.darts.Dart;
import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
import com.shatteredpixel.shatteredpixeldungeon.journal.Journal; import com.shatteredpixel.shatteredpixeldungeon.journal.Journal;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
@ -45,8 +45,8 @@ import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextMultiline; import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextMultiline;
import com.shatteredpixel.shatteredpixeldungeon.ui.Window; import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndBag; import com.shatteredpixel.shatteredpixeldungeon.windows.WndBag;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndDocument;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndInfoItem; import com.shatteredpixel.shatteredpixeldungeon.windows.WndInfoItem;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndJournal;
import com.watabou.gltextures.TextureCache; import com.watabou.gltextures.TextureCache;
import com.watabou.glwrap.Blending; import com.watabou.glwrap.Blending;
import com.watabou.noosa.Camera; import com.watabou.noosa.Camera;
@ -250,7 +250,23 @@ public class AlchemyScene extends PixelScene {
@Override @Override
protected void onClick() { protected void onClick() {
super.onClick(); super.onClick();
AlchemyScene.this.addToFront(new WndDocument(Document.ALCHEMY_GUIDE)); clearSlots();
updateState();
AlchemyScene.this.addToFront(new Window(){
{
WndJournal.AlchemyTab t = new WndJournal.AlchemyTab();
if (SPDSettings.landscape()) {
resize(200, 130);
t.setRect(0, 0, 200, 130);
} else {
resize(130, 180);
t.setRect(0, 0, 130, 180);
}
add(t);
}
});
} }
}; };
btnGuide.setRect(0, 0, 16, 16); btnGuide.setRect(0, 0, 16, 16);
@ -450,6 +466,8 @@ public class AlchemyScene extends PixelScene {
Dungeon.level.drop(inputs[i].item, Dungeon.hero.pos); Dungeon.level.drop(inputs[i].item, Dungeon.hero.pos);
} }
} }
inputs[i].item(null);
inputs[i].slot.item(new WndBag.Placeholder(ItemSpriteSheet.SOMETHING));
} }
} }
} }

View File

@ -81,6 +81,10 @@ public class ItemSprite extends MovieClip {
view (item); view (item);
} }
public ItemSprite( int image ){
this( image, null );
}
public ItemSprite( int image, Glowing glowing ) { public ItemSprite( int image, Glowing glowing ) {
super( Assets.ITEMS ); super( Assets.ITEMS );

View File

@ -36,32 +36,40 @@ public class ItemSpriteSheet {
} }
private static final int PLACEHOLDERS = xy(1, 1); //16 slots private static final int PLACEHOLDERS = xy(1, 1); //16 slots
//null warning occupies space 0, should only show up if there's a bug. //SOMETHING is the default item sprite at position 0. May show up ingame if there are bugs.
public static final int NULLWARN = PLACEHOLDERS+0; public static final int SOMETHING = PLACEHOLDERS+0;
public static final int WEAPON_HOLDER = PLACEHOLDERS+1; public static final int WEAPON_HOLDER = PLACEHOLDERS+1;
public static final int ARMOR_HOLDER = PLACEHOLDERS+2; public static final int ARMOR_HOLDER = PLACEHOLDERS+2;
public static final int WAND_HOLDER = PLACEHOLDERS+3; public static final int MISSILE_HOLDER = PLACEHOLDERS+3;
public static final int RING_HOLDER = PLACEHOLDERS+4; public static final int WAND_HOLDER = PLACEHOLDERS+4;
public static final int ARTIFACT_HOLDER = PLACEHOLDERS+5; public static final int RING_HOLDER = PLACEHOLDERS+5;
public static final int POTION_HOLDER = PLACEHOLDERS+6; public static final int ARTIFACT_HOLDER = PLACEHOLDERS+6;
public static final int SCROLL_HOLDER = PLACEHOLDERS+7; public static final int FOOD_HOLDER = PLACEHOLDERS+7;
public static final int SEED_HOLDER = PLACEHOLDERS+8; public static final int BOMB_HOLDER = PLACEHOLDERS+8;
public static final int STONE_HOLDER = PLACEHOLDERS+9; public static final int POTION_HOLDER = PLACEHOLDERS+9;
public static final int MEAT_HOLDER = PLACEHOLDERS+10; public static final int SCROLL_HOLDER = PLACEHOLDERS+11;
public static final int SOMETHING = PLACEHOLDERS+11; public static final int SEED_HOLDER = PLACEHOLDERS+10;
public static final int STONE_HOLDER = PLACEHOLDERS+12;
public static final int BREW_HOLDER = PLACEHOLDERS+13;
public static final int ELIXIR_HOLDER = PLACEHOLDERS+14;
public static final int SPELL_HOLDER = PLACEHOLDERS+15;
static{ static{
assignItemRect(NULLWARN, 16, 7); assignItemRect(SOMETHING, 8, 13);
assignItemRect(WEAPON_HOLDER, 14, 14); assignItemRect(WEAPON_HOLDER, 14, 14);
assignItemRect(ARMOR_HOLDER, 14, 12); assignItemRect(ARMOR_HOLDER, 14, 12);
assignItemRect(MISSILE_HOLDER, 15, 15);
assignItemRect(WAND_HOLDER, 14, 14); assignItemRect(WAND_HOLDER, 14, 14);
assignItemRect(RING_HOLDER, 8, 10); assignItemRect(RING_HOLDER, 8, 10);
assignItemRect(ARTIFACT_HOLDER, 15, 15); assignItemRect(ARTIFACT_HOLDER, 15, 15);
assignItemRect(FOOD_HOLDER, 15, 11);
assignItemRect(BOMB_HOLDER, 10, 13);
assignItemRect(POTION_HOLDER, 12, 14); assignItemRect(POTION_HOLDER, 12, 14);
assignItemRect(SCROLL_HOLDER, 15, 14);
assignItemRect(SEED_HOLDER, 10, 10); assignItemRect(SEED_HOLDER, 10, 10);
assignItemRect(SCROLL_HOLDER, 15, 14);
assignItemRect(STONE_HOLDER, 14, 12); assignItemRect(STONE_HOLDER, 14, 12);
assignItemRect(MEAT_HOLDER, 15, 11); assignItemRect(BREW_HOLDER, 10, 14);
assignItemRect(SOMETHING, 8, 13); assignItemRect(ELIXIR_HOLDER, 10, 14);
assignItemRect(SPELL_HOLDER, 8, 16);
} }
private static final int UNCOLLECTIBLE = xy(1, 2); //16 slots private static final int UNCOLLECTIBLE = xy(1, 2); //16 slots

View File

@ -0,0 +1,370 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.ui;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.Recipe;
import com.shatteredpixel.shatteredpixeldungeon.items.bombs.Bomb;
import com.shatteredpixel.shatteredpixeldungeon.items.food.Blandfruit;
import com.shatteredpixel.shatteredpixeldungeon.items.food.Feast;
import com.shatteredpixel.shatteredpixeldungeon.items.food.Food;
import com.shatteredpixel.shatteredpixeldungeon.items.food.MysteryMeat;
import com.shatteredpixel.shatteredpixeldungeon.items.food.Pasty;
import com.shatteredpixel.shatteredpixeldungeon.items.food.StewedMeat;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.Potion;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.brews.BlizzardBrew;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.brews.CausticBrew;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.brews.FrigidBrew;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.brews.FrostfireBrew;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.brews.InfernalBrew;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.brews.ShockingBrew;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.brews.WickedBrew;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.elixirs.ElixirOfAquaticRejuvenation;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.elixirs.ElixirOfDragonsBlood;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.elixirs.ElixirOfHoneyedHealing;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.elixirs.ElixirOfIcyTouch;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.elixirs.ElixirOfRestoration;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.elixirs.ElixirOfToxicEssence;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.elixirs.ElixirOfVitality;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.exotic.ExoticPotion;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.Scroll;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ExoticScroll;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.Alchemize;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.AquaBlast;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.BeaconOfReturning;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.CurseInfusion;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.FeatherFall;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.MagicalInfusion;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.MagicalPorter;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.PhaseShift;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.ReclaimTrap;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.Recycle;
import com.shatteredpixel.shatteredpixeldungeon.items.stones.Runestone;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.darts.Dart;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.darts.TippedDart;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.plants.Plant;
import com.shatteredpixel.shatteredpixeldungeon.scenes.AlchemyScene;
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndBag;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndInfoItem;
import com.watabou.noosa.BitmapText;
import com.watabou.noosa.Group;
import com.watabou.noosa.Image;
import com.watabou.noosa.ui.Component;
import java.util.ArrayList;
import java.util.Arrays;
public class QuickRecipe extends Component {
private ArrayList<Item> ingredients;
private ArrayList<ItemSlot> inputs;
private QuickRecipe.arrow arrow;
private ItemSlot output;
public QuickRecipe(Recipe.SimpleRecipe r){
this(r, r.getIngredients(), r.sampleOutput(null));
}
public QuickRecipe(Recipe r, ArrayList<Item> inputs, final Item output) {
ingredients = inputs;
int cost = r.cost(inputs);
boolean hasInputs = true;
this.inputs = new ArrayList<>();
for (final Item in : inputs) {
anonymize(in);
ItemSlot curr;
curr = new ItemSlot(in) {
@Override
protected void onClick() {
ShatteredPixelDungeon.scene().addToFront(new WndInfoItem(in));
}
};
ArrayList<Item> similar = Dungeon.hero.belongings.getAllSimilar(in);
int quantity = 0;
for (Item sim : similar) {
if (sim.isIdentified()) quantity += sim.quantity();
}
if (quantity < in.quantity()) {
curr.icon.alpha(0.3f);
hasInputs = false;
}
curr.showParams(true, false, true);
add(curr);
this.inputs.add(curr);
}
if (cost > 0) {
arrow = new arrow(Icons.get(Icons.RESUME), cost);
arrow.hardlightText(0x00CCFF);
} else {
arrow = new arrow(Icons.get(Icons.RESUME));
}
if (hasInputs) {
arrow.icon.tint(1, 1, 0, 1);
if (!(ShatteredPixelDungeon.scene() instanceof AlchemyScene)) {
arrow.enable(false);
}
} else {
arrow.icon.color(0, 0, 0);
arrow.enable(false);
}
add(arrow);
anonymize(output);
this.output = new ItemSlot(output){
@Override
protected void onClick() {
ShatteredPixelDungeon.scene().addToFront(new WndInfoItem(output));
}
};
this.output.showParams(true, false, true);
add(this.output);
layout();
}
@Override
protected void layout() {
height = 16;
width = 0;
for (ItemSlot item : inputs){
item.setRect(x + width, y, 16, 16);
width += 16;
}
arrow.setRect(x + width, y, 14, 16);
width += 14;
output.setRect(x + width, y, 16, 16);
width += 16;
}
//used to ensure that un-IDed items are not spoiled
private void anonymize(Item item){
if (item instanceof Potion){
((Potion) item).anonymize();
} else if (item instanceof Scroll){
((Scroll) item).anonymize();
}
}
public class arrow extends IconButton {
BitmapText text;
public arrow(){
super();
}
public arrow( Image icon ){
super( icon );
}
public arrow( Image icon, int count ){
super( icon );
text = new BitmapText( Integer.toString(count), PixelScene.pixelFont);
text.measure();
add(text);
}
@Override
protected void layout() {
super.layout();
if (text != null){
text.x = x;
text.y = y;
PixelScene.align(text);
}
}
@Override
protected void onClick() {
super.onClick();
//find the window this is inside of and close it
Group parent = this.parent;
while (parent != null){
if (parent instanceof Window){
((Window) parent).hide();
break;
} else {
parent = parent.parent;
}
}
((AlchemyScene)ShatteredPixelDungeon.scene()).populate(ingredients, Dungeon.hero.belongings);
}
public void hardlightText(int color ){
if (text != null) text.hardlight(color);
}
}
//gets recipes for a particular alchemy guide page
//a null entry indicates a break in section
public static ArrayList<QuickRecipe> getRecipes( int pageIdx ){
ArrayList<QuickRecipe> result = new ArrayList<>();
switch (pageIdx){
case 0: default:
result.add(new QuickRecipe( new Potion.SeedToPotion(), new ArrayList<>(Arrays.asList(new Plant.Seed.PlaceHolder().quantity(3))), new WndBag.Placeholder(ItemSpriteSheet.POTION_HOLDER){
{
name = Messages.get(Potion.SeedToPotion.class, "name");
}
@Override
public String info() {
return "";
}
}));
return result;
case 1:
Recipe r = new Scroll.ScrollToStone();
for (Class<?> cls : Generator.Category.SCROLL.classes){
try{
Scroll scroll = (Scroll) cls.newInstance();
ArrayList<Item> in = new ArrayList<Item>(Arrays.asList(scroll));
result.add(new QuickRecipe( r, in, r.sampleOutput(in)));
} catch (Exception e){
ShatteredPixelDungeon.reportException(e);
}
}
return result;
case 2:
r = new TippedDart.TipDart();
for (Class<?> cls : Generator.Category.SEED.classes){
try{
Plant.Seed seed = (Plant.Seed) cls.newInstance();
ArrayList<Item> in = new ArrayList<>(Arrays.asList(seed, new Dart()));
result.add(new QuickRecipe( r, in, r.sampleOutput(in)));
} catch (Exception e){
ShatteredPixelDungeon.reportException(e);
}
}
return result;
case 3:
r = new ExoticPotion.PotionToExotic();
for (Class<?> cls : Generator.Category.POTION.classes){
try{
Potion pot = (Potion) cls.newInstance();
ArrayList<Item> in = new ArrayList<>(Arrays.asList(pot, new Plant.Seed.PlaceHolder().quantity(2)));
result.add(new QuickRecipe( r, in, r.sampleOutput(in)));
} catch (Exception e){
ShatteredPixelDungeon.reportException(e);
}
}
return result;
case 4:
r = new ExoticScroll.ScrollToExotic();
for (Class<?> cls : Generator.Category.SCROLL.classes){
try{
Scroll scroll = (Scroll) cls.newInstance();
ArrayList<Item> in = new ArrayList<>(Arrays.asList(scroll, new Runestone.PlaceHolder().quantity(2)));
result.add(new QuickRecipe( r, in, r.sampleOutput(in)));
} catch (Exception e){
ShatteredPixelDungeon.reportException(e);
}
}
return result;
case 5:
result.add(new QuickRecipe( new StewedMeat.oneMeat() ));
result.add(new QuickRecipe( new StewedMeat.twoMeat() ));
result.add(new QuickRecipe( new StewedMeat.threeMeat() ));
result.add(null);
result.add(new QuickRecipe( new Feast.Recipe(),
new ArrayList<Item>(Arrays.asList(new Pasty(), new Food(), new MysteryMeat.PlaceHolder())),
new Feast()));
result.add(new QuickRecipe( new Blandfruit.CookFruit(),
new ArrayList<>(Arrays.asList(new Blandfruit(), new Plant.Seed.PlaceHolder())),
new Blandfruit(){
{
name = "Cooked Blandfruit";
}
@Override
public String info() {
return "";
}
})); //TODO
return result;
case 6:
r = new Bomb.EnhanceBomb();
for (Class<?> cls : Bomb.EnhanceBomb.validIngredients.keySet()){
try{
Item item = (Item) cls.newInstance();
ArrayList<Item> in = new ArrayList<Item>(Arrays.asList(new Bomb(), item));
result.add(new QuickRecipe( r, in, r.sampleOutput(in)));
} catch (Exception e){
ShatteredPixelDungeon.reportException(e);
}
}
return result;
case 7:
result.add(new QuickRecipe(new WickedBrew.Recipe()));
result.add(new QuickRecipe(new FrigidBrew.Recipe()));
result.add(new QuickRecipe(new FrostfireBrew.Recipe()));
result.add(new QuickRecipe(new CausticBrew.Recipe()));
result.add(null);
result.add(new QuickRecipe(new InfernalBrew.Recipe()));
result.add(new QuickRecipe(new BlizzardBrew.Recipe()));
result.add(new QuickRecipe(new ShockingBrew.Recipe()));
return result;
case 8:
result.add(new QuickRecipe(new ElixirOfRestoration.Recipe()));
result.add(new QuickRecipe(new ElixirOfVitality.Recipe()));
result.add(new QuickRecipe(new ElixirOfHoneyedHealing.Recipe()));
result.add(new QuickRecipe(new ElixirOfAquaticRejuvenation.Recipe()));
result.add(null);
result.add(new QuickRecipe(new ElixirOfDragonsBlood.Recipe()));
result.add(new QuickRecipe(new ElixirOfIcyTouch.Recipe()));
result.add(new QuickRecipe(new ElixirOfToxicEssence.Recipe()));
return result;
case 9:
result.add(new QuickRecipe(new MagicalPorter.Recipe()));
result.add(new QuickRecipe(new PhaseShift.Recipe()));
result.add(new QuickRecipe(new BeaconOfReturning.Recipe()));
result.add(null);
result.add(new QuickRecipe(new AquaBlast.Recipe()));
result.add(new QuickRecipe(new FeatherFall.Recipe()));
result.add(new QuickRecipe(new ReclaimTrap.Recipe()));
result.add(null);
result.add(new QuickRecipe(new MagicalInfusion.Recipe()));
result.add(new QuickRecipe(new CurseInfusion.Recipe()));
result.add(new QuickRecipe(new Alchemize.Recipe()));
result.add(new QuickRecipe(new Recycle.Recipe()));
return result;
}
}
}

View File

@ -37,6 +37,7 @@ import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.ui.Icons; import com.shatteredpixel.shatteredpixeldungeon.ui.Icons;
import com.shatteredpixel.shatteredpixeldungeon.ui.QuickRecipe;
import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton; import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextMultiline; import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextMultiline;
import com.shatteredpixel.shatteredpixeldungeon.ui.ScrollPane; import com.shatteredpixel.shatteredpixeldungeon.ui.ScrollPane;
@ -53,15 +54,16 @@ import java.util.HashMap;
//FIXME a lot of cleanup and improvements to do here //FIXME a lot of cleanup and improvements to do here
public class WndJournal extends WndTabbed { public class WndJournal extends WndTabbed {
private static final int WIDTH_P = 120; private static final int WIDTH_P = 130;
private static final int HEIGHT_P = 160; private static final int HEIGHT_P = 180;
private static final int WIDTH_L = 160; private static final int WIDTH_L = 200;
private static final int HEIGHT_L = 128; private static final int HEIGHT_L = 130;
private static final int ITEM_HEIGHT = 18; private static final int ITEM_HEIGHT = 18;
private GuideTab guideTab; private GuideTab guideTab;
private AlchemyTab alchemyTab;
private NotesTab notesTab; private NotesTab notesTab;
private CatalogTab catalogTab; private CatalogTab catalogTab;
@ -79,6 +81,10 @@ public class WndJournal extends WndTabbed {
guideTab.setRect(0, 0, width, height); guideTab.setRect(0, 0, width, height);
guideTab.updateList(); guideTab.updateList();
alchemyTab = new AlchemyTab();
add(alchemyTab);
alchemyTab.setRect(0, 0, width, height);
notesTab = new NotesTab(); notesTab = new NotesTab();
add(notesTab); add(notesTab);
notesTab.setRect(0, 0, width, height); notesTab.setRect(0, 0, width, height);
@ -97,18 +103,25 @@ public class WndJournal extends WndTabbed {
if (value) last_index = 0; if (value) last_index = 0;
} }
}, },
new IconTab( new ItemSprite(ItemSpriteSheet.ALCH_PAGE, null) ) {
protected void select( boolean value ) {
super.select( value );
alchemyTab.active = alchemyTab.visible = value;
if (value) last_index = 1;
}
},
new IconTab( Icons.get(Icons.DEPTH) ) { new IconTab( Icons.get(Icons.DEPTH) ) {
protected void select( boolean value ) { protected void select( boolean value ) {
super.select( value ); super.select( value );
notesTab.active = notesTab.visible = value; notesTab.active = notesTab.visible = value;
if (value) last_index = 1; if (value) last_index = 2;
} }
}, },
new IconTab( new ItemSprite(ItemSpriteSheet.WEAPON_HOLDER, null) ) { new IconTab( new ItemSprite(ItemSpriteSheet.WEAPON_HOLDER, null) ) {
protected void select( boolean value ) { protected void select( boolean value ) {
super.select( value ); super.select( value );
catalogTab.active = catalogTab.visible = value; catalogTab.active = catalogTab.visible = value;
if (value) last_index = 2; if (value) last_index = 3;
} }
} }
}; };
@ -223,7 +236,7 @@ public class WndJournal extends WndTabbed {
line.y = pos; line.y = pos;
content.add(line); content.add(line);
RenderedTextMultiline title = PixelScene.renderMultiline(Messages.get(this, "title"), 9); RenderedTextMultiline title = PixelScene.renderMultiline(Document.ADVENTURERS_GUIDE.title(), 9);
title.hardlight(TITLE_COLOR); title.hardlight(TITLE_COLOR);
title.maxWidth( (int)width() - 2 ); title.maxWidth( (int)width() - 2 );
title.setPos( (width() - title.width())/2f, pos + 1 + ((ITEM_HEIGHT) - title.height())/2f); title.setPos( (width() - title.width())/2f, pos + 1 + ((ITEM_HEIGHT) - title.height())/2f);
@ -232,8 +245,8 @@ public class WndJournal extends WndTabbed {
pos += Math.max(ITEM_HEIGHT, title.height()); pos += Math.max(ITEM_HEIGHT, title.height());
for (Document doc : Document.values()){ for (String page : Document.ADVENTURERS_GUIDE.pages()){
GuideItem item = new GuideItem( doc ); GuideItem item = new GuideItem( page );
item.setRect( 0, pos, width(), ITEM_HEIGHT ); item.setRect( 0, pos, width(), ITEM_HEIGHT );
content.add( item ); content.add( item );
@ -249,18 +262,18 @@ public class WndJournal extends WndTabbed {
private static class GuideItem extends ListItem { private static class GuideItem extends ListItem {
private boolean found = false; private boolean found = false;
private Document doc; private String page;
public GuideItem( Document doc ){ public GuideItem( String page ){
super( new ItemSprite( doc.pageSprite(), null), super( new ItemSprite( ItemSpriteSheet.GUIDE_PAGE, null),
Messages.titleCase( doc.title() ), -1); Messages.titleCase(Document.ADVENTURERS_GUIDE.pageTitle(page)), -1);
this.doc = doc; this.page = page;
found = doc.hasAnyPages(); found = Document.ADVENTURERS_GUIDE.hasPage(page);
if (!found) { if (!found) {
icon.hardlight( 0.5f, 0.5f, 0.5f); icon.hardlight( 0.5f, 0.5f, 0.5f);
label.text( Messages.titleCase( "???" )); label.text( Messages.titleCase(Messages.get( this, "missing" )));
label.hardlight( 0x999999 ); label.hardlight( 0x999999 );
} }
@ -268,7 +281,7 @@ public class WndJournal extends WndTabbed {
public boolean onClick( float x, float y ) { public boolean onClick( float x, float y ) {
if (inside( x, y ) && found) { if (inside( x, y ) && found) {
GameScene.show( new WndDocument(doc)); GameScene.show( new WndStory( Document.ADVENTURERS_GUIDE.pageBody(page) ));
return true; return true;
} else { } else {
return false; return false;
@ -279,6 +292,129 @@ public class WndJournal extends WndTabbed {
} }
public static class AlchemyTab extends Component {
private RedButton[] pageButtons;
private static final int NUM_BUTTONS = 10;
private static final int spriteIndexes[] = {10, 12, 3, 9, 11, 7, 8, 13, 14, 15};
private static int currentPageIdx = -1;
private IconTitle title;
private RenderedTextMultiline body;
private ArrayList<QuickRecipe> recipes = new ArrayList<>();
@Override
protected void createChildren() {
pageButtons = new RedButton[NUM_BUTTONS];
for (int i = 0; i < NUM_BUTTONS; i++){
final int idx = i;
pageButtons[i] = new RedButton( "" ){
@Override
protected void onClick() {
currentPageIdx = idx;
updateList();
}
};
if (Document.ALCHEMY_GUIDE.hasPage(i)) {
pageButtons[i].icon(new ItemSprite(ItemSpriteSheet.SOMETHING + spriteIndexes[i], null));
} else {
pageButtons[i].icon(new ItemSprite(ItemSpriteSheet.SOMETHING, null));
pageButtons[i].enable(false);
}
add( pageButtons[i] );
}
title = new IconTitle();
title.icon( new ItemSprite(ItemSpriteSheet.ALCH_PAGE));
title.visible = false;
add(title);
body = PixelScene.renderMultiline(6);
add(body);
}
@Override
protected void layout() {
super.layout();
int perRow = NUM_BUTTONS / (SPDSettings.landscape() ? 1 : 2);
float buttonWidth = width()/perRow;
for (int i = 0; i < NUM_BUTTONS; i++) {
pageButtons[i].setRect((i%perRow) * (buttonWidth), (i/perRow) * (ITEM_HEIGHT),
buttonWidth, ITEM_HEIGHT);
PixelScene.align(pageButtons[i]);
}
updateList();
}
private void updateList() {
for (int i = 0; i < NUM_BUTTONS; i++) {
if (i == currentPageIdx) {
pageButtons[i].icon().color(TITLE_COLOR);
} else {
pageButtons[i].icon().resetColor();
}
}
if (currentPageIdx == -1){
return;
}
title.visible = true;
title.label(Document.ALCHEMY_GUIDE.pageTitle(currentPageIdx));
title.setRect(0, pageButtons[NUM_BUTTONS-1].bottom(), width(), 10);
body.maxWidth((int)width());
body.text(Document.ALCHEMY_GUIDE.pageBody(currentPageIdx));
body.setPos(0, title.bottom());
for (QuickRecipe r : recipes){
if (r != null) {
r.killAndErase();
r.destroy();
}
}
recipes.clear();
ArrayList<QuickRecipe> toAdd = QuickRecipe.getRecipes(currentPageIdx);
float left;
float top = body.bottom() + 2;
int w;
ArrayList<QuickRecipe> toAddThisRow = new ArrayList<>();
while (!toAdd.isEmpty()){
while (!toAdd.isEmpty() && toAdd.get(0) == null){
top += 2;
toAdd.remove(0);
}
w = 0;
while(!toAdd.isEmpty() && toAdd.get(0) != null
&& w + toAdd.get(0).width() <= width()){
toAddThisRow.add(toAdd.remove(0));
w += toAddThisRow.get(0).width();
}
float spacing = (width() - w)/(toAddThisRow.size() + 1);
left = spacing;
for (QuickRecipe r : toAddThisRow){
r.setPos(left, top);
left += r.width() + spacing;
recipes.add(r);
add(r);
}
top += 17;
toAddThisRow.clear();
}
}
}
private static class NotesTab extends Component { private static class NotesTab extends Component {
private ScrollPane list; private ScrollPane list;
@ -363,6 +499,7 @@ public class WndJournal extends WndTabbed {
private static int currentItemIdx = 0; private static int currentItemIdx = 0;
//sprite locations
private static final int WEAPON_IDX = 0; private static final int WEAPON_IDX = 0;
private static final int ARMOR_IDX = 1; private static final int ARMOR_IDX = 1;
private static final int WAND_IDX = 2; private static final int WAND_IDX = 2;
@ -371,6 +508,8 @@ public class WndJournal extends WndTabbed {
private static final int POTION_IDX = 5; private static final int POTION_IDX = 5;
private static final int SCROLL_IDX = 6; private static final int SCROLL_IDX = 6;
private static final int spriteIndexes[] = {1, 2, 4, 5, 6, 9, 11};
private ScrollPane list; private ScrollPane list;
private ArrayList<CatalogItem> items = new ArrayList<>(); private ArrayList<CatalogItem> items = new ArrayList<>();
@ -387,7 +526,7 @@ public class WndJournal extends WndTabbed {
updateList(); updateList();
} }
}; };
itemButtons[i].icon(new ItemSprite(ItemSpriteSheet.WEAPON_HOLDER + i, null)); itemButtons[i].icon(new ItemSprite(ItemSpriteSheet.SOMETHING + spriteIndexes[i], null));
add( itemButtons[i] ); add( itemButtons[i] );
} }
@ -405,8 +544,6 @@ public class WndJournal extends WndTabbed {
add( list ); add( list );
} }
private static final int BUTTON_HEIGHT = 17;
@Override @Override
protected void layout() { protected void layout() {
super.layout(); super.layout();
@ -415,8 +552,8 @@ public class WndJournal extends WndTabbed {
float buttonWidth = width()/perRow; float buttonWidth = width()/perRow;
for (int i = 0; i < NUM_BUTTONS; i++) { for (int i = 0; i < NUM_BUTTONS; i++) {
itemButtons[i].setRect((i%perRow) * (buttonWidth), (i/perRow) * (BUTTON_HEIGHT + 1), itemButtons[i].setRect((i%perRow) * (buttonWidth), (i/perRow) * (ITEM_HEIGHT ),
buttonWidth, BUTTON_HEIGHT); buttonWidth, ITEM_HEIGHT);
PixelScene.align(itemButtons[i]); PixelScene.align(itemButtons[i]);
} }
@ -512,11 +649,11 @@ public class WndJournal extends WndTabbed {
this.seen = seen; this.seen = seen;
if (!seen) { if (!seen) {
icon.copy( new ItemSprite( ItemSpriteSheet.WEAPON_HOLDER + currentItemIdx, null) ); icon.copy( new ItemSprite( ItemSpriteSheet.SOMETHING + spriteIndexes[currentItemIdx], null) );
label.text("???"); label.text("???");
label.hardlight( 0x999999 ); label.hardlight( 0x999999 );
} else if (!IDed) { } else if (!IDed) {
icon.copy( new ItemSprite( ItemSpriteSheet.WEAPON_HOLDER + currentItemIdx, null) ); icon.copy( new ItemSprite( ItemSpriteSheet.SOMETHING + spriteIndexes[currentItemIdx], null) );
label.hardlight( 0xCCCCCC ); label.hardlight( 0xCCCCCC );
} }

View File

@ -22,33 +22,25 @@ journal.document.adventurers_guide.magic.body=Magical attacks cut right through
journal.document.alchemy_guide.title=Alchemy Guide journal.document.alchemy_guide.title=Alchemy Guide
journal.document.alchemy_guide.potions.title=Creating Potions journal.document.alchemy_guide.potions.title=Creating Potions
journal.document.alchemy_guide.potions.body=Welcome to Practical Applications of Alchemy!\n\nThis book serves as a recipe reference for hobbyist alchemists and adventurers looking to get their hands dirty.\n\nWe will start with the most iconic alchemy recipe: Place any three seeds into an alchemy pot to brew a potion!\n\nEvery seed type has a potion counterpart. The potion you create may relate to one of the seeds used, but there is a chance it will be random as well. Using multiple of the same type of seed will decrease the chance for a random potion. journal.document.alchemy_guide.potions.body=Welcome to Practical Applications of Alchemy!\n\nThis book serves as a recipe reference for hobbyist alchemists and adventurers looking to get their hands dirty.\n\nWe will start with the most iconic alchemy recipe: Place any three seeds into an alchemy pot to brew a random potion!\n\nEvery seed has a potion counterpart. The potion you create may relate to one of the seeds used, and using multiple of the same seed will increase the chance for this to occur.
journal.document.alchemy_guide.stones.title=Creating Runestones journal.document.alchemy_guide.stones.title=Creating Runestones
journal.document.alchemy_guide.stones.body=Mixing a single scroll into an alchemy pot will imbue its magic into two or three rocks within the pot. This creates runestones that correspond to that scroll.\n\nRunestones are a lesser variant of scrolls, just as seeds are a lesser variant of potions. Unlike seeds however, runestones cannot be combined to make a scroll. journal.document.alchemy_guide.stones.body=Mixing a single scroll into an alchemy pot will imbue its magic into two or three rocks within the pot. This creates runestones that correspond to that scroll.
journal.document.alchemy_guide.darts.title=Tipping Darts journal.document.alchemy_guide.darts.title=Tipping Darts
journal.document.alchemy_guide.darts.body=A single seed can be combined with one or two darts to tip them. Each type of seed produces its own type of tipped dart, with a unique effect.\n\nTipped darts only last for one use however, and will revert to regular darts when recovered. journal.document.alchemy_guide.darts.body=A single seed can be combined with one or two darts to tip them. Each type of seed produces its own type of tipped dart, with a unique single-use effect.
journal.document.alchemy_guide.exotics.title=Exotic Potions and Scrolls journal.document.alchemy_guide.exotic_potions.title=Exotic Potions
journal.document.alchemy_guide.exotics.body=The power of a potion or scroll can be augmented to create a new 'exotic' variant. These exotic items have more powerful effects, but they are often useful in different ways as well.\n\nCombining a potion and any two seeds will produce an exotic potion. Combining a scroll with any two runestones will produce an exotic scroll. journal.document.alchemy_guide.exotic_potions.body=The power of a potion can be augmented with two seeds to create a new 'exotic' variant. They have more powerful effects, but are often useful in different ways as well.
journal.document.alchemy_guide.energy.title=Alchemical Energy journal.document.alchemy_guide.exotic_scrolls.title=Exotic Scrolls
journal.document.alchemy_guide.energy.body=While the recipes we have discussed so far only need their ingredients, some recipes require energy from the alchemy pot itself. This Energy is a limited resource, but can be used to make recipes that are more than the sum of their ingredients. Some recipes will require a small amount of energy, some require a lot.\n\nAll further recipes discussed in this book will make use of Alchemical Energy. journal.document.alchemy_guide.exotic_scrolls.body=Exotic scrolls can also be made with two runestones and a scroll. They're generally a bit stronger than exotic potions, but stones are also harder to come by.
journal.document.alchemy_guide.food.title=Food Recipes journal.document.alchemy_guide.energy_food.title=Energy and Food
journal.document.alchemy_guide.food.body=Not all recipes involve magical potions or scrolls, some are more traditional.\n\nRaw meat can be stewed in an alchemy pot, and the pot's energy will cleanse the meat of disease. The more meat that is used at once, the greater the efficiency of the recipe.\n\nA raw blandfruit can be combined with a seed to create a cooked blandfruit. The cooked fruit will emulate whatever potion the seed corresponds to.\n\nA pasty, full ration, and a piece of meat (any type will do) can be combined to create a feast! Feasts provide an unparalleled culinary experience, but cost a fair amount of energy. journal.document.alchemy_guide.energy_food.body=Some recipes require energy from the alchemy pot itself. Energy is used to make recipes that are more than the sum of their ingredients, but it's a limited resource.\n\nNot all energy recipes are especially mystical however. These recipes more resemble traditional cooking than alchemy.
journal.document.alchemy_guide.bombs.title=Enhanced Bombs journal.document.alchemy_guide.bombs.title=Enhanced Bombs
journal.document.alchemy_guide.bombs.body=A standard black powder bomb can be mixed with a specific item to create an enhanced bomb. The amount of energy needed varies by the item used.\n\nThe following items can produce an enhanced bomb:\n- Potion of Liquid Flame\n- Potion of Frost\n- Potion of Healing\n- Potion of Invisibility\n- Scroll of Recharging\n- Scroll of Remove Curse\n- Scroll of Mirror Image\n- Scroll of Rage\n- Blob of Goo\n- Cursed Metal Shard journal.document.alchemy_guide.bombs.body=A standard black powder bomb can be mixed with a specific item to create an enhanced bomb. The amount of energy needed varies by the item used.
journal.document.alchemy_guide.combo_brews.title=Combination Brews journal.document.alchemy_guide.brews.title=Brews
journal.document.alchemy_guide.combo_brews.body=Combination brews are the most simple form of brew, combining the effects of two harmful potions into one.\n\n\nA wicked brew is created by mixing a potion of toxic gas with a potion of paralytic gas.\n\nA frigid brew is created by mixing a potion of frost with a potion of storm clouds.\n\nA frostfire brew is created by mixing a potion of liquid flame with a potion of snap freeze. journal.document.alchemy_guide.brews.body=Brews are made from potion-based recipes, and are centered around inflicting harmful effects on your enemies.
journal.document.alchemy_guide.heal_elixirs.title=Healing Elixirs journal.document.alchemy_guide.elixirs.title=Elixirs
journal.document.alchemy_guide.heal_elixirs.body=Healing elixirs are also quite simple, combining healing and another effect into one item.\n\n\nAn elixir of restoration is created by mixing a potion of healing with a potion of cleansing.\n\nAn elixir of vitality is created by mixing a potion of healing with a potion of shielding.\n\nAn elixir of honeyed healing is created by mixing a potion of healing with a shattered honeypot.\n\nAn elixir of aquatic rejuvenation is created by mixing a potion of healing and a blob of goo. journal.document.alchemy_guide.elixirs.body=Elixirs are made from potion-based recipes, and focus on healing or boosting your abilities.
journal.document.alchemy_guide.aoe_brews.title=Area of Effect Brews journal.document.alchemy_guide.spells.title=Spells
journal.document.alchemy_guide.aoe_brews.body=Area of effect brews spread a harmful effect over a large area. They are more expensive than combination brews, but also more powerful.\n\n\nAn infernal brew is created by mixing a potion of dragon's breath and a potion of liquid flame.\n\nA blizzard brew is created by mixing a potion of snap freeze and a potion of frost.\n\nA shocking brew is created by mixing a potion of paralytic gas and a potion of storm clouds.\n\nA caustic brew is created by mixing a potion of toxic gas and a blob of goo. journal.document.alchemy_guide.spells.body=Spells are made from scroll-based recipes, and provide a variety of effects with multiple uses.
journal.document.alchemy_guide.imbue_elixirs.title=Imbuing Elixirs
journal.document.alchemy_guide.imbue_elixirs.body=Imbuing Elixirs will imbue the drinker with a unique power which may be temporary or permanent. They are more expensive than healing elixirs, but also more powerful.\n\n\nAn elixir of dragon's blood is created by mixing a potion of liquid flame and a potion of purity.\n\nAn elixir of toxic essence is created by mixing a potion of toxic gas and a potion of purity.\n\nAn elixir of icy touch is created by mixing a potion of frost and a potion of purity.\n\nAn elixir of might is created by mixing a potion of strength and a large amount of alchemical energy.
journal.document.alchemy_guide.tele_spells.title=Teleportation Spells
journal.document.alchemy_guide.tele_spells.body=Combining certain ingredients in an alchemy pot will cause magical crystals to precipitate out of the water. The energy in these crystals can be channeled to cast spells! Most spells have multiple uses, but the specific amount varies by spell.\n\nTeleportation spells contain magic that changes the positioning of yourself, enemies, or items in various useful ways.\n\n\nMagical porter is created by mixing a scroll of identification with a merchant's beacon.\n\nPhase shift is created by mixing a scroll of teleportation with a scroll of terror.\n\nBeacon of returning is created by mixing a scroll of passage, a scroll of magic mapping, and a lot of alchemical energy.
journal.document.alchemy_guide.item_spells.title=Item Manipulation Spells
journal.document.alchemy_guide.item_spells.body=Item manipulation spells affect the items in your inventory in a variety of different ways.\n\n\nMagical infusion is created by mixing a scroll of upgrade with a stone of enchantment.\n\nCurse infusion is created by mixing a scroll of remove curse with a cursed metal shard.\n\nAlchemize is created by mixing a scroll of recharging with a potion of liquid flame.\n\nRecycle is created by mixing a scroll of transmutation with a scroll of divination.
journal.document.alchemy_guide.enviro_spells.title=Environmental Spells
journal.document.alchemy_guide.enviro_spells.body=Environmental spells give you new ways to change or interact with the terrain of the dungeon.\n\n\nReclaim trap is created by mixing a scroll of recharging with a cursed metal shard.\n\nAqua blast is created by mixing a scroll of identify with a potion of storm clouds.\n\nFeather fall is created by mixing a scroll of lullaby, a potion of levitation, and a good amount of alchemical energy.
journal.notes$landmark.well_of_health=well of health journal.notes$landmark.well_of_health=well of health
journal.notes$landmark.well_of_awareness=well of awareness journal.notes$landmark.well_of_awareness=well of awareness