/** * License * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS * CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. * ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR * COPYRIGHT LAW IS PROHIBITED. * * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND * AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE * MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED * HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. * */ package l1j.server.server.model; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.logging.Logger; import l1j.server.Config; import l1j.server.server.model.Instance.L1PcInstance; import l1j.server.server.model.Instance.L1PetInstance; import l1j.server.server.model.Instance.L1SummonInstance; import l1j.server.server.model.map.L1Map; import l1j.server.server.serverpackets.S_SystemMessage; import l1j.server.server.serverpackets.ServerBasePacket; import l1j.server.server.types.Point; import l1j.server.server.utils.collections.Lists; import l1j.server.server.utils.collections.Maps; public class L1World { private static Logger _log = Logger.getLogger(L1World.class.getName()); private final Map<String, L1PcInstance> _allPlayers; private final Map<Integer, L1PetInstance> _allPets; private final Map<Integer, L1SummonInstance> _allSummons; private final Map<Integer, L1Object> _allObjects; private final Map<Integer, L1Object>[] _visibleObjects; private final List<L1War> _allWars; private final Map<String, L1Clan> _allClans; private int _weather = 4; private boolean _worldChatEnabled = true; private boolean _processingContributionTotal = false; private static final int MAX_MAP_ID = 10000; private static L1World _instance; @SuppressWarnings("unchecked") private L1World() { _allPlayers = Maps.newConcurrentMap(); // 全てのプレイヤー _allPets = Maps.newConcurrentMap(); // 全てのペット _allSummons = Maps.newConcurrentMap(); // 全てのサモンモンスター _allObjects = Maps.newConcurrentMap(); // 全てのオブジェクト(L1ItemInstance入り、L1Inventoryはなし) _visibleObjects = new Map[MAX_MAP_ID + 1]; // マップ毎のオブジェクト(L1Inventory入り、L1ItemInstanceはなし) _allWars = Lists.newConcurrentList(); // 全ての戦争 _allClans = Maps.newConcurrentMap(); // 所有的血盟物件 (Online/Offline) for (int i = 0; i <= MAX_MAP_ID; i++) { _visibleObjects[i] = Maps.newConcurrentMap(); } } public static L1World getInstance() { if (_instance == null) { _instance = new L1World(); } return _instance; } /** * 全ての状態をクリアする。<br> * デバッグ、テストなどの特殊な目的以外で呼び出してはならない。 */ public void clear() { _instance = new L1World(); } public void storeObject(L1Object object) { if (object == null) { throw new NullPointerException(); } _allObjects.put(object.getId(), object); if (object instanceof L1PcInstance) { _allPlayers.put(((L1PcInstance) object).getName(), (L1PcInstance) object); } if (object instanceof L1PetInstance) { _allPets.put(object.getId(), (L1PetInstance) object); } if (object instanceof L1SummonInstance) { _allSummons.put(object.getId(), (L1SummonInstance) object); } } public void removeObject(L1Object object) { if (object == null) { throw new NullPointerException(); } _allObjects.remove(object.getId()); if (object instanceof L1PcInstance) { _allPlayers.remove(((L1PcInstance) object).getName()); } if (object instanceof L1PetInstance) { _allPets.remove(object.getId()); } if (object instanceof L1SummonInstance) { _allSummons.remove(object.getId()); } } public L1Object findObject(int oID) { return _allObjects.get(oID); } // _allObjectsのビュー private Collection<L1Object> _allValues; public Collection<L1Object> getObject() { Collection<L1Object> vs = _allValues; return (vs != null) ? vs : (_allValues = Collections.unmodifiableCollection(_allObjects.values())); } public L1GroundInventory getInventory(int x, int y, short map) { int inventoryKey = ((x - 30000) * 10000 + (y - 30000)) * -1; // xyのマイナス値をインベントリキーとして使用 Object object = _visibleObjects[map].get(inventoryKey); if (object == null) { return new L1GroundInventory(inventoryKey, x, y, map); } else { return (L1GroundInventory) object; } } public L1GroundInventory getInventory(L1Location loc) { return getInventory(loc.getX(), loc.getY(), (short) loc.getMap().getId()); } public void addVisibleObject(L1Object object) { if (object.getMapId() <= MAX_MAP_ID) { _visibleObjects[object.getMapId()].put(object.getId(), object); } } public void removeVisibleObject(L1Object object) { if (object.getMapId() <= MAX_MAP_ID) { _visibleObjects[object.getMapId()].remove(object.getId()); } } public void moveVisibleObject(L1Object object, int newMap) // set_Mapで新しいMapにするまえに呼ぶこと { if (object.getMapId() != newMap) { if (object.getMapId() <= MAX_MAP_ID) { _visibleObjects[object.getMapId()].remove(object.getId()); } if (newMap <= MAX_MAP_ID) { _visibleObjects[newMap].put(object.getId(), object); } } } private Map<Integer, Integer> createLineMap(Point src, Point target) { Map<Integer, Integer> lineMap = Maps.newConcurrentMap(); /* * http://www2.starcat.ne.jp/~fussy/algo/algo1-1.htmより */ int E; int x; int y; int key; int i; int x0 = src.getX(); int y0 = src.getY(); int x1 = target.getX(); int y1 = target.getY(); int sx = (x1 > x0) ? 1 : -1; int dx = (x1 > x0) ? x1 - x0 : x0 - x1; int sy = (y1 > y0) ? 1 : -1; int dy = (y1 > y0) ? y1 - y0 : y0 - y1; x = x0; y = y0; /* 傾きが1以下の場合 */ if (dx >= dy) { E = -dx; for (i = 0; i <= dx; i++) { key = (x << 16) + y; lineMap.put(key, key); x += sx; E += 2 * dy; if (E >= 0) { y += sy; E -= 2 * dx; } } /* 傾きが1より大きい場合 */ } else { E = -dy; for (i = 0; i <= dy; i++) { key = (x << 16) + y; lineMap.put(key, key); y += sy; E += 2 * dx; if (E >= 0) { x += sx; E -= 2 * dy; } } } return lineMap; } public List<L1Object> getVisibleLineObjects(L1Object src, L1Object target) { Map<Integer, Integer> lineMap = createLineMap(src.getLocation(), target.getLocation()); int map = target.getMapId(); List<L1Object> result = Lists.newList(); if (map <= MAX_MAP_ID) { for (L1Object element : _visibleObjects[map].values()) { if (element.equals(src)) { continue; } int key = (element.getX() << 16) + element.getY(); if (lineMap.containsKey(key)) { result.add(element); } } } return result; } public List<L1Object> getVisibleBoxObjects(L1Object object, int heading, int width, int height) { int x = object.getX(); int y = object.getY(); int map = object.getMapId(); L1Location location = object.getLocation(); List<L1Object> result = Lists.newList(); int headingRotate[] = { 6, 7, 0, 1, 2, 3, 4, 5 }; double cosSita = Math.cos(headingRotate[heading] * Math.PI / 4); double sinSita = Math.sin(headingRotate[heading] * Math.PI / 4); if (map <= MAX_MAP_ID) { for (L1Object element : _visibleObjects[map].values()) { if (element.equals(object)) { continue; } if (map != element.getMapId()) { continue; } // 同じ座標に重なっている場合は範囲内とする if (location.isSamePoint(element.getLocation())) { result.add(element); continue; } int distance = location.getTileLineDistance(element.getLocation()); // 直線距離が高さ、幅どちらよりも大きい場合、計算するまでもなく範囲外 if ((distance > height) && (distance > width)) { continue; } // objectの位置を原点とするための座標補正 int x1 = element.getX() - x; int y1 = element.getY() - y; // Z軸回転させ角度を0度にする。 int rotX = (int) Math.round(x1 * cosSita + y1 * sinSita); int rotY = (int) Math.round(-x1 * sinSita + y1 * cosSita); int xmin = 0; int xmax = height; int ymin = -width; int ymax = width; // 奥行きが射程とかみ合わないので直線距離で判定するように変更。 // if (rotX > xmin && rotX <= xmax && rotY >= ymin && rotY <= // ymax) { if ((rotX > xmin) && (distance <= xmax) && (rotY >= ymin) && (rotY <= ymax)) { result.add(element); } } } return result; } public List<L1Object> getVisibleObjects(L1Object object) { return getVisibleObjects(object, -1); } public List<L1Object> getVisibleObjects(L1Object object, int radius) { L1Map map = object.getMap(); Point pt = object.getLocation(); List<L1Object> result = Lists.newList(); if (map.getId() <= MAX_MAP_ID) { for (L1Object element : _visibleObjects[map.getId()].values()) { if (element.equals(object)) { continue; } if (map != element.getMap()) { continue; } if (radius == -1) { if (pt.isInScreen(element.getLocation())) { result.add(element); } } else if (radius == 0) { if (pt.isSamePoint(element.getLocation())) { result.add(element); } } else { if (pt.getTileLineDistance(element.getLocation()) <= radius) { result.add(element); } } } } return result; } public List<L1Object> getVisiblePoint(L1Location loc, int radius) { List<L1Object> result = Lists.newList(); int mapId = loc.getMapId(); // ループ内で呼ぶと重いため if (mapId <= MAX_MAP_ID) { for (L1Object element : _visibleObjects[mapId].values()) { if (mapId != element.getMapId()) { continue; } if (loc.getTileLineDistance(element.getLocation()) <= radius) { result.add(element); } } } return result; } public List<L1PcInstance> getVisiblePlayer(L1Object object) { return getVisiblePlayer(object, -1); } public List<L1PcInstance> getVisiblePlayer(L1Object object, int radius) { int map = object.getMapId(); Point pt = object.getLocation(); List<L1PcInstance> result = Lists.newList(); for (L1PcInstance element : _allPlayers.values()) { if (element.equals(object)) { continue; } if (map != element.getMapId()) { continue; } if (radius == -1) { if (pt.isInScreen(element.getLocation())) { result.add(element); } } else if (radius == 0) { if (pt.isSamePoint(element.getLocation())) { result.add(element); } } else { if (pt.getTileLineDistance(element.getLocation()) <= radius) { result.add(element); } } } return result; } public List<L1PcInstance> getVisiblePlayerExceptTargetSight(L1Object object, L1Object target) { int map = object.getMapId(); Point objectPt = object.getLocation(); Point targetPt = target.getLocation(); List<L1PcInstance> result = Lists.newList(); for (L1PcInstance element : _allPlayers.values()) { if (element.equals(object)) { continue; } if (map != element.getMapId()) { continue; } if (Config.PC_RECOGNIZE_RANGE == -1) { if (objectPt.isInScreen(element.getLocation())) { if (!targetPt.isInScreen(element.getLocation())) { result.add(element); } } } else { if (objectPt.getTileLineDistance(element.getLocation()) <= Config.PC_RECOGNIZE_RANGE) { if (targetPt.getTileLineDistance(element.getLocation()) > Config.PC_RECOGNIZE_RANGE) { result.add(element); } } } } return result; } /** * objectを認識できる範囲にいるプレイヤーを取得する * * @param object * @return */ public List<L1PcInstance> getRecognizePlayer(L1Object object) { return getVisiblePlayer(object, Config.PC_RECOGNIZE_RANGE); } // _allPlayersのビュー private Collection<L1PcInstance> _allPlayerValues; public Collection<L1PcInstance> getAllPlayers() { Collection<L1PcInstance> vs = _allPlayerValues; return (vs != null) ? vs : (_allPlayerValues = Collections.unmodifiableCollection(_allPlayers.values())); } /** * ワールド内にいる指定された名前のプレイヤーを取得する。 * * @param name * - プレイヤー名(小文字・大文字は無視される) * @return 指定された名前のL1PcInstance。該当プレイヤーが存在しない場合はnullを返す。 */ public L1PcInstance getPlayer(String name) { if (_allPlayers.containsKey(name)) { return _allPlayers.get(name); } for (L1PcInstance each : getAllPlayers()) { if (each.getName().equalsIgnoreCase(name)) { return each; } } return null; } // _allPetsのビュー private Collection<L1PetInstance> _allPetValues; public Collection<L1PetInstance> getAllPets() { Collection<L1PetInstance> vs = _allPetValues; return (vs != null) ? vs : (_allPetValues = Collections.unmodifiableCollection(_allPets.values())); } // _allSummonsのビュー private Collection<L1SummonInstance> _allSummonValues; public Collection<L1SummonInstance> getAllSummons() { Collection<L1SummonInstance> vs = _allSummonValues; return (vs != null) ? vs : (_allSummonValues = Collections.unmodifiableCollection(_allSummons.values())); } public final Map<Integer, L1Object> getAllVisibleObjects() { return _allObjects; } public final Map<Integer, L1Object>[] getVisibleObjects() { return _visibleObjects; } public final Map<Integer, L1Object> getVisibleObjects(int mapId) { return _visibleObjects[mapId]; } public Object getRegion(Object object) { return null; } public void addWar(L1War war) { if (!_allWars.contains(war)) { _allWars.add(war); } } public void removeWar(L1War war) { if (_allWars.contains(war)) { _allWars.remove(war); } } // _allWarsのビュー private List<L1War> _allWarList; public List<L1War> getWarList() { List<L1War> vs = _allWarList; return (vs != null) ? vs : (_allWarList = Collections.unmodifiableList(_allWars)); } public void storeClan(L1Clan clan) { L1Clan temp = getClan(clan.getClanName()); if (temp == null) { _allClans.put(clan.getClanName(), clan); } } public void removeClan(L1Clan clan) { L1Clan temp = getClan(clan.getClanName()); if (temp != null) { _allClans.remove(clan.getClanName()); } } public L1Clan getClan(String clan_name) { return _allClans.get(clan_name); } // _allClansのビュー private Collection<L1Clan> _allClanValues; public Collection<L1Clan> getAllClans() { Collection<L1Clan> vs = _allClanValues; return (vs != null) ? vs : (_allClanValues = Collections.unmodifiableCollection(_allClans.values())); } public void setWeather(int weather) { _weather = weather; } public int getWeather() { return _weather; } public void set_worldChatElabled(boolean flag) { _worldChatEnabled = flag; } public boolean isWorldChatElabled() { return _worldChatEnabled; } public void setProcessingContributionTotal(boolean flag) { _processingContributionTotal = flag; } public boolean isProcessingContributionTotal() { return _processingContributionTotal; } /** * ワールド上に存在する全てのプレイヤーへパケットを送信する。 * * @param packet * 送信するパケットを表すServerBasePacketオブジェクト。 */ public void broadcastPacketToAll(ServerBasePacket packet) { _log.finest("players to notify : " + getAllPlayers().size()); for (L1PcInstance pc : getAllPlayers()) { pc.sendPackets(packet); } } /** * ワールド上に存在する全てのプレイヤーへサーバーメッセージを送信する。 * * @param message * 送信するメッセージ */ public void broadcastServerMessage(String message) { broadcastPacketToAll(new S_SystemMessage(message)); } }