Advertisement
Nik

Fences core 5685

Nik
Oct 24th, 2012
97
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. Index: com/l2jserver/gameserver/GameServer.java
  2. ===================================================================
  3. --- com/l2jserver/gameserver/GameServer.java    (revision 5685)
  4. +++ com/l2jserver/gameserver/GameServer.java    (working copy)
  5. @@ -49,6 +49,7 @@
  6.  import com.l2jserver.gameserver.datatables.EnchantOptionsData;
  7.  import com.l2jserver.gameserver.datatables.EventDroplist;
  8.  import com.l2jserver.gameserver.datatables.ExperienceTable;
  9. +import com.l2jserver.gameserver.datatables.FencesTable;
  10.  import com.l2jserver.gameserver.datatables.FishData;
  11.  import com.l2jserver.gameserver.datatables.FishingMonstersData;
  12.  import com.l2jserver.gameserver.datatables.FishingRodsData;
  13. @@ -256,7 +257,7 @@
  14.             PathFinding.getInstance();
  15.         }
  16.        
  17. -       printSection("NPCs");
  18. +       printSection("WorldObjects");
  19.         HerbDropTable.getInstance();
  20.         NpcTable.getInstance();
  21.         NpcWalkerRoutesData.getInstance();
  22. @@ -269,6 +270,7 @@
  23.         FortManager.getInstance().loadInstances();
  24.         NpcBufferTable.getInstance();
  25.         SpawnTable.getInstance();
  26. +       FencesTable.getInstance();
  27.         HellboundManager.getInstance();
  28.         RaidBossSpawnManager.getInstance();
  29.         DayNightSpawnManager.getInstance().trim().notifyChangeMode();
  30. Index: com/l2jserver/gameserver/datatables/FencesTable.java
  31. ===================================================================
  32. --- com/l2jserver/gameserver/datatables/FencesTable.java    (revision 0)
  33. +++ com/l2jserver/gameserver/datatables/FencesTable.java    (revision 0)
  34. @@ -0,0 +1,173 @@
  35. +/*
  36. + * This program is free software: you can redistribute it and/or modify it under
  37. + * the terms of the GNU General Public License as published by the Free Software
  38. + * Foundation, either version 3 of the License, or (at your option) any later
  39. + * version.
  40. + *
  41. + * This program is distributed in the hope that it will be useful, but WITHOUT
  42. + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  43. + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  44. + * details.
  45. + *
  46. + * You should have received a copy of the GNU General Public License along with
  47. + * this program. If not, see <http://www.gnu.org/licenses/>.
  48. + */
  49. +package com.l2jserver.gameserver.datatables;
  50. +
  51. +import java.sql.Connection;
  52. +import java.sql.PreparedStatement;
  53. +import java.sql.ResultSet;
  54. +import java.sql.SQLException;
  55. +import java.util.HashMap;
  56. +import java.util.Map;
  57. +import java.util.logging.Level;
  58. +import java.util.logging.Logger;
  59. +
  60. +import com.l2jserver.L2DatabaseFactory;
  61. +import com.l2jserver.gameserver.model.Location;
  62. +import com.l2jserver.gameserver.model.actor.instance.L2FenceInstance;
  63. +import com.l2jserver.util.StringUtil;
  64. +
  65. +/**
  66. + * @author Nik based on Christian's work.
  67. + */
  68. +public class FencesTable
  69. +{
  70. +   protected static final Logger _log = Logger.getLogger(FencesTable.class.getName());
  71. +  
  72. +   private static final class SingletonHolder
  73. +   {
  74. +       protected static final FencesTable _instance = new FencesTable();
  75. +   }
  76. +  
  77. +   public static FencesTable getInstance()
  78. +   {
  79. +       return SingletonHolder._instance;
  80. +   }
  81. +  
  82. +   private final Map<Integer, L2FenceInstance> _fences = new HashMap<>();
  83. +  
  84. +   protected FencesTable()
  85. +   {
  86. +       load();
  87. +       _log.log(Level.INFO, StringUtil.concat("Loaded ", String.valueOf(_fences.size()), " stored fences."));
  88. +   }
  89. +  
  90. +   private void load()
  91. +   {
  92. +       try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  93. +           PreparedStatement stmt = con.prepareStatement("SELECT * FROM fences"))
  94. +       {
  95. +           ResultSet res = stmt.executeQuery();
  96. +           while (res.next())
  97. +           {
  98. +               L2FenceInstance fence = new L2FenceInstance(res.getInt("object_id"), res.getInt("state"), res.getInt("x"), res.getInt("y"), res.getInt("z"), res.getInt("width"), res.getInt("length"), res.getInt("height"));
  99. +               fence.setStoredToDB(true); // They are loaded from DB, therefore they are stored there.
  100. +               fence.spawnMe();
  101. +           }
  102. +       }
  103. +       catch (SQLException sqle)
  104. +       {
  105. +           _log.log(Level.WARNING, "Exception while loading fences: ", sqle);
  106. +       }
  107. +   }
  108. +  
  109. +   public void reload()
  110. +   {
  111. +       for (L2FenceInstance fence : _fences.values())
  112. +       {
  113. +           // Do not delete temp fences, they aren't stored in DB therefore they won't be reloaded, but deleted.
  114. +           if (!fence.isStoredToDB())
  115. +           {
  116. +               fence.decayMe();
  117. +           }
  118. +       }
  119. +      
  120. +       load();
  121. +   }
  122. +  
  123. +   public void addFence(L2FenceInstance fence)
  124. +   {
  125. +       _fences.put(fence.getObjectId(), fence);
  126. +   }
  127. +  
  128. +   public L2FenceInstance getFence(int objectId)
  129. +   {
  130. +       return _fences.get(objectId);
  131. +   }
  132. +  
  133. +   public L2FenceInstance removeFence(int objectId)
  134. +   {
  135. +       return _fences.remove(objectId);
  136. +   }
  137. +  
  138. +   public void deleteFence(int objectId, boolean deleteFromDB)
  139. +   {
  140. +       L2FenceInstance fence = removeFence(objectId);
  141. +       if (fence != null)
  142. +       {
  143. +           fence.decayMe();
  144. +       }
  145. +      
  146. +       if (deleteFromDB)
  147. +       {
  148. +           deleteStoredFence(objectId);
  149. +       }
  150. +   }
  151. +  
  152. +   /**
  153. +    * @param fence - Stores the fence to the database.
  154. +    */
  155. +   public void storeFence(L2FenceInstance fence)
  156. +   {
  157. +       try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  158. +           PreparedStatement stmt = con.prepareStatement("INSERT INTO fences (object_id,state,x,y,z,width,length,height) VALUES(?,?,?,?,?,?,?,?)"))
  159. +       {
  160. +           stmt.setInt(1, fence.getObjectId());
  161. +           stmt.setInt(2, fence.getX());
  162. +           stmt.setInt(3, fence.getY());
  163. +           stmt.setInt(4, fence.getZ());
  164. +           stmt.setInt(5, fence.getWidth());
  165. +           stmt.setInt(6, fence.getLength());
  166. +           stmt.setInt(6, fence.getHeight());
  167. +           stmt.executeUpdate();
  168. +       }
  169. +       catch (SQLException sqle)
  170. +       {
  171. +           _log.log(Level.WARNING, "Error while storing fence:", sqle);
  172. +       }
  173. +   }
  174. +  
  175. +   public boolean deleteStoredFence(int objectId)
  176. +   {
  177. +       try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  178. +           PreparedStatement stmt = con.prepareStatement("DELETE FROM fences WHERE object_id=?");)
  179. +       {
  180. +          
  181. +           stmt.setInt(1, objectId);
  182. +           if (stmt.executeUpdate() > 0)
  183. +           {
  184. +               return true;
  185. +           }
  186. +       }
  187. +       catch (SQLException sqle)
  188. +       {
  189. +           _log.log(Level.WARNING, "Error while deleting stored fence:", sqle);
  190. +       }
  191. +      
  192. +       return false;
  193. +   }
  194. +  
  195. +   public boolean checkIfBetweenFences(Location loc1, Location loc2)
  196. +   {
  197. +       for (L2FenceInstance fence : _fences.values())
  198. +       {
  199. +           if (!fence.canSee(loc1, loc2))
  200. +           {
  201. +               return true;
  202. +           }
  203. +       }
  204. +      
  205. +       return true;
  206. +   }
  207. +}
  208. Index: com/l2jserver/gameserver/model/L2Object.java
  209. ===================================================================
  210. --- com/l2jserver/gameserver/model/L2Object.java    (revision 5685)
  211. +++ com/l2jserver/gameserver/model/L2Object.java    (working copy)
  212. @@ -64,6 +64,7 @@
  213.         L2Object(null),
  214.         L2ItemInstance(L2Object),
  215.         L2Character(L2Object),
  216. +       L2FenceInstance(L2Object),
  217.         L2Npc(L2Character),
  218.         L2Playable(L2Character),
  219.         L2Summon(L2Playable),
  220. Index: com/l2jserver/gameserver/model/actor/instance/L2FenceInstance.java
  221. ===================================================================
  222. --- com/l2jserver/gameserver/model/actor/instance/L2FenceInstance.java  (revision 0)
  223. +++ com/l2jserver/gameserver/model/actor/instance/L2FenceInstance.java  (revision 0)
  224. @@ -0,0 +1,214 @@
  225. +/*
  226. + * This program is free software: you can redistribute it and/or modify it under
  227. + * the terms of the GNU General Public License as published by the Free Software
  228. + * Foundation, either version 3 of the License, or (at your option) any later
  229. + * version.
  230. + *
  231. + * This program is distributed in the hope that it will be useful, but WITHOUT
  232. + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  233. + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  234. + * details.
  235. + *
  236. + * You should have received a copy of the GNU General Public License along with
  237. + * this program. If not, see <http://www.gnu.org/licenses/>.
  238. + */
  239. +
  240. +package com.l2jserver.gameserver.model.actor.instance;
  241. +
  242. +import java.awt.Rectangle;
  243. +
  244. +import com.l2jserver.gameserver.datatables.FencesTable;
  245. +import com.l2jserver.gameserver.idfactory.IdFactory;
  246. +import com.l2jserver.gameserver.model.L2Object;
  247. +import com.l2jserver.gameserver.model.Location;
  248. +import com.l2jserver.gameserver.model.actor.L2Character;
  249. +import com.l2jserver.gameserver.network.serverpackets.ExColosseumFenceInfo;
  250. +
  251. +/**
  252. + * @author Nik
  253. + */
  254. +public final class L2FenceInstance extends L2Object
  255. +{
  256. +   public static final int HIDDEN = 0;
  257. +   public static final int UNCLOSED = 1;
  258. +   public static final int CLOSED = 2;
  259. +  
  260. +   private int _state;
  261. +   private final int _width;
  262. +   private final int _length;
  263. +   private int _height;
  264. +   private Rectangle _shape;
  265. +   private boolean _storedToDB = false;
  266. +  
  267. +   public L2FenceInstance(int objectId, int state, int x, int y, int z, int width, int length, int height)
  268. +   {
  269. +       super(objectId);
  270. +       setInstanceType(InstanceType.L2FenceInstance);
  271. +       initPosition();
  272. +       setXYZ(x, y, z);
  273. +      
  274. +       if ((state < 0) || (state > 2))
  275. +       {
  276. +           state = 0;
  277. +       }
  278. +       _state = state;
  279. +       _width = width;
  280. +       _length = length;
  281. +       _height = height;
  282. +       _shape = new Rectangle(getX(), getY(), _width, _length);
  283. +   }
  284. +  
  285. +   public L2FenceInstance(int x, int y, int z, int width, int length)
  286. +   {
  287. +       super(IdFactory.getInstance().getNextId());
  288. +       setInstanceType(InstanceType.L2FenceInstance);
  289. +       initPosition();
  290. +       setXYZ(x, y, z);
  291. +       _state = CLOSED; // Its a better default than HIDDEN.
  292. +       _width = width;
  293. +       _length = length;
  294. +       _height = 50;
  295. +       _shape = new Rectangle(getX(), getY(), _width, _length);
  296. +   }
  297. +  
  298. +   public boolean isLocInside(Location loc)
  299. +   {
  300. +       boolean isInsideZ = (loc.getZ() >= getZ()) && (loc.getZ() <= (getZ() + _height));
  301. +       return _shape.contains(loc.getX(), loc.getY()) && isInsideZ && (getInstanceId() == loc.getInstanceId());
  302. +   }
  303. +  
  304. +   public boolean isLocOutside(Location loc)
  305. +   {
  306. +       return !isLocInside(loc);
  307. +   }
  308. +  
  309. +   /**
  310. +    * @param x1 - obj1's X loc
  311. +    * @param y1 - obj1's Y loc
  312. +    * @param x2 - obj2's X loc
  313. +    * @param y2 - obj2's Y loc
  314. +    * @return can obj1 see obj2 through this fence (their LOS do not intersect the fence's bounds). <B>No Z</B> checks are done. <B>No Instance</B> checks are done.
  315. +    */
  316. +   public boolean canSee(int x1, int y1, int x2, int y2)
  317. +   {
  318. +       return !_shape.intersectsLine(x1, y1, x2, y2);
  319. +   }
  320. +  
  321. +   /**
  322. +    * @param loc1 - obj1's xyz & instanceId location
  323. +    * @param loc2 - obj2's xyz & instanceId location
  324. +    * @return can obj1 see obj2 through this fence (their LOS do not intersect the fence's bounds). <B>Z</B> checks are done. <B>Instance</B> checks are done.
  325. +    */
  326. +   public boolean canSee(Location loc1, Location loc2)
  327. +   {
  328. +       if ((getState() == CLOSED) && (loc1.getInstanceId() == loc2.getInstanceId()))
  329. +       {
  330. +           // Those Z checks are probably not 100% accurate, rework them if it must.
  331. +           int higherZ = loc1.getZ() >= loc2.getZ() ? loc1.getZ() : loc2.getZ();
  332. +           if ((higherZ >= getZ()) && (higherZ <= (getZ() + _height)))
  333. +           {
  334. +               return canSee(loc1.getX(), loc1.getY(), loc2.getX(), loc2.getY());
  335. +           }
  336. +       }
  337. +      
  338. +       return true;
  339. +   }
  340. +  
  341. +   /**
  342. +    * @param obj1
  343. +    * @param obj2
  344. +    * @return can obj1 see obj2 through this fence (their LOS do not intersect the fence's bounds). <B>Z</B> checks are done. <B>Instance</B> checks are done.
  345. +    */
  346. +   public boolean canSee(L2Object obj1, L2Object obj2)
  347. +   {
  348. +       if ((getState() == CLOSED) && (obj1.getInstanceId() == obj2.getInstanceId()))
  349. +       {
  350. +           // Those Z checks are probably not 100% accurate, rework them if it must.
  351. +           int higherZ = obj1.getZ() >= obj2.getZ() ? obj1.getZ() : obj2.getZ();
  352. +           if ((higherZ >= getZ()) && (higherZ <= (getZ() + _height)))
  353. +           {
  354. +               return canSee(obj1.getX(), obj1.getY(), obj2.getX(), obj2.getY());
  355. +           }
  356. +       }
  357. +      
  358. +       return true;
  359. +   }
  360. +  
  361. +   @Override
  362. +   public void sendInfo(L2PcInstance activeChar)
  363. +   {
  364. +       activeChar.sendPacket(new ExColosseumFenceInfo(this));
  365. +   }
  366. +  
  367. +   @Override
  368. +   public boolean isAutoAttackable(L2Character attacker)
  369. +   {
  370. +       return false;
  371. +   }
  372. +  
  373. +   /**
  374. +    * Also removes the fence from FencesTable, but its not removed from DB
  375. +    */
  376. +   @Override
  377. +   public void decayMe()
  378. +   {
  379. +       FencesTable.getInstance().removeFence(getObjectId());
  380. +   }
  381. +  
  382. +   @Override
  383. +   public void onSpawn()
  384. +   {
  385. +       // Maybe someone for some reason decided to respawn this fence on different XYZ. RESHAPE!!!
  386. +       _shape = new Rectangle(getX(), getY(), _width, _length);
  387. +       FencesTable.getInstance().addFence(this);
  388. +   }
  389. +  
  390. +   /**
  391. +    * @param height - how high the fence is :D :D Although the client height of the fence is ~50, you can set it to as much as you want. <BR>
  392. +    *            Setting it to lower might result in some strange LOS checks. Setting it too high might result in weird LOS checks in all the floors of TOI :P
  393. +    */
  394. +   public void setHeight(int height)
  395. +   {
  396. +       _height = height;
  397. +   }
  398. +  
  399. +   public void setState(int state)
  400. +   {
  401. +       if ((state < 0) || (state > 2))
  402. +       {
  403. +           state = 0;
  404. +       }
  405. +      
  406. +       _state = state;
  407. +   }
  408. +  
  409. +   public int getState()
  410. +   {
  411. +       return _state;
  412. +   }
  413. +  
  414. +   public int getWidth()
  415. +   {
  416. +       return _width;
  417. +   }
  418. +  
  419. +   public int getLength()
  420. +   {
  421. +       return _length;
  422. +   }
  423. +  
  424. +   public int getHeight()
  425. +   {
  426. +       return _height;
  427. +   }
  428. +  
  429. +   public void setStoredToDB(boolean storedToDB)
  430. +   {
  431. +       _storedToDB = storedToDB;
  432. +   }
  433. +  
  434. +   public boolean isStoredToDB()
  435. +   {
  436. +       return _storedToDB;
  437. +   }
  438. +}
  439. \ No newline at end of file
  440. Index: com/l2jserver/gameserver/GeoEngine.java
  441. ===================================================================
  442. --- com/l2jserver/gameserver/GeoEngine.java (revision 5685)
  443. +++ com/l2jserver/gameserver/GeoEngine.java (working copy)
  444. @@ -34,6 +34,7 @@
  445.  
  446.  import com.l2jserver.Config;
  447.  import com.l2jserver.gameserver.datatables.DoorTable;
  448. +import com.l2jserver.gameserver.datatables.FencesTable;
  449.  import com.l2jserver.gameserver.model.L2Object;
  450.  import com.l2jserver.gameserver.model.L2Spawn;
  451.  import com.l2jserver.gameserver.model.L2World;
  452. @@ -101,6 +102,12 @@
  453.         {
  454.             return false;
  455.         }
  456. +      
  457. +       if (FencesTable.getInstance().checkIfBetweenFences(new Location(cha), new Location(target.getX(), target.getY(), target.getZ(), 0, cha.getInstanceId())))
  458. +       {
  459. +           return false;
  460. +       }
  461. +      
  462.         if (cha.getZ() >= target.getZ())
  463.         {
  464.             return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), target.getX(), target.getY(), target.getZ());
  465. @@ -138,6 +145,10 @@
  466.         {
  467.             return true; // door coordinates are hinge coords..
  468.         }
  469. +       if (FencesTable.getInstance().checkIfBetweenFences(new Location(cha), new Location(target)))
  470. +       {
  471. +           return false;
  472. +       }
  473.         if (target instanceof L2DefenderInstance)
  474.         {
  475.             z2 += 30; // well they don't move closer to balcony fence at the moment :(
  476. @@ -189,6 +200,10 @@
  477.         {
  478.             return startpoint;
  479.         }
  480. +       if (FencesTable.getInstance().checkIfBetweenFences(new Location(x, y, z, 0, instanceId), new Location(tx, ty, tz, 0, instanceId)))
  481. +       {
  482. +           return startpoint;
  483. +       }
  484.        
  485.         Location destiny = new Location(tx, ty, tz);
  486.         return moveCheck(startpoint, destiny, (x - L2World.MAP_MIN_X) >> 4, (y - L2World.MAP_MIN_Y) >> 4, z, (tx - L2World.MAP_MIN_X) >> 4, (ty - L2World.MAP_MIN_Y) >> 4, tz);
  487. Index: com/l2jserver/gameserver/idfactory/IdFactory.java
  488. ===================================================================
  489. --- com/l2jserver/gameserver/idfactory/IdFactory.java   (revision 5685)
  490. +++ com/l2jserver/gameserver/idfactory/IdFactory.java   (working copy)
  491. @@ -102,7 +102,8 @@
  492.         "SELECT ally_id     FROM clan_data             WHERE ally_id >= ?     AND ally_id < ?",
  493.         "SELECT leader_id   FROM clan_data             WHERE leader_id >= ?   AND leader_id < ?",
  494.         "SELECT item_obj_id FROM pets                  WHERE item_obj_id >= ? AND item_obj_id < ?",
  495. -       "SELECT object_id   FROM itemsonground        WHERE object_id >= ?   AND object_id < ?"
  496. +       "SELECT object_id   FROM itemsonground        WHERE object_id >= ?   AND object_id < ?",
  497. +       "SELECT object_id   FROM fences        WHERE object_id >= ?   AND object_id < ?"
  498.     };
  499.    
  500.     private static final String[] TIMESTAMPS_CLEAN =
  501. @@ -400,6 +401,20 @@
  502.                     temp.add(rs.getInt(1));
  503.                 }
  504.             }
  505. +          
  506. +           try (ResultSet rs = s.executeQuery("SELECT COUNT(*) FROM fences"))
  507. +           {
  508. +               rs.next();
  509. +               temp.ensureCapacity(temp.size() + rs.getInt(1));
  510. +           }
  511. +          
  512. +           try (ResultSet rs = s.executeQuery("SELECT object_id FROM fences"))
  513. +           {
  514. +               while (rs.next())
  515. +               {
  516. +                   temp.add(rs.getInt(1));
  517. +               }
  518. +           }
  519.             temp.sort();
  520.             return temp.toArray();
  521.         }
  522. Index: com/l2jserver/gameserver/network/serverpackets/ExColosseumFenceInfo.java
  523. ===================================================================
  524. --- com/l2jserver/gameserver/network/serverpackets/ExColosseumFenceInfo.java    (revision 0)
  525. +++ com/l2jserver/gameserver/network/serverpackets/ExColosseumFenceInfo.java    (revision 0)
  526. @@ -0,0 +1,57 @@
  527. +/*
  528. + * This program is free software: you can redistribute it and/or modify it under
  529. + * the terms of the GNU General Public License as published by the Free Software
  530. + * Foundation, either version 3 of the License, or (at your option) any later
  531. + * version.
  532. + *
  533. + * This program is distributed in the hope that it will be useful, but WITHOUT
  534. + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  535. + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  536. + * details.
  537. + *
  538. + * You should have received a copy of the GNU General Public License along with
  539. + * this program. If not, see <http://www.gnu.org/licenses/>.
  540. + */
  541. +package com.l2jserver.gameserver.network.serverpackets;
  542. +
  543. +import com.l2jserver.gameserver.model.actor.instance.L2FenceInstance;
  544. +
  545. +/**
  546. + * OP: 0xFE<br>
  547. + * OP2: 0x0003<br>
  548. + * Format: ddddddd<br>
  549. + * - d: object id<br>
  550. + * - d: state(0=hidden, 1=unconnected corners, 2=connected corners)<br>
  551. + * - d: x<br>
  552. + * - d: y<br>
  553. + * - d: z<br>
  554. + * - d: a side length<br>
  555. + * - d: b side length<br>
  556. + */
  557. +public class ExColosseumFenceInfo extends L2GameServerPacket
  558. +{
  559. +   private final L2FenceInstance _fence;
  560. +  
  561. +   public ExColosseumFenceInfo(L2FenceInstance fence)
  562. +   {
  563. +       _fence = fence;
  564. +   }
  565. +  
  566. +   /**
  567. +    * @see com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket#writeImpl()
  568. +    */
  569. +   @Override
  570. +   protected void writeImpl()
  571. +   {
  572. +       writeC(0xfe);
  573. +       writeH(0x0003);
  574. +      
  575. +       writeD(_fence.getObjectId());
  576. +       writeD(_fence.getState());
  577. +       writeD(_fence.getX());
  578. +       writeD(_fence.getY());
  579. +       writeD(_fence.getZ());
  580. +       writeD(_fence.getWidth());
  581. +       writeD(_fence.getLength());
  582. +   }
  583. +}
  584. \ No newline at end of file
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement