v0.9.3: implemented the wild magic ability

This commit is contained in:
Evan Debenham 2021-05-18 15:10:14 -04:00
parent 0272216f9a
commit 78d4632068
18 changed files with 163 additions and 43 deletions

View File

@ -351,6 +351,7 @@ actors.hero.abilities.mage.elementalblast.name=elemental blast
actors.hero.abilities.mage.elementalblast.short_desc=The Mage emits an _Elemental Blast_ from his staff, covering a large area around him in an effect that varies based on the wand in his staff.
actors.hero.abilities.mage.elementalblast.desc=TODO
actors.hero.abilities.mage.wildmagic.name=wild magic
actors.hero.abilities.mage.wildmagic.no_wands=You have no wands to zap with!
actors.hero.abilities.mage.wildmagic.short_desc=The Mage unleashes the _Wild Magic_ contained in his wands, randomly firing them multiple times at a chosen target over a single turn.
actors.hero.abilities.mage.wildmagic.desc=TODO
actors.hero.abilities.mage.magicalchains.name=magical chains
@ -553,15 +554,15 @@ actors.hero.talent.elemental_power.desc=_+1:_ The power of elemental blast is in
actors.hero.talent.reactive_barrier.title=reactive barrier
actors.hero.talent.reactive_barrier.desc=_+1:_ The Mage gains _2 shielding_ for every character affected by elemental blast.\n\n_+2:_ The Mage gains _4 shielding_ for every character affected by elemental blast.\n\n_+3:_ The Mage gains _6 shielding_ for every character affected by elemental blast.\n\n_+4:_ The Mage gains _8 shielding_ for every character affected by elemental blast.
actors.hero.talent.mage_2_1.title=TODO NAME
actors.hero.talent.mage_2_1.desc=TODO DESC
actors.hero.talent.mage_2_2.title=TODO NAME
actors.hero.talent.mage_2_2.desc=TODO DESC
actors.hero.talent.mage_2_3.title=TODO NAME
actors.hero.talent.mage_2_3.desc=TODO DESC
actors.hero.talent.wild_power.title=wild power
actors.hero.talent.wild_power.desc=_+1:_ When using wild magic, wands will be treated as either _+1 or +2_, instead of +1.\n\n_+2:_ When using wild magic, wands are now treated as if they are _+2_, instead of +1.\n\n_+3:_ When using wild magic, wands will be treated as either _+2 or +3_, instead of +1.\n\n_+4:_ When using wild magic, wands are now treated as if they are _+3_, instead of +1.
actors.hero.talent.fire_everything.title=fire everything
actors.hero.talent.fire_everything.desc=_+1:_ Wild magic now fires _5 times_, up from 4.\n\n_+2:_ Wild magic now fires _6 times_, up from 4.\n\n_+3:_ Wild magic now fires _7 times_, up from 4.\n\n_+4:_ Wild magic now fires _8 times_, up from 4.
actors.hero.talent.conserved_magic.title=conserved magic
actors.hero.talent.conserved_magic.desc=_+1:_ Each zap from wild magic now uses _0.71 charges_, instead of 1.\n\n_+2:_ Each zap from wild magic now uses _0.50 charges_, instead of 1.\n\n_+3:_ Each zap from wild magic now uses _0.35 charges_, instead of 1.\n\n_+4:_ Each zap from wild magic now uses _0.25 charges_, instead of 1.
actors.hero.talent.mage_3_1.title=TODO NAME
actors.hero.talent.mage_3_1.desc=TODO DESC
actors.hero.talent.mage_3_1.desc=_+1:_ \n\n_+2:_ \n\n_+3:_ \n\n_+4:_
actors.hero.talent.mage_3_2.title=TODO NAME
actors.hero.talent.mage_3_2.desc=TODO DESC
actors.hero.talent.mage_3_3.title=TODO NAME

View File

