v1.2.0: balance changes to spells (except summon elemental)

This commit is contained in:
Evan Debenham 2022-03-07 14:13:18 -05:00
parent 81ebd0c940
commit 3c8e5c48dd
22 changed files with 92 additions and 78 deletions

View File

@ -1048,7 +1048,7 @@ items.spells.alchemize$wndalchemizeitem.energize_1=Turn 1 into %d energy
items.spells.alchemize$wndalchemizeitem.energize_all=Turn all into %d energy
items.spells.aquablast.name=aqua blast
items.spells.aquablast.desc=This spell will create a burst of water at the target location. It isn't forceful enough to do damage (even to fiery enemies), but it will spread water to nearby terrain and very briefly stun anything caught in the center of the burst.
items.spells.aquablast.desc=This spell will create a burst of water at the target location. It isn't forceful enough to do damage (even to fiery enemies), but it will spread water to nearby terrain and knock back characters near the burst.
items.spells.arcanecatalyst.name=arcane catalyst
items.spells.arcanecatalyst.desc=This ball of golden dust is made from the deconstructed essence of a scroll. It glimmers in the darkness of the dungeon.\n\nThis catalyst is primarily useful as an alchemy ingredient, but you can also channel the magic directly to get the effect of a random scroll.
@ -1065,7 +1065,7 @@ items.spells.beaconofreturning.desc=This intricate spell grants the user the abi
items.spells.curseinfusion.name=curse infusion
items.spells.curseinfusion.inv_title=Curse an item
items.spells.curseinfusion.desc=This spell infuses a piece of equipment with the same powerful malignant magic present within DM-300. The item it is used on will immediately be cursed, and any enchantment or glyph it may have had will be overridden.\n\nIn the case of weapons, armor, and wands, the item will be upgraded in addition to being cursed. Curse infusion upgrades do not stack, and the upgrade is lost if the item becomes uncursed.
items.spells.curseinfusion.desc=This spell infuses a piece of equipment with the same powerful malignant magic present within DM-300. The item it is used on will immediately be cursed, and any enchantment or glyph it may have had will be overridden.\n\nIn the case of weapons, armor, and wands, the item will gain upgrades in addition to being cursed. Curse infusion upgrades do not stack, and the upgrades are lost if the item becomes uncursed.
items.spells.featherfall.name=feather fall
items.spells.featherfall.light=You feel light as a feather!
@ -1085,7 +1085,9 @@ items.spells.magicalporter.nowhere=There is nowhere for you to port an item to.
items.spells.magicalporter.desc=This spell will magically transport any item it is cast on. Unlike a merchant's beacon however, the items will be transported to the entrance of the next boss floor.
items.spells.phaseshift.name=phase shift
items.spells.phaseshift.desc=This chaotic spell will teleport any character it is aimed at to a random location on the current floor. This spell can be directed at a target or at the user themselves.
items.spells.phaseshift.no_self=You cannot teleport yourself with that.
items.spells.phaseshift.no_target=There is nothing to teleport there.
items.spells.phaseshift.desc=This chaotic spell will teleport any character it is aimed at to a random location on the current floor, and stun them for a significant amount of time. Powerful foes will be able to resist the stunning effect. This spell can be directed at a target or at the user themselves.
items.spells.reclaimtrap.name=reclaim trap
items.spells.reclaimtrap.no_trap=There is no trap there.

View File

