diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index 6b4d9e030..175c42989 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -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 diff --git a/core/src/main/assets/sprites/items.png b/core/src/main/assets/sprites/items.png index 0c46d64da..c34b39568 100644 Binary files a/core/src/main/assets/sprites/items.png and b/core/src/main/assets/sprites/items.png differ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java index 7572d7d43..b43f9c560 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java @@ -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); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Bleeding.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Bleeding.java index 11b0c72c7..751224e1e 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Bleeding.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Bleeding.java @@ -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 { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogFist.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogFist.java index 33e43b92c..30e48878a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogFist.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogFist.java @@ -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; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Generator.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Generator.java index 27aa5fa7b..c19a71f28 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Generator.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Generator.java @@ -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[]{ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/MeleeWeapon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/MeleeWeapon.java index 834f01b57..ba18faf07 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/MeleeWeapon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/MeleeWeapon.java @@ -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)); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Sickle.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Sickle.java new file mode 100644 index 000000000..7e99762d6 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Sickle.java @@ -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 + */ + +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; + }; + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/WarScythe.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/WarScythe.java new file mode 100644 index 000000000..fd363c147 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/WarScythe.java @@ -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 + */ + +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); + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ItemSpriteSheet.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ItemSpriteSheet.java index 44b22b2b0..e6d2a6687 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ItemSpriteSheet.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ItemSpriteSheet.java @@ -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