v0.7.4: initial implement of wand of living earth, needs lots of polishing

This commit is contained in:
Evan Debenham 2019-06-29 00:23:06 -04:00
parent ddce36a6a6
commit 0f77d26986
5 changed files with 396 additions and 5 deletions

View File

@ -52,7 +52,6 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.SnipersMark;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vertigo;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Weakness;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.NPC;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.CheckedCell;
import com.shatteredpixel.shatteredpixeldungeon.effects.Flare;
@ -93,6 +92,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfTenacity;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.Scroll;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfMagicMapping;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfLivingEarth;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.SpiritBow;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Blocking;
@ -102,14 +102,12 @@ import com.shatteredpixel.shatteredpixeldungeon.journal.Notes;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.Chasm;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.Trap;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.plants.Earthroot;
import com.shatteredpixel.shatteredpixeldungeon.plants.Swiftthistle;
import com.shatteredpixel.shatteredpixeldungeon.scenes.AlchemyScene;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.scenes.InterlevelScene;
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.shatteredpixel.shatteredpixeldungeon.scenes.SurfaceScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.HeroSprite;
@ -992,6 +990,11 @@ public class Hero extends Char {
if (armor != null) {
damage = armor.absorb( damage );
}
WandOfLivingEarth.RockArmor rockArmor = buff(WandOfLivingEarth.RockArmor.class);
if (rockArmor != null) {
damage = rockArmor.absorb(damage);
}
return damage;
}

View File

