v2.1.0: implemented the Sickle and War Scythe weapons at T2 and T5

This commit is contained in:
Evan Debenham
2023-04-20 17:10:04 -04:00
parent 613182fd8d
commit 7948faf090
10 changed files with 222 additions and 6 deletions

View File

@@ -1685,6 +1685,12 @@ items.weapon.melee.scimitar.desc=A thick curved blade. Its shape allows for fast
items.weapon.melee.scimitar$sworddance.name=sword dance
items.weapon.melee.scimitar$sworddance.desc=The Duelist is making quick momentum based strikes in a sort of dance. While this stance is active, she attacks 60%% faster (enough to attack exactly twice a turn with a scimitar), but suffers -20%% accuracy.\n\nTurns remaining: %s.
items.weapon.melee.sickle.name=sickle
items.weapon.melee.sickle.stats_desc=This is a rather inaccurate weapon.
items.weapon.melee.sickle.ability_name=harvest
items.weapon.melee.sickle.ability_desc=The Duelist can _harvest_ an enemy with a sickle. This devastating attack inflicts bleed equal to 100% of damage and is guaranteed to hit, but costs 2 charges.
items.weapon.melee.sickle.desc=A handheld farming tool that can double as a strong but unwieldy weapon.
items.weapon.melee.spear.name=spear
items.weapon.melee.spear.stats_desc=This is a rather slow weapon.\nThis weapon has extra reach.
items.weapon.melee.spear.ability_name=spike
@@ -1704,6 +1710,12 @@ items.weapon.melee.warhammer.ability_name=heavy blow
items.weapon.melee.warhammer.ability_desc=The Duelist can perform a _heavy blow_ with a war hammer. This strong but predictable attack has -75% accuracy, but deals +50% damage and applies vulnerable and weaken for 5 turns if it hits. Heavy blow can surprise attack.
items.weapon.melee.warhammer.desc=Few creatures can withstand the crushing blow of this towering mass of lead and steel, but it takes great strength to use effectively.
items.weapon.melee.warscythe.name=war scythe
items.weapon.melee.warscythe.stats_desc=This is a rather inaccurate weapon.
items.weapon.melee.warscythe.ability_name=harvest
items.weapon.melee.warscythe.ability_desc=The Duelist can _harvest_ an enemy with a war scythe. This devastating attack inflicts bleed equal to 80% of damage and is guaranteed to hit, but costs 2 charges.
items.weapon.melee.warscythe.desc=This large and unwieldy tool has been reinforced to make it better at cutting foes than crops.
items.weapon.melee.whip.name=whip
items.weapon.melee.whip.stats_desc=This weapon has tremendous reach.
items.weapon.melee.whip.ability_name=lash

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -99,6 +99,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Blazin
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Grim;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Kinetic;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Shocking;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Sickle;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.darts.ShockingDart;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
@@ -693,6 +694,19 @@ public abstract class Char extends Actor {
dmg -= Random.NormalIntRange(0, buff(ArcaneArmor.class).level());
if (dmg < 0) dmg = 0;
}
if (buff(Sickle.HarvestBleedTracker.class) != null){
Bleeding b = buff(Bleeding.class);
if (b == null){
b = new Bleeding();
}
b.announced = false;
b.set(dmg*buff(Sickle.HarvestBleedTracker.class).bleedFactor, Sickle.HarvestBleedTracker.class);
b.attachTo(this);
sprite.showStatus(CharSprite.WARNING, Messages.titleCase(b.name()) + " " + (int)b.level());
buff(Sickle.HarvestBleedTracker.class).detach();
return;
}
if (buff( Paralysis.class ) != null) {
buff( Paralysis.class ).processDamage(dmg);

View File

@@ -25,6 +25,8 @@ import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.effects.Splash;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.curses.Sacrificial;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MeleeWeapon;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Sickle;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.Chasm;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
@@ -112,6 +114,10 @@ public class Bleeding extends Buff {
Dungeon.fail( getClass() );
GLog.n( Messages.get(this, "ondeath") );
}
if (source == Sickle.HarvestBleedTracker.class && !target.isAlive()){
MeleeWeapon.onAbilityKill(Dungeon.hero);
}
spend( TICK );
} else {

View File

@@ -44,6 +44,7 @@ import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.LeafParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.glyphs.Viscosity;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTeleportation;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Sickle;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
@@ -372,7 +373,9 @@ public abstract class YogFist extends Mob {
@Override
public void damage(int dmg, Object src) {
if (!isInvulnerable(src.getClass()) && !(src instanceof Bleeding)){
if (!isInvulnerable(src.getClass())
&& !(src instanceof Bleeding)
&& buff(Sickle.HarvestBleedTracker.class) == null){
dmg = Math.round( dmg * resist( src.getClass() ));
if (dmg < 0){
return;

View File

@@ -141,9 +141,11 @@ import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.RunicBlade;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Sai;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Scimitar;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Shortsword;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Sickle;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Spear;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Sword;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.WarHammer;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.WarScythe;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Whip;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.WornShortsword;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.Bolas;
@@ -358,9 +360,10 @@ public class Generator {
HandAxe.class,
Spear.class,
Quarterstaff.class,
Dirk.class
Dirk.class,
Sickle.class
};
WEP_T2.probs = new float[]{ 6, 5, 5, 4, 4 };
WEP_T2.probs = new float[]{ 6, 5, 5, 4, 4, 4 };
WEP_T3.classes = new Class<?>[]{
Sword.class,
@@ -389,9 +392,10 @@ public class Generator {
Glaive.class,
Greataxe.class,
Greatshield.class,
Gauntlet.class
Gauntlet.class,
WarScythe.class
};
WEP_T5.probs = new float[]{ 6, 5, 5, 4, 4, 4 };
WEP_T5.probs = new float[]{ 6, 5, 5, 4, 4, 4, 4 };
//see Generator.randomArmor
ARMOR.classes = new Class<?>[]{

View File

@@ -253,7 +253,7 @@ public class MeleeWeapon extends Weapon {
}
}
public void onAbilityKill( Hero hero ){
public static void onAbilityKill( Hero hero ){
if (hero.hasTalent(Talent.LETHAL_HASTE)){
//effectively 2/3 turns of haste
Buff.prolong(hero, Haste.class, 1.67f+hero.pointsInTalent(Talent.LETHAL_HASTE));

View File

@@ -0,0 +1,117 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2023 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.melee;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.ui.AttackIndicator;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Callback;
public class Sickle extends MeleeWeapon {
{
image = ItemSpriteSheet.SICKLE;
hitSound = Assets.Sounds.HIT_SLASH;
hitSoundPitch = 1f;
tier = 2;
ACC = 0.68f; //32% penalty to accuracy
}
@Override
public int max(int lvl) {
return Math.round(6.67f*(tier+1)) + //20 base, up from 15
lvl*(tier+1); //scaling unchanged
}
@Override
public String targetingPrompt() {
return Messages.get(this, "prompt");
}
@Override
protected void duelistAbility(Hero hero, Integer target) {
harvestAbility(hero, target, 1f, this);
}
public static void harvestAbility(Hero hero, Integer target, float bleedFactor, MeleeWeapon wep){
if (target == null) {
return;
}
Char enemy = Actor.findChar(target);
if (enemy == null || enemy == hero || hero.isCharmedBy(enemy) || !Dungeon.level.heroFOV[target]) {
GLog.w(Messages.get(wep, "ability_no_target"));
return;
}
hero.belongings.abilityWeapon = wep;
if (!hero.canAttack(enemy)){
GLog.w(Messages.get(wep, "ability_bad_position"));
hero.belongings.abilityWeapon = null;
return;
}
hero.belongings.abilityWeapon = null;
hero.sprite.attack(enemy.pos, new Callback() {
@Override
public void call() {
wep.beforeAbilityUsed(hero);
AttackIndicator.target(enemy);
Buff.affect(enemy, HarvestBleedTracker.class, 0).bleedFactor = bleedFactor;
if (hero.attack(enemy, 1, 0, Char.INFINITE_ACCURACY)){
Sample.INSTANCE.play(Assets.Sounds.HIT_STRONG);
}
Invisibility.dispel();
hero.spendAndNext(hero.attackDelay());
if (!enemy.isAlive()){
wep.onAbilityKill(hero);
Buff.prolong(hero, Sword.CleaveTracker.class, 5f);
} else {
if (hero.buff(Sword.CleaveTracker.class) != null) {
hero.buff(Sword.CleaveTracker.class).detach();
}
}
wep.afterAbilityUsed(hero);
}
});
}
public static class HarvestBleedTracker extends FlavourBuff{
public float bleedFactor;
};
}

View File

@@ -0,0 +1,56 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2023 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.melee;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
public class WarScythe extends MeleeWeapon {
{
image = ItemSpriteSheet.WAR_SCYTHE;
hitSound = Assets.Sounds.HIT_SLASH;
hitSoundPitch = 0.9f;
tier = 5;
ACC = 0.8f; //20% penalty to accuracy
}
@Override
public int max(int lvl) {
return Math.round(6.67f*(tier+1)) + //40 base, up from 30
lvl*(tier+1); //scaling unchanged
}
@Override
public String targetingPrompt() {
return Messages.get(this, "prompt");
}
@Override
protected void duelistAbility(Hero hero, Integer target) {
Sickle.harvestAbility(hero, target, 0.8f, this);
}
}

View File

@@ -214,12 +214,14 @@ public class ItemSpriteSheet {
public static final int SPEAR = WEP_TIER2+2;
public static final int QUARTERSTAFF = WEP_TIER2+3;
public static final int DIRK = WEP_TIER2+4;
public static final int SICKLE = WEP_TIER2+5;
static{
assignItemRect(SHORTSWORD, 13, 13);
assignItemRect(HAND_AXE, 12, 14);
assignItemRect(SPEAR, 16, 16);
assignItemRect(QUARTERSTAFF, 16, 16);
assignItemRect(DIRK, 13, 14);
assignItemRect(SICKLE, 15, 15);
}
private static final int WEP_TIER3 = xy(1, 8); //8 slots
@@ -263,6 +265,7 @@ public class ItemSpriteSheet {
public static final int GREATAXE = WEP_TIER5+3;
public static final int GREATSHIELD = WEP_TIER5+4;
public static final int GAUNTLETS = WEP_TIER5+5;
public static final int WAR_SCYTHE = WEP_TIER5+6;
static{
assignItemRect(GREATSWORD, 16, 16);
assignItemRect(WAR_HAMMER, 16, 16);
@@ -270,6 +273,7 @@ public class ItemSpriteSheet {
assignItemRect(GREATAXE, 12, 16);
assignItemRect(GREATSHIELD, 12, 16);
assignItemRect(GAUNTLETS, 13, 15);
assignItemRect(WAR_SCYTHE, 14, 15);
}
//8 free slots