v0.9.4: implemented liquid metal and added a sprite for arcane resin

This commit is contained in:
Evan Debenham 2021-07-16 20:02:55 -04:00
parent e9e32835a7
commit 849c25d3e5
7 changed files with 226 additions and 3 deletions

View File

@ -1695,6 +1695,13 @@ items.kingscrown.upgraded=The crown glows brightly and then disintegrates as you
items.kingscrown.ratgraded=Rat King's paws glow brightly as your armor transforms!
items.kingscrown.desc=The crown of the last dwarven king, it glows with tremendous magical energy.\n\nWhen worn, the crown's magic will flow into the armor you're currently wearing, transforming it into a _unique epic armor with special abilities._ The new armor will keep all the properties of the original armor.
items.liquidmetal.name=liquid metal
items.liquidmetal.ac_apply=APPLY
items.liquidmetal.prompt=Select a thrown weapon
items.liquidmetal.already_fixed=That thrown weapon is already in perfect condition!
items.liquidmetal.apply=You use %d liquid metal to repair your thrown weapon.
items.liquidmetal.desc=When poured over a thrown weapon, this magical liquid will fill into the cracks and tears from use, restoring the thrown weapon to perfect condition!\n\nA tier 1 weapon requires 10 liquid metal to be fully repaired, a tier 5 weapon requires 30. Each upgrade also doubles the amount of metal needed.\n\nLiquid metal cannot be used to repair the tips on tipped darts.
items.merchantsbeacon.name=merchant's beacon
items.merchantsbeacon.ac_use=USE
items.merchantsbeacon.desc=This odd piece of dwarven technology allows you to communicate from great distances.\n\nAfter being activated, this beacon will let you sell items to Pixel Mart from anywhere in the dungeon.\n\nHowever, the magic within the beacon will only last for one session, so use it wisely.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,192 @@
package com.shatteredpixel.shatteredpixeldungeon.items;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.effects.Splash;
import com.shatteredpixel.shatteredpixeldungeon.items.bags.Bag;
import com.shatteredpixel.shatteredpixeldungeon.items.bags.MagicalHolster;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.Potion;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.darts.TippedDart;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndBag;
import com.watabou.noosa.audio.Sample;
import java.util.ArrayList;
//these aren't considered potions internally as most potion effects shouldn't apply to them
//mainly due to their high quantity
public class LiquidMetal extends Item {
{
image = ItemSpriteSheet.LIQUID_METAL;
stackable = true;
defaultAction = AC_APPLY;
bones = true;
}
private static final String AC_APPLY = "APPLY";
@Override
public ArrayList<String> actions( Hero hero ) {
ArrayList<String> actions = super.actions( hero );
actions.add( AC_APPLY );
return actions;
}
@Override
public void execute( Hero hero, String action ) {
super.execute( hero, action );
if (action.equals(AC_APPLY)) {
curUser = hero;
GameScene.selectItem( itemSelector );
}
}
@Override
protected void onThrow( int cell ) {
if (Dungeon.level.map[cell] == Terrain.WELL || Dungeon.level.pit[cell]) {
super.onThrow( cell );
} else {
Dungeon.level.pressCell( cell );
if (Dungeon.level.heroFOV[cell]) {
GLog.i( Messages.get(Potion.class, "shatter") );
Sample.INSTANCE.play( Assets.Sounds.SHATTER );
Splash.at( cell, 0xBFBFBF, 5 );
}
}
}
@Override
public boolean isUpgradable() {
return false;
}
@Override
public boolean isIdentified() {
return true;
}
@Override
public int value() {
return Math.max(1, quantity/2);
}
private final WndBag.ItemSelector itemSelector = new WndBag.ItemSelector() {
@Override
public String textPrompt() {
return Messages.get(LiquidMetal.class, "prompt");
}
@Override
public Class<?extends Bag> preferredBag(){
return MagicalHolster.class;
}
@Override
public boolean itemSelectable(Item item) {
return item instanceof MissileWeapon && !(item instanceof TippedDart);
}
@Override
public void onSelect( Item item ) {
if (item != null && item instanceof MissileWeapon) {
MissileWeapon m = (MissileWeapon)item;
int maxToUse = 5*(m.tier+1);
maxToUse *= Math.pow(2, m.level());
float durabilityPerUse = 100 / (float)maxToUse;
//we remove a tiny amount here to account for rounding errors
float percentDurabilityLeft = 0.999f - (m.durabilityLeft()/100f);
maxToUse = (int)Math.ceil(maxToUse*percentDurabilityLeft);
if (maxToUse == 0){
GLog.w(Messages.get(LiquidMetal.class, "already_fixed"));
return;
} else if (maxToUse < quantity()) {
m.repair(maxToUse*durabilityPerUse);
quantity(quantity()-maxToUse);
GLog.i(Messages.get(LiquidMetal.class, "apply", maxToUse));
} else {
m.repair(quantity()*durabilityPerUse);
GLog.i(Messages.get(LiquidMetal.class, "apply", quantity()));
detachAll(Dungeon.hero.belongings.backpack);
}
curUser.sprite.operate(curUser.pos);
Sample.INSTANCE.play(Assets.Sounds.DRINK);
curUser.sprite.emitter().start(Speck.factory(Speck.LIGHT), 0.1f, 10);
}
}
};
public static class Recipe extends com.shatteredpixel.shatteredpixeldungeon.items.Recipe {
@Override
public boolean testIngredients(ArrayList<Item> ingredients) {
for (Item i : ingredients){
if (!(i instanceof MissileWeapon)){
return false;
}
}
return !ingredients.isEmpty();
}
@Override
public int cost(ArrayList<Item> ingredients) {
int cost = 1;
for (Item i : ingredients){
cost += i.quantity();
}
return cost;
}
@Override
public Item brew(ArrayList<Item> ingredients) {
Item result = sampleOutput(ingredients);
for (Item i : ingredients){
i.quantity(0);
}
return result;
}
@Override
public Item sampleOutput(ArrayList<Item> ingredients) {
int metalQuantity = 0;
for (Item i : ingredients){
MissileWeapon m = (MissileWeapon) i;
float quantity = m.quantity()-1;
quantity += 0.25f + 0.0075f*m.durabilityLeft();
quantity *= Math.pow(2, m.level());
metalQuantity += Math.round((5*(m.tier+1))*quantity);
}
return new LiquidMetal().quantity(metalQuantity);
}
}
}

