From 50a23d5c1299ec32b7a4c97f3db73238ba2a47ed Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Fri, 3 Feb 2023 13:41:45 -0500 Subject: [PATCH] v2.0.0: implemented the elemental strike armor ability and talents --- .../src/main/assets/interfaces/hero_icons.png | Bin 1413 -> 1452 bytes .../main/assets/interfaces/talent_icons.png | Bin 6102 -> 6277 bytes .../assets/messages/actors/actors.properties | 11 +- .../assets/messages/items/items.properties | 24 +- .../actors/hero/HeroClass.java | 3 +- .../actors/hero/Talent.java | 2 +- .../abilities/duelist/ElementalStrike.java | 532 ++++++++++++++++++ .../items/armor/glyphs/AntiMagic.java | 4 + .../items/weapon/Weapon.java | 7 + .../shatteredpixeldungeon/ui/HeroIcon.java | 2 +- 10 files changed, 578 insertions(+), 7 deletions(-) create mode 100644 core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/duelist/ElementalStrike.java diff --git a/core/src/main/assets/interfaces/hero_icons.png b/core/src/main/assets/interfaces/hero_icons.png index d33a0a7ce6adb19f9a04081804ee3791bd22aed1..7eedc2f63d1d93a6e00eeb2c0d4207a42f93a34a 100644 GIT binary patch delta 1280 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 O^T=U`Lxl(3Z~Q-iOBW{q diff --git a/core/src/main/assets/messages/actors/actors.properties b/core/src/main/assets/messages/actors/actors.properties index 557e3c11d..320af8d62 100644 --- a/core/src/main/assets/messages/actors/actors.properties +++ b/core/src/main/assets/messages/actors/actors.properties @@ -467,6 +467,10 @@ actors.hero.abilities.duelist.challenge.short_desc=The Duelist _challenges_ a ne actors.hero.abilities.duelist.challenge.desc=The Duelist challenges a nearby enemy to a duel. That enemy is compelled to fight her while all other enemies are temporarily frozen in time.\n\nThe target must be reachable and within 5 tiles of the Duelist. The duel lasts until 10 turns pass, the enemy dies, or the Duelist is more than 5 tiles away from the enemy.\n\nFrozen enemies are invulnerable. The Duelist's allies are not frozen by this ability, but if a boss is targeted its minions will not be frozen either. actors.hero.abilities.duelist.challenge$duelparticipant.name=duel participant actors.hero.abilities.duelist.challenge$duelparticipant.desc=This character is participating in a duel. They, and any of their allies or minions, are allowed to act normally while others are frozen.\n\nThe duel will last for a set amount of time, until one participant dies, or until the participants move more than 5 tiles away from eachother.\n\nTurns remaining: %d. +actors.hero.abilities.duelist.elementalstrike.name=elemental strike +actors.hero.abilities.duelist.elementalstrike.short_desc=The Duelist performs an _elemental strike_, spreading an effect in a conical AOE based on her weapon enchantment. +actors.hero.abilities.duelist.elementalstrike.desc=The Duelist strikes an enemy or location, performing a regular attack that's guaranteed to hit and spreading a magical effect that travels up to 3 tiles in a 65 degree cone. This magical effect varies based on the enchantment on the Duelist's primary weapon. +actors.hero.abilities.duelist.elementalstrike.generic_desc=An elemental strike with no enchantment will release a small burst of magic, dealing 5-10 damage to all enemies in range. actors.hero.abilities.ratmogrify.name=ratmogrify actors.hero.abilities.ratmogrify.cant_transform=You can't ratmogrify that! @@ -870,7 +874,12 @@ actors.hero.talent.invigorating_victory.desc=_+1:_ If the Duelist defeats her ta actors.hero.talent.elimination_match.title=elimination match actors.hero.talent.elimination_match.desc=_+1:_ If the Duelist challenges again within 3 turns of a duel ending, that challenge has a _20% reduced_ charge cost.\n\n_+2:_ If the Duelist challenges again within 3 turns of a duel ending, that challenge has a _36% reduced_ charge cost.\n\n_+3:_ If the Duelist challenges again within 3 turns of a duel ending, that challenge has a _50% reduced_ charge cost.\n\n_+4:_ If the Duelist challenges again within 3 turns of a duel ending, that challenge has a _60% reduced_ charge cost. -#second armor ability +actors.hero.talent.elemental_reach.title=elemental reach +actors.hero.talent.elemental_reach.desc=_+1:_ Elemental strike's range is increased to _4 tiles_ from 3, and its width is increased to _70 degrees_ from 65.\n\n_+2:_ Elemental strike's range is increased to _5 tiles_ from 3, and its width is increased to _75 degrees_ from 65.\n\n_+3:_ Elemental strike's range is increased to _6 tiles_ from 3, and its width is increased to _80 degrees_ from 65.\n\n_+4:_ Elemental strike's range is increased to _7 tiles_ from 3, and its width is increased to _85 degrees_ from 65. +actors.hero.talent.striking_force.title=striking force +actors.hero.talent.striking_force.desc=_+1:_ The power of elemental strike's effect is increased by _25%_.\n\n_+2:_ The power of elemental strike's effect is increased by _50%_.\n\n_+3:_ The power of elemental strike's effect is increased by _75%_.\n\n_+4:_ The power of elemental strike's effect is increased by _100%_. +actors.hero.talent.directed_power.title=directed power +actors.hero.talent.directed_power.desc=_+1:_ The direct attack from elemental strike gains _+25% enchantment power_ for each enemy in range of elemental strike's effect, including the attack target.\n\n_+2:_ The direct attack from elemental strike gains _+50% enchantment power_ for each enemy in range of elemental strike's effect, including the attack target.\n\n_+3:_ The direct attack from elemental strike gains _+75% enchantment power_ for each enemy in range of elemental strike's effect, including the attack target.\n\n_+4:_ The direct attack from elemental strike gains _+100% enchantment power_ for each enemy in range of elemental strike's effect, including the attack target. #third armor ability diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index 59581da68..4849ff648 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -1406,12 +1406,15 @@ items.weapon.curses.annoying.msg_11=I didn't want to be a weapon, I wanted to be items.weapon.curses.annoying.msg_12=Remember, overconfidence is a slow and insidious killer. items.weapon.curses.annoying.msg_13=ALL YOUR BASE ARE BELONG TO US! items.weapon.curses.annoying.desc=Annoying weapons are capable of speech, but they're a bit too energetic. They will often draw attention to you without meaning to. +items.weapon.curses.annoying.elestrike_desc=An elemental strike with an annoying curse has a 20% chance to amok each enemy in range for 5 turns. items.weapon.curses.dazzling.name=dazzling %s items.weapon.curses.dazzling.desc=Dazzling weapons will sometimes flash with dazzling light, blinding everything that can see them. +items.weapon.curses.dazzling.elestrike_desc=An elemental strike with a dazzling curse has a 50% chance to blind each enemy in range for 5 turns. items.weapon.curses.displacing.name=displacing %s items.weapon.curses.displacing.desc=Displacing weapons are infused with chaotic teleportation magic, possessing the ability to warp enemies around the floor randomly. +items.weapon.curses.displacing.elestrike_desc=An elemental strike with a displacing curse has a 50% chance to teleport each enemy in range. items.weapon.curses.explosive.name=explosive %s items.weapon.curses.explosive.warm=Warm... @@ -1420,67 +1423,82 @@ items.weapon.curses.explosive.desc=Explosive weapons steadily build up power and items.weapon.curses.explosive.desc_cool=Your weapon is currently cool to the touch. items.weapon.curses.explosive.desc_warm=Your weapon is building energy and getting warm... items.weapon.curses.explosive.desc_hot=Your weapon is hot! It's about to explode! +items.weapon.curses.explosive.elestrike_desc=An elemental strike with an explosive curse has a 50% chance to cause an explosion to appear on a random enemy in range. items.weapon.curses.friendly.name=friendly %s items.weapon.curses.friendly.desc=Friendly weapons are best suited for pacifists, occasionally triggering magic that makes it impossible to fight. +items.weapon.curses.friendly.elestrike_desc=An elemental strike with a friendly curse has a 50% chance to charm each enemy in range for 5 turns. items.weapon.curses.polarized.name=polarized %s items.weapon.curses.polarized.desc=A polarized weapon is affected by magic that causes its attack to either deal 50% more damage, or no damage at all. +items.weapon.curses.polarized.elestrike_desc=An elemental strike with a polarized curse has a 50% chance to deal 20-30 damage to each enemy in range. items.weapon.curses.sacrificial.name=sacrificial %s items.weapon.curses.sacrificial.desc=Sacrificial weapons will demand blood from the wearer in return for attacking foes. The more healthy the wearer is, the more blood the curse will take. +items.weapon.curses.sacrificial.elestrike_desc=An elemental strike with a sacrificial curse causes the hero and each enemy in range to bleed for 10 HP. items.weapon.curses.wayward.name=wayward %s items.weapon.curses.wayward.desc=Wayward weapons will sometimes become extremely inaccurate. This magic lasts for a little while when it activates, but can be dispelled by landing a blow with the wayward weapon. +items.weapon.curses.wayward.elestrike_desc=An elemental strike with a wayward curse has a 50% chance to hex each enemy in range for 5 turns. items.weapon.curses.wayward$waywardbuff.name=wayward items.weapon.curses.wayward$waywardbuff.desc=Your wayward weapon's magic has activated, making it extremely inaccurate for a short time. Note that this does not affect attacks which are guaranteed to hit, such as surprise attacks. Successfully attacking with the wayward weapon will clear this effect immediately.\n\nTurns remaining: %s. ###enchantments items.weapon.enchantments.blazing.name=blazing %s items.weapon.enchantments.blazing.desc=This enchantment causes flames to spit forth from a weapon, igniting enemies and dealing bonus damage to enemies that are already aflame. +items.weapon.enchantments.blazing.elestrike_desc=An elemental strike with a blazing enchantment spreads fire to every tile in range, which lasts 6 turns. items.weapon.enchantments.blocking.name=blocking %s items.weapon.enchantments.blocking.desc=Blocking weapons have a chance to briefly shield you after attacking with them. +items.weapon.enchantments.blocking.elestrike_desc=An elemental strike with a blocking enchantment grants the Duelist an extra 5 shielding for each enemy in range. items.weapon.enchantments.blocking$blockbuff.name=blocking items.weapon.enchantments.blocking$blockbuff.desc=Your weapon's blocking enchantment has granted you a brief boost to your defensive power!\n\nShielding remaining: %d\n\nTurns remaining: %s. items.weapon.enchantments.blooming.name=blooming %s items.weapon.enchantments.blooming.desc=Blooming weapons contain magic which will cause vegetation to sprout on or around those struck by them. +items.weapon.enchantments.blooming.elestrike_desc=An elemental strike with a blooming enchantment spreads grass to up to 6 tiles in range, and roots all enemies in range for 5 turns. items.weapon.enchantments.chilling.name=chilling %s items.weapon.enchantments.chilling.desc=Enemies struck with this enchantment are chilled, slowing their movement and attacks. +items.weapon.enchantments.chilling.elestrike_desc=An elemental strike with a chilling enchantment spreads chilling air to every tile in range, which lasts 6 turns. items.weapon.enchantments.kinetic.name=kinetic %s items.weapon.enchantments.kinetic.desc=When an enemy is killed with a kinetic weapon, any excess force is stored in the weapon and will be applied to the next successful attack. +items.weapon.enchantments.kinetic.elestrike_desc=An elemental strike with a kinetic enchantment applies 33% of stored damage to each enemy in range, except the primary target. items.weapon.enchantments.kinetic$conserveddamage.name=conserved damage items.weapon.enchantments.kinetic$conserveddamage.desc=Your weapon has stored the excess force from a previous killing blow, and will apply it as bonus damage to your next attack. The energy will slowly fade over time, however.\n\nConserved Damage: %d. items.weapon.enchantments.corrupting.name=corrupting %s items.weapon.enchantments.corrupting.desc=This powerful enchantment possesses the ability to turn enemies to your will. When an enemy is killed with this weapon, there is a chance they will become corrupted instead of dying. +items.weapon.enchantments.corrupting.elestrike_desc=An elemental strike with a corrupting enchantment has a 4-20% chance (based on missing HP) to corrupt each enemy in range, except the primary target. items.weapon.enchantments.elastic.name=elastic %s items.weapon.enchantments.elastic.desc=Elastic weapons have a chance to send enemies flying back short distances. +items.weapon.enchantments.elastic.elestrike_desc=An elemental strike with an elastic enchantment knocks all enemies in range back 4 tiles. items.weapon.enchantments.grim.name=grim %s items.weapon.enchantments.grim.desc=This powerful enchantment possesses the power to instantly execute an enemy. The effect is more likely to occur the weaker the enemy is. +items.weapon.enchantments.grim.elestrike_desc=An elemental strike with a grim enchantment has up to a 6-30% chance (based on missing HP) to kill each enemy in range, except the primary target. items.weapon.enchantments.lucky.name=lucky %s items.weapon.enchantments.lucky.desc=Enemies which are killed by a lucky weapon have a chance to drop extra loot. - -items.weapon.enchantments.precise.name=precise %s -items.weapon.enchantments.precise.desc=A precise weapon has a chance to guarantee a hit on an enemy, regardless of the circumstances. +items.weapon.enchantments.lucky.elestrike_desc=An elemental strike with a lucky enchantment has a 10% chance to spawn loot under each enemy in range. This effect can only trigger once per enemy. items.weapon.enchantments.projecting.name=projecting %s items.weapon.enchantments.projecting.desc=With this enchantment melee weapons will gain extra reach. Ranged weapons will be able to penetrate nearby walls. +items.weapon.enchantments.projecting.elestrike_desc=An elemental strike with a projecting enchantment deals 25% damage to each enemy in range, except the primary target. items.weapon.enchantments.shocking.name=shocking %s items.weapon.enchantments.shocking.desc=Electricity arcs from a shocking weapon, dealing extra damage to all nearby enemies. +items.weapon.enchantments.shocking.elestrike_desc=An elemental strike with a shocking enchantment spreads electricity to every tile in range, which lasts 6 turns. items.weapon.enchantments.unstable.name=unstable %s items.weapon.enchantments.unstable.desc=This enchantment radiates chaotic energy, acting as a different enchantment with each hit. +items.weapon.enchantments.unstable.elestrike_desc=An elemental strike with an unstable enchantment applies a random enchantment effect to each enemy in range, except the primary target. items.weapon.enchantments.vampiric.name=vampiric %s items.weapon.enchantments.vampiric.desc=This powerful enchantment leeches life force from enemies with each blow, funneling it back into the wearer. The effect is stronger the lower the wearer's health. +items.weapon.enchantments.vampiric.elestrike_desc=An elemental strike with a vampiric enchantment heals the Duelist for 2 HP for each enemy in range. ###melee weapons diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java index 9e1c32f7e..2d137efff 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java @@ -29,6 +29,7 @@ import com.shatteredpixel.shatteredpixeldungeon.QuickSlot; import com.shatteredpixel.shatteredpixeldungeon.SPDSettings; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.ArmorAbility; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.duelist.Challenge; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.duelist.ElementalStrike; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.huntress.NaturesPower; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.huntress.SpiritHawk; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.huntress.SpectralBlades; @@ -253,7 +254,7 @@ public enum HeroClass { case HUNTRESS: return new ArmorAbility[]{new SpectralBlades(), new NaturesPower(), new SpiritHawk()}; case DUELIST: - return new ArmorAbility[]{new Challenge()}; + return new ArmorAbility[]{new Challenge(), new ElementalStrike()}; } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Talent.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Talent.java index d0477fd45..edbd9801a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Talent.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Talent.java @@ -160,7 +160,7 @@ public enum Talent { //Duelist A1 T4 CLOSE_THE_GAP(145, 4), INVIGORATING_VICTORY(146, 4), ELIMINATION_MATCH(147, 4), //Duelist A2 T4 - DUELIST_A2_1(148, 4), DUELIST_A2_2(149, 4), DUELIST_A2_3(150, 4), + ELEMENTAL_REACH(148, 4), STRIKING_FORCE(149, 4), DIRECTED_POWER(150, 4), //Duelist A3 T4 DUELIST_A3_1(151, 4), DUELIST_A3_2(152, 4), DUELIST_A3_3(153, 4), diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/duelist/ElementalStrike.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/duelist/ElementalStrike.java new file mode 100644 index 000000000..e3c8a74de --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/duelist/ElementalStrike.java @@ -0,0 +1,532 @@ +/* + * 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.actors.hero.abilities.duelist; + +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.blobs.Blob; +import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Electricity; +import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Fire; +import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Freezing; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AllyBuff; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Barrier; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Bleeding; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Blindness; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Charm; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Corruption; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Hex; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.ArmorAbility; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.mage.ElementalBlast; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; +import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; +import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile; +import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; +import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle; +import com.shatteredpixel.shatteredpixeldungeon.items.Item; +import com.shatteredpixel.shatteredpixeldungeon.items.KindOfWeapon; +import com.shatteredpixel.shatteredpixeldungeon.items.armor.ClassArmor; +import com.shatteredpixel.shatteredpixeldungeon.items.bombs.Bomb; +import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTeleportation; +import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfBlastWave; +import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfCorrosion; +import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfCorruption; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.curses.Annoying; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.curses.Dazzling; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.curses.Displacing; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.curses.Explosive; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.curses.Friendly; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.curses.Polarized; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.curses.Sacrificial; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.curses.Wayward; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Blazing; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Blocking; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Blooming; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Chilling; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Corrupting; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Elastic; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Grim; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Kinetic; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Lucky; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Projecting; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Shocking; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Unstable; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Vampiric; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MeleeWeapon; +import com.shatteredpixel.shatteredpixeldungeon.levels.Level; +import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain; +import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica; +import com.shatteredpixel.shatteredpixeldungeon.mechanics.ConeAOE; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; +import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite; +import com.shatteredpixel.shatteredpixeldungeon.ui.AttackIndicator; +import com.shatteredpixel.shatteredpixeldungeon.ui.HeroIcon; +import com.watabou.noosa.Game; +import com.watabou.noosa.audio.Sample; +import com.watabou.utils.Callback; +import com.watabou.utils.Random; + +import java.util.ArrayList; +import java.util.HashMap; + +public class ElementalStrike extends ArmorAbility { + + //TODO a few duplicates here (curse duplicates are fine) + private static final HashMap, Integer> effectTypes = new HashMap<>(); + static { + effectTypes.put(Blazing.class, MagicMissile.FIRE_CONE); + effectTypes.put(Chilling.class, MagicMissile.FROST_CONE); + effectTypes.put(Kinetic.class, MagicMissile.FORCE_CONE); + effectTypes.put(Shocking.class, MagicMissile.SPARK_CONE); + effectTypes.put(Blocking.class, MagicMissile.WARD_CONE); + effectTypes.put(Blooming.class, MagicMissile.FOLIAGE_CONE); + effectTypes.put(Elastic.class, MagicMissile.FORCE_CONE); + effectTypes.put(Lucky.class, MagicMissile.RAINBOW_CONE); + effectTypes.put(Projecting.class, MagicMissile.PURPLE_CONE); + effectTypes.put(Unstable.class, MagicMissile.RAINBOW_CONE); + effectTypes.put(Corrupting.class, MagicMissile.SHADOW_CONE); + effectTypes.put(Grim.class, MagicMissile.SHADOW_CONE); + effectTypes.put(Vampiric.class, MagicMissile.BLOOD_CONE); + + effectTypes.put(Annoying.class, MagicMissile.SHADOW_CONE); + effectTypes.put(Displacing.class, MagicMissile.SHADOW_CONE); + effectTypes.put(Dazzling.class, MagicMissile.SHADOW_CONE); + effectTypes.put(Explosive.class, MagicMissile.SHADOW_CONE); + effectTypes.put(Sacrificial.class, MagicMissile.SHADOW_CONE); + effectTypes.put(Wayward.class, MagicMissile.SHADOW_CONE); + effectTypes.put(Polarized.class, MagicMissile.SHADOW_CONE); + effectTypes.put(Friendly.class, MagicMissile.SHADOW_CONE); + + effectTypes.put(null, MagicMissile.MAGIC_MISS_CONE); + } + + { + baseChargeUse = 25; + } + + @Override + public String targetingPrompt() { + return Messages.get(this, "prompt"); + } + + @Override + public int targetedPos(Char user, int dst) { + return dst; + } + + //3 tiles in a 65 degree cone + // boostable to 4/5/6/7 tiles in a 70/75/80/85 degree cone + + @Override + protected void activate(ClassArmor armor, Hero hero, Integer target) { + if (target == null){ + return; + } + + armor.charge -= chargeUse(hero); + Item.updateQuickslot(); + + Ballistica aim = new Ballistica(hero.pos, target, Ballistica.WONT_STOP); + + int maxDist = 3 + hero.pointsInTalent(Talent.ELEMENTAL_REACH); + int dist = Math.min(aim.dist, maxDist); + + ConeAOE cone = new ConeAOE(aim, + dist, + 65 + 5*hero.pointsInTalent(Talent.ELEMENTAL_REACH), + Ballistica.STOP_SOLID | Ballistica.STOP_TARGET); + + KindOfWeapon w = hero.belongings.weapon(); + Weapon.Enchantment enchantment = ((MeleeWeapon) w).enchantment; + Class enchCls = null; + if (enchantment != null){ + enchCls = enchantment.getClass(); + } + + //cast to cells at the tip, rather than all cells, better performance. + for (Ballistica ray : cone.outerRays){ + ((MagicMissile)hero.sprite.parent.recycle( MagicMissile.class )).reset( + effectTypes.get(enchCls), + hero.sprite, + ray.path.get(ray.dist), + null + ); + } + + hero.sprite.attack(target, new Callback() { + @Override + public void call() { + + Char enemy = Actor.findChar(target); + + if (enemy != null) { + if (hero.isCharmedBy(enemy)) { + enemy = null; + } else if (enemy.alignment == hero.alignment) { + enemy = null; + } else if (!hero.canAttack(enemy)) { + enemy = null; + } + } + + preAttackEffect(cone, hero, enchantment); + + if (enemy != null){ + AttackIndicator.target(enemy); + if (hero.attack(enemy, 1, 0, Char.INFINITE_ACCURACY)) { + Sample.INSTANCE.play(Assets.Sounds.HIT_STRONG); + } + } + + perCellEffect(cone, enchantment); + + perCharEffect(cone, hero, enemy, enchantment); + + Invisibility.dispel(); + hero.spendAndNext(hero.attackDelay()); + } + }); + + Sample.INSTANCE.play(Assets.Sounds.CHARGEUP); + hero.busy(); + + } + + //effects that trigger before the attack + private void preAttackEffect(ConeAOE cone, Hero hero, Weapon.Enchantment ench){ + + int targetsHit = 0; + for (Char ch : Actor.chars()){ + if (ch.alignment == Char.Alignment.ENEMY && cone.cells.contains(ch.pos)){ + targetsHit++; + } + } + + if (hero.hasTalent(Talent.DIRECTED_POWER)){ + float enchBoost = 0.25f * targetsHit * hero.pointsInTalent(Talent.DIRECTED_POWER); + Buff.affect(hero, DirectedPowerTracker.class, 0f).enchBoost = enchBoost; + } + + float powerMulti = 1f + 0.25f*Dungeon.hero.pointsInTalent(Talent.STRIKING_FORCE); + + //*** Kinetic *** + if (ench instanceof Kinetic){ + if (hero.buff(Kinetic.KineticTracker.class) != null) { + storedKineticDamage = hero.buff(Kinetic.KineticTracker.class).conservedDamage; + } + + //*** Blocking *** + } else if (ench instanceof Blocking){ + if (targetsHit > 0){ + Buff.affect(hero, Barrier.class).setShield(Math.round(5f*targetsHit*powerMulti)); + } + + //*** Vampiric *** + } else if (ench instanceof Vampiric){ + if (targetsHit > 0){ + int heal = Math.round(2f*targetsHit*powerMulti); + heal = Math.min( heal, hero.HT - hero.HP ); + if (heal > 0){ + hero.HP += heal; + hero.sprite.emitter().start( Speck.factory( Speck.HEALING ), 0.4f, 1 ); + hero.sprite.showStatus( CharSprite.POSITIVE, Integer.toString( heal ) ); + } + } + + //*** Sacrificial *** + } else if (ench instanceof Sacrificial){ + Buff.affect(hero, Bleeding.class).set(10 * powerMulti); + } + + } + + public static class DirectedPowerTracker extends FlavourBuff{ + public float enchBoost = 0f; + } + + public static class ElementalStrikeLuckyTracker extends Buff{}; + + private int storedKineticDamage = 0; + + //effects that affect the cells of the environment themselves + private void perCellEffect(ConeAOE cone, Weapon.Enchantment ench){ + + float powerMulti = 1f + 0.25f*Dungeon.hero.pointsInTalent(Talent.STRIKING_FORCE); + + //*** Blazing *** + if (ench instanceof Blazing){ + for (int cell : cone.cells) { + GameScene.add(Blob.seed(cell, Math.round(6 * powerMulti), Fire.class)); + } + + //*** Chilling *** + } else if (ench instanceof Chilling){ + for (int cell : cone.cells) { + GameScene.add(Blob.seed(cell, Math.round(6 * powerMulti), Freezing.class)); + } + + //*** Shocking *** + } else if (ench instanceof Shocking){ + for (int cell : cone.cells) { + GameScene.add(Blob.seed(cell, Math.round(6 * powerMulti), Electricity.class)); + } + + //*** Blooming *** + } else if (ench instanceof Blooming){ + ArrayList cells = new ArrayList<>(cone.cells); + Random.shuffle(cells); + int grassToPlace = Math.round(6*powerMulti); + + for (int cell : cells) { + int terr = Dungeon.level.map[cell]; + if (terr == Terrain.EMPTY || terr == Terrain.EMBERS || terr == Terrain.EMPTY_DECO || + terr == Terrain.GRASS) { + if (grassToPlace > 0){ + Level.set(cell, Terrain.HIGH_GRASS); + grassToPlace--; + } else { + Level.set(cell, Terrain.GRASS); + } + GameScene.updateMap( cell ); + } + } + Dungeon.observe(); + } + } + + //effects that affect the characters within the cone AOE + private void perCharEffect(ConeAOE cone, Hero hero, Char primaryTarget, Weapon.Enchantment ench) { + + float powerMulti = 1f + 0.25f * Dungeon.hero.pointsInTalent(Talent.STRIKING_FORCE); + + ArrayList affected = new ArrayList<>(); + + for (Char ch : Actor.chars()) { + if (ch.alignment != Char.Alignment.ALLY && cone.cells.contains(ch.pos)) { + affected.add(ch); + } + } + + //*** no enchantment *** + if (ench == null) { + for (Char ch : affected){ + ch.damage(Math.round(powerMulti*Random.NormalIntRange(5, 10)), ElementalStrike.this); + } + + //*** Kinetic *** + } else if (ench instanceof Kinetic){ + for (Char ch : affected){ + if (ch != primaryTarget) { + ch.damage(Math.round(storedKineticDamage * 0.33f * powerMulti), ench); + } + } + + //*** Blooming *** + } else if (ench instanceof Blooming){ + for (Char ch : affected){ + Buff.affect(ch, Roots.class, Math.round(5f*powerMulti)); + } + + //*** Elastic *** + } else if (ench instanceof Elastic){ + //TODO sort affected by distance first? So further ones get knocked back first + for (Char ch : affected){ + Ballistica aim = new Ballistica(hero.pos, ch.pos, Ballistica.WONT_STOP); + int knockback = Math.round(4*powerMulti); + WandOfBlastWave.throwChar(ch, + new Ballistica(ch.pos, aim.collisionPos, Ballistica.MAGIC_BOLT), + knockback, + true, + true, + ElementalStrike.this.getClass()); + } + + //*** Lucky *** + } else if (ench instanceof Lucky){ + for (Char ch : affected){ + if (Random.Float() < 0.1f*powerMulti && + ch.buff(ElementalStrikeLuckyTracker.class) == null) { + Dungeon.level.drop(Lucky.genLoot(), ch.pos).sprite.drop(); + Lucky.showFlare(ch.sprite); + Buff.affect(ch, ElementalStrikeLuckyTracker.class); + } + } + + //*** Projecting *** + } else if (ench instanceof Projecting){ + for (Char ch : affected){ + if (ch != primaryTarget) { + ch.damage(Math.round(hero.damageRoll() * 0.25f * powerMulti), ench); + } + } + + //*** Unstable *** + } else if (ench instanceof Unstable){ + KindOfWeapon w = hero.belongings.weapon(); + if (w instanceof Weapon) { + for (Char ch : affected){ + if (ch != primaryTarget) { + ench.proc((Weapon) w, hero, ch, w.damageRoll(hero)); + } + } + } + + //*** Corrupting *** + } else if (ench instanceof Corrupting){ + for (Char ch : affected){ + if (ch != primaryTarget + && !ch.isImmune(Corruption.class) + && ch.buff(Corruption.class) == null + && ch instanceof Mob + && ch.isAlive()) { + float hpMissing = 1f - (ch.HP / (float)ch.HT); + if (Random.Float() < 0.2f*powerMulti*hpMissing){ + Corruption.corruptionHeal(ch); + AllyBuff.affectAndLoot((Mob) ch, hero, Corruption.class); + } + } + } + + //*** Grim *** + } else if (ench instanceof Grim){ + for (Char ch : affected){ + if (ch != primaryTarget) { + float hpMissing = 1f - (ch.HP / (float)ch.HT); + if (Random.Float() < 0.3f*powerMulti*hpMissing){ + ch.damage( ch.HP, Grim.class ); + ch.sprite.emitter().burst( ShadowParticle.UP, 5 ); + } + } + } + + //*** Annoying *** + } else if (ench instanceof Annoying){ + for (Char ch : affected){ + if (Random.Float() < 0.1f*powerMulti){ + //TODO totally should add a bit of dialogue here + Buff.affect(ch, Amok.class, 5f); + } + } + + //*** Displacing *** + } else if (ench instanceof Displacing){ + for (Char ch : affected){ + if (Random.Float() < 0.5f*powerMulti){ + int oldpos = ch.pos; + if (ScrollOfTeleportation.teleportChar(ch)){ + if (Dungeon.level.heroFOV[oldpos]) { + CellEmitter.get( oldpos ).start( Speck.factory( Speck.LIGHT ), 0.2f, 3 ); + } + + if (ch instanceof Mob && ((Mob) ch).state == ((Mob) ch).HUNTING){ + ((Mob) ch).state = ((Mob) ch).WANDERING; + } + } + } + } + + //*** Dazzling *** + } else if (ench instanceof Dazzling){ + for (Char ch : affected){ + if (Random.Float() < 0.5f*powerMulti){ + Buff.affect(ch, Blindness.class, 5f); + } + } + + //*** Explosive *** + } else if (ench instanceof Explosive){ + if (Random.Float() < 0.5f*powerMulti){ + Char exploding = Random.element(affected); + if (exploding != null) new Bomb.MagicalBomb().explode(exploding.pos); + } + + //*** Sacrificial *** + } else if (ench instanceof Sacrificial){ + for (Char ch : affected){ + Buff.affect(ch, Bleeding.class).set(10f*powerMulti); + } + + //*** Wayward *** + } else if (ench instanceof Wayward){ + for (Char ch : affected){ + if (Random.Float() < 0.5f*powerMulti){ + Buff.affect(ch, Hex.class, 5f); + } + } + + //*** Polarized *** + } else if (ench instanceof Polarized){ + for (Char ch : affected){ + if (Random.Float() < 0.5f*powerMulti){ + ch.damage(Random.NormalIntRange(20, 30), ElementalStrike.this); + } + } + + //*** Friendly *** + } else if (ench instanceof Friendly){ + for (Char ch : affected){ + if (Random.Float() < 0.5f*powerMulti){ + Buff.affect(ch, Charm.class, 5f).target = hero; + } + } + } + + } + + @Override + public String desc() { + String desc = Messages.get(this, "desc"); + if (Game.scene() instanceof GameScene){ + KindOfWeapon w = Dungeon.hero.belongings.weapon(); + if (w instanceof MeleeWeapon && ((MeleeWeapon) w).enchantment != null){ + desc += "\n\n" + Messages.get(((MeleeWeapon) w).enchantment, "elestrike_desc"); + } else { + desc += "\n\n" + Messages.get(this, "generic_desc"); + } + } else { + desc += "\n\n" + Messages.get(this, "generic_desc"); + } + desc += "\n\n" + Messages.get(this, "cost", (int)baseChargeUse); + return desc; + } + + @Override + public int icon() { + return HeroIcon.ELEMENTAL_STRIKE; + } + + @Override + public Talent[] talents() { + return new Talent[]{Talent.ELEMENTAL_REACH, Talent.STRIKING_FORCE, Talent.DIRECTED_POWER, Talent.HEROIC_ENERGY}; + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/glyphs/AntiMagic.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/glyphs/AntiMagic.java index b51e05b42..30808b087 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/glyphs/AntiMagic.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/glyphs/AntiMagic.java @@ -28,6 +28,8 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Hex; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MagicalSleep; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vulnerable; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Weakness; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.duelist.ElementalStrike; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.mage.ElementalBlast; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.mage.WarpBeacon; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.DM100; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Eye; @@ -81,6 +83,7 @@ public class AntiMagic extends Armor.Glyph { RESISTS.add( ScrollOfPsionicBlast.class ); RESISTS.add( ScrollOfTeleportation.class ); + RESISTS.add( ElementalBlast.class ); RESISTS.add( CursedWand.class ); RESISTS.add( WandOfBlastWave.class ); RESISTS.add( WandOfDisintegration.class ); @@ -93,6 +96,7 @@ public class AntiMagic extends Armor.Glyph { RESISTS.add( WandOfTransfusion.class ); RESISTS.add( WandOfWarding.Ward.class ); + RESISTS.add( ElementalStrike.class ); RESISTS.add( Blazing.class ); RESISTS.add( Shocking.class ); RESISTS.add( Grim.class ); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/Weapon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/Weapon.java index 294e7d66b..a461b4977 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/Weapon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/Weapon.java @@ -28,6 +28,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Berserk; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MagicImmune; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.duelist.ElementalStrike; import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.KindOfWeapon; import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfArcana; @@ -392,6 +393,12 @@ abstract public class Weapon extends KindOfWeapon { if (attacker.buff(RunicBlade.RunicSlashTracker.class) != null){ multi += 2f; + attacker.buff(RunicBlade.RunicSlashTracker.class).detach(); + } + + if (attacker.buff(ElementalStrike.DirectedPowerTracker.class) != null){ + multi += attacker.buff(ElementalStrike.DirectedPowerTracker.class).enchBoost; + attacker.buff(ElementalStrike.DirectedPowerTracker.class).detach(); } if (attacker.buff(Talent.SpiritBladesTracker.class) != null diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/HeroIcon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/HeroIcon.java index d1e76d185..557b017e7 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/HeroIcon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/HeroIcon.java @@ -62,7 +62,7 @@ public class HeroIcon extends Image { public static final int NATURES_POWER = 26; public static final int SPIRIT_HAWK = 27; public static final int CHALLENGE = 28; - public static final int DUELIST_2 = 29; + public static final int ELEMENTAL_STRIKE= 29; public static final int DUELIST_3 = 30; public static final int RATMOGRIFY = 33;