/** @param observer */
 public void removeObserver(ActionObserver observer) {
   observers.remove(observer);
   lock.lock();
   try {
     onceUsedObservers.remove(observer);
   } finally {
     lock.unlock();
   }
 }
 /**
  * Return true if remove operation is successful Return false if remove encountered some problems
  *
  * @param item
  * @return true or false
  */
 public boolean removeItemFromStorage(Item item) {
   return storageItems.remove(item);
 }
  @Override
  public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon) {
    int npcId = npc.getId();

    if (npcId == LOCK) {
      _lock = null;
      cancelQuestTimers("spawn_lock");
      startQuestTimer("spawn_lock", 300000, null, null);
    } else if (Arrays.binarySearch(TOWER_MONSTERS, npcId) >= 0) {
      int managerId = 0;

      for (L2ZoneType zone :
          ZoneManager.getInstance().getZones(npc.getX(), npc.getY(), npc.getZ())) {
        if (ZONES.containsValue(zone.getId())) {
          for (int i : ZONES.keySet()) {
            if (ZONES.get(i) == zone.getId()) {
              managerId = i;
              break;
            }
          }
        }
      }

      if ((managerId > 0) && _spawns.containsKey(managerId)) {
        List<L2Npc> spawned = _spawns.get(managerId);
        spawned.remove(npc);
        if (spawned.isEmpty() && DOORS.containsKey(managerId)) {
          int[] doorList = DOORS.get(managerId);
          DoorTable.getInstance().getDoor(doorList[1]).openMe();
          _spawns.remove(managerId);
        }
      }
    } else if (npcId == MUTATED_ELPY) {
      _challengeState = STATE_SPORE_CHALLENGE_IN_PROGRESS;
      markElpyRespawn();
      DoorTable.getInstance().getDoor(18250025).closeMe();
      ZoneManager.getInstance().getZoneById(200100).setEnabled(true);

      for (int i = 0; i < 10; i++) {
        addSpawn(SPORE_BASIC, -45474, 247450, -13994, 49152, false, 0, false);
      }
    } else if ((npcId == SPORE_BASIC) && (_challengeState == STATE_SPORE_CHALLENGE_IN_PROGRESS)) {
      _sporeSpawn.remove(npc);
      spawnRandomSpore();
      spawnRandomSpore();
    } else if ((npcId >= SPORE_FIRE)
        && (npcId <= SPORE_EARTH)
        && ((_challengeState == STATE_SPORE_CHALLENGE_IN_PROGRESS)
            || (_challengeState == STATE_SPORE_CHALLENGE_SUCCESSFULL))) {
      _sporeSpawn.remove(npc);

      if (_challengeState == STATE_SPORE_CHALLENGE_IN_PROGRESS) {
        _despawnedSporesCount--;
        int sporeGroup = getSporeGroup(npcId);

        if (sporeGroup >= 0) {
          if ((npcId == SPORE_FIRE) || (npcId == SPORE_WIND)) {
            _indexCount[sporeGroup] += 2;
          } else {
            _indexCount[sporeGroup] -= 2;
          }

          if (_indexCount[Math.abs(sporeGroup - 1)] > 0) {
            _indexCount[Math.abs(sporeGroup - 1)]--;
          } else if (_indexCount[Math.abs(sporeGroup - 1)] < 0) {
            _indexCount[Math.abs(sporeGroup - 1)]++;
          }

          if ((Math.abs(_indexCount[sporeGroup]) < ELEMENT_INDEX_LIMIT)
              && (Math.abs(_indexCount[sporeGroup]) > 0)
              && ((_indexCount[sporeGroup] % 20) == 0)
              && (getRandom(100) < 50)) {
            String el = ELEMENTS_NAME[Arrays.binarySearch(ELEMENTS, npcId)];
            for (L2Npc spore : _sporeSpawn) {
              if ((spore != null) && !spore.isDead() && (spore.getId() == npcId)) {
                broadcastNpcSay(spore, Say2.NPC_ALL, SPORES_NPCSTRING_ID[getRandom(4)], el);
              }
            }
          }
          if (Math.abs(_indexCount[sporeGroup]) < ELEMENT_INDEX_LIMIT) {
            if ((((_indexCount[sporeGroup] > 0) && ((npcId == SPORE_FIRE) || (npcId == SPORE_WIND)))
                    || ((_indexCount[sporeGroup] <= 0)
                        && ((npcId == SPORE_WATER) || (npcId == SPORE_EARTH))))
                && (getRandom(1000) > 200)) {
              spawnOppositeSpore(npcId);
            } else {
              spawnRandomSpore();
            }
          } else
          // index value was reached
          {
            _challengeState = STATE_SPORE_CHALLENGE_SUCCESSFULL;
            _despawnedSporesCount = 0;
            _winIndex = Arrays.binarySearch(ELEMENTS, npcId);
            int[] coord = SPORES_MERGE_POSITION[_winIndex];

            for (L2Npc spore : _sporeSpawn) {
              if ((spore != null) && !spore.isDead()) {
                moveTo(spore, coord);
              }
            }

            startQuestTimer("despawn_total", 3000, null, null);
          }
        }
      }
    }
    return super.onKill(npc, killer, isSummon);
  }
  @Override
  public final String onAdvEvent(String event, L2Npc npc, L2PcInstance player) {
    String htmltext = event;

    // Timer. Spawns Naia Lock
    if (event.equalsIgnoreCase("spawn_lock")) {
      htmltext = null;
      _lock = (L2MonsterInstance) addSpawn(LOCK, 16409, 244438, 11620, -1048, false, 0, false);
      _counter = 90;
    }

    // Timer. Depending of _challengeState despans all spawned spores, or spores, reached assembly
    // point
    else if (event.equalsIgnoreCase("despawn_total")) {
      // Spores is not attacked too long - despawn them all, reinit values
      if (_challengeState == STATE_SPORE_IDLE_TOO_LONG) {
        for (L2Npc spore : _sporeSpawn) {
          if ((spore != null) && !spore.isDead()) {
            spore.deleteMe();
          }
        }
        _sporeSpawn.clear();
        initSporeChallenge();
      }
      // Spores are moving to assembly point. Despawn all reached, check for reached spores count.
      else if ((_challengeState == STATE_SPORE_CHALLENGE_SUCCESSFULL) && (_winIndex >= 0)) {
        // Requirements are met, despawn all spores, spawn Epidos
        if ((_despawnedSporesCount >= 10) || _sporeSpawn.isEmpty()) {
          if (!_sporeSpawn.isEmpty()) {
            for (L2Npc spore : _sporeSpawn) {
              if ((spore != null) && !spore.isDead()) {
                spore.deleteMe();
              }
            }
          }
          _sporeSpawn.clear();
          _despawnedSporesCount = 0;
          int[] coords = SPORES_MERGE_POSITION[_winIndex];
          addSpawn(EPIDOSES[_winIndex], coords[0], coords[1], coords[2], 0, false, 0, false);
          initSporeChallenge();
        }
        // Requirements aren't met, despawn reached spores
        else {
          Iterator<L2Npc> it = _sporeSpawn.iterator();
          while (it.hasNext()) {
            L2Npc spore = it.next();
            if ((spore != null)
                && !spore.isDead()
                && (spore.getX() == spore.getSpawn().getX())
                && (spore.getY() == spore.getSpawn().getY())) {
              spore.deleteMe();
              it.remove();
              _despawnedSporesCount++;
            }
          }

          startQuestTimer("despawn_total", 3000, null, null);
        }
      }
    }

    if (npc == null) {
      return null;
    }

    final int npcId = npc.getId();

    if (event.equalsIgnoreCase("despawn_spore")
        && !npc.isDead()
        && (_challengeState == STATE_SPORE_CHALLENGE_IN_PROGRESS)) {
      htmltext = null;

      _sporeSpawn.remove(npc);
      npc.deleteMe();

      if (npcId == SPORE_BASIC) {
        spawnRandomSpore();
        spawnRandomSpore();
      } else if ((npcId >= SPORE_FIRE) && (npcId <= SPORE_EARTH)) {
        _despawnedSporesCount++;

        if (_despawnedSporesCount < SELF_DESPAWN_LIMIT) {
          spawnOppositeSpore(npcId);
        } else {
          _challengeState = STATE_SPORE_IDLE_TOO_LONG;
          startQuestTimer("despawn_total", 60000, null, null);
        }
      }
    } else if (event.equalsIgnoreCase("18492-05.htm")) {
      if ((_lock == null) || (_lock.getCurrentHp() > (_lock.getMaxHp() / 10))) {
        htmltext = null;
        if (_lock != null) {
          _lock.deleteMe();
          _lock = null;
        }
        cancelQuestTimers("spawn_lock");
        startQuestTimer("spawn_lock", 300000, null, null);
        npc.setTarget(player);
        npc.doCast(SkillData.getInstance().getSkill(5527, 1));
      }
    } else if (event.equalsIgnoreCase("teleport") && (_lock != null)) {
      htmltext = null;
      L2Party party = player.getParty();
      if (party != null) {
        if (Util.checkIfInRange(3000, party.getLeader(), npc, true)) {
          for (L2PcInstance partyMember : party.getMembers()) {
            if (Util.checkIfInRange(2000, partyMember, npc, true)) {
              partyMember.teleToLocation(-47271, 246098, -9120, true);
            }
          }
          _lock.deleteMe();
          _lock = null;
          cancelQuestTimers("spawn_lock");
          startQuestTimer("spawn_lock", 1200000, null, null);
        } else {
          npc.setTarget(player);
          npc.doCast(SkillData.getInstance().getSkill(5527, 1));
        }
      } else {
        player.teleToLocation(-47271, 246098, -9120);
        _lock.deleteMe();
        _lock = null;
        cancelQuestTimers("spawn_lock");
        startQuestTimer("spawn_lock", 1200000, null, null);
      }
    } else if (event.equalsIgnoreCase("go")
        && _activeRooms.containsKey(npcId)
        && !_activeRooms.get(npcId)) {
      htmltext = null;
      L2Party party = player.getParty();

      if (party != null) {
        removeForeigners(npcId, party);
        startRoom(npcId);
        ThreadPoolManager.getInstance().scheduleGeneral(new StopRoomTask(npcId), 300000);
      } else {
        player.sendPacket(SystemMessageId.CAN_OPERATE_MACHINE_WHEN_IN_PARTY);
      }
    }
    return htmltext;
  }