@ -58,6 +58,7 @@ public class MagicMissile extends Emitter {
public static final int BEACON = 6;
public static final int SHADOW = 7;
public static final int RAINBOW = 8;
public static final int EARTH = 9;
public static final int FIRE_CONE = 100;
public static final int FOLIAGE_CONE = 101;
@ -133,6 +134,10 @@ public class MagicMissile extends Emitter {
size( 4 );
pour( RainbowParticle.BURST, 0.01f );
break;
case EARTH:
size( 4 );
pour( EarthParticle.FACTORY, 0.01f );
break;
case FIRE_CONE:
size( 10 );
@ -245,13 +250,25 @@ public class MagicMissile extends Emitter {
}
};
public static final Emitter.Factory BURST = new Factory() {
@Override
public void emit( Emitter emitter, int index, float x, float y ) {
((EarthParticle)emitter.recycle( EarthParticle.class )).resetBurst( x, y );
}
};
public static final Emitter.Factory ATTRACT = new Factory() {
@Override
public void emit( Emitter emitter, int index, float x, float y ) {
((EarthParticle)emitter.recycle( EarthParticle.class )).resetAttract( x, y );
}
};
public EarthParticle() {
super();
lifespan = 0.5f;
color( ColorMath.random( 0x555555, 0x777766 ) );
acc.set( 0, +40 );
}
@ -264,8 +281,31 @@ public class MagicMissile extends Emitter {
left = lifespan;
size = 4;
if (Random.Int(10) == 0){
color(ColorMath.random(0xFFF568, 0x80791A));
} else {
color(ColorMath.random(0x805500, 0x332500));
}
speed.set( Random.Float( -10, +10 ), Random.Float( -10, +10 ) );
}
public void resetBurst( float x, float y ){
reset(x, y);
speed.polar( Random.Float( PointF.PI2 ), Random.Float( 40, 60 ) );
}
public void resetAttract( float x, float y ){
reset(x, y);
speed.polar( Random.Float( PointF.PI2 ), Random.Float( 24, 32 ) );
this.x = x - speed.x * lifespan;
this.y = y - speed.y * lifespan;
acc.set( 0, 0 );
}
}
public static class WhiteParticle extends PixelParticle {

View File

@ -26,6 +26,7 @@ import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfLivingEarth;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.plants.Earthroot;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
@ -92,6 +93,11 @@ public class ChaliceOfBlood extends Artifact {
damage = armor.absorb(damage);
}
WandOfLivingEarth.RockArmor rockArmor = hero.buff(WandOfLivingEarth.RockArmor.class);
if (rockArmor != null) {
damage = rockArmor.absorb(damage);
}
damage -= hero.drRoll();
hero.sprite.operate( hero.pos );

View File

@ -0,0 +1,333 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2019 Evan Debenham
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.items.wands;
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.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.NPC;
import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MagesStaff;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.sprites.StatueSprite;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Bundle;
import com.watabou.utils.Callback;
import com.watabou.utils.PathFinder;
import com.watabou.utils.Random;
public class WandOfLivingEarth extends DamageWand {
{
image = ItemSpriteSheet.WAND_LIVING_EARTH;
}
@Override
public int min(int lvl) {
return 2 + lvl;
}
@Override
public int max(int lvl) {
return 6 + 3*lvl;
}
@Override
protected void onZap(Ballistica bolt) {
Char ch = Actor.findChar(bolt.collisionPos);
int damage = damageRoll();
EarthGuardian guardian = null;
for (Mob m : Dungeon.level.mobs){
if (m instanceof EarthGuardian){
guardian = (EarthGuardian) m;
break;
}
}
RockArmor buff = curUser.buff(RockArmor.class);
if (buff == null && guardian == null){
buff = Buff.affect(curUser, RockArmor.class);
}
if (buff != null){
buff.addArmor(level(), damage);
}
//shooting at the guardian
if (guardian != null && guardian == ch){
guardian.sprite.centerEmitter().burst(MagicMissile.EarthParticle.ATTRACT, 5 + level() / 2);
guardian.setInfo(curUser, level(), damage);
processSoulMark(guardian, chargesPerCast());
//shooting the guardian at a location
} else if ( guardian == null && buff != null && buff.armor >= buff.armorToGuardian()){
//create a new guardian
guardian = new EarthGuardian();
guardian.setInfo(curUser, level(), buff.armor);
//if the collision pos is occupied (likely will be), then spawn the guardian in the
//adjacent cell which is closes to the user of the wand.
if (ch != null){
ch.sprite.centerEmitter().burst(MagicMissile.EarthParticle.BURST, 5 + level()/2);
processSoulMark(ch, chargesPerCast());
ch.damage(damage, this);
int closest = -1;
boolean[] passable = Dungeon.level.passable;
for (int n : PathFinder.NEIGHBOURS9) {
int c = bolt.collisionPos + n;
if (passable[c] && Actor.findChar( c ) == null
&& (closest == -1 || (Dungeon.level.trueDistance(c, curUser.pos) < (Dungeon.level.trueDistance(closest, curUser.pos))))) {
closest = c;
}
}
if (closest == -1){
curUser.sprite.centerEmitter().burst(MagicMissile.EarthParticle.ATTRACT, 5 + level()/2);
return; //do not spawn guardian or detach buff
} else {
guardian.pos = closest;
GameScene.add(guardian);
}
guardian.aggro(ch);
} else {
guardian.pos = bolt.collisionPos;
GameScene.add(guardian);
}
guardian.sprite.centerEmitter().burst(MagicMissile.EarthParticle.ATTRACT, 5 + level()/2);
buff.detach();
//shooting at a location/enemy with no guardian being shot
} else {
if (ch != null) {
ch.sprite.centerEmitter().burst(MagicMissile.EarthParticle.BURST, 5 + level() / 2);
processSoulMark(ch, chargesPerCast());
ch.damage(damage, this);
} else {
Dungeon.level.press(bolt.collisionPos, null, true);
}
if (guardian == null) {
curUser.sprite.centerEmitter().burst(MagicMissile.EarthParticle.ATTRACT, 5 + level() / 2);
} else {
guardian.sprite.centerEmitter().burst(MagicMissile.EarthParticle.ATTRACT, 5 + level() / 2);
guardian.setInfo(curUser, level(), damage);
if (ch != null) guardian.aggro( ch );
}
}
}
@Override
protected void fx(Ballistica bolt, Callback callback) {
MagicMissile.boltFromChar(curUser.sprite.parent,
MagicMissile.EARTH,
curUser.sprite,
bolt.collisionPos,
callback);
Sample.INSTANCE.play(Assets.SND_ZAP);
}
@Override
public void onHit(MagesStaff staff, Char attacker, Char defender, int damage) {
EarthGuardian guardian = null;
for (Mob m : Dungeon.level.mobs){
if (m instanceof EarthGuardian){
guardian = (EarthGuardian) m;
break;
}
}
if (guardian != null){
guardian.sprite.centerEmitter().burst(MagicMissile.EarthParticle.ATTRACT, 5 + level() / 2);
guardian.setInfo(Dungeon.hero, level(), damage);
} else {
attacker.sprite.centerEmitter().burst(MagicMissile.EarthParticle.ATTRACT, 5 + level() / 2);
Buff.affect(attacker, RockArmor.class).addArmor(level(), damage);
}
}
@Override
public void staffFx(MagesStaff.StaffParticle particle) {
//TODO
super.staffFx(particle);
}
public static class RockArmor extends Buff {
private int wandLevel;
private int armor;
private void addArmor( int wandLevel, int armor ){
this.wandLevel = Math.max(this.wandLevel, wandLevel);
this.armor += armor;
}
private int armorToGuardian(){
return 10 + wandLevel*5;
}
public int absorb( int damage ) {
int block = damage - damage/2;
if (armor <= block) {
detach();
return damage - armor;
} else {
armor -= block;
BuffIndicator.refreshHero();
return damage - block;
}
}
@Override
public int icon() {
return BuffIndicator.ARMOR;
}
@Override
public String toString() {
return Messages.get(this, "name");
}
@Override
public String desc() {
return Messages.get( this, "desc", armor, armorToGuardian());
}
private static final String WAND_LEVEL = "wand_level";
private static final String ARMOR = "armor";
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
bundle.put(WAND_LEVEL, wandLevel);
bundle.put(ARMOR, armor);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
wandLevel = bundle.getInt(WAND_LEVEL);
armor = bundle.getInt(ARMOR);
}
}
public static class EarthGuardian extends NPC {
{
//TODO visuals
spriteClass = StatueSprite.class;
alignment = Alignment.ALLY;
state = HUNTING;
intelligentAlly = true;
WANDERING = new Wandering();
//before other mobs
actPriority = MOB_PRIO + 1;
HP = HT = 0;
}
private int wandLevel = -1;
private void setInfo(Hero hero, int wandLevel, int healthToAdd){
if (wandLevel > this.wandLevel) {
this.wandLevel = wandLevel;
HT = 20 + 10 * wandLevel;
}
HP = Math.min(HT, HP + healthToAdd);
//same as the hero
defenseSkill = hero.lvl + 4;
}
@Override
public int attackSkill(Char target) {
//same as the hero
return defenseSkill + 5;
}
@Override
public int damageRoll() {
return Random.NormalIntRange(wandLevel, 6 + 3*wandLevel);
}
@Override
public int drRoll() {
return Random.NormalIntRange(wandLevel, 4 + 4*wandLevel);
}
private static final String DEFENCE = "defence";
private static final String WAND_LEVEL = "wand_level";
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
bundle.put(DEFENCE, defenseSkill);
bundle.put(WAND_LEVEL, wandLevel);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
defenseSkill = bundle.getInt(DEFENCE);
wandLevel = bundle.getInt(WAND_LEVEL);
}
private class Wandering extends Mob.Wandering{
@Override
public boolean act(boolean enemyInFOV, boolean justAlerted) {
if (!enemyInFOV){
Buff.affect(Dungeon.hero, RockArmor.class).addArmor(wandLevel, HP);
Dungeon.hero.sprite.centerEmitter().burst(MagicMissile.EarthParticle.ATTRACT, 5 + HT/10);
destroy();
sprite.die();
return true;
} else {
return super.act(enemyInFOV, justAlerted);
}
}
}
}
}