View File

@ -56,6 +56,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.spells.ReclaimTrap;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.Recycle;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.WildEnergy;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
import com.watabou.utils.Reflection;
import java.util.ArrayList;
@ -163,6 +164,10 @@ public abstract class Recipe {
//*******
// Static members
//*******
private static Recipe[] variableRecipes = new Recipe[]{
new LiquidMetal.Recipe()
};
private static Recipe[] oneIngredientRecipes = new Recipe[]{
new AlchemistsToolkit.upgradeKit(),
@ -209,7 +214,13 @@ public abstract class Recipe {
};
public static Recipe findRecipe(ArrayList<Item> ingredients){
for (Recipe recipe : variableRecipes){
if (recipe.testIngredients(ingredients)){
return recipe;
}
}
if (ingredients.size() == 1){
for (Recipe recipe : oneIngredientRecipes){
if (recipe.testIngredients(ingredients)){
@ -237,7 +248,7 @@ public abstract class Recipe {
public static boolean usableInRecipe(Item item){
return !item.cursed
&& (!(item instanceof EquipableItem) || (item instanceof AlchemistsToolkit && item.isIdentified()))
&& (!(item instanceof EquipableItem) || (item instanceof AlchemistsToolkit && item.isIdentified()) || item instanceof MissileWeapon)
&& !(item instanceof Wand);
}
}

View File

@ -22,6 +22,7 @@
package com.shatteredpixel.shatteredpixeldungeon.items.bags;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.LiquidMetal;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.Potion;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
@ -33,7 +34,7 @@ public class PotionBandolier extends Bag {
@Override
public boolean canHold( Item item ) {
if (item instanceof Potion){
if (item instanceof Potion || item instanceof LiquidMetal){
return super.canHold(item);
} else {
return false;

View File

@ -257,6 +257,14 @@ abstract public class MissileWeapon extends Weapon {
parent = null;
super.onThrow(cell);
}
public float durabilityLeft(){
return durability;
}
public void repair( float amount ){
durability += amount;
}
protected float durabilityPerUse(){
float usages = baseUses * (float)(Math.pow(3, level()));

View File

@ -466,10 +466,12 @@ public class ItemSpriteSheet {
public static final int SCROLL_TIWAZ = SCROLLS+11;
public static final int SCROLL_CATALYST = SCROLLS+13;
public static final int ARCANE_RESIN = SCROLLS+14;
static {
for (int i = SCROLLS; i < SCROLLS+16; i++)
assignItemRect(i, 15, 14);
assignItemRect(SCROLL_CATALYST, 12, 11);
assignItemRect(ARCANE_RESIN , 12, 11);
}
private static final int EXOTIC_SCROLLS = xy(1, 20); //16 slots
@ -522,10 +524,12 @@ public class ItemSpriteSheet {
public static final int POTION_SILVER = POTIONS+10;
public static final int POTION_IVORY = POTIONS+11;
public static final int POTION_CATALYST = POTIONS+13;
public static final int LIQUID_METAL = POTIONS+14;
static {
for (int i = POTIONS; i < POTIONS+16; i++)
assignItemRect(i, 12, 14);
assignItemRect(POTION_CATALYST, 6, 15);
assignItemRect(LIQUID_METAL, 8, 15);
}
private static final int EXOTIC_POTIONS = xy(1, 23); //16 slots