@Override
  protected boolean canAttack(Char enemy) {

    beam = new Ballistica(pos, enemy.pos, Ballistica.STOP_TERRAIN);

    return beam.subPath(1, beam.dist).contains(enemy.pos);
  }
  @Override
  public boolean attack(Char enemy) {

    for (int pos : beam.subPath(1, beam.dist)) {

      Char ch = Actor.findChar(pos);
      if (ch == null) {
        continue;
      }

      if (hit(this, ch, true)) {
        ch.damage(Random.NormalIntRange(14, 20), 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(Utils.format(ResultDescriptions.MOB, Utils.indefinite(name)));
          GLog.n(TXT_DEATHGAZE_KILLED, name);
        }
      } else {
        ch.sprite.showStatus(CharSprite.NEUTRAL, ch.defenseVerb());
      }
    }

    return true;
  }
  @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 (rayVisible) {
      sprite.attack(beam.collisionPos);
      return false;
    } else {
      attack(enemy);
      return true;
    }
  }