v0.4.1: reworked evil eyes

This commit is contained in:
Evan Debenham 2016-07-20 16:29:17 -04:00 committed by Evan Debenham
parent 52fc2b1a7d
commit 093635c454
4 changed files with 189 additions and 47 deletions

View File

@ -31,11 +31,14 @@ import com.shatteredpixel.shatteredpixeldungeon.items.Dewdrop;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfDisintegration;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Grim;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Vampiric;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.EyeSprite;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.utils.Bundle;
import com.watabou.utils.Random;
import java.util.HashSet;
@ -53,12 +56,24 @@ public class Eye extends Mob {
maxLvl = 25;
flying = true;
HUNTING = new Hunting();
loot = new Dewdrop();
lootChance = 0.5f;
properties.add(Property.DEMONIC);
}
@Override
public int damageRoll() {
return Random.NormalIntRange(20, 30);
}
@Override
public int attackSkill( Char target ) {
return 30;
}
@Override
public int drRoll() {
@ -66,65 +81,89 @@ public class Eye extends Mob {
}
private Ballistica beam;
private int beamCooldown;
public boolean beamCharged;
@Override
protected boolean canAttack( Char enemy ) {
beam = new Ballistica( pos, enemy.pos, Ballistica.STOP_TERRAIN);
return beam.subPath(1, beam.dist).contains(enemy.pos);
if (beamCooldown == 0) {
Ballistica aim = new Ballistica(pos, enemy.pos, Ballistica.STOP_TERRAIN);
if (enemy.invisible == 0 && aim.subPath(1, aim.dist).contains(enemy.pos)){
beam = aim;
return true;
} else
//if the beam is charged, it has to attack, will aim at previous location of hero.
return beamCharged;
} else
return super.canAttack(enemy);
}
@Override
public int attackSkill( Char target ) {
return 30;
}
@Override
protected float attackDelay() {
return 1.6f;
protected boolean act() {
if (beamCooldown > 0)
beamCooldown--;
return super.act();
}
@Override
protected boolean doAttack( Char enemy ) {
spend( attackDelay() );
boolean rayVisible = false;
for (int i : beam.subPath(0, beam.dist)) {
if (Dungeon.visible[i]) {
rayVisible = true;
if (beamCooldown > 0) {
return super.doAttack(enemy);
} else if (!beamCharged){
((EyeSprite)sprite).charge( enemy.pos );
spend( attackDelay()*2f );
beamCharged = true;
return true;
} else {
spend( attackDelay() );
if (Dungeon.visible[pos]) {
sprite.zap( beam.collisionPos );
return false;
} else {
deathGaze();
return true;
}
}
if (rayVisible) {
sprite.attack( beam.collisionPos );
return false;
} else {
attack( enemy );
return true;
}
}
@Override
public boolean attack( Char enemy ) {
public void deathGaze(){
if (!beamCharged || beamCooldown > 0 || beam == null)
return;
beamCharged = false;
beamCooldown = Random.IntRange(3, 6);
boolean terrainAffected = false;
for (int pos : beam.subPath(1, beam.dist)) {
if (Level.flamable[pos]) {
Dungeon.level.destroy( pos );
GameScene.updateMap( pos );
terrainAffected = true;
}
Char ch = Actor.findChar( pos );
if (ch == null) {
continue;
}
if (hit( this, ch, true )) {
ch.damage( Random.NormalIntRange( 14, 20 ), this );
ch.damage( Random.NormalIntRange( 30, 40 ), this );
if (Dungeon.visible[pos]) {
ch.sprite.flash();
CellEmitter.center( pos ).burst( PurpleParticle.BURST, Random.IntRange( 1, 2 ) );
}
if (!ch.isAlive() && ch == Dungeon.hero) {
Dungeon.fail( getClass() );
GLog.n( Messages.get(this, "deathgaze_kill") );
@ -133,10 +172,37 @@ public class Eye extends Mob {
ch.sprite.showStatus( CharSprite.NEUTRAL, ch.defenseVerb() );
}
}
return true;
if (terrainAffected) {
Dungeon.observe();
}
beam = null;
sprite.idle();
}
private static final String BEAM_TARGET = "beamTarget";
private static final String BEAM_COOLDOWN = "beamCooldown";
private static final String BEAM_CHARGED = "beamCharged";
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
if (beam != null)
bundle.put( BEAM_TARGET, beam.collisionPos);
bundle.put( BEAM_COOLDOWN, beamCooldown );
bundle.put( BEAM_CHARGED, beamCharged );
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
if (bundle.contains(BEAM_TARGET))
beam = new Ballistica(pos, bundle.getInt(BEAM_TARGET), Ballistica.STOP_TERRAIN);
beamCooldown = bundle.getInt(BEAM_COOLDOWN);
beamCharged = bundle.getBoolean(BEAM_CHARGED);
}
private static final HashSet<Class<?>> RESISTANCES = new HashSet<>();
static {
RESISTANCES.add( WandOfDisintegration.class );
@ -158,4 +224,14 @@ public class Eye extends Mob {
public HashSet<Class<?>> immunities() {
return IMMUNITIES;
}
private class Hunting extends Mob.Hunting{
@Override
public boolean act(boolean enemyInFOV, boolean justAlerted) {
//always attack if the beam is charged, no exceptions
if (beamCharged)
enemyInFOV = true;
return super.act(enemyInFOV, justAlerted);
}
}
}

View File

@ -192,6 +192,17 @@ public class MagicMissile extends Emitter {
return true;
};
};
public static final Emitter.Factory ATTRACTING = new Factory() {
@Override
public void emit( Emitter emitter, int index, float x, float y ) {
((MagicParticle)emitter.recycle( MagicParticle.class )).resetAttract( x, y );
}
@Override
public boolean lightMode() {
return true;
};
};
public MagicParticle() {
super();
@ -210,6 +221,17 @@ public class MagicMissile extends Emitter {
left = lifespan;
}
public void resetAttract( float x, float y) {
revive();
//size = 8;
left = lifespan;
speed.polar( Random.Float( PointF.PI2 ), Random.Float( 16, 32 ) );
this.x = x - speed.x * lifespan;
this.y = y - speed.y * lifespan;
}
@Override
public void update() {

View File

@ -384,7 +384,7 @@ actors.mobs.elemental.desc=Wandering fire elementals are a byproduct of summonin
actors.mobs.eye.name=evil eye
actors.mobs.eye.deathgaze_kill=The deathgaze killed you...
actors.mobs.eye.desc=One of this demon's other names is "orb of hatred", because when it sees an enemy, it uses its deathgaze recklessly, often ignoring its allies and wounding them.
actors.mobs.eye.desc=Evil Eyes are floating balls of pent up demonic energy. While they are capable of melee combat, their true strength comes from their magic.\n\nAfter building energy for a short time an Evil Eye will unleash a devastating beam of energy called the _deathgaze._ Anything within the Evil Eye's sights will take tremendous damage, wise adventurers will run for cover.
actors.mobs.fetidrat.name=fetid rat
actors.mobs.fetidrat.desc=Something is clearly wrong with this rat. Its greasy black fur and rotting skin are very different from the healthy rats you've seen previously. It's pale green eyes make it seem especially menacing.\n\nThe rat carries a cloud of horrible stench with it, it's overpoweringly strong up close.\n\nDark ooze dribbles from the rat's mouth, it eats through the floor but seems to dissolve in water.

View File

@ -23,12 +23,19 @@ package com.shatteredpixel.shatteredpixeldungeon.sprites;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.DungeonTilemap;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Eye;
import com.shatteredpixel.shatteredpixeldungeon.effects.Beam;
import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile;
import com.watabou.noosa.TextureFilm;
import com.watabou.noosa.particles.Emitter;
public class EyeSprite extends MobSprite {
private int attackPos;
private int zapPos;
private Animation charging;
private Emitter chargeParticles;
public EyeSprite() {
super();
@ -39,33 +46,70 @@ public class EyeSprite extends MobSprite {
idle = new Animation( 8, true );
idle.frames( frames, 0, 1, 2 );
charging = new Animation( 12, true);
charging.frames( frames, 3, 4 );
chargeParticles = centerEmitter();
chargeParticles.autoKill = false;
chargeParticles.pour(MagicMissile.MagicParticle.ATTRACTING, 0.05f);
chargeParticles.on = false;
run = new Animation( 12, true );
run.frames( frames, 5, 6 );
attack = new Animation( 8, false );
attack.frames( frames, 4, 3 );
zap = attack.clone();
die = new Animation( 8, false );
die.frames( frames, 7, 8, 9 );
play( idle );
}
@Override
public void attack( int pos ) {
attackPos = pos;
super.attack( pos );
public void link(Char ch) {
super.link(ch);
if (((Eye)ch).beamCharged) play(charging);
}
@Override
public void update() {
super.update();
chargeParticles.pos(center());
chargeParticles.visible = visible;
}
public void charge( int pos ){
turnTo(ch.pos, pos);
play(charging);
}
@Override
public void play(Animation anim) {
chargeParticles.on = anim == charging;
super.play(anim);
}
@Override
public void zap( int pos ) {
zapPos = pos;
super.zap( pos );
}
@Override
public void onComplete( Animation anim ) {
super.onComplete( anim );
if (anim == attack) {
if (Dungeon.visible[ch.pos] || Dungeon.visible[attackPos]) {
parent.add( new Beam.DeathRay( center(), DungeonTilemap.tileCenterToWorld( attackPos ) ) );
if (anim == zap) {
if (Dungeon.visible[ch.pos] || Dungeon.visible[zapPos]) {
parent.add( new Beam.DeathRay( center(), DungeonTilemap.tileCenterToWorld( zapPos ) ) );
}
((Eye)ch).deathGaze();
ch.next();
} else if (anim == die){
chargeParticles.killAndErase();
}
}
}