@Override
  public boolean attack(Char enemy) {

    for (int i = 1; i < Ballistica.distance; i++) {

      int pos = Ballistica.trace[i];

      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.getSprite().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(getName()), Dungeon.depth));
          GLog.n(TXT_DEATHGAZE_KILLED, getName());
        }
      } else {
        ch.getSprite().showStatus(CharSprite.NEUTRAL, ch.defenseVerb());
      }
    }

    return true;
  }
  private void jump() {
    timeToJump = JUMP_DELAY;

    for (int i = 0; i < 4; i++) {
      int trapPos;
      do {
        trapPos = Random.Int(Level.LENGTH);
      } while (!Level.fieldOfView[trapPos] || !Level.passable[trapPos]);

      if (Dungeon.level.map[trapPos] == Terrain.INACTIVE_TRAP) {
        Level.set(trapPos, Terrain.POISON_TRAP);
        GameScene.updateMap(trapPos);
        ScrollOfMagicMapping.discover(trapPos);
      }
    }

    int newPos;
    do {
      newPos = Random.Int(Level.LENGTH);
    } while (!Level.fieldOfView[newPos]
        || !Level.passable[newPos]
        || (enemy != null && Level.adjacent(newPos, enemy.pos))
        || Actor.findChar(newPos) != null);

    sprite.move(pos, newPos);
    move(newPos);

    if (Dungeon.visible[newPos]) {
      CellEmitter.get(newPos).burst(Speck.factory(Speck.WOOL), 6);
      Sample.INSTANCE.play(Assets.SND_PUFF);
    }

    spend(1 / speed());
  }
  public void wither() {
    Dungeon.level.uproot(pos);

    sprite.kill();
    if (Dungeon.visible[pos]) {
      CellEmitter.get(pos).burst(LeafParticle.GENERAL, 6);
    }

    if (Dungeon.hero.subClass == HeroSubClass.WARDEN) {
      if (Random.Int(5) == 0) {
        Dungeon.level.drop(Generator.random(Generator.Category.SEED), pos).sprite.drop();
      }
      if (Random.Int(5) == 0) {
        Dungeon.level.drop(new Dewdrop(), pos).sprite.drop();
      }
    }
  }
  public void effect(int pos, Char ch) {
    if (ch instanceof Hero) {
      Hero hero = (Hero) ch;
      ScrollOfTeleportation.teleportHero(hero);
      hero.spendAndNext(1);
      hero.curAction = null;

    } else if (ch instanceof Mob) {

      int newPos = Dungeon.level.randomRespawnCell();
      if (newPos != -1) {
        ch.setPos(newPos);
        ch.getSprite().place(ch.getPos());
        ch.getSprite().setVisible(Dungeon.visible[pos]);
      }
    }

    if (Dungeon.visible[pos]) {
      CellEmitter.get(pos).start(Speck.factory(Speck.LIGHT), 0.2f, 3);
    }
  }
  @Override
  public void shatter(int cell) {

    PathFinder.buildDistanceMap(cell, BArray.not(Level.losBlocking, null), DISTANCE);

    boolean procd = false;

    Blob[] blobs = {
      Dungeon.level.blobs.get(ToxicGas.class), Dungeon.level.blobs.get(ParalyticGas.class)
    };

    for (int j = 0; j < blobs.length; j++) {

      Blob blob = blobs[j];
      if (blob == null) {
        continue;
      }

      for (int i = 0; i < Level.LENGTH; i++) {
        if (PathFinder.distance[i] < Integer.MAX_VALUE) {

          int value = blob.cur[i];
          if (value > 0) {

            blob.cur[i] = 0;
            blob.volume -= value;
            procd = true;

            if (Dungeon.visible[i]) {
              CellEmitter.get(i).burst(Speck.factory(Speck.DISCOVER), 1);
            }
          }
        }
      }
    }

    boolean heroAffected = PathFinder.distance[Dungeon.hero.pos] < Integer.MAX_VALUE;

    if (procd) {

      if (Dungeon.visible[cell]) {
        splash(cell);
        Sample.INSTANCE.play(Assets.SND_SHATTER);
      }

      setKnown();

      if (heroAffected) {
        GLog.p(TXT_FRESHNESS);
      }

    } else {

      super.shatter(cell);

      if (heroAffected) {
        GLog.i(TXT_FRESHNESS);
        setKnown();
      }
    }
  }