v0.7.1: reworked huntress's signature weapon

This commit is contained in:
Evan Debenham 2018-11-22 01:29:53 -05:00
parent 621659c9c5
commit bd3b87c9af
15 changed files with 270 additions and 29 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -92,12 +92,15 @@ public class QuickSlot {
}
public void convertToPlaceholder(Item item){
Item placeholder = item.virtual();
if (placeholder != null && contains(item))
for (int i = 0; i < SIZE; i++)
if (getItem(i) == item)
setSlot( i , placeholder );
if (contains(item)) {
Item placeholder = item.virtual();
if (placeholder == null) return;
for (int i = 0; i < SIZE; i++) {
if (getItem(i) == item) setSlot(i, placeholder);
}
}
}
public Item randomNonePlaceholder(){

View File

@ -1248,6 +1248,8 @@ public class Hero extends Char {
sprite.showStatus( CharSprite.POSITIVE, Messages.get(Hero.class, "level_up") );
Sample.INSTANCE.play( Assets.SND_LEVELUP );
Item.updateQuickslot();
Badges.validateLevelReached();
}
}

View File

@ -39,11 +39,11 @@ import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfMindVision
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfMagicMapping;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfMagicMissile;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.SpiritBow;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Dagger;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Knuckles;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MagesStaff;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.WornShortsword;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.Boomerang;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.ThrowingKnife;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.ThrowingStone;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
@ -168,10 +168,10 @@ public enum HeroClass {
private static void initHuntress( Hero hero ) {
(hero.belongings.weapon = new Knuckles()).identify();
Boomerang boomerang = new Boomerang();
boomerang.identify().collect();
SpiritBow bow = new SpiritBow();
bow.identify().collect();
Dungeon.quickslot.setSlot(0, boomerang);
Dungeon.quickslot.setSlot(0, bow);
new VelvetPouch().collect();
Dungeon.LimitedDrops.VELVET_POUCH.drop();

View File

@ -447,7 +447,7 @@ public class Item implements Bundlable {
return quantity != 1 ? Integer.toString( quantity ) : null;
}
public void updateQuickslot() {
public static void updateQuickslot() {
QuickSlotButton.refresh();
}

View File

@ -491,7 +491,7 @@ public class Armor extends EquipableItem {
public Armor inscribe( Glyph glyph ) {
this.glyph = glyph;
updateQuickslot();
return this;
}

View File

@ -0,0 +1,221 @@
/*
* 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.items.weapon;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroClass;
import com.shatteredpixel.shatteredpixeldungeon.effects.Splash;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.CellSelector;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.watabou.utils.Random;
import java.util.ArrayList;
public class SpiritBow extends Weapon {
public static final String AC_SHOOT = "SHOOT";
{
image = ItemSpriteSheet.SPIRIT_BOW;
defaultAction = AC_SHOOT;
usesTargeting = true;
unique = true;
bones = false;
}
@Override
public ArrayList<String> actions(Hero hero) {
ArrayList<String> actions = super.actions(hero);
actions.remove(AC_EQUIP);
actions.add(AC_SHOOT);
return actions;
}
@Override
public void execute(Hero hero, String action) {
super.execute(hero, action);
if (action.equals(AC_SHOOT)) {
GameScene.selectCell( shooter );
}
}
@Override
public String info() {
String info = desc();
info += "\n\n" + Messages.get( SpiritBow.class, "stats",
Math.round(augment.damageFactor(min())),
Math.round(augment.damageFactor(max())),
STRReq());
if (STRReq() > Dungeon.hero.STR()) {
info += " " + Messages.get(Weapon.class, "too_heavy");
} else if (Dungeon.hero.heroClass == HeroClass.HUNTRESS && Dungeon.hero.STR() > STRReq()){
info += " " + Messages.get(Weapon.class, "excess_str", Dungeon.hero.STR() - STRReq());
}
switch (augment) {
case SPEED:
info += "\n\n" + Messages.get(Weapon.class, "faster");
break;
case DAMAGE:
info += "\n\n" + Messages.get(Weapon.class, "stronger");
break;
case NONE:
}
if (enchantment != null && (cursedKnown || !enchantment.curse())){
info += "\n\n" + Messages.get(Weapon.class, "enchanted", enchantment.name());
info += " " + Messages.get(enchantment, "desc");
}
if (cursed && isEquipped( Dungeon.hero )) {
info += "\n\n" + Messages.get(Weapon.class, "cursed_worn");
} else 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");
return info;
}
@Override
public int STRReq(int lvl) {
lvl = Math.max(0, lvl);
//strength req decreases at +1,+3,+6,+10,etc.
return 10 - (int)(Math.sqrt(8 * lvl + 1) - 1)/2;
}
@Override
public int min(int lvl) {
return 1 + Dungeon.hero.lvl/6;
}
@Override
public int max(int lvl) {
return 6 + Dungeon.hero.lvl/3;
}
@Override
public int damageRoll(Char owner) {
int damage = augment.damageFactor(super.damageRoll( owner ));
if (owner instanceof Hero) {
int exStr = ((Hero)owner).STR() - STRReq();
if (exStr > 0) {
damage += Random.IntRange( 0, exStr );
}
}
return damage;
}
@Override
public int level() {
return Dungeon.hero.lvl/5;
}
@Override
public int visiblyUpgraded() {
return level();
}
//for fetching upgrades from a boomerang from pre-0.7.0
//TODO implement on this
public int spentUpgrades() {
return super.level();
}
@Override
public boolean isUpgradable() {
return false;
}
public class SpiritArrow extends MissileWeapon {
{
image = ItemSpriteSheet.SPIRIT_ARROW;
}
@Override
public int damageRoll(Char owner) {
return SpiritBow.this.damageRoll(owner);
}
@Override
public boolean hasEnchant(Class<? extends Enchantment> type, Char owner) {
return SpiritBow.this.hasEnchant(type, owner);
}
@Override
public int proc(Char attacker, Char defender, int damage) {
return SpiritBow.this.proc(attacker, defender, damage);
}
@Override
public float castDelay(Char user, int dst) {
return SpiritBow.this.speedFactor(user);
}
@Override
public int STRReq(int lvl) {
return SpiritBow.this.STRReq(lvl);
}
@Override
protected void onThrow( int cell ) {
Char enemy = Actor.findChar( cell );
if (enemy == null || enemy == curUser) {
parent = null;
Splash.at( cell, 0xCC99FFFF, 1 );
} else {
if (!curUser.shoot( enemy, this )) {
Splash.at(cell, 0xCC99FFFF, 1);
}
}
}
}
private CellSelector.Listener shooter = new CellSelector.Listener() {
@Override
public void onSelect( Integer target ) {
if (target != null) {
new SpiritArrow().cast(curUser, target);
}
}
@Override
public String prompt() {
return Messages.get(SpiritBow.class, "prompt");
}
};
}

View File

@ -239,6 +239,7 @@ abstract public class Weapon extends KindOfWeapon {
public Weapon enchant( Enchantment ench ) {
enchantment = ench;
updateQuickslot();
return this;
}

View File

@ -24,8 +24,6 @@ package com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.darts;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MagicImmune;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Projecting;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Crossbow;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
@ -76,12 +74,11 @@ public class Dart extends MissileWeapon {
}
@Override
public int throwPos(Hero user, int dst) {
if (bow != null && bow.hasEnchant(Projecting.class, user)
&& !Dungeon.level.solid[dst] && Dungeon.level.distance(user.pos, dst) <= 4){
return dst;
public boolean hasEnchant(Class<? extends Enchantment> type, Char owner) {
if (bow != null && bow.hasEnchant(type, owner)){
return true;
} else {
return super.throwPos(user, dst);
return super.hasEnchant(type, owner);
}
}

View File

@ -77,14 +77,17 @@ public class ItemSpriteSheet {
public static final int DEWDROP = UNCOLLECTIBLE+1;
public static final int PETAL = UNCOLLECTIBLE+2;
public static final int SANDBAG = UNCOLLECTIBLE+3;
public static final int SPIRIT_ARROW = UNCOLLECTIBLE+4;
public static final int GUIDE_PAGE = UNCOLLECTIBLE+5;
public static final int ALCH_PAGE = UNCOLLECTIBLE+6;
public static final int GUIDE_PAGE = UNCOLLECTIBLE+6;
public static final int ALCH_PAGE = UNCOLLECTIBLE+7;
static{
assignItemRect(GOLD, 15, 13);
assignItemRect(DEWDROP, 10, 10);
assignItemRect(PETAL, 8, 8);
assignItemRect(SANDBAG, 10, 10);
assignItemRect(SPIRIT_ARROW,11, 11);
assignItemRect(GUIDE_PAGE, 10, 11);
assignItemRect(ALCH_PAGE, 10, 11);
}
@ -256,7 +259,7 @@ public class ItemSpriteSheet {
//8 free slots
private static final int MISSILE_WEP = xy(1, 10); //16 slots. 3 per tier + boomerang
public static final int BOOMERANG = MISSILE_WEP+0;
public static final int SPIRIT_BOW = MISSILE_WEP+0;
public static final int DART = MISSILE_WEP+1;
public static final int THROWING_KNIFE = MISSILE_WEP+2;
@ -267,6 +270,7 @@ public class ItemSpriteSheet {
public static final int THROWING_SPEAR = MISSILE_WEP+7;
public static final int BOLAS = MISSILE_WEP+8;
public static final int BOOMERANG = MISSILE_WEP+9;
public static final int JAVELIN = MISSILE_WEP+10;
public static final int TOMAHAWK = MISSILE_WEP+11;
@ -275,7 +279,7 @@ public class ItemSpriteSheet {
public static final int THROWING_HAMMER = MISSILE_WEP+14;
static{
assignItemRect(BOOMERANG, 14, 14);
assignItemRect(SPIRIT_BOW, 16, 16);
assignItemRect(DART, 15, 15);
assignItemRect(THROWING_KNIFE, 12, 13);
@ -286,6 +290,7 @@ public class ItemSpriteSheet {
assignItemRect(THROWING_SPEAR, 13, 13);
assignItemRect(BOLAS, 15, 14);
assignItemRect(BOOMERANG, 14, 14);
assignItemRect(JAVELIN, 16, 16);
assignItemRect(TOMAHAWK, 13, 13);

View File

@ -23,6 +23,7 @@ package com.shatteredpixel.shatteredpixeldungeon.sprites;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.SpiritBow;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Crossbow;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.Bolas;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.Boomerang;
@ -80,6 +81,7 @@ public class MissileSprite extends ItemSprite implements Tweener.Listener {
private static final HashMap<Class<?extends Item>, Integer> ANGULAR_SPEEDS = new HashMap<>();
static {
ANGULAR_SPEEDS.put(SpiritBow.SpiritArrow.class, 0);
ANGULAR_SPEEDS.put(Dart.class, 0);
ANGULAR_SPEEDS.put(ThrowingKnife.class, 0);
ANGULAR_SPEEDS.put(FishingSpear.class, 0);
@ -132,6 +134,9 @@ public class MissileSprite extends ItemSprite implements Tweener.Listener {
if (item instanceof Dart && Dungeon.hero.belongings.weapon instanceof Crossbow){
speed *= 3f;
}
if (item instanceof SpiritBow.SpiritArrow){
speed *= 1.5f;
}
PosTweener tweener = new PosTweener( this, to, d.length() / speed );
tweener.listener = this;
parent.add( tweener );

View File

@ -44,6 +44,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTransmutat
import com.shatteredpixel.shatteredpixeldungeon.items.spells.Recycle;
import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfDetectCurse;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.SpiritBow;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MeleeWeapon;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.Boomerang;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
@ -393,7 +394,7 @@ public class WndBag extends WndTabbed {
mode == Mode.QUICKSLOT && (item.defaultAction != null) ||
mode == Mode.WEAPON && (item instanceof MeleeWeapon || item instanceof Boomerang) ||
mode == Mode.ARMOR && (item instanceof Armor) ||
mode == Mode.ENCHANTABLE && (item instanceof MeleeWeapon || item instanceof Boomerang || item instanceof Armor) ||
mode == Mode.ENCHANTABLE && (item instanceof MeleeWeapon || item instanceof SpiritBow || item instanceof Armor) ||
mode == Mode.WAND && (item instanceof Wand) ||
mode == Mode.SEED && (item instanceof Seed) ||
mode == Mode.FOOD && (item instanceof Food) ||

View File

@ -333,9 +333,9 @@ public class WndStartGame extends Window {
heroMisc.icon(Icons.get(Icons.DEPTH));
break;
case HUNTRESS:
heroItem.icon(new ItemSprite(ItemSpriteSheet.BOOMERANG, null));
heroItem.icon(new ItemSprite(ItemSpriteSheet.SPIRIT_BOW, null));
heroLoadout.icon(new ItemSprite(ItemSpriteSheet.KNUCKLEDUSTER, null));
heroMisc.icon(new ItemSprite(ItemSpriteSheet.DART, null));
heroMisc.icon(new Image(Assets.TILES_SEWERS, 112, 96, 16, 16 ));
break;
}

View File

@ -312,9 +312,9 @@ actors.hero.heroclass.rogue_desc_misc=The Rogue detects secrets and traps from a
actors.hero.heroclass.rogue_desc_subclasses=A subclass can be chosen after defeating the second boss. The Rogue has two subclasses:
actors.hero.heroclass.huntress=huntress
actors.hero.heroclass.huntress_desc_item=The Huntress starts with a _unique boomerang,_ which can be thrown an infinite number of times.\n\nThe boomerang is upgradeable and can be imbued and enchanted, just like a melee weapon.
actors.hero.heroclass.huntress_desc_loadout=The Huntress starts with _knuckledusters,_ which attack much faster than other starter weapons.\n\nThe Huntress starts with her boomerang as a ranged option.\n\nThe Huntress starts with a _velvet pouch,_ which can store small items like seeds and runestones.
actors.hero.heroclass.huntress_desc_misc=The Huntress gains bonus damage from excess strength on thrown weapons.\n\nThe Huntress can use thrown weapons for longer before they break.\n\nThe Huntress senses nearby enemies even if they are hidden behind obstacles.\n\nThe Huntress automatically identifies potions of mind vision.
actors.hero.heroclass.huntress_desc_item=The Huntress starts with a _unique spirit bow,_ which can fire an infinite number of conjured arrows.\n\nThe bow steadily grows stronger as the huntress levels up, and can be augmented and enchanted.
actors.hero.heroclass.huntress_desc_loadout=The Huntress starts with _knuckledusters,_ which attack much faster than other starter weapons.\n\nThe Huntress starts with her bow as a ranged option.\n\nThe Huntress starts with a _velvet pouch,_ which can store small items like seeds and runestones.
actors.hero.heroclass.huntress_desc_misc=The Huntress can travel through tall grass without trampling it.\n\nThe Huntress gains bonus damage from excess strength on thrown weapons.\n\nThe Huntress can use thrown weapons for longer before they break.\n\nThe Huntress senses nearby enemies even if they are hidden behind obstacles.\n\nThe Huntress automatically identifies potions of mind vision.
actors.hero.heroclass.huntress_desc_subclasses=A subclass can be chosen after defeating the second boss. The Huntress has two subclasses:
actors.hero.herosubclass.gladiator=gladiator

View File

@ -1410,6 +1410,12 @@ items.weapon.missiles.trident.name=trident
items.weapon.missiles.trident.desc=Massive throwing spears with three deadly prongs on the end. They are powerful, but quite heavy.
items.weapon.spiritbow.name=Spirit Bow
items.weapon.spiritbow.ac_shoot=SHOOT
items.weapon.spiritbow.prompt=Choose a target
items.weapon.spiritbow.stats=The bow cannot be directly upgraded, but will instead steadily grow stronger as you level up. At your current level, arrows shot from the bow will deal _%1$d-%2$d damage_ and require _%3$d strength_ to use properly.
items.weapon.spiritbow.desc=A bow made of out ancient magical wood. The bow's string and etchings glow with a pale blue light. When the string is pulled this bow will conjure a magical arrow which can be fired at an enemy.
items.weapon.weapon.identify=You are now familiar enough with your weapon to identify it.
items.weapon.weapon.too_heavy=Because of your inadequate strength this weapon will hinder your attack speed, accuracy, and ability to surprise attack.
items.weapon.weapon.excess_str=Because of your excess strength, you will deal up to _%d bonus damage_ with this weapon.