View File

@ -1174,6 +1174,15 @@ items.wands.wandoflightning.ondeath=You killed yourself with your own Wand of Li
items.wands.wandoflightning.desc=This wand is made out of solid metal, making it surprisingly heavy. Two prongs curve together at the tip, and electricity arcs between them.
items.wands.wandoflightning.stats_desc=This wand sends powerful lightning arcing through whatever it is shot at, dealing _%1$d-%2$d damage._ This electricity can bounce between many nearby foes, spreading damage between them. The lightning and damage spread much more effectively in water. If you're too close, you may get shocked as well!
items.wands.wandoflivingearth.name=wand of living earth
items.wands.wandoflivingearth.staff_name=staff of living earth
items.wands.wandoflivingearth.desc=TODO
items.wands.wandoflivingearth.stats_desc=TODO\n\ndeals _%1$d-%2$d damage._
items.wands.wandoflivingearth$rockarmor.name=Rock Armor
items.wands.wandoflivingearth$rockarmor.desc=TODO\n\nArmor: %1$d\n\nNeeded for Guardian: %2$d
items.wands.wandoflivingearth$earthguardian.name=earthen guardian
items.wands.wandoflivingearth$earthguardian.desc=TODO (probably want to give some basic stats)
items.wands.wandofmagicmissile.name=wand of magic missile
items.wands.wandofmagicmissile.staff_name=staff of magic missile
items.wands.wandofmagicmissile.desc=This fairly plain wand launches missiles of pure magical energy. While not as strong as other wands, it makes up for it somewhat with more available charges.