v3.2.0: reworked thrown weapons to work in 'sets':
- sets are upgraded/enchanted/etc. as a unit, default size of 3 - upgrading completely repairs a set - durability boost per upgrade down to 1.5x from 3x - Internal buff added to prevent upgrade repair duplication exploits - lots of effects that apply to melee weapons now apply to thrown: identification, enchants, curses, etc. - liquid metal recipe adjusted (liquid metal itself still needs changes) - Huntress ID talent changed now that thrown weapons are IDable - darts effectively unchanged, they all belong to the same set, still not upgradeable - no balance changes to accomodate this yet, tbd.
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.5 KiB |
@@ -1091,7 +1091,7 @@ actors.hero.talent.perfect_copy.desc=_+1:_ The shadow clone gains _10%_ of the h
|
||||
actors.hero.talent.natures_bounty.title=nature's bounty
|
||||
actors.hero.talent.natures_bounty.desc=_+1:_ The Huntress can find _4 berries_ hidden in tall grass as she explores the next few floors of the dungeon.\n\n_+2:_ The Huntress can find _6 berries_ hidden in tall grass as she explores the next few floors of the dungeon.\n\nBerries are eaten quickly, restore a small amount of satiety, and might contain a usable seed.
|
||||
actors.hero.talent.survivalists_intuition.title=survivalist's intuition
|
||||
actors.hero.talent.survivalists_intuition.desc=_+1:_ The Huntress identifies all equipment _1.75x faster_.\n\n_+2:_ The Huntress identifies all equipment _2.5x faster_.
|
||||
actors.hero.talent.survivalists_intuition.desc=_+1:_ The Huntress identifies thrown weapons _3x faster_.\n\n_+2:_ The Huntress identifies thrown weapons _when she attacks with them_.
|
||||
actors.hero.talent.followup_strike.title=followup strike
|
||||
actors.hero.talent.followup_strike.desc=_+1:_ When the Huntress hits an enemy with her bow or a thrown weapon, her next melee attack against that enemy deals _2 bonus damage_.\n\n_+2:_ When the Huntress hits an enemy with her bow or a thrown weapon, her next melee attack against that enemy deals _3 bonus damage_.
|
||||
actors.hero.talent.natures_aid.title=nature's aid
|
||||
|
||||
@@ -1163,7 +1163,7 @@ items.scrolls.scrollofupgrade.name=scroll of upgrade
|
||||
items.scrolls.scrollofupgrade.inv_title=Upgrade an item
|
||||
items.scrolls.scrollofupgrade.weaken_curse=The scroll of upgrade weakens the curse on your item.
|
||||
items.scrolls.scrollofupgrade.remove_curse=The scroll of upgrade cleanses the curse on your item!
|
||||
items.scrolls.scrollofupgrade.desc=This scroll will upgrade a single item. Wands will increase in power and number of charges, weapons and armor will deal and block more damage, and the effects of rings will intensify.\n\nIt can even weaken or sometimes totally dispel curses, though it is not as potent as a scroll of remove curse. Unfortunately, the upgrading magic can also erase enchantments or glyphs on higher level gear.
|
||||
items.scrolls.scrollofupgrade.desc=This scroll will upgrade a single item or set of thrown weapons. Wands will increase in power and number of charges, weapons and armor will deal and block more damage, thrown weapon sets will be fully repaired and more durable, and the effects of rings will intensify.\n\nIt can even weaken or sometimes totally dispel curses, though it is not as potent as a scroll of remove curse. Unfortunately, the upgrading magic can also erase enchantments or glyphs on higher level gear.
|
||||
|
||||
|
||||
|
||||
@@ -2105,7 +2105,7 @@ items.weapon.missiles.darts.dart.tip_two=tip 2 darts with 1 seed
|
||||
items.weapon.missiles.darts.dart.tip_one=tip 1 dart with 1 seed
|
||||
items.weapon.missiles.darts.dart.tip_cancel=cancel
|
||||
items.weapon.missiles.darts.dart.desc=These simple shafts of spike-tipped wood are weighted to fly true and sting their prey with a flick of the wrist. They can be tipped with seeds to gain bonus effects when thrown.
|
||||
items.weapon.missiles.darts.dart.unlimited_uses=However, due to their simple construction darts will effectively last forever.
|
||||
items.weapon.missiles.darts.dart.unlimited_uses=Due to their simple construction darts will effectively last forever.
|
||||
|
||||
items.weapon.missiles.darts.displacingdart.name=displacing dart
|
||||
items.weapon.missiles.darts.displacingdart.desc=These darts are tipped with a fadeleaf-based compound which will teleport their target a short distance away.
|
||||
@@ -2138,7 +2138,6 @@ items.weapon.missiles.darts.tippeddart.clean_desc=This action will remove the se
|
||||
items.weapon.missiles.darts.tippeddart.clean_all=clean all
|
||||
items.weapon.missiles.darts.tippeddart.clean_one=clean one
|
||||
items.weapon.missiles.darts.tippeddart.cancel=cancel
|
||||
items.weapon.missiles.darts.tippeddart.durability=Tipped darts will lose their tips and become regular darts when used.
|
||||
items.weapon.missiles.darts.tippeddart.uses_left=This stack of darts has _%d/%d_ uses left before one tip wears off.
|
||||
items.weapon.missiles.darts.tippeddart.unlimited_uses=_But these are of such high quality that they will effectively last forever._
|
||||
items.weapon.missiles.darts.tippeddart.about_to_break=Your dart's tip is about to expire.
|
||||
@@ -2169,13 +2168,18 @@ items.weapon.missiles.javelin.desc=These larger throwing spears are weighted to
|
||||
items.weapon.missiles.kunai.name=kunai
|
||||
items.weapon.missiles.kunai.desc=These small knives are very powerful in the hands of a skilled user. They are most effective against unaware enemies.
|
||||
|
||||
items.weapon.missiles.missileweapon.stats=This _tier-%1$d_ thrown weapon deals _%2$d-%3$d damage_ and requires _%4$d strength_ to use properly.
|
||||
items.weapon.missiles.missileweapon.distance=This weapon is designed to be used at a distance, it is more accurate against distant enemies, but also much less accurate at melee range.
|
||||
items.weapon.missiles.missileweapon.stats_known=This set of _tier-%1$d_ thrown weapons deals _%2$d-%3$d damage_ and requires _%4$d strength_ to use properly.
|
||||
items.weapon.missiles.missileweapon.stats_unknown=Typically this set of _tier-%1$d_ thrown weapons deals _%2$d-%3$d damage_ and requires _%4$d strength_ to use properly.
|
||||
items.weapon.missiles.missileweapon.probably_too_heavy=Probably this weapon is too heavy for you.
|
||||
items.weapon.missiles.missileweapon.distance=Thrown weapons are more accurate against distant enemies, but less accurate at melee range.
|
||||
items.weapon.missiles.missileweapon.durability=Thrown weapons will wear out and break as they are used.
|
||||
items.weapon.missiles.missileweapon.uses_left=This stack of weapons has _%d/%d_ uses left before one breaks.
|
||||
items.weapon.missiles.missileweapon.unlimited_uses=_But these are of such high quality that they will effectively last forever._
|
||||
items.weapon.missiles.missileweapon.uses_left=This set of thrown weapons has _%d/%d_ uses left before one breaks.
|
||||
items.weapon.missiles.missileweapon.unlimited_uses=This set is of such high quality that it will effectively last forever.
|
||||
items.weapon.missiles.missileweapon.unknown_uses=Typically this set of thrown weapons will have _%d_ uses before one breaks.
|
||||
items.weapon.missiles.missileweapon.curse_discover=This thrown weapon is cursed!
|
||||
items.weapon.missiles.missileweapon.about_to_break=Your thrown weapon is about to break.
|
||||
items.weapon.missiles.missileweapon.has_broken=One of your thrown weapons has broken.
|
||||
items.weapon.missiles.missileweapon.dust=The thrown weapon crumbles to dust as you touch it.
|
||||
items.weapon.missiles.missileweapon$placeholder.name=thrown weapon
|
||||
|
||||
items.weapon.missiles.shuriken.name=shuriken
|
||||
@@ -2195,11 +2199,11 @@ items.weapon.missiles.throwingspear.name=throwing spear
|
||||
items.weapon.missiles.throwingspear.desc=These lightweight spears have thin frames which are clearly designed to be thrown, and not thrusted.
|
||||
|
||||
items.weapon.missiles.throwingspike.name=throwing spike
|
||||
items.weapon.missiles.throwingspike.desc=These pointed shafts of metal are meant to be thrown into distant enemies. While they aren't very strong, their simple all-metal construction makes them reasonably durable.
|
||||
items.weapon.missiles.throwingspike.desc=These durable pointed shafts of metal are meant to be thrown into distant enemies.
|
||||
items.weapon.missiles.throwingspike.discover_hint=One of the heroes starts with this item.
|
||||
|
||||
items.weapon.missiles.throwingstone.name=throwing stone
|
||||
items.weapon.missiles.throwingstone.desc=These stones are sanded down to make them able to be thrown with more power than a regular stone. Despite the craftsmanship they still aren't a very reliable weapon, but at least they won't stick to enemies.
|
||||
items.weapon.missiles.throwingstone.desc=These stones are sanded down to make them able to be thrown with more power than a regular stone.
|
||||
items.weapon.missiles.throwingstone.discover_hint=One of the heroes starts with this item.
|
||||
|
||||
items.weapon.missiles.tomahawk.name=tomahawk
|
||||
|
||||
@@ -77,7 +77,7 @@ journal.document.alchemy_guide.exotic_scrolls.body=Exotic scrolls can be made wi
|
||||
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.
|
||||
journal.document.alchemy_guide.weapons.title=Enhancing Weapons
|
||||
journal.document.alchemy_guide.weapons.body=Some of the lighter or more magical weapons can be used in alchemy!\n\nEach thrown weapon produces enough liquid metal to fully repair another weapon of the same level and tier.\n\nOne wand will produce enough arcane resin to upgrade two wands of the same level, but no higher than +3.
|
||||
journal.document.alchemy_guide.weapons.body=Some of the lighter or more magical weapons can be used in alchemy!\n\nEach thrown weapon set produces enough liquid metal to fully repair another set of the same level and tier. Note that alchemizing a set will destroy the whole set, even if it isn't all present!\n\nOne wand will produce enough arcane resin to upgrade two wands of the same level, but no higher than +3.
|
||||
journal.document.alchemy_guide.brews_elixirs.title=Brews and Elixirs
|
||||
journal.document.alchemy_guide.brews_elixirs.body=Brews and elixirs are advanced potions with a variety of effects, usually with a single use.
|
||||
journal.document.alchemy_guide.spells.title=Spells
|
||||
|
||||
@@ -375,6 +375,7 @@ windows.wndupgrade.damage=Damage
|
||||
windows.wndupgrade.blocking=Blocking
|
||||
windows.wndupgrade.weight=Weight
|
||||
windows.wndupgrade.durability=Durability
|
||||
windows.wndupgrade.quantity=Quantity
|
||||
windows.wndupgrade.zap_damage=Zap Damage
|
||||
windows.wndupgrade.corrosion_damage=Corrosion Damage
|
||||
windows.wndupgrade.ward_damage=Ward Damage
|
||||
|
||||
@@ -1067,7 +1067,8 @@ public class Hero extends Char {
|
||||
|| item instanceof TimekeepersHourglass.sandBag
|
||||
|| item instanceof DriedRose.Petal
|
||||
|| item instanceof Key
|
||||
|| item instanceof Guidebook) {
|
||||
|| item instanceof Guidebook
|
||||
|| item.quantity() == 0) {
|
||||
//Do Nothing
|
||||
} else if (item instanceof DarkGold) {
|
||||
DarkGold existing = belongings.getItem(DarkGold.class);
|
||||
|
||||
@@ -690,6 +690,10 @@ public enum Talent {
|
||||
if (item instanceof Wand){
|
||||
factor *= 1f + 2.0f*hero.pointsInTalent(SCHOLARS_INTUITION);
|
||||
}
|
||||
// 3x/instant speed with Huntress talent (see MissileWeapon.proc)
|
||||
if (item instanceof MissileWeapon){
|
||||
factor *= 1f + 2.0f*hero.pointsInTalent(SURVIVALISTS_INTUITION);
|
||||
}
|
||||
// 2x/instant for Rogue (see onItemEqupped), also id's type on equip/on pickup
|
||||
if (item instanceof Ring){
|
||||
factor *= 1f + hero.pointsInTalent(THIEFS_INTUITION);
|
||||
|
||||
+13
-15
@@ -23,6 +23,7 @@ package com.shatteredpixel.shatteredpixeldungeon.items;
|
||||
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Assets;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.Splash;
|
||||
@@ -128,6 +129,10 @@ public class LiquidMetal extends Item {
|
||||
return item instanceof MissileWeapon && !(item instanceof Dart);
|
||||
}
|
||||
|
||||
//TODO this needs to fix broken thrown weapons too
|
||||
// should also only apply to IDed thrown weapons?
|
||||
// TODO maybe thrown weps and wands can just be known uncursed in order to make recipe?
|
||||
|
||||
@Override
|
||||
public void onSelect( Item item ) {
|
||||
if (item != null && item instanceof MissileWeapon) {
|
||||
@@ -171,31 +176,24 @@ public class LiquidMetal extends Item {
|
||||
|
||||
@Override
|
||||
public boolean testIngredients(ArrayList<Item> ingredients) {
|
||||
for (Item i : ingredients){
|
||||
if (!(i instanceof MissileWeapon)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !ingredients.isEmpty();
|
||||
return ingredients.size() == 1
|
||||
&& ingredients.get(0) instanceof MissileWeapon
|
||||
&& ingredients.get(0).isIdentified()
|
||||
&& !ingredients.get(0).cursed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int cost(ArrayList<Item> ingredients) {
|
||||
int cost = 1;
|
||||
for (Item i : ingredients){
|
||||
cost += i.quantity();
|
||||
}
|
||||
return cost;
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item brew(ArrayList<Item> ingredients) {
|
||||
Item result = sampleOutput(ingredients);
|
||||
|
||||
for (Item i : ingredients){
|
||||
i.quantity(0);
|
||||
}
|
||||
MissileWeapon w = (MissileWeapon) ingredients.get(0);
|
||||
w.quantity(0);
|
||||
Buff.affect(Dungeon.hero, MissileWeapon.UpgradedSetTracker.class).levelThresholds.put(w.setID, Integer.MAX_VALUE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ public abstract class Recipe {
|
||||
//*******
|
||||
|
||||
private static Recipe[] variableRecipes = new Recipe[]{
|
||||
new LiquidMetal.Recipe()
|
||||
//none for now
|
||||
};
|
||||
|
||||
private static Recipe[] oneIngredientRecipes = new Recipe[]{
|
||||
@@ -176,6 +176,7 @@ public abstract class Recipe {
|
||||
new ExoticPotion.PotionToExotic(),
|
||||
new ExoticScroll.ScrollToExotic(),
|
||||
new ArcaneResin.Recipe(),
|
||||
new LiquidMetal.Recipe(),
|
||||
new BlizzardBrew.Recipe(),
|
||||
new InfernalBrew.Recipe(),
|
||||
new AquaBrew.Recipe(),
|
||||
@@ -254,9 +255,10 @@ public abstract class Recipe {
|
||||
}
|
||||
|
||||
public static boolean usableInRecipe(Item item){
|
||||
//only upgradeable thrown weapons and wands allowed among equipment items
|
||||
if (item instanceof EquipableItem){
|
||||
//only thrown weapons and wands allowed among equipment items
|
||||
return item.isIdentified() && !item.cursed && item instanceof MissileWeapon;
|
||||
return item.isIdentified() && !item.cursed &&
|
||||
item instanceof MissileWeapon && item.isUpgradable();
|
||||
} else if (item instanceof Wand) {
|
||||
return item.isIdentified() && !item.cursed;
|
||||
} else {
|
||||
|
||||
+2
-2
@@ -26,8 +26,8 @@ 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.armor.Armor;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.SpiritBow;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MeleeWeapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.journal.Catalog;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
|
||||
@@ -76,7 +76,7 @@ public class PotionOfMastery extends ExoticPotion {
|
||||
@Override
|
||||
public boolean itemSelectable(Item item) {
|
||||
return
|
||||
(item instanceof MeleeWeapon && !((MeleeWeapon) item).masteryPotionBonus)
|
||||
(item instanceof Weapon && !(item instanceof SpiritBow) && !((Weapon) item).masteryPotionBonus)
|
||||
|| (item instanceof Armor && !((Armor) item).masteryPotionBonus);
|
||||
}
|
||||
|
||||
|
||||
+12
-2
@@ -24,6 +24,7 @@ package com.shatteredpixel.shatteredpixeldungeon.items.scrolls;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Challenges;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Statistics;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.effects.Transmuting;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.EquipableItem;
|
||||
@@ -124,7 +125,11 @@ public class ScrollOfTransmutation extends InventoryScroll {
|
||||
}
|
||||
Dungeon.hero.spend(-Dungeon.hero.cooldown()); //cancel equip/unequip time
|
||||
} else {
|
||||
item.detach(Dungeon.hero.belongings.backpack);
|
||||
if (item instanceof MissileWeapon){
|
||||
item.detachAll(Dungeon.hero.belongings.backpack);
|
||||
} else {
|
||||
item.detach(Dungeon.hero.belongings.backpack);
|
||||
}
|
||||
if (!result.collect()) {
|
||||
Dungeon.level.drop(result, curUser.pos).sprite.drop();
|
||||
} else if (result.stackable && Dungeon.hero.belongings.getSimilar(result) != null){
|
||||
@@ -237,7 +242,7 @@ public class ScrollOfTransmutation extends InventoryScroll {
|
||||
} while (Challenges.isItemBlocked(n) || n.getClass() == w.getClass());
|
||||
|
||||
n.level(0);
|
||||
n.quantity(1);
|
||||
n.quantity(w.quantity());
|
||||
int level = w.trueLevel();
|
||||
if (level > 0) {
|
||||
n.upgrade( level );
|
||||
@@ -253,6 +258,11 @@ public class ScrollOfTransmutation extends InventoryScroll {
|
||||
n.cursed = w.cursed;
|
||||
n.augment = w.augment;
|
||||
n.enchantHardened = w.enchantHardened;
|
||||
|
||||
//technically a new set, ensure old one is destroyed (except for darts)
|
||||
if (w instanceof MissileWeapon && w.isUpgradable()){
|
||||
Buff.affect(Dungeon.hero, MissileWeapon.UpgradedSetTracker.class).levelThresholds.put(((MissileWeapon) w).setID, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
return n;
|
||||
|
||||
|
||||
+1
-3
@@ -29,9 +29,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.bags.Bag;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.InventoryScroll;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfEnchantment;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.SpiritBow;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MeleeWeapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
|
||||
@@ -68,7 +66,7 @@ public class ScrollOfEnchantment extends ExoticScroll {
|
||||
}
|
||||
|
||||
public static boolean enchantable( Item item ){
|
||||
return (item instanceof MeleeWeapon || item instanceof SpiritBow || item instanceof Armor);
|
||||
return (item instanceof Weapon || item instanceof Armor);
|
||||
}
|
||||
|
||||
private void confirmCancelation() {
|
||||
|
||||
+2
-5
@@ -32,11 +32,8 @@ import com.shatteredpixel.shatteredpixeldungeon.items.quest.MetalShard;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfMight;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfRemoveCurse;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.SpiritBow;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MagesStaff;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MeleeWeapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.journal.Catalog;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
|
||||
import com.watabou.noosa.audio.Sample;
|
||||
@@ -53,7 +50,7 @@ public class CurseInfusion extends InventorySpell {
|
||||
|
||||
@Override
|
||||
protected boolean usableOnItem(Item item) {
|
||||
return ((item instanceof EquipableItem && !(item instanceof MissileWeapon)) || item instanceof Wand);
|
||||
return ((item instanceof EquipableItem && item.isUpgradable()) || item instanceof Wand);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,7 +60,7 @@ public class CurseInfusion extends InventorySpell {
|
||||
Sample.INSTANCE.play(Assets.Sounds.CURSED);
|
||||
|
||||
item.cursed = true;
|
||||
if (item instanceof MeleeWeapon || item instanceof SpiritBow) {
|
||||
if (item instanceof Weapon) {
|
||||
Weapon w = (Weapon) item;
|
||||
if (w.enchantment != null) {
|
||||
//if we are freshly applying curse infusion, don't replace an existing curse
|
||||
|
||||
+7
@@ -295,6 +295,13 @@ public class SpiritBow extends Weapon {
|
||||
image = ItemSpriteSheet.SPIRIT_ARROW;
|
||||
|
||||
hitSound = Assets.Sounds.HIT_ARROW;
|
||||
|
||||
setID = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int defaultQuantity() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+12
-8
@@ -67,6 +67,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Vampir
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MeleeWeapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.RunicBlade;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Scimitar;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.journal.Catalog;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
|
||||
@@ -108,10 +109,12 @@ abstract public class Weapon extends KindOfWeapon {
|
||||
}
|
||||
|
||||
public Augment augment = Augment.NONE;
|
||||
|
||||
private static final int USES_TO_ID = 20;
|
||||
private float usesLeftToID = USES_TO_ID;
|
||||
private float availableUsesToID = USES_TO_ID/2f;
|
||||
|
||||
protected int usesToID(){
|
||||
return 20;
|
||||
}
|
||||
protected float usesLeftToID = usesToID();
|
||||
protected float availableUsesToID = usesToID()/2f;
|
||||
|
||||
public Enchantment enchantment;
|
||||
public boolean enchantHardened = false;
|
||||
@@ -191,9 +194,10 @@ abstract public class Weapon extends KindOfWeapon {
|
||||
|
||||
public void onHeroGainExp( float levelPercent, Hero hero ){
|
||||
levelPercent *= Talent.itemIDSpeedFactor(hero, this);
|
||||
if (!levelKnown && isEquipped(hero) && availableUsesToID <= USES_TO_ID/2f) {
|
||||
if (!levelKnown && (isEquipped(hero) || this instanceof MissileWeapon)
|
||||
&& availableUsesToID <= usesToID()/2f) {
|
||||
//gains enough uses to ID over 0.5 levels
|
||||
availableUsesToID = Math.min(USES_TO_ID/2f, availableUsesToID + levelPercent * USES_TO_ID);
|
||||
availableUsesToID = Math.min(usesToID()/2f, availableUsesToID + levelPercent * usesToID());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,8 +237,8 @@ abstract public class Weapon extends KindOfWeapon {
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
usesLeftToID = USES_TO_ID;
|
||||
availableUsesToID = USES_TO_ID/2f;
|
||||
usesLeftToID = usesToID();
|
||||
availableUsesToID = usesToID()/2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+13
-2
@@ -28,13 +28,13 @@ import com.shatteredpixel.shatteredpixeldungeon.effects.particles.SmokeParticle;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.bombs.Bomb;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
|
||||
import com.watabou.utils.Bundle;
|
||||
import com.watabou.utils.PathFinder;
|
||||
import com.watabou.utils.Random;
|
||||
|
||||
public class Explosive extends Weapon.Enchantment {
|
||||
|
||||
@@ -47,7 +47,7 @@ public class Explosive extends Weapon.Enchantment {
|
||||
public int proc( Weapon weapon, Char attacker, Char defender, int damage ) {
|
||||
|
||||
//average value of 5, or 20 hits to an explosion
|
||||
int durToReduce = Math.round(Random.IntRange(0, 10) * procChanceMultiplier(attacker));
|
||||
int durToReduce = Math.round(24 * procChanceMultiplier(attacker));
|
||||
int currentDurability = durability;
|
||||
durability -= durToReduce;
|
||||
|
||||
@@ -82,9 +82,20 @@ public class Explosive extends Weapon.Enchantment {
|
||||
Item.updateQuickslot();
|
||||
}
|
||||
|
||||
if (weapon instanceof MissileWeapon
|
||||
&& ((MissileWeapon)weapon).parent != null && ((MissileWeapon) weapon).parent.enchantment instanceof Explosive){
|
||||
((Explosive) ((MissileWeapon) weapon).parent.enchantment).durability = durability;
|
||||
}
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
public void merge(Explosive other){
|
||||
if (other.durability < durability){
|
||||
durability = other.durability;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean curse() {
|
||||
return true;
|
||||
|
||||
+259
-78
@@ -21,6 +21,7 @@
|
||||
|
||||
package com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles;
|
||||
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Assets;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
|
||||
@@ -36,23 +37,29 @@ import com.shatteredpixel.shatteredpixeldungeon.items.Item;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.bags.Bag;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.bags.MagicalHolster;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfSharpshooting;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.ParchmentScrap;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.ShardOfOblivion;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.SpiritBow;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.curses.Explosive;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Projecting;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.darts.Dart;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
|
||||
import com.watabou.noosa.audio.Sample;
|
||||
import com.watabou.utils.Bundle;
|
||||
import com.watabou.utils.Random;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
abstract public class MissileWeapon extends Weapon {
|
||||
|
||||
{
|
||||
stackable = true;
|
||||
levelKnown = true;
|
||||
quantity = defaultQuantity();
|
||||
|
||||
bones = true;
|
||||
|
||||
@@ -60,6 +67,9 @@ abstract public class MissileWeapon extends Weapon {
|
||||
usesTargeting = true;
|
||||
}
|
||||
|
||||
//TODO maybe make this like actor IDs, instead of random? collisions unlikely, but it's messy
|
||||
public long setID = new SecureRandom().nextLong();
|
||||
|
||||
//whether or not this instance of the item exists purely to trigger its effect. i.e. no dropping
|
||||
public boolean spawnedForEffect = false;
|
||||
|
||||
@@ -72,9 +82,13 @@ abstract public class MissileWeapon extends Weapon {
|
||||
public boolean holster;
|
||||
|
||||
//used to reduce durability from the source weapon stack, rather than the one being thrown.
|
||||
protected MissileWeapon parent;
|
||||
public MissileWeapon parent;
|
||||
|
||||
public int tier;
|
||||
|
||||
protected int usesToID(){
|
||||
return 10; //half of a melee weapon
|
||||
}
|
||||
|
||||
@Override
|
||||
public int min() {
|
||||
@@ -107,7 +121,11 @@ abstract public class MissileWeapon extends Weapon {
|
||||
}
|
||||
|
||||
public int STRReq(int lvl){
|
||||
return STRReq(tier, lvl) - 1; //1 less str than normal for their tier
|
||||
int req = STRReq(tier, lvl) - 1; //1 less str than normal for their tier
|
||||
if (masteryPotionBonus){
|
||||
req -= 2;
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
//use the parent item if this has been thrown from a parent
|
||||
@@ -118,41 +136,24 @@ abstract public class MissileWeapon extends Weapon {
|
||||
return super.buffedLvl();
|
||||
}
|
||||
}
|
||||
|
||||
public Item upgrade( boolean enchant ) {
|
||||
if (!bundleRestoring) {
|
||||
durability = MAX_DURABILITY;
|
||||
quantity = fullSetQuantity = defaultQuantity();
|
||||
Buff.affect(Dungeon.hero, UpgradedSetTracker.class).levelThresholds.put(setID, level()+1);
|
||||
}
|
||||
return super.upgrade( enchant );
|
||||
}
|
||||
|
||||
@Override
|
||||
//FIXME some logic here assumes the items are in the player's inventory. Might need to adjust
|
||||
public Item upgrade() {
|
||||
if (!bundleRestoring) {
|
||||
durability = MAX_DURABILITY;
|
||||
if (quantity > 1) {
|
||||
MissileWeapon upgraded = (MissileWeapon) split(1);
|
||||
upgraded.parent = null;
|
||||
|
||||
upgraded = (MissileWeapon) upgraded.upgrade();
|
||||
|
||||
//try to put the upgraded into inventory, if it didn't already merge
|
||||
if (upgraded.quantity() == 1 && !upgraded.collect()) {
|
||||
Dungeon.level.drop(upgraded, Dungeon.hero.pos);
|
||||
}
|
||||
updateQuickslot();
|
||||
return upgraded;
|
||||
} else {
|
||||
super.upgrade();
|
||||
|
||||
Item similar = Dungeon.hero.belongings.getSimilar(this);
|
||||
if (similar != null){
|
||||
detach(Dungeon.hero.belongings.backpack);
|
||||
Item result = similar.merge(this);
|
||||
updateQuickslot();
|
||||
return result;
|
||||
}
|
||||
updateQuickslot();
|
||||
return this;
|
||||
}
|
||||
|
||||
} else {
|
||||
return super.upgrade();
|
||||
quantity = fullSetQuantity = defaultQuantity();
|
||||
Buff.affect(Dungeon.hero, UpgradedSetTracker.class).levelThresholds.put(setID, level()+1);
|
||||
}
|
||||
return super.upgrade();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,7 +170,7 @@ abstract public class MissileWeapon extends Weapon {
|
||||
}
|
||||
|
||||
public boolean isSimilar( Item item ) {
|
||||
return level() == item.level() && getClass() == item.getClass();
|
||||
return trueLevel() == item.trueLevel() && getClass() == item.getClass() && setID == (((MissileWeapon) item).setID);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -269,23 +270,82 @@ abstract public class MissileWeapon extends Weapon {
|
||||
}
|
||||
}
|
||||
|
||||
return super.proc(attacker, defender, damage);
|
||||
if ((cursed || hasCurseEnchant()) && !cursedKnown){
|
||||
GLog.n(Messages.get(this, "curse_discover"));
|
||||
}
|
||||
cursedKnown = true;
|
||||
if (parent != null) parent.cursedKnown = true;
|
||||
|
||||
//instant ID with the right talent
|
||||
if (attacker == Dungeon.hero && Dungeon.hero.pointsInTalent(Talent.SURVIVALISTS_INTUITION) == 2){
|
||||
usesLeftToID = Math.min(usesLeftToID, 0);
|
||||
}
|
||||
|
||||
int result = super.proc(attacker, defender, damage);
|
||||
|
||||
//handle ID progress over parent/child
|
||||
if (parent != null && parent.usesLeftToID > usesLeftToID){
|
||||
float diff = parent.usesLeftToID - usesLeftToID;
|
||||
parent.usesLeftToID -= diff;
|
||||
parent.availableUsesToID -= diff;
|
||||
if (usesLeftToID <= 0) {
|
||||
if (ShardOfOblivion.passiveIDDisabled()){
|
||||
parent.setIDReady();
|
||||
} else {
|
||||
parent.identify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item virtual() {
|
||||
Item item = super.virtual();
|
||||
|
||||
((MissileWeapon)item).setID = setID;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public int defaultQuantity(){
|
||||
return 3;
|
||||
}
|
||||
|
||||
//this is tracked to show warnings when upgrading and some of the set isn't present
|
||||
public int fullSetQuantity = defaultQuantity();
|
||||
|
||||
@Override
|
||||
public Item random() {
|
||||
if (!stackable) return this;
|
||||
|
||||
//2: 66.67% (2/3)
|
||||
//3: 26.67% (4/15)
|
||||
//4: 6.67% (1/15)
|
||||
quantity = 2;
|
||||
if (Random.Int(3) == 0) {
|
||||
quantity++;
|
||||
//+0: 75% (3/4)
|
||||
//+1: 20% (4/20)
|
||||
//+2: 5% (1/20)
|
||||
int n = 0;
|
||||
if (Random.Int(4) == 0) {
|
||||
n++;
|
||||
if (Random.Int(5) == 0) {
|
||||
quantity++;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
level(n);
|
||||
|
||||
//we use a separate RNG here so that variance due to things like parchment scrap
|
||||
//does not affect levelgen
|
||||
Random.pushGenerator(Random.Long());
|
||||
|
||||
//30% chance to be cursed
|
||||
//10% chance to be enchanted
|
||||
float effectRoll = Random.Float();
|
||||
if (effectRoll < 0.3f * ParchmentScrap.curseChanceMultiplier()) {
|
||||
enchant(Enchantment.randomCurse());
|
||||
cursed = true;
|
||||
} else if (effectRoll >= 1f - (0.1f * ParchmentScrap.enchantChanceMultiplier())){
|
||||
enchant();
|
||||
}
|
||||
|
||||
Random.popGenerator();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -327,14 +387,16 @@ abstract public class MissileWeapon extends Weapon {
|
||||
durability += amount;
|
||||
durability = Math.min(durability, MAX_DURABILITY);
|
||||
}
|
||||
|
||||
public float durabilityPerUse(){
|
||||
//classes that override durabilityPerUse can turn rounding off, to do their own rounding after more logic
|
||||
return durabilityPerUse(true);
|
||||
|
||||
public final float durabilityPerUse(){
|
||||
return durabilityPerUse(level());
|
||||
}
|
||||
|
||||
protected final float durabilityPerUse( boolean rounded){
|
||||
float usages = baseUses * (float)(Math.pow(3, level()));
|
||||
//classes that add steps onto durabilityPerUse can turn rounding off, to do their own rounding after more logic
|
||||
protected boolean useRoundingInDurabilityCalc = true;
|
||||
|
||||
public float durabilityPerUse( int level ){
|
||||
float usages = baseUses * (float)(Math.pow(1.5f, level));
|
||||
|
||||
//+50%/75% durability
|
||||
if (Dungeon.hero != null && Dungeon.hero.hasTalent(Talent.DURABLE_PROJECTILES)){
|
||||
@@ -349,7 +411,7 @@ abstract public class MissileWeapon extends Weapon {
|
||||
//at 100 uses, items just last forever.
|
||||
if (usages >= 100f) return 0;
|
||||
|
||||
if (rounded){
|
||||
if (useRoundingInDurabilityCalc){
|
||||
usages = Math.round(usages);
|
||||
//add a tiny amount to account for rounding error for calculations like 1/3
|
||||
return (MAX_DURABILITY/usages) + 0.001f;
|
||||
@@ -366,6 +428,7 @@ abstract public class MissileWeapon extends Weapon {
|
||||
if (parent.durability <= parent.durabilityPerUse()){
|
||||
durability = 0;
|
||||
parent.durability = MAX_DURABILITY;
|
||||
parent.fullSetQuantity--;
|
||||
if (parent.durabilityPerUse() < 100f) {
|
||||
GLog.n(Messages.get(this, "has_broken"));
|
||||
}
|
||||
@@ -419,6 +482,34 @@ abstract public class MissileWeapon extends Weapon {
|
||||
quantity -= 1;
|
||||
durability += MAX_DURABILITY;
|
||||
}
|
||||
|
||||
masteryPotionBonus = masteryPotionBonus || ((MissileWeapon) other).masteryPotionBonus;
|
||||
levelKnown = levelKnown || other.levelKnown;
|
||||
cursedKnown = cursedKnown || other.cursedKnown;
|
||||
enchantHardened = enchantHardened || ((MissileWeapon) other).enchantHardened;
|
||||
|
||||
//if other has a curse/enchant status that's a higher priority, copy it. in the following order:
|
||||
//curse infused
|
||||
if (!curseInfusionBonus && ((MissileWeapon) other).curseInfusionBonus && ((MissileWeapon) other).hasCurseEnchant()){
|
||||
enchantment = ((MissileWeapon) other).enchantment;
|
||||
curseInfusionBonus = true;
|
||||
cursed = cursed || other.cursed;
|
||||
//enchanted
|
||||
} else if (!curseInfusionBonus && !hasGoodEnchant() && ((MissileWeapon) other).hasGoodEnchant()){
|
||||
enchantment = ((MissileWeapon) other).enchantment;
|
||||
cursed = other.cursed;
|
||||
//nothing
|
||||
} else if (!curseInfusionBonus && hasCurseEnchant() && !((MissileWeapon) other).hasCurseEnchant()){
|
||||
enchantment = ((MissileWeapon) other).enchantment;
|
||||
cursed = other.cursed;
|
||||
}
|
||||
//cursed (no copy as other cannot have a higher priority status)
|
||||
|
||||
//special case for explosive, as it tracks a variable
|
||||
if (((MissileWeapon) other).enchantment instanceof Explosive
|
||||
&& enchantment instanceof Explosive){
|
||||
((Explosive) enchantment).merge((Explosive) ((MissileWeapon) other).enchantment);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -428,7 +519,7 @@ abstract public class MissileWeapon extends Weapon {
|
||||
bundleRestoring = true;
|
||||
Item split = super.split(amount);
|
||||
bundleRestoring = false;
|
||||
|
||||
|
||||
//unless the thrown weapon will break, split off a max durability item and
|
||||
//have it reduce the durability of the main stack. Cleaner to the player this way
|
||||
if (split != null){
|
||||
@@ -443,59 +534,71 @@ abstract public class MissileWeapon extends Weapon {
|
||||
@Override
|
||||
public boolean doPickUp(Hero hero, int pos) {
|
||||
parent = null;
|
||||
return super.doPickUp(hero, pos);
|
||||
if (!UpgradedSetTracker.pickupValid(hero, this)){
|
||||
Sample.INSTANCE.play( Assets.Sounds.ITEM );
|
||||
hero.spendAndNext( TIME_TO_PICK_UP );
|
||||
GLog.w(Messages.get(this, "dust"));
|
||||
quantity(0);
|
||||
return true;
|
||||
} else {
|
||||
return super.doPickUp(hero, pos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdentified() {
|
||||
return true;
|
||||
return levelKnown && cursedKnown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String info() {
|
||||
|
||||
String info = super.info();
|
||||
|
||||
info += "\n\n" + Messages.get( MissileWeapon.class, "stats",
|
||||
tier,
|
||||
Math.round(augment.damageFactor(min())),
|
||||
Math.round(augment.damageFactor(max())),
|
||||
STRReq());
|
||||
|
||||
if (Dungeon.hero != null) {
|
||||
if (STRReq() > Dungeon.hero.STR()) {
|
||||
info += " " + Messages.get(Weapon.class, "too_heavy");
|
||||
} else if (Dungeon.hero.STR() > STRReq()) {
|
||||
info += " " + Messages.get(Weapon.class, "excess_str", Dungeon.hero.STR() - STRReq());
|
||||
if (levelKnown) {
|
||||
info += "\n\n" + Messages.get(MissileWeapon.class, "stats_known", tier, augment.damageFactor(min()), augment.damageFactor(max()), STRReq());
|
||||
if (Dungeon.hero != null) {
|
||||
if (STRReq() > Dungeon.hero.STR()) {
|
||||
info += " " + Messages.get(Weapon.class, "too_heavy");
|
||||
} else if (Dungeon.hero.STR() > STRReq()) {
|
||||
info += " " + Messages.get(Weapon.class, "excess_str", Dungeon.hero.STR() - STRReq());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info += "\n\n" + Messages.get(MissileWeapon.class, "stats_unknown", tier, min(0), max(0), STRReq(0));
|
||||
if (Dungeon.hero != null && STRReq(0) > Dungeon.hero.STR()) {
|
||||
info += " " + Messages.get(MissileWeapon.class, "probably_too_heavy");
|
||||
}
|
||||
}
|
||||
|
||||
if (enchantment != null && (cursedKnown || !enchantment.curse())){
|
||||
info += "\n\n" + Messages.get(Weapon.class, "enchanted", enchantment.name());
|
||||
if (enchantHardened) info += " " + Messages.get(Weapon.class, "enchant_hardened");
|
||||
info += " " + Messages.get(enchantment, "desc");
|
||||
} else if (enchantHardened){
|
||||
info += "\n\n" + Messages.get(Weapon.class, "hardened_no_enchant");
|
||||
}
|
||||
|
||||
if (cursed && isEquipped( Dungeon.hero )) {
|
||||
info += "\n\n" + Messages.get(Weapon.class, "cursed_worn");
|
||||
} else if (cursedKnown && cursed) {
|
||||
if (cursedKnown && cursed) {
|
||||
info += "\n\n" + Messages.get(Weapon.class, "cursed");
|
||||
} else if (!isIdentified() && cursedKnown){
|
||||
info += "\n\n" + Messages.get(Weapon.class, "not_cursed");
|
||||
}
|
||||
|
||||
info += "\n\n" + Messages.get(MissileWeapon.class, "distance");
|
||||
|
||||
info += "\n\n" + Messages.get(this, "durability");
|
||||
|
||||
if (durabilityPerUse() > 0){
|
||||
info += " " + Messages.get(this, "uses_left",
|
||||
(int)Math.ceil(durability/durabilityPerUse()),
|
||||
(int)Math.ceil(MAX_DURABILITY/durabilityPerUse()));
|
||||
} else {
|
||||
info += " " + Messages.get(this, "unlimited_uses");
|
||||
|
||||
if (levelKnown) {
|
||||
if (durabilityPerUse() > 0) {
|
||||
info += "\n\n" + Messages.get(this, "uses_left",
|
||||
(int) Math.ceil(durability / durabilityPerUse()),
|
||||
(int) Math.ceil(MAX_DURABILITY / durabilityPerUse()));
|
||||
} else {
|
||||
info += "\n\n" + Messages.get(this, "unlimited_uses");
|
||||
}
|
||||
} else {
|
||||
info += "\n\n" + Messages.get(this, "unknown_uses", (int) Math.ceil(MAX_DURABILITY / durabilityPerUse(0)));
|
||||
}
|
||||
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -504,14 +607,19 @@ abstract public class MissileWeapon extends Weapon {
|
||||
return 6 * tier * quantity * (level() + 1);
|
||||
}
|
||||
|
||||
private static final String SET_ID = "set_id";
|
||||
|
||||
private static final String SPAWNED = "spawned";
|
||||
private static final String DURABILITY = "durability";
|
||||
private static final String FULL_QUANTITY = "full_quantity";
|
||||
|
||||
@Override
|
||||
public void storeInBundle(Bundle bundle) {
|
||||
super.storeInBundle(bundle);
|
||||
bundle.put(SET_ID, setID);
|
||||
bundle.put(SPAWNED, spawnedForEffect);
|
||||
bundle.put(DURABILITY, durability);
|
||||
bundle.put(FULL_QUANTITY, fullSetQuantity);
|
||||
}
|
||||
|
||||
private static boolean bundleRestoring = false;
|
||||
@@ -521,8 +629,29 @@ abstract public class MissileWeapon extends Weapon {
|
||||
bundleRestoring = true;
|
||||
super.restoreFromBundle(bundle);
|
||||
bundleRestoring = false;
|
||||
|
||||
if (bundle.contains(SET_ID)){
|
||||
setID = bundle.getLong(SET_ID);
|
||||
//pre v3.2.0 logic
|
||||
} else {
|
||||
//if we have a higher than 0 level, assume that this was a solitary thrown wep upgrade
|
||||
//turn it into a set of full quantity
|
||||
if (level() > 0){
|
||||
//set ID will be a random long
|
||||
quantity = defaultQuantity();
|
||||
fullSetQuantity = quantity;
|
||||
|
||||
//otherwise treat all currently spawned thrown weapons of the same class as if they are part of the same set
|
||||
//darts already do this though and need no conversion
|
||||
} else if (!(this instanceof Dart)){
|
||||
levelKnown = cursedKnown = true;
|
||||
setID = getClass().getSimpleName().hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
spawnedForEffect = bundle.getBoolean(SPAWNED);
|
||||
durability = bundle.getFloat(DURABILITY);
|
||||
fullSetQuantity = bundle.getInt(FULL_QUANTITY);
|
||||
}
|
||||
|
||||
public static class PlaceHolder extends MissileWeapon {
|
||||
@@ -533,7 +662,13 @@ abstract public class MissileWeapon extends Weapon {
|
||||
|
||||
@Override
|
||||
public boolean isSimilar(Item item) {
|
||||
return item instanceof MissileWeapon;
|
||||
//yes, even though it uses a dart outline
|
||||
return item instanceof MissileWeapon && !(item instanceof Dart);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String status() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -541,4 +676,50 @@ abstract public class MissileWeapon extends Weapon {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
//also used by liquid metal crafting to track when a set is consumed
|
||||
public static class UpgradedSetTracker extends Buff {
|
||||
|
||||
public HashMap<Long, Integer> levelThresholds = new HashMap<>();
|
||||
|
||||
public static boolean pickupValid(Hero h, MissileWeapon w){
|
||||
if (h.buff(UpgradedSetTracker.class) != null){
|
||||
HashMap<Long, Integer> levelThresholds = h.buff(UpgradedSetTracker.class).levelThresholds;
|
||||
if (levelThresholds.containsKey(w.setID)){
|
||||
return w.level() >= levelThresholds.get(w.setID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final String SET_IDD = "set_ids";
|
||||
public static final String SET_LEVELS = "set_levels";
|
||||
|
||||
@Override
|
||||
public void storeInBundle(Bundle bundle) {
|
||||
super.storeInBundle(bundle);
|
||||
long[] IDs = new long[levelThresholds.size()];
|
||||
int[] levels = new int[levelThresholds.size()];
|
||||
int i = 0;
|
||||
for (Long ID : levelThresholds.keySet()){
|
||||
IDs[i] = ID;
|
||||
levels[i] = levelThresholds.get(ID);
|
||||
i++;
|
||||
}
|
||||
bundle.put(SET_IDD, IDs);
|
||||
bundle.put(SET_LEVELS, levels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreFromBundle(Bundle bundle) {
|
||||
super.restoreFromBundle(bundle);
|
||||
long[] IDs = bundle.getLongArray(SET_IDD);
|
||||
int[] levels = bundle.getIntArray(SET_LEVELS);
|
||||
levelThresholds.clear();
|
||||
for (int i = 0; i <IDs.length; i++){
|
||||
levelThresholds.put(IDs[i], levels[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-1
@@ -48,6 +48,8 @@ import java.util.ArrayList;
|
||||
public class Dart extends MissileWeapon {
|
||||
|
||||
{
|
||||
levelKnown = true;
|
||||
|
||||
image = ItemSpriteSheet.DART;
|
||||
hitSound = Assets.Sounds.HIT_ARROW;
|
||||
hitSoundPitch = 1.3f;
|
||||
@@ -56,6 +58,9 @@ public class Dart extends MissileWeapon {
|
||||
|
||||
//infinite, even with penalties
|
||||
baseUses = 1000;
|
||||
|
||||
//all darts share a set ID
|
||||
setID = 0L;
|
||||
}
|
||||
|
||||
protected static final String AC_TIP = "TIP";
|
||||
@@ -242,7 +247,17 @@ public class Dart extends MissileWeapon {
|
||||
public boolean isUpgradable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isIdentified() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int defaultQuantity() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int value() {
|
||||
return super.value()/2; //half normal value
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ public class RotDart extends TippedDart {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float durabilityPerUse() {
|
||||
public float durabilityPerUse(int level) {
|
||||
return MAX_DURABILITY/5f; //always 5 uses
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -163,9 +163,13 @@ public abstract class TippedDart extends Dart {
|
||||
|
||||
private static int targetPos = -1;
|
||||
|
||||
{
|
||||
useRoundingInDurabilityCalc = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float durabilityPerUse() {
|
||||
float use = super.durabilityPerUse(false);
|
||||
public float durabilityPerUse(int level) {
|
||||
float use = super.durabilityPerUse(level);
|
||||
|
||||
if (Dungeon.hero != null) {
|
||||
use /= (1 + Dungeon.hero.pointsInTalent(Talent.DURABLE_TIPS));
|
||||
|
||||
+4
-3
@@ -38,6 +38,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.Recipe;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.AlchemistsToolkit;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.bags.Bag;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.TrinketCatalyst;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.journal.Catalog;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.journal.Journal;
|
||||
@@ -299,7 +300,7 @@ public class AlchemyScene extends PixelScene {
|
||||
if (item != null && inputs[0] != null) {
|
||||
for (int i = 0; i < inputs.length; i++) {
|
||||
if (inputs[i].item() == null) {
|
||||
if (item instanceof LiquidMetal){
|
||||
if (item instanceof LiquidMetal || item instanceof MissileWeapon){
|
||||
inputs[i].item(item.detachAll(Dungeon.hero.belongings.backpack));
|
||||
} else {
|
||||
inputs[i].item(item.detach(Dungeon.hero.belongings.backpack));
|
||||
@@ -570,7 +571,7 @@ public class AlchemyScene extends PixelScene {
|
||||
if (item != null && inputs[0] != null) {
|
||||
for (int i = 0; i < inputs.length; i++) {
|
||||
if (inputs[i].item() == null) {
|
||||
if (item instanceof LiquidMetal){
|
||||
if (item instanceof LiquidMetal || item instanceof MissileWeapon){
|
||||
inputs[i].item(item.detachAll(Dungeon.hero.belongings.backpack));
|
||||
} else {
|
||||
inputs[i].item(item.detach(Dungeon.hero.belongings.backpack));
|
||||
@@ -792,7 +793,7 @@ public class AlchemyScene extends PixelScene {
|
||||
ArrayList<Item> found = inventory.getAllSimilar(finding);
|
||||
while (!found.isEmpty() && needed > 0){
|
||||
Item detached;
|
||||
if (finding instanceof LiquidMetal) {
|
||||
if (finding instanceof LiquidMetal || finding instanceof MissileWeapon) {
|
||||
detached = found.get(0).detachAll(inventory.backpack);
|
||||
} else {
|
||||
detached = found.get(0).detach(inventory.backpack);
|
||||
|
||||
@@ -345,14 +345,6 @@ public class QuickRecipe extends Component {
|
||||
result.add(new QuickRecipe( new LiquidMetal.Recipe(),
|
||||
new ArrayList<Item>(Arrays.asList(new MissileWeapon.PlaceHolder())),
|
||||
new LiquidMetal()));
|
||||
result.add(new QuickRecipe( new LiquidMetal.Recipe(),
|
||||
new ArrayList<Item>(Arrays.asList(new MissileWeapon.PlaceHolder().quantity(2))),
|
||||
new LiquidMetal()));
|
||||
result.add(new QuickRecipe( new LiquidMetal.Recipe(),
|
||||
new ArrayList<Item>(Arrays.asList(new MissileWeapon.PlaceHolder().quantity(3))),
|
||||
new LiquidMetal()));
|
||||
result.add(null);
|
||||
result.add(null);
|
||||
result.add(new QuickRecipe( new ArcaneResin.Recipe(),
|
||||
new ArrayList<Item>(Arrays.asList(new Wand.PlaceHolder())),
|
||||
new ArcaneResin()));
|
||||
|
||||
+9
-5
@@ -24,6 +24,7 @@ package com.shatteredpixel.shatteredpixeldungeon.windows;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Assets;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Badges;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Belongings;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Blacksmith;
|
||||
@@ -35,7 +36,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.bags.Bag;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MeleeWeapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.journal.Catalog;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.journal.Notes;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
|
||||
@@ -272,13 +273,16 @@ public class WndBlacksmith extends Window {
|
||||
if (second.isEquipped( Dungeon.hero )) {
|
||||
((EquipableItem)second).doUnequip( Dungeon.hero, false );
|
||||
}
|
||||
second.detach( Dungeon.hero.belongings.backpack );
|
||||
second.detachAll( Dungeon.hero.belongings.backpack );
|
||||
|
||||
if (second instanceof Armor){
|
||||
BrokenSeal seal = ((Armor) second).checkSeal();
|
||||
if (seal != null){
|
||||
Dungeon.level.drop( seal, Dungeon.hero.pos );
|
||||
}
|
||||
} else if (second instanceof MissileWeapon){
|
||||
Buff.affect(Dungeon.hero, MissileWeapon.UpgradedSetTracker.class)
|
||||
.levelThresholds.put(((MissileWeapon) second).setID, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
//preserves enchant/glyphs if present
|
||||
@@ -346,8 +350,8 @@ public class WndBlacksmith extends Window {
|
||||
} else if (item1.getClass() != item2.getClass()) {
|
||||
btnReforge.enable(false);
|
||||
|
||||
//and not the literal same item (unless quantity is >1)
|
||||
} else if (item1 == item2 && item1.quantity() == 1) {
|
||||
//and not the literal same item
|
||||
} else if (item1 == item2) {
|
||||
btnReforge.enable(false);
|
||||
|
||||
} else {
|
||||
@@ -375,7 +379,7 @@ public class WndBlacksmith extends Window {
|
||||
public boolean itemSelectable(Item item) {
|
||||
return item.isUpgradable()
|
||||
&& item.isIdentified() && !item.cursed
|
||||
&& ((item instanceof MeleeWeapon && !((Weapon) item).enchantHardened)
|
||||
&& ((item instanceof Weapon && !((Weapon) item).enchantHardened)
|
||||
|| (item instanceof Armor && !((Armor) item).glyphHardened));
|
||||
}
|
||||
|
||||
|
||||
+18
-2
@@ -245,12 +245,24 @@ public class WndUpgrade extends Window {
|
||||
//durability
|
||||
if (toUpgrade instanceof MissileWeapon){
|
||||
//missile weapons are always IDed currently, so we always use true level
|
||||
int uses1 = (int)Math.ceil(100f/((MissileWeapon) toUpgrade).durabilityPerUse());
|
||||
int uses2 = (int)Math.ceil(300f/((MissileWeapon) toUpgrade).durabilityPerUse());
|
||||
int uses1, uses2;
|
||||
if (toUpgrade.levelKnown) {
|
||||
uses1 = (int) Math.ceil(100f / ((MissileWeapon) toUpgrade).durabilityPerUse(toUpgrade.level()));
|
||||
uses2 = (int) Math.ceil(100f / ((MissileWeapon) toUpgrade).durabilityPerUse(toUpgrade.level()+1));
|
||||
} else {
|
||||
uses1 = (int) Math.ceil(100f / ((MissileWeapon) toUpgrade).durabilityPerUse(0));
|
||||
uses2 = (int) Math.ceil(100f / ((MissileWeapon) toUpgrade).durabilityPerUse(1));
|
||||
}
|
||||
bottom = fillFields(Messages.get(this, "durability"),
|
||||
uses1 >= 100 ? "∞" : Integer.toString(uses1),
|
||||
uses2 >= 100 ? "∞" : Integer.toString(uses2),
|
||||
bottom);
|
||||
|
||||
bottom = fillFields(Messages.get(this, "quantity"),
|
||||
Integer.toString(toUpgrade.quantity()),
|
||||
Integer.toString(((MissileWeapon) toUpgrade).defaultQuantity()),
|
||||
bottom);
|
||||
|
||||
}
|
||||
|
||||
//we use a separate reference for wand properties so that mage's staff can include them
|
||||
@@ -393,6 +405,10 @@ public class WndUpgrade extends Window {
|
||||
bottom = addMessage(Messages.get(this, "resin"), CharSprite.WARNING, bottom);
|
||||
}
|
||||
|
||||
if (toUpgrade instanceof MissileWeapon && toUpgrade.quantity() < ((MissileWeapon) toUpgrade).fullSetQuantity){
|
||||
bottom = addMessage("Weapons from this set that aren't in your inventory will crumble to dust.", CharSprite.WARNING, bottom);
|
||||
}
|
||||
|
||||
// *** Buttons for confirming/cancelling ***
|
||||
|
||||
btnUpgrade = new RedButton(Messages.get(this, "upgrade")){
|
||||
|
||||
Reference in New Issue
Block a user