MapleMap.java
Upload User: gwt600
Upload Date: 2021-06-03
Package Size: 704k
Code Size: 91k
Category:

Games

Development Platform:

Java

  1.     /*
  2. This file is part of the OdinMS Maple Story Server
  3. Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
  4. Matthias Butz <matze@odinms.de>
  5. Jan Christian Meyer <vimes@odinms.de>
  6. This program is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU Affero General Public License version 3
  8. as published by the Free Software Foundation. You may not use, modify
  9. or distribute this program under any other version of the
  10. GNU Affero General Public License.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU Affero General Public License for more details.
  15. You should have received a copy of the GNU Affero General Public License
  16. along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  */
  18. package net.sf.odinms.server.maps;
  19. import java.awt.Point;
  20. import java.awt.Rectangle;
  21. import java.rmi.RemoteException;
  22. import java.util.ArrayList;
  23. import java.util.Arrays;
  24. import java.util.Collection;
  25. import java.util.Collections;
  26. import java.util.HashMap;
  27. import java.util.HashSet;
  28. import java.util.LinkedHashMap;
  29. import java.util.LinkedHashSet;
  30. import java.util.LinkedList;
  31. import java.util.List;
  32. import java.util.Map;
  33. import java.util.Random;
  34. import java.util.Set;
  35. import java.util.concurrent.ScheduledFuture;
  36. import java.util.concurrent.atomic.AtomicInteger;
  37. import java.util.Calendar;
  38. import net.sf.odinms.client.Equip;
  39. import net.sf.odinms.client.IItem;
  40. import net.sf.odinms.client.Item;
  41. import net.sf.odinms.client.MapleBuffStat;
  42. import net.sf.odinms.client.MapleCharacter;
  43. import net.sf.odinms.client.MapleClient;
  44. import java.sql.SQLException;
  45. import net.sf.odinms.client.MapleInventoryType;
  46. import net.sf.odinms.client.MaplePet;
  47. import net.sf.odinms.client.SkillFactory;
  48. import net.sf.odinms.client.messages.MessageCallback;
  49. import net.sf.odinms.client.status.MonsterStatus;
  50. import net.sf.odinms.client.status.MonsterStatusEffect;
  51. import net.sf.odinms.net.MaplePacket;
  52. import net.sf.odinms.net.channel.ChannelServer;
  53. import net.sf.odinms.database.DatabaseConnection;
  54. import net.sf.odinms.net.world.MaplePartyCharacter;
  55. import net.sf.odinms.server.MapleItemInformationProvider;
  56. import net.sf.odinms.server.MaplePortal;
  57. import net.sf.odinms.server.MapleStatEffect;
  58. import net.sf.odinms.server.TimerManager;
  59. import java.sql.PreparedStatement;
  60. import net.sf.odinms.server.life.MapleMonster;
  61. import net.sf.odinms.server.life.MapleNPC;
  62. import net.sf.odinms.server.life.SpawnPoint;
  63. import net.sf.odinms.server.maps.pvp.PvPLibrary;
  64. import net.sf.odinms.tools.MaplePacketCreator;
  65. import org.slf4j.Logger;
  66. import org.slf4j.LoggerFactory;
  67. public class MapleMap {
  68.     private static final int MAX_OID = 20000;
  69.     private static final List<MapleMapObjectType> rangedMapobjectTypes = Arrays.asList(MapleMapObjectType.ITEM,
  70.             MapleMapObjectType.MONSTER, MapleMapObjectType.DOOR, MapleMapObjectType.SUMMON, MapleMapObjectType.REACTOR);
  71.     /**
  72.      * Holds a mapping of all oid -> MapleMapObject on this map. mapobjects is NOT a synchronized collection since it
  73.      * has to be synchronized together with runningOid that's why all access to mapobjects have to be done trough an
  74.      * explicit synchronized block
  75.      */
  76.     private Map<Integer, MapleMapObject> mapobjects = new LinkedHashMap<Integer, MapleMapObject>();
  77.     private Collection<SpawnPoint> monsterSpawn = new LinkedList<SpawnPoint>();
  78.     private AtomicInteger spawnedMonstersOnMap = new AtomicInteger(0);
  79.     private Collection<MapleCharacter> characters = new LinkedHashSet<MapleCharacter>();
  80.     private Map<Integer, MaplePortal> portals = new HashMap<Integer, MaplePortal>();
  81.     private List<Rectangle> areas = new ArrayList<Rectangle>();
  82.     private MapleFootholdTree footholds = null;
  83.     private int mapid;
  84.     private int runningOid = 100;
  85.     private int returnMapId;
  86.     private boolean hasOwner = false;
  87.     private int channel;
  88.     private float monsterRate;
  89.     private String owner;
  90.     private boolean Closed;
  91.     private boolean dropsDisabled = false;
  92.     private int mapPrice;
  93.     private boolean clock;
  94.     private boolean boat;
  95.     private boolean docked;
  96.     private String mapName;
  97.     private boolean Public = true;
  98.     private String streetName;
  99.     private MapleMapEffect mapEffect = null;
  100.     private boolean everlast = false;
  101.     private int forcedReturnMap = 999999999;
  102.     private int timeLimit;
  103.     private static Logger log = LoggerFactory.getLogger(MapleMap.class);
  104.     private MapleMapTimer mapTimer = null;
  105.     private int dropLife = 180000; //Time in milliseconds drops last before disappearing
  106.     private int decHP = 0;
  107.     private int protectItem = 0;
  108.     private boolean town;
  109.     private boolean showGate = false;
  110.     public MapleMap(int mapid, int channel, int returnMapId, float monsterRate) {
  111.         this.mapid = mapid;
  112.         this.channel = channel;
  113.         this.returnMapId = returnMapId;
  114.         if (monsterRate > 0) {
  115.             this.monsterRate = monsterRate;
  116.             boolean greater1 = monsterRate > 1.0;
  117.             this.monsterRate = (float) Math.abs(1.0 - this.monsterRate);
  118.             this.monsterRate = this.monsterRate / 2.0f;
  119.             if (greater1) {
  120.                 this.monsterRate = 1.0f + this.monsterRate;
  121.             } else {
  122.                 this.monsterRate = 1.0f - this.monsterRate;
  123.             }
  124.             TimerManager.getInstance().register(new RespawnWorker(), 10000);
  125.         }
  126.     }
  127.     public void killAllmonster(MapleClient c)
  128.     {
  129.       List<MapleMapObject> monsters = getMapObjectsInRange(c.getPlayer().getPosition(), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER));
  130.             for (MapleMapObject monstermo : monsters) {
  131.                 MapleMonster monster = (MapleMonster) monstermo;
  132.                 killMonster(monster, c.getPlayer(), false);
  133.             }
  134.     }
  135.     
  136.     public void toggleDrops() {
  137.         dropsDisabled = !dropsDisabled;
  138.     }
  139.     public int getId() {
  140.         return mapid;
  141.     }
  142.     public MapleMap getReturnMap() {
  143.         return ChannelServer.getInstance(channel).getMapFactory().getMap(returnMapId);
  144.     }
  145.     public int getReturnMapId() {
  146.         return returnMapId;
  147.     }
  148.     public int getForcedReturnId() {
  149.         return forcedReturnMap;
  150.     }
  151.     public MapleMap getForcedReturnMap() {
  152.         return ChannelServer.getInstance(channel).getMapFactory().getMap(forcedReturnMap);
  153.     }
  154.     public void setForcedReturnMap(int map) {
  155.         this.forcedReturnMap = map;
  156.     }
  157.     public int getTimeLimit() {
  158.         return timeLimit;
  159.     }
  160.     public void setTimeLimit(int timeLimit) {
  161.         this.timeLimit = timeLimit;
  162.     }
  163.     public int getCurrentPartyId() {
  164.         for (MapleCharacter chr : this.getCharacters()) {
  165.             if (chr.getPartyId() != -1) {
  166.                 return chr.getPartyId();
  167.             }
  168.         }
  169.         return -1;
  170.     }
  171.     public void addMapObject(MapleMapObject mapobject) {
  172.         synchronized (this.mapobjects) {
  173.             mapobject.setObjectId(runningOid);
  174.             this.mapobjects.put(Integer.valueOf(runningOid), mapobject);
  175.             incrementRunningOid();
  176.         }
  177.     }
  178.     private void spawnAndAddRangedMapObject(MapleMapObject mapobject, DelayedPacketCreation packetbakery, SpawnCondition condition) {
  179.         synchronized (this.mapobjects) {
  180.             mapobject.setObjectId(runningOid);
  181.             synchronized (characters) {
  182.                 for (MapleCharacter chr : characters) {
  183.                     if (condition == null || condition.canSpawn(chr)) {
  184.                         if (chr.getPosition().distanceSq(mapobject.getPosition()) <= MapleCharacter.MAX_VIEW_RANGE_SQ && !chr.isFake()) {
  185.                             packetbakery.sendPackets(chr.getClient());
  186.                             chr.addVisibleMapObject(mapobject);
  187.                         }
  188.                     }
  189.                 }
  190.             }
  191.             this.mapobjects.put(Integer.valueOf(runningOid), mapobject);
  192.             incrementRunningOid();
  193.         }
  194.     }
  195.     public void setPublic(boolean pubic) {
  196.             this.Public = pubic;
  197.         }
  198.     public void removeNpcs() {
  199.             try {
  200.                 java.sql.Connection con = DatabaseConnection.getConnection();
  201.                 PreparedStatement ps;
  202.                 ps = con.prepareStatement("DELETE FROM guildmaps WHERE npcs = npcs");
  203.                 ps.executeUpdate();
  204.                 ps.close();
  205.             } catch (SQLException e) {
  206.             }
  207.         }
  208.     public void removeMobs() {
  209.             try {
  210.                 java.sql.Connection con = DatabaseConnection.getConnection();
  211.                 PreparedStatement ps;
  212.                 ps = con.prepareStatement("DELETE FROM guildmaps WHERE mobs = mobs");
  213.                 ps.executeUpdate();
  214.                 ps.close();
  215.             } catch (SQLException e) {
  216.             }
  217.         }
  218.     public void saveGuildMaps(boolean full) {
  219.             try {
  220.                 java.sql.Connection con = DatabaseConnection.getConnection();
  221.                 PreparedStatement ps;
  222.                 if (full) {
  223.                     ps = con.prepareStatement("INSERT INTO guildmaps (map, owner, price, `public`, owned) VALUES (?, ?, ?, ?, ? );");
  224.                     ps.setInt(1, this.mapid);
  225.                     ps.setString(2, this.owner);
  226.                     ps.setInt(3, this.mapPrice);
  227.                     ps.setInt(4, Public ? 1: 0);
  228.                     ps.setInt(5, hasOwner ? 1: 0);
  229.                     ps.execute();
  230.                 } else {
  231.                     ps = con.prepareStatement("UPDATE guildmaps SET owner = ? WHERE map = ?");
  232.                     ps.setString(1, this.owner);
  233.                     ps.setInt(2, this.mapid);
  234.                     ps.execute();
  235.                     ps.close();
  236.                     ps = con.prepareStatement("UPDATE guildmaps SET price = ? where map = ?");
  237.                     ps.setInt(1, this.mapPrice);
  238.                     ps.setInt(2, this.mapid);
  239.                     ps.close();
  240.                     ps = con.prepareStatement("UPDATE guildmaps SET `public` where map = ?");
  241.                     ps.setInt(1, this.Public ? 1 : 0);
  242.                     ps.setInt(2, this.mapid);
  243.                     ps.close();
  244.                 }
  245.             } catch (SQLException e) {
  246.             }
  247.         }
  248.     public void setPrice(int price) {
  249.             this.mapPrice = price;
  250.         }
  251.     public void setOwned(boolean owner) {
  252.             this.hasOwner = owner;
  253.         }
  254.     public int getPrice() {
  255.             return this.mapPrice;
  256.         }
  257.     public void setOwner(String name) {
  258.             this.owner = name;
  259.         }
  260.     public void setOwnerByName(MapleCharacter gm) {
  261.             this.owner = gm.getGuild().getName();
  262.         }
  263.     private void incrementRunningOid() {
  264.         runningOid++;
  265.         for (int numIncrements = 1; numIncrements < MAX_OID; numIncrements++) {
  266.             if (runningOid > MAX_OID) {
  267.                 runningOid = 100;
  268.             }
  269.             if (this.mapobjects.containsKey(Integer.valueOf(runningOid))) {
  270.                 runningOid++;
  271.             } else {
  272.                 return;
  273.             }
  274.         }
  275.         throw new RuntimeException("Out of OIDs on map " + mapid + " (channel: " + channel + ")");
  276.     }
  277.     public void removeMapObject(int num) {
  278.         synchronized (this.mapobjects) {
  279.             if (mapobjects.containsKey(num)) {
  280.                 this.mapobjects.remove(Integer.valueOf(num));
  281.             }
  282.         }
  283.     }
  284.     public void removeMapObject(MapleMapObject obj) {
  285.         removeMapObject(obj.getObjectId());
  286.     }
  287.     private Point calcPointBelow(Point initial) {
  288.         MapleFoothold fh = footholds.findBelow(initial);
  289.         if (fh == null) {
  290.             return null;
  291.         }
  292.         int dropY = fh.getY1();
  293.         if (!fh.isWall() && fh.getY1() != fh.getY2()) {
  294.             double s1 = Math.abs(fh.getY2() - fh.getY1());
  295.             double s2 = Math.abs(fh.getX2() - fh.getX1());
  296.             double s4 = Math.abs(initial.x - fh.getX1());
  297.             double alpha = Math.atan(s2 / s1);
  298.             double beta = Math.atan(s1 / s2);
  299.             double s5 = Math.cos(alpha) * (s4 / Math.cos(beta));
  300.             if (fh.getY2() < fh.getY1()) {
  301.                 dropY = fh.getY1() - (int) s5;
  302.             } else {
  303.                 dropY = fh.getY1() + (int) s5;
  304.             }
  305.         }
  306.         return new Point(initial.x, dropY);
  307.     }
  308.     private Point calcDropPos(Point initial, Point fallback) {
  309.         Point ret = calcPointBelow(new Point(initial.x, initial.y - 99));
  310.         if (ret == null) {
  311.             return fallback;
  312.         }
  313.         return ret;
  314.     }
  315.     public boolean isOwned() {
  316.             return this.hasOwner;
  317.         }
  318.     public boolean isClosed() {
  319.             return this.Closed;
  320.         }
  321.     public void saveNPCS(String owner) {
  322.             try {
  323.                 java.sql.Connection con = DatabaseConnection.getConnection();
  324.                 PreparedStatement ps;
  325.                 ps = con.prepareStatement("UPDATE guildspawns SET hidden = 1 WHERE guild = ?");
  326.                 ps.setString(1, this.owner);
  327.             } catch (SQLException e) {
  328.             }
  329.         }
  330.     private void dropFromMonster(MapleCharacter dropOwner, MapleMonster monster) {
  331.         if (dropsDisabled || monster.dropsDisabled()) {
  332.             return;
  333.         }
  334.         /*
  335.          * drop logic: decide based on monster what the max drop count is get drops (not allowed: multiple mesos,
  336.          * multiple items of same type exception: event drops) calculate positions
  337.          */
  338.         int maxDrops;
  339.         MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
  340.         final boolean isBoss = monster.isBoss();
  341.         ChannelServer cserv = dropOwner.getClient().getChannelServer();
  342.         if (isBoss) {
  343.             maxDrops = 10 * cserv.getBossDropRate();
  344.         } else {
  345.             maxDrops = 4 * cserv.getDropRate();
  346.         }
  347.         List<Integer> toDrop = new ArrayList<Integer>();
  348.         for (int i = 0; i < maxDrops; i++) {
  349.             toDrop.add(monster.getDrop());
  350.         }
  351.         Set<Integer> alreadyDropped = new HashSet<Integer>();
  352.         int htpendants = 0;
  353.         int htstones = 0;
  354.         int mesos = 0;
  355.         for (int i = 0; i < toDrop.size(); i++) {
  356.             if (toDrop.get(i) == -1) {
  357.                 if (!this.isPQMap()) {
  358.                     if (alreadyDropped.contains(-1)) {
  359.                         if (!isBoss) {
  360.                             toDrop.remove(i);
  361.                             i--;
  362.                         } else {
  363.                             if (mesos < 7) { // 8 bags
  364.                                 mesos++;
  365.                             } else {
  366.                                 toDrop.remove(i);
  367.                                 i--;
  368.                             }
  369.                         }
  370.                     } else {
  371.                         alreadyDropped.add(-1);
  372.                     }
  373.                 }
  374.             } else {
  375.                 if (alreadyDropped.contains(toDrop.get(i)) && !isBoss) {
  376.                     toDrop.remove(i);
  377.                     i--;
  378.                 } else {
  379.                     if (toDrop.get(i) == 2041200) { // stone
  380.                         if (htstones > 2) {
  381.                             toDrop.remove(i);
  382.                             i--;
  383.                             continue;
  384.                         } else {
  385.                             htstones++;
  386.                         }
  387.                     } else if (toDrop.get(i) == 1122000) { // pendant
  388.                         if (htstones > 2) {
  389.                             toDrop.remove(i);
  390.                             i--;
  391.                             continue;
  392.                         } else {
  393.                             htpendants++;
  394.                         }
  395.                     }
  396.                     alreadyDropped.add(toDrop.get(i));
  397.                 }
  398.             }
  399.         }
  400.         if (toDrop.size() > maxDrops) {
  401.             toDrop = toDrop.subList(0, maxDrops);
  402.         }
  403.         if (mesos < 7 && isBoss) {
  404.             for (int i = mesos; i < 7; i++) {
  405.                 toDrop.add(-1);
  406.             }
  407.         }
  408.         if (Math.rint(Math.random() * 20) <= 2 ) {
  409.             toDrop.add(2022065);
  410.         }
  411.         if (Math.rint(Math.random() * 2000) <= 2 ) {
  412.             toDrop.add(5200002);
  413.         }
  414.         if (Math.rint(Math.random() * 20) <= 2 ) {
  415.             toDrop.add(4031250);          /*易宝*/
  416.         }
  417.         Point[] toPoint = new Point[toDrop.size()];
  418.         int shiftDirection = 0;
  419.         int shiftCount = 0;
  420.         int curX = Math.min(Math.max(monster.getPosition().x - 25 * (toDrop.size() / 2), footholds.getMinDropX() + 25), footholds.getMaxDropX() - toDrop.size() * 25);
  421.         int curY = Math.max(monster.getPosition().y, footholds.getY1());
  422.         //int monsterShift = curX -
  423.         while (shiftDirection < 3 && shiftCount < 1000) {
  424.             // TODO for real center drop the monster width is needed o.o"
  425.             if (shiftDirection == 1) {
  426.                 curX += 25;
  427.             } else if (shiftDirection == 2) {
  428.                 curX -= 25;
  429.             }
  430.             // now do it
  431.             for (int i = 0; i < toDrop.size(); i++) {
  432.                 MapleFoothold wall = footholds.findWall(new Point(curX, curY), new Point(curX + toDrop.size() * 25, curY));
  433.                 if (wall != null) {
  434.                     //System.out.println("found a wall. wallX " + wall.getX1() + " curX " + curX);
  435.                     if (wall.getX1() < curX) {
  436.                         shiftDirection = 1;
  437.                         shiftCount++;
  438.                         break;
  439.                     } else if (wall.getX1() == curX) {
  440.                         if (shiftDirection == 0) {
  441.                             shiftDirection = 1;
  442.                         }
  443.                         shiftCount++;
  444.                         break;
  445.                     } else {
  446.                         shiftDirection = 2;
  447.                         shiftCount++;
  448.                         break;
  449.                     }
  450.                 } else if (i == toDrop.size() - 1) {
  451.                     //System.out.println("ok " + curX);
  452.                     shiftDirection = 3;
  453.                 }
  454.                 final Point dropPos = calcDropPos(new Point(curX + i * 25, curY), new Point(monster.getPosition()));
  455.                 toPoint[i] = new Point(curX + i * 25, curY);
  456.                 final int drop = toDrop.get(i);
  457.                 if (drop == -1) { // meso
  458.                     final int mesoRate = ChannelServer.getInstance(dropOwner.getClient().getChannel()).getMesoRate();
  459.                     final int nxRate = ChannelServer.getInstance(dropOwner.getClient().getChannel()).getnxRate();
  460.                     Random r = new Random();
  461.                     double mesoDecrease = Math.pow(0.93, monster.getExp() / 300.0);
  462.                     if (mesoDecrease > 1.0) {
  463.                         mesoDecrease = 1.0;
  464.                     }
  465.                     int tempmeso = Math.min(30000, (int) (mesoDecrease * (monster.getExp()) * (1.0 + r.nextInt(20)) / 10.0));
  466.                     if (dropOwner.getBuffedValue(MapleBuffStat.MESOUP) != null) {
  467.                         tempmeso = (int) (tempmeso * dropOwner.getBuffedValue(MapleBuffStat.MESOUP).doubleValue() / 100.0);
  468.                     }
  469.                     int numbers = (int) Math.floor(Math.random()*4 + 1);
  470.                         int addnx = 1 * nxRate * numbers - 1;
  471.                         int addnxvip = 1 * nxRate * numbers - 1 * 2;
  472.                     if (dropOwner.getClient().getChannel() == ChannelServer.getInstance(dropOwner.getClient().getChannel()).getblackmsVipCh()) {
  473.                         dropOwner.modifyCSPoints(1, addnxvip);
  474.                         dropOwner.getClient().getSession().write(MaplePacketCreator.serverNotice(5, "你所在的频道是VIP频道,你从怪物身上获得 (" + addnxvip +")点点卷 [是普通频道的2倍]."));
  475.                     } else if (dropOwner.getClient().getChannel() != ChannelServer.getInstance(dropOwner.getClient().getChannel()).getblackmsVipCh()) {
  476.                         dropOwner.modifyCSPoints(1, addnx);
  477.                         dropOwner.getClient().getSession().write(MaplePacketCreator.serverNotice(5, "你从怪物身上获得 (" + addnx +")点点卷."));
  478.                     }
  479.                     final int meso = tempmeso * mesoRate;
  480.                     if (meso > 0) {
  481.                         final MapleMonster dropMonster = monster;
  482.                         final MapleCharacter dropChar = dropOwner;
  483.                         TimerManager.getInstance().schedule(new Runnable() {
  484.                             public void run() {
  485.                                 if (dropChar.getMesoMode() == 1 && dropChar.getVip() >= 1)
  486.                                 dropChar.gainMeso(meso, true, true);
  487.                                 else
  488.                                 spawnMesoDrop(meso, meso, dropPos, dropMonster, dropChar, isBoss);
  489.                             }
  490.                         }, monster.getAnimationTime("die1"));
  491.                     }
  492.                 } else {
  493.                     IItem idrop;
  494.                     MapleInventoryType type = ii.getInventoryType(drop);
  495.                     if (type.equals(MapleInventoryType.EQUIP)) {
  496.                         Equip nEquip = ii.randomizeStats(dropOwner.getClient(), (Equip) ii.getEquipById(drop));
  497.                         idrop = nEquip;
  498.                     } else {
  499.                         idrop = new Item(drop, (byte) 0, (short) 1);
  500.                         // Randomize quantity for certain items
  501.                         if (ii.isArrowForBow(drop) || ii.isArrowForCrossBow(drop)) {
  502.                             if (dropOwner.getJob().getId() / 100 == 3) // is there a better way to do this ):
  503.                             {
  504.                                 idrop.setQuantity((short) (1 + 100 * Math.random()));
  505.                             }
  506.                         } else if (ii.isThrowingStar(drop) || ii.isBullet(drop)) {
  507.                             idrop.setQuantity((short) (1));
  508.                         }
  509.                     }
  510.                     final MapleMapItem mdrop = new MapleMapItem(idrop, dropPos, monster, dropOwner);
  511.                     final MapleMapObject dropMonster = monster;
  512.                     final MapleCharacter dropChar = dropOwner;
  513.                     final TimerManager tMan = TimerManager.getInstance();
  514.                     tMan.schedule(new Runnable() {
  515.                         public void run() {
  516.                             spawnAndAddRangedMapObject(mdrop, new DelayedPacketCreation() {
  517.                                 public void sendPackets(MapleClient c) {
  518.                                     c.getSession().write(MaplePacketCreator.dropItemFromMapObject(drop, mdrop.getObjectId(), dropMonster.getObjectId(), isBoss ? 0 : dropChar.getId(), dropMonster.getPosition(), dropPos, (byte) 1));
  519.                                 }
  520.                             }, null);
  521.                             tMan.schedule(new ExpireMapItemJob(mdrop), dropLife);
  522.                         }
  523.                     }, monster.getAnimationTime("die1"));
  524.                 }
  525.             }
  526.         }
  527.     }
  528.    public boolean damageMonster(MapleCharacter chr, MapleMonster monster, int damage) {
  529.         if (monster.getId() == 8800000) {
  530.             Collection<MapleMapObject> objects = chr.getMap().getMapObjects();
  531.             for (MapleMapObject object : objects) {
  532.                 MapleMonster mons = chr.getMap().getMonsterByOid(object.getObjectId());
  533.                 if (mons != null) {
  534.                     if (mons.getId() >= 8800003 && mons.getId() <= 8800010) {
  535.                         return true;
  536.                     }
  537.                 }
  538.             }
  539.         }
  540.         // double checking to potentially avoid synchronisation overhead
  541.         if (monster.isAlive()) {
  542.             synchronized (monster) {
  543.                 if (!monster.isAlive())
  544.                     return false;
  545.                 int sttr = chr.getTotalDex() + chr.getTotalInt() + chr.getTotalLuk() + chr.getTotalStr();
  546. if (chr.getTotalWatk() <= 50000 && damage > 200000000 && sttr <= 128000)
  547. {
  548. MapleMap to = ChannelServer.getInstance(chr.getClient().getChannel()).getMapFactory().getMap(200090300);
  549. try
  550. {
  551. chr.getClient().getChannelServer().getWorldInterface().broadcastMessage(null, MaplePacketCreator.serverNotice(5, (new StringBuilder()).append("[公告]玩家: ").append(chr.getName()).append(" 由于破攻过高,所以被系统直接送往监狱,希望他能痛改前非..!").toString()).getBytes());
  552. }
  553. catch (RemoteException e) { }
  554. chr.dropMessage((new StringBuilder()).append("时间:").append(chr.getDay()).append(",").append(chr.getHour()).append(",").append(chr.getMin()).append(",由于你攻击过高,能打到").append(damage).append("的血。所以系统送你去监狱,呆几天才可以出来!~~~~PS:如果你有任何理由,请直接将这句话截图发给GM理论!!").toString());
  555. chr.changeMap(to, to.getPortal(0));
  556. }
  557. if (chr.getTotalWatk() >= 1000 && chr.getTotalWatk() <= 10000 && damage > 400000000 && sttr <= 248000)
  558. {
  559. MapleMap to = ChannelServer.getInstance(chr.getClient().getChannel()).getMapFactory().getMap(200090300);
  560. try
  561. {
  562. chr.getClient().getChannelServer().getWorldInterface().broadcastMessage(null, MaplePacketCreator.serverNotice(5, (new StringBuilder()).append("[公告]玩家: ").append(chr.getName()).append(" 由于破攻过高,所以被系统直接送往监狱,希望他能痛改前非..!").toString()).getBytes());
  563. }
  564. catch (RemoteException e) { }
  565. chr.dropMessage((new StringBuilder()).append("时间:").append(chr.getDay()).append(",").append(chr.getHour()).append(",").append(chr.getMin()).append(",由于你攻击过高,能打到").append(damage).append("的血。所以系统送你去监狱,呆几天才可以出来!~~~~PS:如果你有任何理由,请直接将这句话截图发给GM理论!!").toString());
  566. chr.changeMap(to, to.getPortal(0));
  567. }
  568. if (chr.getTotalWatk() < 1000 && damage > 200000000 && sttr <= 248000)
  569. {
  570. MapleMap to = ChannelServer.getInstance(chr.getClient().getChannel()).getMapFactory().getMap(200090300);
  571. try
  572. {
  573. chr.getClient().getChannelServer().getWorldInterface().broadcastMessage(null, MaplePacketCreator.serverNotice(5, (new StringBuilder()).append("[公告]玩家: ").append(chr.getName()).append(" 由于破攻过高,所以被系统直接送往监狱,希望他能痛改前非..!").toString()).getBytes());
  574. }
  575. catch (RemoteException e) { }
  576. chr.dropMessage((new StringBuilder()).append("时间:").append(chr.getDay()).append(",").append(chr.getHour()).append(",").append(chr.getMin()).append(",由于你攻击过高,能打到").append(damage).append("的血。所以系统送你去监狱,呆几天才可以出来!~~~~PS:如果你有任何理由,请直接将这句话截图发给GM理论!!").toString());
  577. chr.changeMap(to, to.getPortal(0));
  578. }
  579. if (chr.getTotalWatk() < 1000 && damage > 100000000 && chr.getReborns() <= 200)
  580. {
  581. MapleMap to = ChannelServer.getInstance(chr.getClient().getChannel()).getMapFactory().getMap(200090300);
  582. try
  583. {
  584. chr.getClient().getChannelServer().getWorldInterface().broadcastMessage(null, MaplePacketCreator.serverNotice(5, (new StringBuilder()).append("[公告]玩家: ").append(chr.getName()).append(" 由于破攻过高,所以被系统直接送往监狱,希望他能痛改前非..!").toString()).getBytes());
  585. }
  586. catch (RemoteException e) { }
  587. chr.dropMessage((new StringBuilder()).append("时间:").append(chr.getDay()).append(",").append(chr.getHour()).append(",").append(chr.getMin()).append(",由于你攻击过高,能打到").append(damage).append("的血。所以系统送你去监狱,呆几天才可以出来!~~~~PS:如果你有任何理由,请直接将这句话截图发给GM理论!!").toString());
  588. chr.changeMap(to, to.getPortal(0));
  589. }
  590. if (chr.getTotalWatk() < 2000 && damage > 150000000 && chr.getReborns() <= 200)
  591. {
  592. MapleMap to = ChannelServer.getInstance(chr.getClient().getChannel()).getMapFactory().getMap(200090300);
  593. try
  594. {
  595. chr.getClient().getChannelServer().getWorldInterface().broadcastMessage(null, MaplePacketCreator.serverNotice(5, (new StringBuilder()).append("[公告]玩家: ").append(chr.getName()).append(" 由于破攻过高,所以被系统直接送往监狱,希望他能痛改前非..!").toString()).getBytes());
  596. }
  597. catch (RemoteException e) { }
  598. chr.dropMessage((new StringBuilder()).append("时间:").append(chr.getDay()).append(",").append(chr.getHour()).append(",").append(chr.getMin()).append(",由于你攻击过高,能打到").append(damage).append("的血。所以系统送你去监狱,呆几天才可以出来!~~~~PS:如果你有任何理由,请直接将这句话截图发给GM理论!!").toString());
  599. chr.changeMap(to, to.getPortal(0));
  600. }
  601.                 if (damage > 0) {
  602.                     int monsterhp = monster.getHp();
  603.                     monster.damage(chr, damage, true);
  604.                     if (!monster.isAlive()) { // monster just died
  605.                         killMonster(monster, chr, true);
  606.                         if (monster.getId() >= 8810002 && monster.getId() <= 8810009) {
  607.                             Collection<MapleMapObject> objects = chr.getMap().getMapObjects();
  608.                             for (MapleMapObject object : objects) {
  609.                                 MapleMonster mons = chr.getMap().getMonsterByOid(object.getObjectId());
  610.                                 if (mons != null) {
  611.                                     if (mons.getId() == 8810018) {
  612.                                         damageMonster(chr, mons, monsterhp);
  613.                                     }
  614.                                 }
  615.                             }
  616.                         }
  617.                     } else {
  618.                         if (monster.getId() >= 8810002 && monster.getId() <= 8810009) {
  619.                             Collection<MapleMapObject> objects = chr.getMap().getMapObjects();
  620.                             for (MapleMapObject object : objects) {
  621.                                 MapleMonster mons = chr.getMap().getMonsterByOid(object.getObjectId());
  622.                                 if (mons != null) {
  623.                                     if (mons.getId() == 8810018) {
  624.                                         damageMonster(chr, mons, damage);
  625.                                     }
  626.                                 }
  627.                             }
  628.                         }
  629.                     }
  630.                 }
  631.             }
  632.             // the monster is dead, as damageMonster returns immediately for dead monsters this makes
  633.             // this block implicitly synchronized for ONE monster
  634.             return true;
  635.         }
  636.         return false;
  637.     }
  638.     public boolean isPubic() {
  639.             return this.Public;
  640.         }
  641.     public String getOwner() {
  642.             return this.owner;
  643.         }
  644.     public void killMonster(final MapleMonster monster, final MapleCharacter chr, final boolean withDrops) {
  645.         killMonster(monster, chr, withDrops, false, 1);
  646.     }
  647.     public void killMonster(final MapleMonster monster, final MapleCharacter chr, final boolean withDrops, final boolean secondTime) {
  648.         killMonster(monster, chr, withDrops, secondTime, 1);
  649.     }
  650.     @SuppressWarnings("static-access")
  651.     public void killMonster(final MapleMonster monster, final MapleCharacter chr, final boolean withDrops, final boolean secondTime, int animation) {
  652.         if (monster.getId() == 8810018 && !secondTime) {
  653.             TimerManager.getInstance().schedule(new Runnable() {
  654.                 @Override
  655.                 public void run() {
  656.                     killMonster(monster, chr, withDrops, true, 1);
  657.                     killAllMonsters(false);
  658.                 }
  659.             }, 3000);
  660.             return;
  661.         }
  662.         if (monster.getBuffToGive() > -1) {
  663.             broadcastMessage(MaplePacketCreator.showOwnBuffEffect(monster.getBuffToGive(), 11));
  664.             MapleItemInformationProvider mii = MapleItemInformationProvider.getInstance();
  665.             MapleStatEffect statEffect = mii.getItemEffect(monster.getBuffToGive());
  666.             synchronized (this.characters) {
  667.                 for (MapleCharacter character : this.characters) {
  668.                     if (character.isAlive()) {
  669.                         statEffect.applyTo(character);
  670.                         broadcastMessage(MaplePacketCreator.showBuffeffect(character.getId(), monster.getBuffToGive(), 11, (byte) 1));
  671.                     }
  672.                 }
  673.             }
  674.         }
  675.         if (monster.getId() == 8810018) {
  676.             try {
  677.                 chr.getClient().getChannelServer().getWorldInterface().broadcastMessage(chr.getName(), MaplePacketCreator.serverNotice(6, "To the crew that have finally conquered Horned Tail after numerous attempts, I salute thee! You are the true heroes of Leafre!!").getBytes());
  678.             } catch (RemoteException e) {
  679.                 chr.getClient().getChannelServer().reconnectWorld();
  680.             }
  681.         }
  682.         spawnedMonstersOnMap.decrementAndGet();
  683.         monster.setHp(0);
  684.         broadcastMessage(MaplePacketCreator.killMonster(monster.getObjectId(), animation), monster.getPosition());
  685.         removeMapObject(monster);
  686.         if (monster.getId() >= 8800003 && monster.getId() <= 8800010) {
  687.             boolean makeZakReal = true;
  688.             Collection<MapleMapObject> objects = getMapObjects();
  689.             for (MapleMapObject object : objects) {
  690.                 MapleMonster mons = getMonsterByOid(object.getObjectId());
  691.                 if (mons != null) {
  692.                     if (mons.getId() >= 8800003 && mons.getId() <= 8800010) {
  693.                         makeZakReal = false;
  694.                     }
  695.                 }
  696.             }
  697.             if (makeZakReal) {
  698.                 for (MapleMapObject object : objects) {
  699.                     MapleMonster mons = chr.getMap().getMonsterByOid(object.getObjectId());
  700.                     if (mons != null) {
  701.                         if (mons.getId() == 8800000) {
  702.                             makeMonsterReal(mons);
  703.                             updateMonsterController(mons);
  704.                         }
  705.                     }
  706.                 }
  707.             }
  708.         }
  709.         MapleCharacter dropOwner = monster.killBy(chr);
  710.         if (withDrops && !monster.dropsDisabled()) {
  711.             if (dropOwner == null) {
  712.                 dropOwner = chr;
  713.             }
  714.             dropFromMonster(dropOwner, monster);
  715.         }
  716.     }
  717.     public boolean isCPQMap() {
  718. switch (this.getId()) {
  719. case 980000101:
  720. case 980000201:
  721. case 980000301:
  722. case 980000401:
  723. case 980000501:
  724. case 980000601:
  725. return true;
  726. }
  727. return false;
  728. }
  729.     public void spawnItemDropdir(final MapleMapObject dropper, final MapleCharacter owner, final IItem item, final boolean ffaDrop, boolean expire) {
  730.         TimerManager tMan = TimerManager.getInstance();
  731. int posx = (int)(Math.floor(Math.random() * 1274D) - 366D);
  732. int posy = (int)(32D - Math.floor(Math.random() * 450D));
  733. Point pos1 = new Point(posx, posy);
  734. final Point droppos = calcDropPos(pos1, pos1);
  735. final MapleMapItem drop = new MapleMapItem(item, droppos, dropper, owner);
  736.         spawnAndAddRangedMapObject(drop, new DelayedPacketCreation() {
  737.             public void sendPackets(MapleClient c) {
  738.                 c.getSession().write(MaplePacketCreator.dropItemFromMapObject(item.getItemId(), drop.getObjectId(), 0, ffaDrop ? 0 : owner.getId(),
  739.                         dropper.getPosition(), droppos, (byte) 1));
  740.             }
  741.         }, null);
  742.         broadcastMessage(MaplePacketCreator.dropItemFromMapObject(item.getItemId(), drop.getObjectId(), 0, ffaDrop ? 0
  743.                 : owner.getId(), dropper.getPosition(), droppos, (byte) 0), drop.getPosition());
  744.         if (expire) {
  745.             tMan.schedule(new ExpireMapItemJob(drop), dropLife);
  746.         }
  747.         activateItemReactors(drop);
  748.     }
  749.     public List<MapleMapObject> getMapObjectsInBox(Rectangle box, List<MapleMapObjectType> types) {
  750.         List<MapleMapObject> ret = new LinkedList<MapleMapObject>();
  751.         synchronized (mapobjects) {
  752.             for (MapleMapObject l : mapobjects.values()) {
  753.                 if (types.contains(l.getType())) {
  754.                     if (box.contains(l.getPosition())) {
  755.                         ret.add(l);
  756.                     }
  757.                 }
  758.             }
  759.         }
  760.         return ret;
  761.     }
  762.     public void killAllMonsters(boolean drop) {
  763.         List<MapleMapObject> players = null;
  764.         if (drop) {
  765.             players = getAllPlayer();
  766.         }
  767.         List<MapleMapObject> monsters = getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER));
  768.         for (MapleMapObject monstermo : monsters) {
  769.             MapleMonster monster = (MapleMonster) monstermo;
  770.             spawnedMonstersOnMap.decrementAndGet();
  771.             monster.setHp(0);
  772.             broadcastMessage(MaplePacketCreator.killMonster(monster.getObjectId(), true), monster.getPosition());
  773.             removeMapObject(monster);
  774.             if (drop) {
  775.                 int random = (int) Math.random() * (players.size());
  776.                 dropFromMonster((MapleCharacter) players.get(random), monster);
  777.             }
  778.         }
  779.     }
  780.     public void killAllMonsters() {
  781.         List<MapleMapObject> monsters = getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER));
  782.         for (MapleMapObject monstermo : monsters) {
  783.             MapleMonster monster = (MapleMonster) monstermo;
  784.             spawnedMonstersOnMap.decrementAndGet();
  785.             removeMapObject(monster);
  786.         }
  787.     }
  788.     public boolean isPurpleCPQMap() {
  789. switch (this.getId()) {
  790. case 980000301:
  791. case 980000401:
  792. return true;
  793. }
  794. return false;
  795. }
  796.     public void killMonster(int monsId) {
  797.         for (MapleMapObject mmo : getMapObjects()) {
  798.             if (mmo instanceof MapleMonster) {
  799.                 if (((MapleMonster) mmo).getId() == monsId) {
  800.                     this.killMonster((MapleMonster) mmo, (MapleCharacter) getAllPlayer().get(0), false);
  801.                 }
  802.             }
  803.         }
  804.     }
  805.     public List<MapleMapObject> getAllPlayer() {
  806.         return getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.PLAYER));
  807.     }
  808.     public void destroyReactor(int oid) {
  809.         synchronized (this.mapobjects) {
  810.             final MapleReactor reactor = getReactorByOid(oid);
  811.             TimerManager tMan = TimerManager.getInstance();
  812.             broadcastMessage(MaplePacketCreator.destroyReactor(reactor));
  813.             reactor.setAlive(false);
  814.             removeMapObject(reactor);
  815.             reactor.setTimerActive(false);
  816.             if (reactor.getDelay() > 0) {
  817.                 tMan.schedule(new Runnable() {
  818.                     @Override
  819.                     public void run() {
  820.                         respawnReactor(reactor);
  821.                     }
  822.                 }, reactor.getDelay());
  823.             }
  824.         }
  825.     }
  826.     /*
  827.      * command to reset all item-reactors in a map to state 0 for GM/NPC use - not tested (broken reactors get removed
  828.      * from mapobjects when destroyed) Should create instances for multiple copies of non-respawning reactors...
  829.      */
  830.     public void resetReactors() {
  831.         synchronized (this.mapobjects) {
  832.             for (MapleMapObject o : mapobjects.values()) {
  833.                 if (o.getType() == MapleMapObjectType.REACTOR) {
  834.                     ((MapleReactor) o).setState((byte) 0);
  835.                     ((MapleReactor) o).setTimerActive(false);
  836.                     broadcastMessage(MaplePacketCreator.triggerReactor((MapleReactor) o, 0));
  837.                 }
  838.             }
  839.         }
  840.     }
  841.     /*
  842.      * command to shuffle the positions of all reactors in a map for PQ purposes (such as ZPQ/LMPQ)
  843.      */
  844.     public void shuffleReactors() {
  845.         List<Point> points = new ArrayList<Point>();
  846.         synchronized (this.mapobjects) {
  847.             for (MapleMapObject o : mapobjects.values()) {
  848.                 if (o.getType() == MapleMapObjectType.REACTOR) {
  849.                     points.add(((MapleReactor) o).getPosition());
  850.                 }
  851.             }
  852.             Collections.shuffle(points);
  853.             for (MapleMapObject o : mapobjects.values()) {
  854.                 if (o.getType() == MapleMapObjectType.REACTOR) {
  855.                     ((MapleReactor) o).setPosition(points.remove(points.size() - 1));
  856.                 }
  857.             }
  858.         }
  859.     }
  860.     /**
  861.      * Automagically finds a new controller for the given monster from the chars on the map...
  862.      *
  863.      * @param monster
  864.      */
  865.     public void updateMonsterController(MapleMonster monster) {
  866.         synchronized (monster) {
  867.             if (!monster.isAlive()) {
  868.                 return;
  869.             }
  870.             if (monster.getController() != null) {
  871.                 // monster has a controller already, check if he's still on this map
  872.                 if (monster.getController().getMap() != this) {
  873.                     log.warn("Monstercontroller wasn't on same map");
  874.                     monster.getController().stopControllingMonster(monster);
  875.                 } else {
  876.                     // controller is on the map, monster has an controller, everything is fine
  877.                     return;
  878.                 }
  879.             }
  880.             int mincontrolled = -1;
  881.             MapleCharacter newController = null;
  882.             synchronized (characters) {
  883.                 for (MapleCharacter chr : characters) {
  884.                     if (!chr.isHidden() && (chr.getControlledMonsters().size() < mincontrolled || mincontrolled == -1)) {
  885.                         if (!chr.getName().equals("FaekChar")) { // TODO remove me for production release
  886.                             mincontrolled = chr.getControlledMonsters().size();
  887.                             newController = chr;
  888.                         }
  889.                     }
  890.                 }
  891.             }
  892.             if (newController != null) { // was a new controller found? (if not no one is on the map)
  893.                 if (monster.isFirstAttack()) {
  894.                     newController.controlMonster(monster, true);
  895.                     monster.setControllerHasAggro(true);
  896.                     monster.setControllerKnowsAboutAggro(true);
  897.                 } else {
  898.                     newController.controlMonster(monster, false);
  899.                 }
  900.             }
  901.         }
  902.     }
  903.     public Collection<MapleMapObject> getMapObjects() {
  904.         return Collections.unmodifiableCollection(mapobjects.values());
  905.     }
  906.     public boolean containsNPC(int npcid) {
  907.         synchronized (mapobjects) {
  908.             for (MapleMapObject obj : mapobjects.values()) {
  909.                 if (obj.getType() == MapleMapObjectType.NPC) {
  910.                     if (((MapleNPC) obj).getId() == npcid) {
  911.                         return true;
  912.                     }
  913.                 }
  914.             }
  915.         }
  916.         return false;
  917.     }
  918.     public MapleMapObject getMapObject(int oid) {
  919.         return mapobjects.get(oid);
  920.     }
  921.     /**
  922.      * returns a monster with the given oid, if no such monster exists returns null
  923.      *
  924.      * @param oid
  925.      * @return
  926.      */
  927.     public MapleMonster getMonsterByOid(int oid) {
  928.         MapleMapObject mmo = getMapObject(oid);
  929.         if (mmo == null) {
  930.             return null;
  931.         }
  932.         if (mmo.getType() == MapleMapObjectType.MONSTER) {
  933.             return (MapleMonster) mmo;
  934.         }
  935.         return null;
  936.     }
  937.     public MapleReactor getReactorByOid(int oid) {
  938.         MapleMapObject mmo = getMapObject(oid);
  939.         if (mmo == null) {
  940.             return null;
  941.         }
  942.         if (mmo.getType() == MapleMapObjectType.REACTOR) {
  943.             return (MapleReactor) mmo;
  944.         }
  945.         return null;
  946.     }
  947.     public void addClock(int seconds) {
  948. broadcastMessage(MaplePacketCreator.getClock(seconds));
  949. }
  950.     public MapleReactor getReactorByName(String name) {
  951.         synchronized (mapobjects) {
  952.             for (MapleMapObject obj : mapobjects.values()) {
  953.                 if (obj.getType() == MapleMapObjectType.REACTOR) {
  954.                     if (((MapleReactor) obj).getName().equals(name)) {
  955.                         return (MapleReactor) obj;
  956.                     }
  957.                 }
  958.             }
  959.         }
  960.         return null;
  961.     }
  962.     //backwards compatible
  963.     public void spawnMonsterOnGroudBelow(MapleMonster mob, Point pos) {
  964.         spawnMonsterOnGroundBelow(mob, pos);
  965.     }
  966.     public void spawnMonsterOnGroundBelow(MapleMonster mob, Point pos) {
  967.         Point spos = getGroundBelow(pos);
  968.         mob.setPosition(spos);
  969.         spawnMonster(mob);
  970.     }
  971.     public void spawnFakeMonsterOnGroundBelow(MapleMonster mob, Point pos) {
  972.         Point spos = getGroundBelow(pos);
  973.         mob.setPosition(spos);
  974.         spawnFakeMonster(mob);
  975.     }
  976.     public Point getGroundBelow(Point pos) {
  977.         Point spos = new Point(pos.x, pos.y - 1);
  978.         spos = calcPointBelow(spos);
  979.         spos.y -= 1;
  980.         return spos;
  981.     }
  982.     public void spawnRevives(final MapleMonster monster) {
  983.         monster.setMap(this);
  984.         synchronized (this.mapobjects) {
  985.             spawnAndAddRangedMapObject(monster, new DelayedPacketCreation() {
  986.                 public void sendPackets(MapleClient c) {
  987.                     c.getSession().write(MaplePacketCreator.spawnMonster(monster, false));
  988.                 }
  989.             }, null);
  990.             updateMonsterController(monster);
  991.         }
  992.         spawnedMonstersOnMap.incrementAndGet();
  993.     }
  994.     public void spawnMonster(final MapleMonster monster) {
  995.         monster.setMap(this);
  996.         synchronized (this.mapobjects) {
  997.             spawnAndAddRangedMapObject(monster, new DelayedPacketCreation() {
  998.                 public void sendPackets(MapleClient c) {
  999.                     c.getSession().write(MaplePacketCreator.spawnMonster(monster, true));
  1000.                     if (monster.getId() == 9300166) {
  1001.                         TimerManager.getInstance().schedule(new Runnable() {
  1002.                             @Override
  1003.                             public void run() {
  1004.                                 killMonster(monster, (MapleCharacter) getAllPlayer().get(0), false, false, 3);
  1005.                             }
  1006.                         }, new Random().nextInt(4500 + 500));
  1007.                     }
  1008.                 }
  1009.             }, null);
  1010.             if (monster.hasBossHPBar()) {
  1011.                 broadcastMessage(monster.makeBossHPBarPacket(), monster.getPosition());
  1012.             }
  1013.             updateMonsterController(monster);
  1014.         }
  1015.         spawnedMonstersOnMap.incrementAndGet();
  1016.     }
  1017.     public void spawnMonsterWithEffect(final MapleMonster monster, final int effect, Point pos) {
  1018.         try {
  1019.             monster.setMap(this);
  1020.             Point spos = new Point(pos.x, pos.y - 1);
  1021.             spos = calcPointBelow(spos);
  1022.             spos.y -= 1;
  1023.             monster.setPosition(spos);
  1024.             monster.disableDrops();
  1025.             synchronized (this.mapobjects) {
  1026.                 spawnAndAddRangedMapObject(monster, new DelayedPacketCreation() {
  1027.                     public void sendPackets(MapleClient c) {
  1028.                         c.getSession().write(MaplePacketCreator.spawnMonster(monster, true, effect));
  1029.                     }
  1030.                 }, null);
  1031.                 /*if (monster.hasBossHPBar()) {
  1032.                 broadcastMessage(monster.makeBossHPBarPacket(), monster.getPosition());
  1033.                 }*/
  1034.                 updateMonsterController(monster);
  1035.             }
  1036.             spawnedMonstersOnMap.incrementAndGet();
  1037.         } catch (Exception e) {
  1038.         }
  1039.     }
  1040.     public int countMobOnMap() {
  1041.         int count = 0;
  1042.         Collection<MapleMapObject> mmos = this.getMapObjects();
  1043.         for (MapleMapObject mmo : mmos) {
  1044.             if (mmo instanceof MapleMonster) {
  1045.                 count++;
  1046.             }
  1047.         }
  1048.         return count;
  1049.     }
  1050.     public void spawnFakeMonster(final MapleMonster monster) {
  1051.         monster.setMap(this);
  1052.         monster.setFake(true);
  1053.         synchronized (this.mapobjects) {
  1054.             spawnAndAddRangedMapObject(monster, new DelayedPacketCreation() {
  1055.                 public void sendPackets(MapleClient c) {
  1056.                     c.getSession().write(MaplePacketCreator.spawnFakeMonster(monster, 0));
  1057.                 }
  1058.             }, null);
  1059.             if (monster.hasBossHPBar()) {
  1060.                broadcastMessage(monster.makeBossHPBarPacket(), monster.getPosition());
  1061.             }
  1062.         }
  1063.         spawnedMonstersOnMap.incrementAndGet();
  1064.     }
  1065.     public void makeMonsterReal(final MapleMonster monster) {
  1066.         monster.setFake(false);
  1067.         broadcastMessage(MaplePacketCreator.makeMonsterReal(monster));
  1068.         /*if (monster.hasBossHPBar()) {
  1069.         broadcastMessage(monster.makeBossHPBarPacket(), monster.getPosition());
  1070.         }*/
  1071.         updateMonsterController(monster);
  1072.     }
  1073.     public void spawnReactor(final MapleReactor reactor) {
  1074.         reactor.setMap(this);
  1075.         synchronized (this.mapobjects) {
  1076.             spawnAndAddRangedMapObject(reactor, new DelayedPacketCreation() {
  1077.                 public void sendPackets(MapleClient c) {
  1078.                     c.getSession().write(reactor.makeSpawnData());
  1079.                 }
  1080.             }, null);
  1081.         //broadcastMessage(reactor.makeSpawnData());
  1082.         }
  1083.     }
  1084.     private void respawnReactor(final MapleReactor reactor) {
  1085.         reactor.setState((byte) 0);
  1086.         reactor.setAlive(true);
  1087.         spawnReactor(reactor);
  1088.     }
  1089.     public void spawnDoor(final MapleDoor door) {
  1090.         synchronized (this.mapobjects) {
  1091.             spawnAndAddRangedMapObject(door, new DelayedPacketCreation() {
  1092.                 public void sendPackets(MapleClient c) {
  1093.                     c.getSession().write(MaplePacketCreator.spawnDoor(door.getOwner().getId(), door.getTargetPosition(), false));
  1094.                     if (door.getOwner().getParty() != null && (door.getOwner() == c.getPlayer() || door.getOwner().getParty().containsMembers(new MaplePartyCharacter(c.getPlayer())))) {
  1095.                         c.getSession().write(MaplePacketCreator.partyPortal(door.getTown().getId(), door.getTarget().getId(), door.getTargetPosition()));
  1096.                     }
  1097.                     c.getSession().write(MaplePacketCreator.spawnPortal(door.getTown().getId(), door.getTarget().getId(), door.getTargetPosition()));
  1098.                     c.getSession().write(MaplePacketCreator.enableActions());
  1099.                 }
  1100.             }, new SpawnCondition() {
  1101.                 public boolean canSpawn(MapleCharacter chr) {
  1102.                     return chr.getMapId() == door.getTarget().getId() ||
  1103.                             chr == door.getOwner() && chr.getParty() == null;
  1104.                 }
  1105.             });
  1106.         }
  1107.     }
  1108.     public void spawnSummon(final MapleSummon summon) {
  1109.         spawnAndAddRangedMapObject(summon, new DelayedPacketCreation() {
  1110.             public void sendPackets(MapleClient c) {
  1111.                 int skillLevel = summon.getOwner().getSkillLevel(SkillFactory.getSkill(summon.getSkill()));
  1112.                 c.getSession().write(MaplePacketCreator.spawnSpecialMapObject(summon, skillLevel, true));
  1113.             }
  1114.         }, null);
  1115.     }
  1116.     public void spawnMist(final MapleMist mist, final int duration, boolean poison, boolean fake) {
  1117.         addMapObject(mist);
  1118.         broadcastMessage(fake ? mist.makeFakeSpawnData(30) : mist.makeSpawnData());
  1119.         TimerManager tMan = TimerManager.getInstance();
  1120.         final ScheduledFuture<?> poisonSchedule;
  1121.         if (poison) {
  1122.             Runnable poisonTask = new Runnable() {
  1123.                 @Override
  1124.                 public void run() {
  1125.                     List<MapleMapObject> affectedMonsters = getMapObjectsInRect(mist.getBox(), Collections.singletonList(MapleMapObjectType.MONSTER));
  1126.                     for (MapleMapObject mo : affectedMonsters) {
  1127.                         if (mist.makeChanceResult()) {
  1128.                             MonsterStatusEffect poisonEffect = new MonsterStatusEffect(Collections.singletonMap(MonsterStatus.POISON, 1), mist.getSourceSkill(), false);
  1129.                             ((MapleMonster) mo).applyStatus(mist.getOwner(), poisonEffect, true, duration);
  1130.                         }
  1131.                     }
  1132.                 }
  1133.             };
  1134.             poisonSchedule = tMan.register(poisonTask, 2000, 2500);
  1135.         } else {
  1136.             poisonSchedule = null;
  1137.         }
  1138.         tMan.schedule(new Runnable() {
  1139.             @Override
  1140.             public void run() {
  1141.                 removeMapObject(mist);
  1142.                 if (poisonSchedule != null) {
  1143.                     poisonSchedule.cancel(false);
  1144.                 }
  1145.                 broadcastMessage(mist.makeDestroyData());
  1146.             }
  1147.         }, duration);
  1148.     }
  1149.     public void disappearingItemDrop(final MapleMapObject dropper,
  1150.             final MapleCharacter owner, final IItem item, Point pos) {
  1151.         final Point droppos = calcDropPos(pos, pos);
  1152.         final MapleMapItem drop = new MapleMapItem(item, droppos, dropper, owner);
  1153.         broadcastMessage(MaplePacketCreator.dropItemFromMapObject(item.getItemId(), drop.getObjectId(), 0, 0, dropper.getPosition(), droppos, (byte) 3), drop.getPosition());
  1154.     }
  1155.     public void spawnItemDrop(final MapleMapObject dropper, final MapleCharacter owner, final IItem item, Point pos, final boolean ffaDrop, final boolean expire) {
  1156.         TimerManager tMan = TimerManager.getInstance();
  1157.         final Point droppos = calcDropPos(pos, pos);
  1158.         final MapleMapItem drop = new MapleMapItem(item, droppos, dropper, owner);
  1159.         spawnAndAddRangedMapObject(drop, new DelayedPacketCreation() {
  1160.             public void sendPackets(MapleClient c) {
  1161.                 c.getSession().write(MaplePacketCreator.dropItemFromMapObject(item.getItemId(), drop.getObjectId(), 0, ffaDrop ? 0 : owner.getId(),
  1162.                         dropper.getPosition(), droppos, (byte) 1));
  1163.             }
  1164.         }, null);
  1165.         broadcastMessage(MaplePacketCreator.dropItemFromMapObject(item.getItemId(), drop.getObjectId(), 0, ffaDrop ? 0
  1166.                 : owner.getId(), dropper.getPosition(), droppos, (byte) 0), drop.getPosition());
  1167.         if (expire) {
  1168.             tMan.schedule(new ExpireMapItemJob(drop), dropLife);
  1169.         }
  1170.         activateItemReactors(drop);
  1171.     }
  1172.     private class TimerDestroyWorker implements Runnable {
  1173.         @Override
  1174.         public void run() {
  1175.             if (mapTimer != null) {
  1176.                 int warpMap = mapTimer.warpToMap();
  1177.                 int minWarp = mapTimer.minLevelToWarp();
  1178.                 int maxWarp = mapTimer.maxLevelToWarp();
  1179.                 mapTimer = null;
  1180.                 if (warpMap != -1) {
  1181.                     MapleMap map2wa2 = ChannelServer.getInstance(channel).getMapFactory().getMap(warpMap);
  1182.                     String warpmsg = "You will now be warped to " + map2wa2.getStreetName() + " : " + map2wa2.getMapName();
  1183.                     broadcastMessage(MaplePacketCreator.serverNotice(6, warpmsg));
  1184.                     for (MapleCharacter chr : getCharacters()) {
  1185.                         try {
  1186.                             if (chr.getLevel() >= minWarp && chr.getLevel() <= maxWarp) {
  1187.                                 chr.changeMap(map2wa2, map2wa2.getPortal(0));
  1188.                             } else {
  1189.                                 chr.getClient().getSession().write(MaplePacketCreator.serverNotice(5, "You are not at least level " + minWarp + " or you are higher than level " + maxWarp + "."));
  1190.                             }
  1191.                         } catch (Exception ex) {
  1192.                             String errormsg = "There was a problem warping you. Please contact a GM";
  1193.                             chr.getClient().getSession().write(MaplePacketCreator.serverNotice(5, errormsg));
  1194.                         }
  1195.                     }
  1196.                 }
  1197.             }
  1198.         }
  1199.     }
  1200.     public void addMapTimer(int duration) {
  1201.         ScheduledFuture<?> sf0f = TimerManager.getInstance().schedule(new TimerDestroyWorker(), duration * 1000);
  1202.         mapTimer = new MapleMapTimer(sf0f, duration, -1, -1, -1);
  1203.         // TimerManager.getInstance().
  1204.         broadcastMessage(mapTimer.makeSpawnData());
  1205.     }
  1206.     public void addMapTimer(int duration, int mapToWarpTo) {
  1207.         ScheduledFuture<?> sf0f = TimerManager.getInstance().schedule(new TimerDestroyWorker(), duration * 1000);
  1208.         mapTimer = new MapleMapTimer(sf0f, duration, mapToWarpTo, 0, 256);
  1209.         // TimerManager.getInstance().schedule(new TimerDestroyWorker(), duration * 1000);
  1210.         broadcastMessage(mapTimer.makeSpawnData());
  1211.     }
  1212.     public void addMapTimer(int duration, int mapToWarpTo, int minLevelToWarp) {
  1213.         ScheduledFuture<?> sf0f = TimerManager.getInstance().schedule(new TimerDestroyWorker(), duration * 1000);
  1214.         mapTimer = new MapleMapTimer(sf0f, duration, mapToWarpTo, minLevelToWarp, 256);
  1215.         // TimerManager.getInstance().schedule(new TimerDestroyWorker(), duration * 1000);
  1216.         broadcastMessage(mapTimer.makeSpawnData());
  1217.     }
  1218.     public void addMapTimer(int duration, int mapToWarpTo, int minLevelToWarp, int maxLevelToWarp) {
  1219.         ScheduledFuture<?> sf0f = TimerManager.getInstance().schedule(new TimerDestroyWorker(), duration * 1000);
  1220.         mapTimer = new MapleMapTimer(sf0f, duration, mapToWarpTo, minLevelToWarp, maxLevelToWarp);
  1221.         // TimerManager.getInstance().schedule(new TimerDestroyWorker(), duration * 1000);
  1222.         broadcastMessage(mapTimer.makeSpawnData());
  1223.     }
  1224.     public void clearMapTimer() {
  1225.         if (mapTimer != null) {
  1226.             mapTimer.getSF0F().cancel(true);
  1227.         }
  1228.         mapTimer = null;
  1229.     }
  1230.     private void activateItemReactors(MapleMapItem drop) {
  1231.         IItem item = drop.getItem();
  1232.         final TimerManager tMan = TimerManager.getInstance();
  1233.         //check for reactors on map that might use this item
  1234.         for (MapleMapObject o : mapobjects.values()) {
  1235.             if (o.getType() == MapleMapObjectType.REACTOR) {
  1236.                 if (((MapleReactor) o).getReactorType() == 100) {
  1237.                     if (((MapleReactor) o).getReactItem().getLeft() == item.getItemId() && ((MapleReactor) o).getReactItem().getRight() <= item.getQuantity()) {
  1238.                         Rectangle area = ((MapleReactor) o).getArea();
  1239.                         if (area.contains(drop.getPosition())) {
  1240.                             MapleClient ownerClient = null;
  1241.                             if (drop.getOwner() != null) {
  1242.                                 ownerClient = drop.getOwner().getClient();
  1243.                             }
  1244.                             MapleReactor reactor = (MapleReactor) o;
  1245.                             if (!reactor.isTimerActive()) {
  1246.                                 tMan.schedule(new ActivateItemReactor(drop, reactor, ownerClient), 1000);
  1247.                                 reactor.setTimerActive(true);
  1248.                             }
  1249.                         }
  1250.                     }
  1251.                 }
  1252.             }
  1253.         }
  1254.     }
  1255.     public void AriantPQStart() {
  1256.         int i = 1;
  1257.         for (MapleCharacter chars2 : this.getCharacters()) {
  1258.             broadcastMessage(MaplePacketCreator.updateAriantPQRanking(chars2.getName(), 0, false));
  1259.             broadcastMessage(MaplePacketCreator.serverNotice(0, MaplePacketCreator.updateAriantPQRanking(chars2.getName(), 0, false).toString()));
  1260.             if (this.getCharacters().size() > i) {
  1261.                 broadcastMessage(MaplePacketCreator.updateAriantPQRanking(null, 0, true));
  1262.                 broadcastMessage(MaplePacketCreator.serverNotice(0, MaplePacketCreator.updateAriantPQRanking(chars2.getName(), 0, true).toString()));
  1263.             }
  1264.             i++;
  1265.         }
  1266.     }
  1267.     public void spawnMesoDrop(final int meso, final int displayMeso, Point position, final MapleMapObject dropper, final MapleCharacter owner, final boolean ffaLoot) {
  1268.         TimerManager tMan = TimerManager.getInstance();
  1269.         final Point droppos = calcDropPos(position, position);
  1270.         final MapleMapItem mdrop = new MapleMapItem(meso, displayMeso, droppos, dropper, owner);
  1271.         spawnAndAddRangedMapObject(mdrop, new DelayedPacketCreation() {
  1272.             public void sendPackets(MapleClient c) {
  1273.                 c.getSession().write(MaplePacketCreator.dropMesoFromMapObject(displayMeso, mdrop.getObjectId(), dropper.getObjectId(),
  1274.                         ffaLoot ? 0 : owner.getId(), dropper.getPosition(), droppos, (byte) 1));
  1275.             }
  1276.         }, null);
  1277.         tMan.schedule(new ExpireMapItemJob(mdrop), dropLife);
  1278.     }
  1279.     public void startMapEffect(String msg, int itemId) {
  1280.         if (mapEffect != null) {
  1281.             return;
  1282.         }
  1283.         mapEffect = new MapleMapEffect(msg, itemId);
  1284.         broadcastMessage(mapEffect.makeStartData());
  1285.         TimerManager tMan = TimerManager.getInstance();
  1286.         /*tMan.schedule(new Runnable() {
  1287.         @Override
  1288.         public void run() {
  1289.         mapEffect.setActive(false);
  1290.         broadcastMessage(mapEffect.makeStartData());
  1291.         }
  1292.         }, 20000);*/
  1293.         tMan.schedule(new Runnable() {
  1294.             @Override
  1295.             public void run() {
  1296.                 broadcastMessage(mapEffect.makeDestroyData());
  1297.                 mapEffect = null;
  1298.             }
  1299.         }, 30000);
  1300.     }
  1301.     /**
  1302.      * Adds a player to this map and sends nescessary data
  1303.      *
  1304.      * @param chr
  1305.      */
  1306.     public void addPlayer(MapleCharacter chr) {
  1307.         //log.warn("[dc] [level2] Player {} enters map {}", new Object[] { chr.getName(), mapid });
  1308.         synchronized (characters) {
  1309.             this.characters.add(chr);
  1310.         }
  1311.         synchronized (this.mapobjects) {
  1312.             if (!chr.isHidden()) {
  1313.                 broadcastMessage(chr, (MaplePacketCreator.spawnPlayerMapobject(chr)), false);
  1314.                 MaplePet[] pets = chr.getPets();
  1315.                 for (int i = 0; i < 3; i++) {
  1316.                     if (pets[i] != null) {
  1317.                         pets[i].setPos(getGroundBelow(chr.getPosition()));
  1318.                         broadcastMessage(chr, MaplePacketCreator.showPet(chr, pets[i], false, false), false);
  1319.                     } else {
  1320.                         break;
  1321.                     }
  1322.                 }
  1323.                 if (chr.getChalkboard() != null) {
  1324.                     broadcastMessage(chr, (MaplePacketCreator.useChalkboard(chr, false)), false);
  1325.                 }
  1326.             } else {
  1327.                 broadcastGMMessage(chr, (MaplePacketCreator.spawnPlayerMapobject(chr)), false);
  1328.                 MaplePet[] pets = chr.getPets();
  1329.                 for (int i = 0; i < 3; i++) {
  1330.                     if (pets[i] != null) {
  1331.                         pets[i].setPos(getGroundBelow(chr.getPosition()));
  1332.                         broadcastGMMessage(chr, MaplePacketCreator.showPet(chr, pets[i], false, false), false);
  1333.                     } else {
  1334.                         break;
  1335.                     }
  1336.                 }
  1337.                 if (chr.getChalkboard() != null) {
  1338.                     broadcastGMMessage(chr, (MaplePacketCreator.useChalkboard(chr, false)), false);
  1339.                 }
  1340.             }
  1341.             sendObjectPlacement(chr.getClient());
  1342.             //chr.getClient().getSession().write(MaplePacketCreator.spawnPlayerMapobject(chr));
  1343.             switch (getId()) {
  1344.                 case 1:
  1345.                 case 2:
  1346.                 case 809000101:
  1347.                 case 809000201:
  1348.                     chr.getClient().getSession().write(MaplePacketCreator.showEquipEffect());
  1349.             }
  1350.             MaplePet[] pets = chr.getPets();
  1351.             for (int i = 0; i < 3; i++) {
  1352.                 if (pets[i] != null) {
  1353.                     pets[i].setPos(getGroundBelow(chr.getPosition()));
  1354.                     chr.getClient().getSession().write(MaplePacketCreator.showPet(chr, pets[i], false, false));
  1355.                 }
  1356.             }
  1357.             if (chr.getChalkboard() != null) {
  1358.                 chr.getClient().getSession().write((MaplePacketCreator.useChalkboard(chr, false)));
  1359.             }
  1360.             this.mapobjects.put(Integer.valueOf(chr.getObjectId()), chr);
  1361.         }
  1362.         MapleStatEffect summonStat = chr.getStatForBuff(MapleBuffStat.SUMMON);
  1363.         if (summonStat != null) {
  1364.             MapleSummon summon = chr.getSummons().get(summonStat.getSourceId());
  1365.             summon.setPosition(getGroundBelow(chr.getPosition()));
  1366.             chr.getMap().spawnSummon(summon);
  1367.             updateMapObjectVisibility(chr, summon);
  1368.         }
  1369.         if (mapEffect != null) {
  1370.             mapEffect.sendStartData(chr.getClient());
  1371.         }
  1372.         if (MapleTVEffect.active) {
  1373.             if (hasMapleTV() && MapleTVEffect.packet != null) {
  1374.                 chr.getClient().getSession().write(MapleTVEffect.packet);
  1375.             }
  1376.         }
  1377.         if (getTimeLimit() > 0 && getForcedReturnMap() != null) {
  1378.             chr.getClient().getSession().write(MaplePacketCreator.getClock(getTimeLimit()));
  1379.             chr.startMapTimeLimitTask(this, this.getForcedReturnMap());
  1380.         }
  1381.         if (chr.getEventInstance() != null && chr.getEventInstance().isTimerStarted()) {
  1382.             chr.getClient().getSession().write(MaplePacketCreator.getClock((int) (chr.getEventInstance().getTimeLeft() / 1000)));
  1383.         }
  1384.         if (hasClock()) {
  1385.             Calendar cal = Calendar.getInstance();
  1386.             int hour = cal.get(Calendar.HOUR_OF_DAY);
  1387.             int min = cal.get(Calendar.MINUTE);
  1388.             int second = cal.get(Calendar.SECOND);
  1389.             chr.getClient().getSession().write((MaplePacketCreator.getClockTime(hour, min, second)));
  1390.         }
  1391.         if (hasBoat() == 2) {
  1392.             chr.getClient().getSession().write((MaplePacketCreator.boatPacket(true)));
  1393.         } else if (hasBoat() == 1 && (chr.getMapId() != 200090000 || chr.getMapId() != 200090010)) {
  1394.             chr.getClient().getSession().write(MaplePacketCreator.boatPacket(false));
  1395.         }
  1396.         chr.receivePartyMemberHP();
  1397.     }
  1398.     public void removePlayer(MapleCharacter chr) {
  1399.         //log.warn("[dc] [level2] Player {} leaves map {}", new Object[] { chr.getName(), mapid });
  1400.         synchronized (characters) {
  1401.             characters.remove(chr);
  1402.         }
  1403.         removeMapObject(Integer.valueOf(chr.getObjectId()));
  1404.         broadcastMessage(MaplePacketCreator.removePlayerFromMap(chr.getId()));
  1405.         for (MapleMonster monster : chr.getControlledMonsters()) {
  1406.             monster.setController(null);
  1407.             monster.setControllerHasAggro(false);
  1408.             monster.setControllerKnowsAboutAggro(false);
  1409.             updateMonsterController(monster);
  1410.         }
  1411.         chr.leaveMap();
  1412.         chr.cancelMapTimeLimitTask();
  1413.         for (MapleSummon summon : chr.getSummons().values()) {
  1414.             if (summon.isPuppet()) {
  1415.                 chr.cancelBuffStats(MapleBuffStat.PUPPET);
  1416.             } else {
  1417.                 removeMapObject(summon);
  1418.             }
  1419.         }
  1420.     }
  1421.     /**
  1422.      * Broadcasts the given packet to everyone on the map but the source. source = null Broadcasts to everyone
  1423.      *
  1424.      * @param source
  1425.      * @param packet
  1426.      */
  1427.     // public void broadcastMessage(MapleCharacter source, MaplePacket packet) {
  1428.     // synchronized (characters) {
  1429.     // for (MapleCharacter chr : characters) {
  1430.     // if (chr != source) {
  1431.     // chr.getClient().getSession().write(packet);
  1432.     // }
  1433.     // }
  1434.     // }
  1435.     // }
  1436.     /**
  1437.      * Broadcast a message to everyone in the map
  1438.      *
  1439.      * @param packet
  1440.      */
  1441.     public void broadcastMessage(MaplePacket packet) {
  1442.         broadcastMessage(null, packet, Double.POSITIVE_INFINITY, null);
  1443.     }
  1444.     /**
  1445.      * Nonranged. Repeat to source according to parameter.
  1446.      *
  1447.      * @param source
  1448.      * @param packet
  1449.      * @param repeatToSource
  1450.      */
  1451.     public void broadcastMessage(MapleCharacter source, MaplePacket packet, boolean repeatToSource) {
  1452.         broadcastMessage(repeatToSource ? null : source, packet, Double.POSITIVE_INFINITY, source.getPosition());
  1453.     }
  1454.     /**
  1455.      * Ranged and repeat according to parameters.
  1456.      *
  1457.      * @param source
  1458.      * @param packet
  1459.      * @param repeatToSource
  1460.      * @param ranged
  1461.      */
  1462.     public void broadcastMessage(MapleCharacter source, MaplePacket packet, boolean repeatToSource, boolean ranged) {
  1463.         broadcastMessage(repeatToSource ? null : source, packet, ranged ? MapleCharacter.MAX_VIEW_RANGE_SQ : Double.POSITIVE_INFINITY, source.getPosition());
  1464.     }
  1465.     /**
  1466.      * Always ranged from Point.
  1467.      *
  1468.      * @param packet
  1469.      * @param rangedFrom
  1470.      */
  1471.     public void broadcastMessage(MaplePacket packet, Point rangedFrom) {
  1472.         broadcastMessage(null, packet, MapleCharacter.MAX_VIEW_RANGE_SQ, rangedFrom);
  1473.     }
  1474.     /**
  1475.      * Always ranged from point. Does not repeat to source.
  1476.      *
  1477.      * @param source
  1478.      * @param packet
  1479.      * @param rangedFrom
  1480.      */
  1481.     public void broadcastMessage(MapleCharacter source, MaplePacket packet, Point rangedFrom) {
  1482.         broadcastMessage(source, packet, MapleCharacter.MAX_VIEW_RANGE_SQ, rangedFrom);
  1483.     }
  1484.     private void broadcastMessage(MapleCharacter source, MaplePacket packet, double rangeSq, Point rangedFrom) {
  1485.         synchronized (characters) {
  1486.             for (MapleCharacter chr : characters) {
  1487.                 if (chr != source && !chr.isFake()) {
  1488.                     if (rangeSq < Double.POSITIVE_INFINITY) {
  1489.                         if (rangedFrom.distanceSq(chr.getPosition()) <= rangeSq) {
  1490.                             chr.getClient().getSession().write(packet);
  1491.                         }
  1492.                     } else {
  1493.                         chr.getClient().getSession().write(packet);
  1494.                     }
  1495.                 }
  1496.             }
  1497.         }
  1498.     }
  1499.     public void broadcastGMMessage(MaplePacket packet) {
  1500.         broadcastGMMessage(null, packet, Double.POSITIVE_INFINITY, null);
  1501.     }
  1502.     public void broadcastGMMessage(MapleCharacter source, MaplePacket packet, boolean repeatToSource) {
  1503.         broadcastGMMessage(repeatToSource ? null : source, packet, Double.POSITIVE_INFINITY, source.getPosition());
  1504.     }
  1505.     private void broadcastGMMessage(MapleCharacter source, MaplePacket packet, double rangeSq, Point rangedFrom) {
  1506.         synchronized (characters) {
  1507.             for (MapleCharacter chr : characters) {
  1508.                 if (chr != source && !chr.isFake() && chr.isGM()) {
  1509.                     if (rangeSq < Double.POSITIVE_INFINITY) {
  1510.                         if (rangedFrom.distanceSq(chr.getPosition()) <= rangeSq) {
  1511.                             chr.getClient().getSession().write(packet);
  1512.                         }
  1513.                     } else {
  1514.                         chr.getClient().getSession().write(packet);
  1515.                     }
  1516.                 }
  1517.             }
  1518.         }
  1519.     }
  1520.     public void broadcastNONGMMessage(MaplePacket packet) {
  1521.         broadcastNONGMMessage(null, packet, Double.POSITIVE_INFINITY, null);
  1522.     }
  1523.     public void broadcastNONGMMessage(MapleCharacter source, MaplePacket packet, boolean repeatToSource) {
  1524.         broadcastNONGMMessage(repeatToSource ? null : source, packet, Double.POSITIVE_INFINITY, source.getPosition());
  1525.     }
  1526.     private void broadcastNONGMMessage(MapleCharacter source, MaplePacket packet, double rangeSq, Point rangedFrom) {
  1527.         synchronized (characters) {
  1528.             for (MapleCharacter chr : characters) {
  1529.                 if (chr != source && !chr.isFake() && !chr.isGM()) {
  1530.                     if (rangeSq < Double.POSITIVE_INFINITY) {
  1531.                         if (rangedFrom.distanceSq(chr.getPosition()) <= rangeSq) {
  1532.                             chr.getClient().getSession().write(packet);
  1533.                         }
  1534.                     } else {
  1535.                         chr.getClient().getSession().write(packet);
  1536.                     }
  1537.                 }
  1538.             }
  1539.         }
  1540.     }
  1541.     private boolean isNonRangedType(MapleMapObjectType type) {
  1542.         switch (type) {
  1543.             case NPC:
  1544.             case PLAYER:
  1545.             case HIRED_MERCHANT:
  1546.             case MIST:
  1547.             case PLAYER_NPC:
  1548.                 //case REACTOR:
  1549.                 return true;
  1550.         }
  1551.         return false;
  1552.     }
  1553.     private void sendObjectPlacement(MapleClient mapleClient) {
  1554.         for (MapleMapObject o : mapobjects.values()) {
  1555.             if (isNonRangedType(o.getType())) {
  1556.                 // make sure not to spawn a dead reactor
  1557.                 // if (o.getType() == MapleMapObjectType.REACTOR) {
  1558.                 // if (reactors.get((MapleReactor) o)) {
  1559.                 // o.sendSpawnData(mapleClient);
  1560.                 // }
  1561.                 // } else
  1562.                 o.sendSpawnData(mapleClient);
  1563.             } else if (o.getType() == MapleMapObjectType.MONSTER) {
  1564.                 updateMonsterController((MapleMonster) o);
  1565.             }
  1566.         }
  1567.         MapleCharacter chr = mapleClient.getPlayer();
  1568.         if (chr != null) {
  1569.             for (MapleMapObject o : getMapObjectsInRange(chr.getPosition(), MapleCharacter.MAX_VIEW_RANGE_SQ, rangedMapobjectTypes)) {
  1570.                 if (o.getType() == MapleMapObjectType.REACTOR) {
  1571.                     if (((MapleReactor) o).isAlive()) {
  1572.                         o.sendSpawnData(chr.getClient());
  1573.                         chr.addVisibleMapObject(o);
  1574.                     }
  1575.                 } else {
  1576.                     o.sendSpawnData(chr.getClient());
  1577.                     chr.addVisibleMapObject(o);
  1578.                 }
  1579.             }
  1580.         } else {
  1581.             log.info("sendObjectPlacement invoked with null char");
  1582.         }
  1583.     }
  1584.     public List<MapleMapObject> getMapObjectsInRange(Point from, double rangeSq, List<MapleMapObjectType> types) {
  1585.         List<MapleMapObject> ret = new LinkedList<MapleMapObject>();
  1586.         synchronized (mapobjects) {
  1587.             for (MapleMapObject l : mapobjects.values()) {
  1588.                 if (types.contains(l.getType())) {
  1589.                     if (from.distanceSq(l.getPosition()) <= rangeSq) {
  1590.                         ret.add(l);
  1591.                     }
  1592.                 }
  1593.             }
  1594.         }
  1595.         return ret;
  1596.     }
  1597.     public List<MapleMapObject> getItemsInRange(Point from, double rangeSq) {
  1598.         List<MapleMapObject> ret = new LinkedList<MapleMapObject>();
  1599.         synchronized (mapobjects) {
  1600.             for (MapleMapObject l : mapobjects.values()) {
  1601.                 if (l.getType() == MapleMapObjectType.ITEM) {
  1602.                     if (from.distanceSq(l.getPosition()) <= rangeSq) {
  1603.                         ret.add(l);
  1604.                     }
  1605.                 }
  1606.             }
  1607.         }
  1608.         return ret;
  1609.     }
  1610.     public List<MapleMapObject> getMapObjectsInRect(Rectangle box, List<MapleMapObjectType> types) {
  1611.         List<MapleMapObject> ret = new LinkedList<MapleMapObject>();
  1612.         synchronized (mapobjects) {
  1613.             for (MapleMapObject l : mapobjects.values()) {
  1614.                 if (types.contains(l.getType())) {
  1615.                     if (box.contains(l.getPosition())) {
  1616.                         ret.add(l);
  1617.                     }
  1618.                 }
  1619.             }
  1620.         }
  1621.         return ret;
  1622.     }
  1623.     public List<MapleCharacter> getPlayersInRect(Rectangle box, List<MapleCharacter> chr) {
  1624.         List<MapleCharacter> character = new LinkedList<MapleCharacter>();
  1625.         synchronized (characters) {
  1626.             for (MapleCharacter a : characters) {
  1627.                 if (chr.contains(a.getClient().getPlayer())) {
  1628.                     if (box.contains(a.getPosition())) {
  1629.                         character.add(a);
  1630.                     }
  1631.                 }
  1632.             }
  1633.         }
  1634.         return character;
  1635.     }
  1636.     public void addPortal(MaplePortal myPortal) {
  1637.         portals.put(myPortal.getId(), myPortal);
  1638.     }
  1639.     public MaplePortal getPortal(String portalname) {
  1640.         for (MaplePortal port : portals.values()) {
  1641.             if (port.getName().equals(portalname)) {
  1642.                 return port;
  1643.             }
  1644.         }
  1645.         return null;
  1646.     }
  1647.     public MaplePortal getPortal(int portalid) {
  1648.         return portals.get(portalid);
  1649.     }
  1650.     public void addMapleArea(Rectangle rec) {
  1651.         areas.add(rec);
  1652.     }
  1653.     public List<Rectangle> getAreas() {
  1654.         return new ArrayList<Rectangle>(areas);
  1655.     }
  1656.     public Rectangle getArea(int index) {
  1657.         return areas.get(index);
  1658.     }
  1659.     public void setFootholds(MapleFootholdTree footholds) {
  1660.         this.footholds = footholds;
  1661.     }
  1662.     public MapleFootholdTree getFootholds() {
  1663.         return footholds;
  1664.     }
  1665.     /**
  1666.      * not threadsafe, please synchronize yourself
  1667.      *
  1668.      * @param monster
  1669.      */
  1670.     public void addMonsterSpawn(MapleMonster monster, int mobTime) {
  1671.         Point newpos = calcPointBelow(monster.getPosition());
  1672.         newpos.y -= 1;
  1673.         SpawnPoint sp = new SpawnPoint(monster, newpos, mobTime);
  1674.         monsterSpawn.add(sp);
  1675.         if (sp.shouldSpawn() || mobTime == -1) { // -1 does not respawn and should not either but force ONE spawn
  1676.             sp.spawnMonster(this);
  1677.         }
  1678.     }
  1679.     public float getMonsterRate() {
  1680.         return monsterRate;
  1681.     }
  1682.     public Collection<MapleCharacter> getCharacters() {
  1683.         return Collections.unmodifiableCollection(this.characters);
  1684.     }
  1685.     public MapleCharacter getCharacterById(int id) {
  1686.         for (MapleCharacter c : this.characters) {
  1687.             if (c.getId() == id) {
  1688.                 return c;
  1689.             }
  1690.         }
  1691.         return null;
  1692.     }
  1693.     private void updateMapObjectVisibility(MapleCharacter chr, MapleMapObject mo) {
  1694.         if (chr.isFake()) {
  1695.             return;
  1696.         }
  1697.         if (!chr.isMapObjectVisible(mo)) { // monster entered view range
  1698.             if (mo.getType() == MapleMapObjectType.SUMMON || mo.getPosition().distanceSq(chr.getPosition()) <= MapleCharacter.MAX_VIEW_RANGE_SQ) {
  1699.                 chr.addVisibleMapObject(mo);
  1700.                 mo.sendSpawnData(chr.getClient());
  1701.             }
  1702.         } else { // monster left view range
  1703.             if (mo.getType() != MapleMapObjectType.SUMMON && mo.getPosition().distanceSq(chr.getPosition()) > MapleCharacter.MAX_VIEW_RANGE_SQ) {
  1704.                 chr.removeVisibleMapObject(mo);
  1705.                 mo.sendDestroyData(chr.getClient());
  1706.             }
  1707.         }
  1708.     }
  1709.     public void moveMonster(MapleMonster monster, Point reportedPos) {
  1710.         monster.setPosition(reportedPos);
  1711.         synchronized (characters) {
  1712.             for (MapleCharacter chr : characters) {
  1713.                 updateMapObjectVisibility(chr, monster);
  1714.             }
  1715.         }
  1716.     }
  1717.     public void movePlayer(MapleCharacter player, Point newPosition) {
  1718.         if (player.isFake()) {
  1719.             return;
  1720.         }
  1721.         player.setPosition(newPosition);
  1722.         Collection<MapleMapObject> visibleObjects = player.getVisibleMapObjects();
  1723.         MapleMapObject[] visibleObjectsNow = visibleObjects.toArray(new MapleMapObject[visibleObjects.size()]);
  1724.         for (MapleMapObject mo : visibleObjectsNow) {
  1725.             if (mapobjects.get(mo.getObjectId()) == mo) {
  1726.                 updateMapObjectVisibility(player, mo);
  1727.             } else {
  1728.                 player.removeVisibleMapObject(mo);
  1729.             }
  1730.         }
  1731.         for (MapleMapObject mo : getMapObjectsInRange(player.getPosition(), MapleCharacter.MAX_VIEW_RANGE_SQ,
  1732.                 rangedMapobjectTypes)) {
  1733.             if (!player.isMapObjectVisible(mo)) {
  1734.                 mo.sendSpawnData(player.getClient());
  1735.                 player.addVisibleMapObject(mo);
  1736.             }
  1737.         }
  1738.     }
  1739.     public MaplePortal findClosestSpawnpoint(Point from) {
  1740.         MaplePortal closest = null;
  1741.         double shortestDistance = Double.POSITIVE_INFINITY;
  1742.         for (MaplePortal portal : portals.values()) {
  1743.             double distance = portal.getPosition().distanceSq(from);
  1744.             if (portal.getType() >= 0 && portal.getType() <= 2 && distance < shortestDistance && portal.getTargetMapId() == 999999999) {
  1745.                 closest = portal;
  1746.                 shortestDistance = distance;
  1747.             }
  1748.         }
  1749.         return closest;
  1750.     }
  1751.     public void spawnDebug(MessageCallback mc) {
  1752.         mc.dropMessage("Spawndebug...");
  1753.         synchronized (mapobjects) {
  1754.             mc.dropMessage("Mapobjects in map: " + mapobjects.size() + " "spawnedMonstersOnMap": " +
  1755.                     spawnedMonstersOnMap + " spawnpoints: " + monsterSpawn.size() +
  1756.                     " maxRegularSpawn: " + getMaxRegularSpawn());
  1757.             int numMonsters = 0;
  1758.             for (MapleMapObject mo : mapobjects.values()) {
  1759.                 if (mo instanceof MapleMonster) {
  1760.                     numMonsters++;
  1761.                 }
  1762.             }
  1763.             mc.dropMessage("actual monsters: " + numMonsters);
  1764.         }
  1765.     }
  1766.     private int getMaxRegularSpawn() {
  1767.         return (int) (monsterSpawn.size() / monsterRate);
  1768.     }
  1769.     public Collection<MaplePortal> getPortals() {
  1770.         return Collections.unmodifiableCollection(portals.values());
  1771.     }
  1772.     public String getMapName() {
  1773.         return mapName;
  1774.     }
  1775.     public void setMapName(String mapName) {
  1776.         this.mapName = mapName;
  1777.     }
  1778.     public String getStreetName() {
  1779.         return streetName;
  1780.     }
  1781.     public void setClock(boolean hasClock) {
  1782.         this.clock = hasClock;
  1783.     }
  1784.     public boolean hasClock() {
  1785.         return clock;
  1786.     }
  1787.     public void setTown(boolean isTown) {
  1788.         this.town = isTown;
  1789.     }
  1790.     public boolean isTown() {
  1791.         return town;
  1792.     }
  1793.     public void setStreetName(String streetName) {
  1794.         this.streetName = streetName;
  1795.     }
  1796.     public void setEverlast(boolean everlast) {
  1797.         this.everlast = everlast;
  1798.     }
  1799.     public boolean getEverlast() {
  1800.         return everlast;
  1801.     }
  1802.     public int getSpawnedMonstersOnMap() {
  1803.         return spawnedMonstersOnMap.get();
  1804.     }
  1805.     public Collection<MapleCharacter> getNearestPvpChar(Point attacker, double maxRange, double maxHeight, Collection<MapleCharacter> chr) {
  1806.         Collection<MapleCharacter> character = new LinkedList<MapleCharacter>();
  1807.         for (MapleCharacter a : characters) {
  1808.             if (chr.contains(a.getClient().getPlayer())) {
  1809.                 Point attackedPlayer = a.getPosition();
  1810.                 MaplePortal Port = a.getMap().findClosestSpawnpoint(a.getPosition());
  1811.                 Point nearestPort = Port.getPosition();
  1812.                 double safeDis = attackedPlayer.distance(nearestPort);
  1813.                 double distanceX = attacker.distance(attackedPlayer.getX(), attackedPlayer.getY());
  1814.                 if (PvPLibrary.isLeft) {
  1815.                     if (attacker.x > attackedPlayer.x && distanceX < maxRange && distanceX > 2 &&
  1816.                             attackedPlayer.y >= attacker.y - maxHeight && attackedPlayer.y <= attacker.y + maxHeight && safeDis > 2) {
  1817.                         character.add(a);
  1818.                     }
  1819.                 }
  1820.                 if (PvPLibrary.isRight) {
  1821.                     if (attacker.x < attackedPlayer.x && distanceX < maxRange && distanceX > 2 &&
  1822.                             attackedPlayer.y >= attacker.y - maxHeight && attackedPlayer.y <= attacker.y + maxHeight && safeDis > 2) {
  1823.                         character.add(a);
  1824.                     }
  1825.                 }
  1826.             }
  1827.         }
  1828.         return character;
  1829.     }
  1830.     private class ExpireMapItemJob implements Runnable {
  1831.         private MapleMapItem mapitem;
  1832.         public ExpireMapItemJob(MapleMapItem mapitem) {
  1833.             this.mapitem = mapitem;
  1834.         }
  1835.         @Override
  1836.         public void run() {
  1837.             if (mapitem != null && mapitem == getMapObject(mapitem.getObjectId())) {
  1838.                 synchronized (mapitem) {
  1839.                     if (mapitem.isPickedUp()) {
  1840.                         return;
  1841.                     }
  1842.                     MapleMap.this.broadcastMessage(MaplePacketCreator.removeItemFromMap(mapitem.getObjectId(), 0, 0),
  1843.                             mapitem.getPosition());
  1844.                     MapleMap.this.removeMapObject(mapitem);
  1845.                     mapitem.setPickedUp(true);
  1846.                 }
  1847.             }
  1848.         }
  1849.     }
  1850.     private class ActivateItemReactor implements Runnable {
  1851.         private MapleMapItem mapitem;
  1852.         private MapleReactor reactor;
  1853.         private MapleClient c;
  1854.         public ActivateItemReactor(MapleMapItem mapitem, MapleReactor reactor, MapleClient c) {
  1855.             this.mapitem = mapitem;
  1856.             this.reactor = reactor;
  1857.             this.c = c;
  1858.         }
  1859.         @Override
  1860.         public void run() {
  1861.             if (mapitem != null && mapitem == getMapObject(mapitem.getObjectId())) {
  1862.                 synchronized (mapitem) {
  1863.                     TimerManager tMan = TimerManager.getInstance();
  1864.                     if (mapitem.isPickedUp()) {
  1865.                         return;
  1866.                     }
  1867.                     MapleMap.this.broadcastMessage(MaplePacketCreator.removeItemFromMap(mapitem.getObjectId(), 0, 0), mapitem.getPosition());
  1868.                     MapleMap.this.removeMapObject(mapitem);
  1869.                     reactor.hitReactor(c);
  1870.                     reactor.setTimerActive(false);
  1871.                     //reactor.increaseState();
  1872.                     if (reactor.getDelay() > 0) { //This shit is negative.. Fix?
  1873.                         tMan.schedule(new Runnable() {
  1874.                             @Override
  1875.                             public void run() {
  1876.                                 reactor.setState((byte) 0);
  1877.                                 broadcastMessage(MaplePacketCreator.triggerReactor(reactor, 0));
  1878.                             }
  1879.                         }, reactor.getDelay());
  1880.                     }
  1881.                 }
  1882.             }
  1883.         }
  1884.     }
  1885.     private class RespawnWorker implements Runnable {
  1886.         @Override
  1887.         public void run() {
  1888.             int playersOnMap = characters.size();
  1889.             if (playersOnMap == 0) {
  1890.                 return;
  1891.             }
  1892.             int ispawnedMonstersOnMap = spawnedMonstersOnMap.get();
  1893.             int numShouldSpawn = (int) Math.round(Math.random() * ((2 + playersOnMap / 1.5 + (getMaxRegularSpawn() - ispawnedMonstersOnMap) / 4.0)));
  1894.             if (numShouldSpawn + ispawnedMonstersOnMap > getMaxRegularSpawn()) {
  1895.                 numShouldSpawn = getMaxRegularSpawn() - ispawnedMonstersOnMap;
  1896.             }
  1897.             if (numShouldSpawn <= 0) {
  1898.                 return;
  1899.             }
  1900.             // k find that many monsters that need respawning and respawn them O.o
  1901.             List<SpawnPoint> randomSpawn = new ArrayList<SpawnPoint>(monsterSpawn);
  1902.             Collections.shuffle(randomSpawn);
  1903.             int spawned = 0;
  1904.             for (SpawnPoint spawnPoint : randomSpawn) {
  1905.                 if (spawnPoint.shouldSpawn()) {
  1906.                     spawnPoint.spawnMonster(MapleMap.this);
  1907.                     spawned++;
  1908.                 }
  1909.                 if (spawned >= numShouldSpawn) {
  1910.                     break;
  1911.                 }
  1912.             }
  1913.         }
  1914.     }
  1915.     private static interface DelayedPacketCreation {
  1916.         void sendPackets(MapleClient c);
  1917.     }
  1918.     private static interface SpawnCondition {
  1919.         boolean canSpawn(MapleCharacter chr);
  1920.     }
  1921.     public int getHPDec() {
  1922.         return decHP;
  1923.     }
  1924.     public void setHPDec(int delta) {
  1925.         decHP = delta;
  1926.     }
  1927.     public int getHPDecProtect() {
  1928.         return this.protectItem;
  1929.     }
  1930.     public void setHPDecProtect(int delta) {
  1931.         this.protectItem = delta;
  1932.     }
  1933.     public int hasBoat() {
  1934.         if (boat && docked) {
  1935.             return 2;
  1936.         } else if (boat) {
  1937.             return 1;
  1938.         } else {
  1939.             return 0;
  1940.         }
  1941.     }
  1942.     public void setBoat(boolean hasBoat) {
  1943.         this.boat = hasBoat;
  1944.     }
  1945.     public void setDocked(boolean isDocked) {
  1946.         this.docked = isDocked;
  1947.     }
  1948.     public void addBotPlayer(MapleCharacter chr) {
  1949.         synchronized (characters) {
  1950.             this.characters.add(chr);
  1951.         }
  1952.         synchronized (this.mapobjects) {
  1953.             if (!chr.isHidden()) {
  1954.                 broadcastMessage(chr, (MaplePacketCreator.spawnPlayerMapobject(chr)), false);
  1955.             } else {
  1956.                 broadcastGMMessage(chr, (MaplePacketCreator.spawnPlayerMapobject(chr)), false);
  1957.             }
  1958.             this.mapobjects.put(Integer.valueOf(chr.getObjectId()), chr);
  1959.         }
  1960.     }
  1961.     public int playerCount() {
  1962.         List<MapleMapObject> players = getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.PLAYER));
  1963.         return players.size();
  1964.     }
  1965.     public int mobCount() {
  1966.         List<MapleMapObject> mobsCount = getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER));
  1967.         return mobsCount.size();
  1968.     }
  1969.     public void setReactorState() {
  1970.         synchronized (this.mapobjects) {
  1971.             for (MapleMapObject o : mapobjects.values()) {
  1972.                 if (o.getType() == MapleMapObjectType.REACTOR) {
  1973.                     ((MapleReactor) o).setState((byte) 1);
  1974.                     broadcastMessage(MaplePacketCreator.triggerReactor((MapleReactor) o, 1));
  1975.                 }
  1976.             }
  1977.         }
  1978.     }
  1979.     public void setShowGate(boolean gate) {
  1980.         this.showGate = gate;
  1981.     }
  1982.     public boolean hasShowGate() {
  1983.         return showGate;
  1984.     }
  1985.     public boolean hasMapleTV() {
  1986.         int tvIds[] = {9250042, 9250043, 9250025, 9250045, 9250044, 9270001, 9270002, 9250023, 9250024, 9270003, 9270004, 9250026, 9270006, 9270007, 9250046, 9270000, 9201066, 9270005, 9270008, 9270009, 9270010, 9270011, 9270012, 9270013, 9270014, 9270015, 9270016, 9270040};
  1987.         for (int id : tvIds) {
  1988.             if (containsNPC(id)) {
  1989.                 return true;
  1990.             }
  1991.         }
  1992.         return false;
  1993.     }
  1994.     public void removeMonster(MapleMonster mons) {
  1995.         spawnedMonstersOnMap.decrementAndGet();
  1996.         broadcastMessage(MaplePacketCreator.killMonster(mons.getObjectId(), true), mons.getPosition());
  1997.         removeMapObject(mons);
  1998.     }
  1999.     public boolean isPQMap() {
  2000.         switch (getId()) {
  2001.             case 103000800:
  2002.             case 103000804:
  2003.             case 922010100:
  2004.             case 922010200:
  2005.             case 922010201:
  2006.             case 922010300:
  2007.             case 922010400:
  2008.             case 922010401:
  2009.             case 922010402:
  2010.             case 922010403:
  2011.             case 922010404:
  2012.             case 922010405:
  2013.             case 922010500:
  2014.             case 922010600:
  2015.             case 922010700:
  2016.             case 922010800:
  2017.                 return true;
  2018.             default:
  2019.                 return false;
  2020.         }
  2021.     }
  2022. }