@ -162,6 +162,18 @@ public class Belongings implements Iterable<Item> {
return null;
}
public<T extends Item> ArrayList<T> getAllItems( Class<T> itemClass ) {
ArrayList<T> result = new ArrayList<>();
for (Item item : this) {
if (itemClass.isInstance( item )) {
result.add((T) item);
}
}
return result;
}
public boolean contains( Item contains ){

View File

@ -97,8 +97,8 @@ public enum Talent {
SOUL_EATER(46, 3), SOUL_SIPHON(47, 3), NECROMANCERS_MINIONS(48, 3),
//Elemental Blast T4
BLAST_RADIUS(49, 4), ELEMENTAL_POWER(50, 4), REACTIVE_BARRIER(51, 4),
//??? T4
MAGE_2_1(52, 4), MAGE_2_2(53, 4), MAGE_2_3(54, 4),
//Wild Magic T4
WILD_POWER(52, 4), FIRE_EVERYTHING(53, 4), CONSERVED_MAGIC(54, 4),
//??? T4
MAGE_3_1(55, 4), MAGE_3_2(56, 4), MAGE_3_3(57, 4),

View File

@ -21,20 +21,114 @@
package com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.mage;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff;
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.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.ClassArmor;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.utils.Callback;
import com.watabou.utils.Random;
import java.util.ArrayList;
public class WildMagic extends ArmorAbility {
@Override
protected String targetingPrompt() {
return Messages.get(this, "prompt");
}
@Override
protected void activate(ClassArmor armor, Hero hero, Integer target) {
//TODO
if (target == null){
return;
}
if (target == hero.pos){
GLog.w(Messages.get(this, "self_target"));
return;
}
ArrayList<Wand> wands = hero.belongings.getAllItems(Wand.class);
Random.shuffle(wands);
float chargeUsePerShot = (float)Math.pow(0.707, hero.pointsInTalent(Talent.CONSERVED_MAGIC));
for (Wand w : wands.toArray(new Wand[0])){
if (w.curCharges < 1 && w.partialCharge < chargeUsePerShot){
wands.remove(w);
}
}
int maxWands = 4 + Dungeon.hero.pointsInTalent(Talent.FIRE_EVERYTHING);
if (wands.size() < maxWands){
ArrayList<Wand> dupes = new ArrayList<>(wands);
for (Wand w : dupes.toArray(new Wand[0])){
float totalCharge = w.curCharges + w.partialCharge;
if (totalCharge < 2*chargeUsePerShot){
dupes.remove(w);
}
}
Random.shuffle(dupes);
while (!dupes.isEmpty() && wands.size() < maxWands){
wands.add(dupes.remove(0));
}
}
if (wands.size() == 0){
GLog.w(Messages.get(this, "no_wands"));
return;
}
Random.shuffle(wands);
Buff.affect(hero, WildMagicTracker.class, 0f);
zapWand(wands, hero, target);
}
public static class WildMagicTracker extends FlavourBuff{};
private void zapWand( ArrayList<Wand> wands, Hero hero, int target){
Wand cur = wands.remove(0);
Ballistica aim = new Ballistica(hero.pos, target, cur.collisionProperties(target));
hero.sprite.zap(target);
cur.fx(aim, new Callback() {
@Override
public void call() {
cur.onZap(aim);
cur.partialCharge -= (float)Math.pow(0.707, hero.pointsInTalent(Talent.CONSERVED_MAGIC));
if (cur.partialCharge < 0){
cur.partialCharge++;
cur.curCharges--;
}
if (!wands.isEmpty()){
zapWand(wands, hero, target);
} else {
hero.buff(WildMagicTracker.class).detach();
Item.updateQuickslot();
hero.spendAndNext(Actor.TICK);
}
}
});
}
@Override
public Talent[] talents() {
return new Talent[]{Talent.MAGE_2_1, Talent.MAGE_2_2, Talent.MAGE_2_3, Talent.HEROIC_ENERGY};
return new Talent[]{Talent.WILD_POWER, Talent.FIRE_EVERYTHING, Talent.CONSERVED_MAGIC, Talent.HEROIC_ENERGY};
}
}

View File

@ -37,6 +37,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.SoulMark;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.mage.WildMagic;
import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.TalismanOfForesight;
@ -116,7 +117,7 @@ public abstract class Wand extends Item {
return new Ballistica( user.pos, dst, collisionProperties ).collisionPos;
}
protected abstract void onZap(Ballistica attack );
public abstract void onZap(Ballistica attack);
public abstract void onHit( MagesStaff staff, Char attacker, Char defender, int damage);
@ -306,6 +307,12 @@ public abstract class Wand extends Item {
int lvl = super.buffedLvl();
if (charger != null && charger.target != null) {
if (charger.target.buff(WildMagic.WildMagicTracker.class) != null){
int level = 2 + ((Hero)charger.target).pointsInTalent(Talent.WILD_POWER);
if (Random.Int(2) == 0) level++;
return level/2; // +1/+1.5/+2/+2.5/+3 at 0/1/2/3/4 talent points
}
if (charger.target.buff(ScrollEmpower.class) != null){
lvl += Dungeon.hero.pointsInTalent(Talent.EMPOWERING_SCROLLS);
}
@ -331,7 +338,7 @@ public abstract class Wand extends Item {
return 1;
}
protected void fx( Ballistica bolt, Callback callback ) {
public void fx(Ballistica bolt, Callback callback) {
MagicMissile.boltFromChar( curUser.sprite.parent,
MagicMissile.MAGIC_MISSILE,
curUser.sprite,
@ -470,7 +477,7 @@ public abstract class Wand extends Item {
availableUsesToID = USES_TO_ID/2f;
}
protected int collisionProperties( int target ){
public int collisionProperties(int target){
return collisionProperties;
}

View File

@ -35,10 +35,8 @@ import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.Door;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.TenguDartTrap;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.noosa.Game;
import com.watabou.noosa.Group;
import com.watabou.noosa.Image;
@ -65,7 +63,7 @@ public class WandOfBlastWave extends DamageWand {
}
@Override
protected void onZap(Ballistica bolt) {
public void onZap(Ballistica bolt) {
Sample.INSTANCE.play( Assets.Sounds.BLAST );
BlastWave.blast(bolt.collisionPos);
@ -186,7 +184,7 @@ public class WandOfBlastWave extends DamageWand {
}
@Override
protected void fx(Ballistica bolt, Callback callback) {
public void fx(Ballistica bolt, Callback callback) {
MagicMissile.boltFromChar( curUser.sprite.parent,
MagicMissile.FORCE,
curUser.sprite,

View File

@ -53,7 +53,7 @@ public class WandOfCorrosion extends Wand {
}
@Override
protected void onZap(Ballistica bolt) {
public void onZap(Ballistica bolt) {
CorrosiveGas gas = Blob.seed(bolt.collisionPos, 50 + 10 * buffedLvl(), CorrosiveGas.class);
CellEmitter.get(bolt.collisionPos).burst(Speck.factory(Speck.CORROSION), 10 );
gas.setStrength(2 + buffedLvl());
@ -73,7 +73,7 @@ public class WandOfCorrosion extends Wand {
}
@Override
protected void fx(Ballistica bolt, Callback callback) {
public void fx(Ballistica bolt, Callback callback) {
MagicMissile.boltFromChar(
curUser.sprite.parent,
MagicMissile.CORROSION,

View File

@ -122,7 +122,7 @@ public class WandOfCorruption extends Wand {
}
@Override
protected void onZap(Ballistica bolt) {
public void onZap(Ballistica bolt) {
Char ch = Actor.findChar(bolt.collisionPos);
if (ch != null){
@ -263,7 +263,7 @@ public class WandOfCorruption extends Wand {
}
@Override
protected void fx(Ballistica bolt, Callback callback) {
public void fx(Ballistica bolt, Callback callback) {
MagicMissile.boltFromChar( curUser.sprite.parent,
MagicMissile.SHADOW,
curUser.sprite,

View File

@ -63,7 +63,7 @@ public class WandOfDisintegration extends DamageWand {
}
@Override
protected void onZap( Ballistica beam ) {
public void onZap(Ballistica beam) {
boolean terrainAffected = false;
@ -127,7 +127,7 @@ public class WandOfDisintegration extends DamageWand {
}
@Override
protected void fx( Ballistica beam, Callback callback ) {
public void fx(Ballistica beam, Callback callback) {
int cell = beam.path.get(Math.min(beam.dist, distance()));
curUser.sprite.parent.add(new Beam.DeathRay(curUser.sprite.center(), DungeonTilemap.raisedTileCenterToWorld( cell )));

View File

@ -31,6 +31,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Burning;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Cripple;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Paralysis;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.mage.WildMagic;
import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Blazing;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MagesStaff;
@ -70,7 +71,7 @@ public class WandOfFireblast extends DamageWand {
ConeAOE cone;
@Override
protected void onZap( Ballistica bolt ) {
public void onZap(Ballistica bolt) {
ArrayList<Char> affectedChars = new ArrayList<>();
ArrayList<Integer> adjacentCells = new ArrayList<>();
@ -138,7 +139,7 @@ public class WandOfFireblast extends DamageWand {
}
@Override
protected void fx( Ballistica bolt, Callback callback ) {
public void fx(Ballistica bolt, Callback callback) {
//need to perform flame spread logic here so we can determine what cells to put flames in.
// 5/7/9 distance
@ -172,6 +173,9 @@ public class WandOfFireblast extends DamageWand {
@Override
protected int chargesPerCast() {
if (charger != null && charger.target.buff(WildMagic.WildMagicTracker.class) != null){
return 1;
}
//consumes 30% of current charges, rounded up, with a min of 1 and a max of 3.
return (int) GameMath.gate(1, (int)Math.ceil(curCharges*0.3f), 3);
}

View File

@ -54,7 +54,7 @@ public class WandOfFrost extends DamageWand {
}
@Override
protected void onZap(Ballistica bolt) {
public void onZap(Ballistica bolt) {
Heap heap = Dungeon.level.heaps.get(bolt.collisionPos);
if (heap != null) {
@ -93,7 +93,7 @@ public class WandOfFrost extends DamageWand {
}
@Override
protected void fx(Ballistica bolt, Callback callback) {
public void fx(Ballistica bolt, Callback callback) {
MagicMissile.boltFromChar(curUser.sprite.parent,
MagicMissile.FROST,
curUser.sprite,

View File

@ -63,7 +63,7 @@ public class WandOfLightning extends DamageWand {
}
@Override
protected void onZap( Ballistica bolt ) {
public void onZap(Ballistica bolt) {
//lightning deals less damage per-target, the more targets that are hit.
float multipler = 0.4f + (0.6f/affected.size());
@ -124,7 +124,7 @@ public class WandOfLightning extends DamageWand {
}
@Override
protected void fx( Ballistica bolt, Callback callback ) {
public void fx(Ballistica bolt, Callback callback) {
affected.clear();
arcs.clear();

View File

@ -64,7 +64,7 @@ public class WandOfLivingEarth extends DamageWand {
}
@Override
protected void onZap(Ballistica bolt) {
public void onZap(Ballistica bolt) {
Char ch = Actor.findChar(bolt.collisionPos);
int damage = damageRoll();
int armorToAdd = damage;
@ -175,7 +175,7 @@ public class WandOfLivingEarth extends DamageWand {
}
@Override
protected void fx(Ballistica bolt, Callback callback) {
public void fx(Ballistica bolt, Callback callback) {
MagicMissile.boltFromChar(curUser.sprite.parent,
MagicMissile.EARTH,
curUser.sprite,

View File

@ -53,7 +53,7 @@ public class WandOfMagicMissile extends DamageWand {
}
@Override
protected void onZap( Ballistica bolt ) {
public void onZap(Ballistica bolt) {
Char ch = Actor.findChar( bolt.collisionPos );
if (ch != null) {

View File

@ -65,7 +65,7 @@ public class WandOfPrismaticLight extends DamageWand {
}
@Override
protected void onZap(Ballistica beam) {
public void onZap(Ballistica beam) {
affectMap(beam);
if (Dungeon.level.viewDistance < 6 ){
@ -138,7 +138,7 @@ public class WandOfPrismaticLight extends DamageWand {
}
@Override
protected void fx( Ballistica beam, Callback callback ) {
public void fx(Ballistica beam, Callback callback) {
curUser.sprite.parent.add(
new Beam.LightRay(curUser.sprite.center(), DungeonTilemap.raisedTileCenterToWorld(beam.collisionPos)));
callback.call();

View File

@ -30,6 +30,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Corruption;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Doom;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.mage.WildMagic;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.NPC;
import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile;
import com.shatteredpixel.shatteredpixeldungeon.items.Dewdrop;
@ -82,7 +83,7 @@ public class WandOfRegrowth extends Wand {
}
@Override
protected void onZap( Ballistica bolt ) {
public void onZap(Ballistica bolt) {
ArrayList<Integer> cells = new ArrayList<>(cone.cells);
@ -231,7 +232,7 @@ public class WandOfRegrowth extends Wand {
}
protected void fx( Ballistica bolt, Callback callback ) {
public void fx(Ballistica bolt, Callback callback) {
// 4/6/8 distance
int maxDist = 2 + 2*chargesPerCast();
@ -263,8 +264,11 @@ public class WandOfRegrowth extends Wand {
@Override
protected int chargesPerCast() {
if (charger != null && charger.target.buff(WildMagic.WildMagicTracker.class) != null){
return 1;
}
//consumes 30% of current charges, rounded up, with a min of 1 and a max of 3.
return (int)GameMath.gate(1, (int)Math.ceil(curCharges*0.3f), 3);
return (int) GameMath.gate(1, (int)Math.ceil(curCharges*0.3f), 3);
}
@Override

View File

@ -58,7 +58,7 @@ public class WandOfTransfusion extends Wand {
private boolean freeCharge = false;
@Override
protected void onZap(Ballistica beam) {
public void onZap(Ballistica beam) {
for (int c : beam.subPath(0, beam.dist))
CellEmitter.center(c).burst( BloodParticle.BURST, 1 );
@ -148,7 +148,7 @@ public class WandOfTransfusion extends Wand {
}
@Override
protected void fx(Ballistica beam, Callback callback) {
public void fx(Ballistica beam, Callback callback) {
curUser.sprite.parent.add(
new Beam.HealthRay(curUser.sprite.center(), DungeonTilemap.raisedTileCenterToWorld(beam.collisionPos)));
callback.call();

View File

@ -32,7 +32,7 @@ public class WandOfWarding extends Wand {
}
@Override
protected int collisionProperties(int target) {
public int collisionProperties(int target) {
if (Dungeon.level.heroFOV[target]) return Ballistica.STOP_TARGET;
else return Ballistica.PROJECTILE;
}
@ -77,7 +77,7 @@ public class WandOfWarding extends Wand {
}
@Override
protected void onZap(Ballistica bolt) {
public void onZap(Ballistica bolt) {
int target = bolt.collisionPos;
Char ch = Actor.findChar(target);
@ -122,7 +122,7 @@ public class WandOfWarding extends Wand {
}
@Override
protected void fx(Ballistica bolt, Callback callback) {
public void fx(Ballistica bolt, Callback callback) {
MagicMissile m = MagicMissile.boltFromChar(curUser.sprite.parent,
MagicMissile.WARD,
curUser.sprite,