/* * Pixel Dungeon * Copyright (C) 2012-2015 Oleg Dolya * * Shattered Pixel Dungeon * Copyright (C) 2014-2016 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.mobs; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Light; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror; import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; import com.shatteredpixel.shatteredpixeldungeon.effects.particles.PurpleParticle; 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; public class Eye extends Mob { { spriteClass = EyeSprite.class; HP = HT = 100; defenseSkill = 20; viewDistance = Light.DISTANCE; EXP = 13; 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() { return Random.NormalIntRange(0, 10); } private Ballistica beam; private int beamCooldown; public boolean beamCharged; @Override protected boolean canAttack( Char enemy ) { if (beamCooldown == 0) { Ballistica aim = new Ballistica(pos, enemy.pos, Ballistica.STOP_TERRAIN); if (enemy.invisible == 0 && Level.fieldOfView[enemy.pos] && 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 protected boolean act() { if (beamCooldown > 0) beamCooldown--; return super.act(); } @Override protected Char chooseEnemy() { if (beamCharged && enemy != null) return enemy; return super.chooseEnemy(); } @Override protected boolean doAttack( Char enemy ) { 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; } } } @Override public void damage(int dmg, Object src) { if (beamCharged) dmg /= 4; super.damage(dmg, src); } 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( 30, 50 ), 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") ); } } else { ch.sprite.showStatus( CharSprite.NEUTRAL, ch.defenseVerb() ); } } 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> RESISTANCES = new HashSet<>(); static { RESISTANCES.add( WandOfDisintegration.class ); RESISTANCES.add( Grim.class ); RESISTANCES.add( Vampiric.class ); } @Override public HashSet> resistances() { return RESISTANCES; } private static final HashSet> IMMUNITIES = new HashSet<>(); static { IMMUNITIES.add( Terror.class ); } @Override public HashSet> 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 && enemy != null) enemyInFOV = true; return super.act(enemyInFOV, justAlerted); } } }