@ -314,7 +314,12 @@ public class Item implements Bundlable {
protected void onDetach(){}
//returns the true level of the item, only affected by modifiers which are persistent (e.g. curse infusion)
//returns the true level of the item, ignoring all modifiers aside from upgrades
public final int trueLevel(){
return level;
}
//returns the persistant level of the item, only affected by modifiers which are persistent (e.g. curse infusion)
public int level(){
return level;
}

View File

@ -238,8 +238,7 @@ public class Armor extends EquipableItem {
this.seal = seal;
if (seal.level() > 0){
//doesn't trigger upgrading logic such as affecting curses/glyphs
int newLevel = level()+1;
if (curseInfusionBonus) newLevel--;
int newLevel = trueLevel()+1;
level(newLevel);
Badges.validateItemLevelAquired(this);
}
@ -379,7 +378,9 @@ public class Armor extends EquipableItem {
@Override
public int level() {
return super.level() + (curseInfusionBonus ? 1 : 0);
int level = super.level();
if (curseInfusionBonus) level += 1 + level/6;
return level;
}
//other things can equip these, for now we assume only the hero can be affected by levelling debuffs

View File

@ -104,7 +104,7 @@ abstract public class ClassArmor extends Armor {
break;
}
classArmor.level(armor.level() - (armor.curseInfusionBonus ? 1 : 0));
classArmor.level(armor.trueLevel());
classArmor.tier = armor.tier;
classArmor.augment = armor.augment;
classArmor.inscribe( armor.glyph );

View File

@ -26,6 +26,7 @@ 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.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.levels.RegularLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
@ -273,6 +274,10 @@ public class ScrollOfTeleportation extends Scroll {
Sample.INSTANCE.play(Assets.Sounds.TELEPORT);
}
if (Dungeon.level.heroFOV[ch.pos] && ch != Dungeon.hero ) {
CellEmitter.get(ch.pos).start(Speck.factory(Speck.LIGHT), 0.2f, 3);
}
ch.move( pos, false );
if (ch.pos == pos) ch.sprite.place( pos );

View File

@ -170,8 +170,7 @@ public class ScrollOfTransmutation extends InventoryScroll {
n = (Weapon) Reflection.newInstance(c.classes[Random.chances(c.probs)]);
} while (Challenges.isItemBlocked(n) || n.getClass() == w.getClass());
int level = w.level();
if (w.curseInfusionBonus) level--;
int level = w.trueLevel();
if (level > 0) {
n.upgrade( level );
} else if (level < 0) {
@ -234,9 +233,7 @@ public class ScrollOfTransmutation extends InventoryScroll {
} while ( Challenges.isItemBlocked(n) || n.getClass() == w.getClass());
n.level( 0 );
int level = w.level();
if (w.curseInfusionBonus) level--;
level -= w.resinBonus;
int level = w.trueLevel();
n.upgrade( level );
n.levelKnown = w.levelKnown;

View File

@ -66,7 +66,7 @@ public class Alchemize extends Spell {
inputs = new Class[]{ArcaneCatalyst.class};
inQuantity = new int[]{1};
cost = 3;
cost = 2;
output = Alchemize.class;
outQuantity = 8;

View File

@ -21,49 +21,33 @@
package com.shatteredpixel.shatteredpixeldungeon.items.spells;
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.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.effects.Splash;
import com.shatteredpixel.shatteredpixeldungeon.items.potions.exotic.PotionOfStormClouds;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.GeyserTrap;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.watabou.utils.PathFinder;
import com.watabou.utils.Random;
public class AquaBlast extends TargetedSpell {
{
image = ItemSpriteSheet.AQUA_BLAST;
usesTargeting = true;
}
@Override
protected void affectTarget(Ballistica bolt, Hero hero) {
int cell = bolt.collisionPos;
Splash.at(cell, 0x00AAFF, 10);
for (int i : PathFinder.NEIGHBOURS9){
if (i == 0 || Random.Int(5) != 0){
Dungeon.level.setCellToWater(false, cell+i);
}
}
Char target = Actor.findChar(cell);
if (target != null && target != hero){
//just enough to skip their current turn
Buff.affect(target, Paralysis.class, target.cooldown());
}
GeyserTrap geyser = new GeyserTrap();
geyser.pos = cell;
geyser.centerKnockBackDirection = bolt.path.get(bolt.dist+1);
geyser.activate();
}
@Override
public int value() {
//prices of ingredients, divided by output quantity
return Math.round(quantity * ((60 + 40) / 12f));
return Math.round(quantity * ((60 + 40) / 8f));
}
public static class Recipe extends com.shatteredpixel.shatteredpixeldungeon.items.Recipe.SimpleRecipe {
@ -75,7 +59,7 @@ public class AquaBlast extends TargetedSpell {
cost = 2;
output = AquaBlast.class;
outQuantity = 12;
outQuantity = 8;
}
}

View File

@ -202,7 +202,7 @@ public class BeaconOfReturning extends Spell {
inputs = new Class[]{ScrollOfPassage.class, ArcaneCatalyst.class};
inQuantity = new int[]{1, 1};
cost = 8;
cost = 6;
output = BeaconOfReturning.class;
outQuantity = 5;

View File

@ -99,10 +99,10 @@ public class CurseInfusion extends InventorySpell {
inputs = new Class[]{ScrollOfRemoveCurse.class, MetalShard.class};
inQuantity = new int[]{1, 1};
cost = 4;
cost = 6;
output = CurseInfusion.class;
outQuantity = 3;
outQuantity = 4;
}
}

View File

@ -81,7 +81,7 @@ public class MagicalInfusion extends InventorySpell {
inputs = new Class[]{ScrollOfUpgrade.class, ArcaneCatalyst.class};
inQuantity = new int[]{1, 1};
cost = 3;
cost = 4;
output = MagicalInfusion.class;
outQuantity = 1;

View File

@ -24,6 +24,8 @@ package com.shatteredpixel.shatteredpixeldungeon.items.spells;
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.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTeleportation;
@ -36,23 +38,28 @@ public class PhaseShift extends TargetedSpell {
{
image = ItemSpriteSheet.PHASE_SHIFT;
usesTargeting = true;
}
@Override
protected void affectTarget(Ballistica bolt, Hero hero) {
final Char ch = Actor.findChar(bolt.collisionPos);
if (ch == hero){
//TODO probably want this to not work on the hero for balance reasons?
ScrollOfTeleportation.teleportChar(curUser);
} else if (ch != null) {
if (ch != null) {
if (ScrollOfTeleportation.teleportChar(ch)){
if (ch instanceof Mob && ((Mob) ch).state == ((Mob) ch).HUNTING){
((Mob) ch).state = ((Mob) ch).WANDERING;
if (ch instanceof Mob) {
if (((Mob) ch).state == ((Mob) ch).HUNTING) ((Mob) ch).state = ((Mob) ch).WANDERING;
((Mob) ch).beckon(Dungeon.level.randomDestination( ch ));
}
if (!Char.hasProp(ch, Char.Property.BOSS) && !Char.hasProp(ch, Char.Property.MINIBOSS)) {
Buff.affect(ch, Paralysis.class, Paralysis.DURATION);
}
}
} else {
GLog.w( Messages.get(this, "no_target") );
}
}
@ -68,7 +75,7 @@ public class PhaseShift extends TargetedSpell {
inputs = new Class[]{ScrollOfTeleportation.class, ArcaneCatalyst.class};
inQuantity = new int[]{1, 1};
cost = 6;
cost = 4;
output = PhaseShift.class;
outQuantity = 8;

View File

@ -117,7 +117,7 @@ public class ReclaimTrap extends TargetedSpell {
@Override
public int value() {
//prices of ingredients, divided by output quantity
return Math.round(quantity * ((40 + 50) / 3f));
return Math.round(quantity * ((40 + 50) / 4f));
}
private static final String STORED_TRAP = "stored_trap";
@ -143,7 +143,7 @@ public class ReclaimTrap extends TargetedSpell {
cost = 6;
output = ReclaimTrap.class;
outQuantity = 3;
outQuantity = 4;
}
}

View File

@ -94,7 +94,7 @@ public class Recycle extends InventorySpell {
@Override
public int value() {
//prices of ingredients, divided by output quantity
return Math.round(quantity * ((50 + 40) / 8f));
return Math.round(quantity * ((50 + 40) / 12f));
}
public static class Recipe extends com.shatteredpixel.shatteredpixeldungeon.items.Recipe.SimpleRecipe {
@ -103,10 +103,10 @@ public class Recycle extends InventorySpell {
inputs = new Class[]{ScrollOfTransmutation.class, ArcaneCatalyst.class};
inQuantity = new int[]{1, 1};
cost = 6;
cost = 8;
output = Recycle.class;
outQuantity = 8;
outQuantity = 12;
}
}

View File

@ -54,7 +54,7 @@ public abstract class TargetedSpell extends Spell {
Sample.INSTANCE.play( Assets.Sounds.ZAP );
}
private static CellSelector.Listener targeter = new CellSelector.Listener(){
private static CellSelector.Listener targeter = new CellSelector.Listener(){
@Override
public void onSelect( Integer target ) {

View File

@ -111,16 +111,16 @@ public class TelekineticGrab extends TargetedSpell {
@Override
public int value() {
//prices of ingredients, divided by output quantity (rounded up slightly)
return Math.round(quantity * ((48) / 6f));
return Math.round(quantity * ((5 + 40) / 6f));
}
public static class Recipe extends com.shatteredpixel.shatteredpixeldungeon.items.Recipe.SimpleRecipe {
{
inputs = new Class[]{LiquidMetal.class, ArcaneCatalyst.class};
inQuantity = new int[]{15, 1};
inQuantity = new int[]{10, 1};
cost = 4;
cost = 2;
output = TelekineticGrab.class;
outQuantity = 6;

View File

@ -40,6 +40,7 @@ public class WildEnergy extends TargetedSpell {
{
image = ItemSpriteSheet.WILD_ENERGY;
usesTargeting = true;
}
//we rely on cursedWand to do fx instead
@ -75,7 +76,7 @@ public class WildEnergy extends TargetedSpell {
inputs = new Class[]{ScrollOfMysticalEnergy.class, MetalShard.class};
inQuantity = new int[]{1, 1};
cost = 6;
cost = 4;
output = WildEnergy.class;
outQuantity = 5;

View File

@ -282,7 +282,10 @@ public abstract class Wand extends Item {
curseInfusionBonus = false;
updateLevel();
}
return super.level() + resinBonus + (curseInfusionBonus ? 1 : 0);
int level = super.level();
if (curseInfusionBonus) level += 1 + level/6;
level += resinBonus;
return level;
}
@Override

View File

@ -186,7 +186,7 @@ public class SpiritBow extends Weapon {
public int min(int lvl) {
int dmg = 1 + Dungeon.hero.lvl/5
+ RingOfSharpshooting.levelDamageBonus(Dungeon.hero)
+ (curseInfusionBonus ? 1 : 0);
+ (curseInfusionBonus ? 1 + Dungeon.hero.lvl/30 : 0);
return Math.max(0, dmg);
}
@ -194,7 +194,7 @@ public class SpiritBow extends Weapon {
public int max(int lvl) {
int dmg = 6 + (int)(Dungeon.hero.lvl/2.5f)
+ 2*RingOfSharpshooting.levelDamageBonus(Dungeon.hero)
+ (curseInfusionBonus ? 2 : 0);
+ (curseInfusionBonus ? 2 + Dungeon.hero.lvl/15 : 0);
return Math.max(0, dmg);
}
@ -267,7 +267,9 @@ public class SpiritBow extends Weapon {
@Override
public int level() {
return (Dungeon.hero == null ? 0 : Dungeon.hero.lvl/5) + (curseInfusionBonus ? 1 : 0);
int level = Dungeon.hero == null ? 0 : Dungeon.hero.lvl/5;
if (curseInfusionBonus) level += 1 + level/6;
return level;
}
@Override

View File

@ -229,7 +229,9 @@ abstract public class Weapon extends KindOfWeapon {
@Override
public int level() {
return super.level() + (curseInfusionBonus ? 1 : 0);
int level = super.level();
if (curseInfusionBonus) level += 1 + level/6;
return level;
}
//overrides as other things can equip these

View File

@ -234,10 +234,10 @@ public class MagesStaff extends MeleeWeapon {
wand.updateLevel();
//syncs the level of the two items.
int targetLevel = Math.max(this.level() - (curseInfusionBonus ? 1 : 0), wand.level());
int targetLevel = Math.max(this.trueLevel(), wand.trueLevel());
//if the staff's level is being overridden by the wand, preserve 1 upgrade
if (wand.level() >= this.level() && this.level() > (curseInfusionBonus ? 1 : 0)) targetLevel++;
if (wand.trueLevel() >= this.trueLevel() && this.trueLevel() > 0) targetLevel++;
level(targetLevel);
this.wand = wand;
@ -416,13 +416,12 @@ public class MagesStaff extends MeleeWeapon {
applyWand((Wand)item);
} else {
int newLevel;
int itemLevel = item.level();
itemLevel -= ((Wand)item).resinBonus;
if (itemLevel >= level()){
if (level() > 0) newLevel = itemLevel + 1;
else newLevel = itemLevel;
int itemLevel = item.trueLevel();
if (itemLevel >= trueLevel()){
if (trueLevel() > 0) newLevel = itemLevel + 1;
else newLevel = itemLevel;
} else {
newLevel = level();
newLevel = trueLevel();
}
String bodyText = Messages.get(MagesStaff.class, "imbue_desc", newLevel);

View File

@ -44,6 +44,8 @@ public class GeyserTrap extends Trap {
shape = DIAMOND;
}
public int centerKnockBackDirection = -1;
@Override
public void activate() {
Splash.at( DungeonTilemap.tileCenterToWorld( pos ), -PointF.PI/2, PointF.PI/2, 0x5bc1e3, 100, 0.01f);
@ -51,7 +53,9 @@ public class GeyserTrap extends Trap {
PathFinder.buildDistanceMap( pos, BArray.not( Dungeon.level.solid, null ), 2 );
for (int i = 0; i < PathFinder.distance.length; i++) {
if (PathFinder.distance[i] < Integer.MAX_VALUE) {
if (PathFinder.distance[i] == 2 && Random.Int(3) > 0){
Dungeon.level.setCellToWater(true, i);
} else if (PathFinder.distance[i] < 2){
Dungeon.level.setCellToWater(true, i);
}
}
@ -71,13 +75,15 @@ public class GeyserTrap extends Trap {
Char ch = Actor.findChar(pos);
if (ch != null){
int targetpos = -1;
if (ch == Dungeon.hero){
if (centerKnockBackDirection != -1){
targetpos = centerKnockBackDirection;
} else if (ch == Dungeon.hero){
//if it is the hero, random direction that isn't into a hazard
ArrayList<Integer> candidates = new ArrayList<>();
for (int i : PathFinder.NEIGHBOURS8){
//add as a candidate if both cells on the trajectory are safe
if (!Dungeon.level.avoid[pos + i] && !Dungeon.level.avoid[pos + i + i]){
candidates.add(pos + i + i);
candidates.add(pos + i);
}
}
if (!candidates.isEmpty()){
@ -85,11 +91,11 @@ public class GeyserTrap extends Trap {
}
} else {
//random direction if it isn't the hero
targetpos = pos + 2*PathFinder.NEIGHBOURS8[Random.Int(8)];
targetpos = pos + PathFinder.NEIGHBOURS8[Random.Int(8)];
}
if (targetpos != -1){
//trace a ballistica in the direction of our target
Ballistica trajectory = new Ballistica(pos, targetpos, Ballistica.PROJECTILE);
Ballistica trajectory = new Ballistica(pos, targetpos, Ballistica.MAGIC_BOLT);
//knock them back along that ballistica
WandOfBlastWave.throwChar(ch, trajectory, 2, true);
}