Advertisement
Guest User

Untitled

a guest
Feb 14th, 2016
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 20.70 KB | None | 0 0
  1. // Renamed from BetterFriendlessLeech to FriendlessLeech on 4/27/2013 by Tru
  2. ////////////////////////////////////////////////////////////////////////////////////
  3.  
  4. // ====================================================================================================
  5. // v THIS IS THE JUNK YOU'LL WANT TO EDIT v
  6. // Friendless leech starter setting (set to run with op Etal channel bot by default)
  7. // ====================================================================================================
  8.  
  9. var gamenames = ["TitansBaal"]; // Game name to use
  10. var gamepasswords = [""]; // Password for that game (case sensitive)
  11.  
  12. // ====================================================================================================
  13. // Filter and channel settings
  14. // ====================================================================================================
  15.  
  16. var extrafilters = ["Next game is ",""]; // The 2nd filter should be whatever the bot manager says right after the game name
  17. // If the 2nd filter is empty, the rest of the line is used as the game name
  18. var hasDash = false // set this to false if the game you want to join doesn't have a dash in the name
  19. var joinChannelInChat = "op titansvault"; // leave blank to not join a private channel
  20.  
  21. // =============================================================================
  22. // UseCDKeyChangeTrick :
  23. // if true, the follow bot will say his gamename/gamepwd (set in the OOG) in chat before each game
  24. // in order to increment his number of runs (to switch CDKeys after xx runs)
  25. // if false, the follow bot will never change his CDKey
  26. // =============================================================================
  27. var UseCDKeyChangeTrick = false;
  28.  
  29. // ====================================================================================================
  30. // All of the delays for actions from bot start until in-game are below! Read the notes!!!
  31. // ====================================================================================================
  32. var maxRetriesPerGame = 3; // The # of times to try rejoining a *unique* game
  33. var maxRetries = 3; // The # of *different* games to try joining, if all attempts fail, bot will exit to character selection screen and rejoin lobby
  34. var gameMinLength = 180000; // time in milliseconds, minimum game length, 180 seconds default (1 game/3 minutes)
  35. var unableToConnectRetry = 5; // time in minutes to retry connecting on connection fail (real value is +/- 1 min)
  36. var realmDownRetry = 260; // time in minutes to retry connecting on a realm down (default is 120 minutes)
  37. var disconnectedRetry = 5; // time in minutes to retry on a disconnection (usually ip ban related)
  38. var cdkeyInUseRetry = 1; // time in minutes to retry on a cdkey in use error message (set to 0 to stop)
  39. var connectingToBnetTimeout = 20000; // time in milliseconds to wait for a login to time out and click cancel and retry
  40. var characterScreenTimeout = 5000; // time in milliseconds to wait for character screen to appear
  41. var pleaseWaitTimeout = 2000; // time in milliseconds to wait for a please wait popup
  42. var createGameThreshold = 2000; // time in milliseconds to wait between making games
  43. var createGameThresholdRandom = 500; // time in milliseconds to randomly add +/- to the game create time
  44. var createGameTimeout = 15000; // time in milliseconds to register a failed to create game
  45. var waitInLineTimeout = 60000; // time in milliseconds to wait in lines for a create game (60 second default)
  46. var characterSelectDelay = 1000; // time in milliseconds to wait before selecting a character on the char screen
  47. var loginDelay = 1000; // time in milliseconds to wait before submitting login information
  48. var clickDelay = 500; // wait X milliseconds before next action after a click event
  49. var textDelay = 500; // wait X milliseconds before next action after inserting text into a textbox
  50. var clickDelayRandom = 500; // random amount of time to add to a click
  51. var textDelayRandom = 500; // random amount of time to add to a text set
  52. var gameDoesNotExistDelayMin = 1000; // how long to wait when a Game Does Not Exist occurs - minimum - default 10 minutes
  53. var gameDoesNotExistDelayMax = 2000; // how long to wait when a Game Does Not Exist occurs - maximum - default 15 minutes
  54. var gameDoesNotExistTimeout = 50; // how long to wait for the dialog to disappear (default 30 seconds, don't change this)
  55. var waitBeforeEnterChatMin = 1000; // min how long to wait before entering chat
  56. var waitBeforeEnterChatMax = 2000; // max how long to wait before entering chat
  57. var waitInChatBeforeActionsMin = 1000; // min how long to wait before joining channel
  58. var waitInChatBeforeActionsMax = 2000; // max how long to wait before joining channel
  59. var ftjDelay = 180; // time to idle before trying if leecher failed to join. In seconds.
  60.  
  61. // =================================
  62. // DONT EDIT ANYTHING BELOW THIS
  63. // =================================
  64.  
  65. //case insensitivity
  66. for (i = 0; i < gamenames.length; i++) {
  67. gamenames[i] = gamenames[i].toLowerCase();
  68. }
  69. for (i = 0; i < extrafilters.length; i++) {
  70. extrafilters[i] = extrafilters[i].toLowerCase();
  71. }
  72. joinChannelInChat = joinChannelInChat.toLowerCase();
  73.  
  74. // D2NT Manager Command
  75. const D2NT_MGR_LOADING = 1;
  76. const D2NT_MGR_READY = 2;
  77. const D2NT_MGR_LOGIN = 3;
  78. const D2NT_MGR_CREATE_GAME = 4;
  79. const D2NT_MGR_INGAME = 5;
  80. const D2NT_MGR_RESTART = 6;
  81. const D2NT_MGR_CHICKEN = 7;
  82. const D2NT_MGR_PRINT_STATUS = 8;
  83. const D2NT_MGR_PRINT_LOG = 9;
  84.  
  85. var lastGameMade = GetTickCount();
  86. var nextGameMake = 0;
  87. var inGameAt = 0;
  88. var chatActionsDone = false;
  89. var lastGameStatus = 0; // 0 = OK, 1 = Processing, 2 = Ready for retry
  90. var game = "";
  91. var gamepassword = "";
  92. var retry = 0;
  93. var fullretry = maxRetries;
  94. var samegame = new Array(gamenames.length); // Empty array for however many runs you might be following (default 6)
  95. var ftjcount = 0;
  96.  
  97. Include("libs/controlInfo.ntl");
  98. Include("libs/common/NTColorConverter.ntl");
  99.  
  100. var controlData = new controlInfo();
  101.  
  102. function NTMain()
  103. {
  104. Delay(1000);
  105.  
  106. var _ingame = false;
  107.  
  108. controlData.clickDelay = clickDelay;
  109. controlData.textDelay = textDelay;
  110. controlData.clickDelayRandom = clickDelayRandom;
  111. controlData.textDelayRandom = textDelayRandom;
  112.  
  113. while (1)
  114. {
  115. if (me.ingame)
  116. {
  117. if (!inGameAt)
  118. inGameAt = GetTickCount();
  119.  
  120. if (!_ingame)
  121. {
  122. RunGC(); // run garbage collector between each game
  123.  
  124. if (Load("NTBot/NTBotGame.ntj"))
  125. {
  126. _ingame = true;
  127.  
  128. if (me.playtype > 0)
  129. sendEventToOOG(D2NT_MGR_INGAME, "In Game.. [IP:" + me.gameserverip.split(".")[3] + "]", 0);
  130. else
  131. sendEventToOOG(D2NT_MGR_INGAME, "In Game..", 0);
  132.  
  133. fullretry = maxRetries;
  134. lastGameStatus = 0;
  135. game = "";
  136. }
  137. }
  138.  
  139. Delay(1000);
  140. }
  141. else
  142. {
  143. Delay(500);
  144. if (_ingame)
  145. {
  146. _ingame = false;
  147.  
  148. // --------------------------
  149. // Prevent NT from closing D2
  150. // --------------------------
  151.  
  152. // Wait for lobby or lobby chat
  153. var _location;
  154. do
  155. {
  156. _location = controlData.getLocation();
  157. Delay(50);
  158. }
  159. while (_location.id != 3 && _location.id != 1);
  160.  
  161. if (_location.id == 3) // Lobby Chat
  162. {
  163. // We need to go somewhere besides the lobby chat so NT can type crap and think its creating a game
  164. Delay(100);
  165. Say("/join " + getRandomString(Random(3,10)));
  166. Delay(1000);
  167. }
  168. sendEventToOOG(D2NT_MGR_CREATE_GAME, _location.name, 0);
  169. outputGameLength();
  170. setNextGameMake();
  171. // --------------------------
  172.  
  173. sendEventToOOG(D2NT_MGR_READY, "", 0);
  174. }
  175.  
  176. locationAction(controlData.getLocation());
  177.  
  178. Delay(500);
  179. }
  180. }
  181. }
  182.  
  183. function locationAction(location)
  184. {
  185. switch (location.id)
  186. {
  187. case 3: // Lobby Chat
  188. //ftjcheck
  189. if (ftjcount >= 10 && lastGameStatus == 1) {
  190. sendEventToOOG(D2NT_MGR_PRINT_LOG, COLOR_1 + "FTJ detected...........Idling", 0);
  191. ftjcount = 0;
  192. inGameAt = 0;
  193. lastGameStatus = 0;
  194. setNextGameMake();
  195. locationTimeout(ftjDelay*1000, location);
  196. }
  197. // Not retrying anymore, clear the game name so that if user tries to do it themself, bot won't fill in with old game
  198. if (lastGameStatus == 0 || retry == 0)
  199. {
  200. // Clear these flags just in case
  201. retry = 0;
  202. game = "";
  203. }
  204.  
  205. // Handle non-existent or full games:
  206. // When the join button is hit, lastGameStatus = 1.
  207. // When in-game, lastGameStatus = 0.
  208. // So this gets executed when NTMain() doesn't reset the flag to 0.
  209. if (lastGameStatus == 1)
  210. {
  211. if (fullretry == 0) // If we have tried [maxRetries] different games ([maxRetriesPerGame] retries for each game)
  212. {
  213. lastGameStatus = 0;
  214. fullretry = maxRetries;
  215.  
  216. // We need to close out of chat and come back
  217. sendEventToOOG(D2NT_MGR_PRINT_LOG, COLOR_1 + "Game doesn't exist or its full. Rejoining chat.", 0);
  218. controlData.click(controlData.controls.lobby.button.quit);
  219. }
  220. else
  221. {
  222. lastGameStatus = 2; // Ready for retry
  223.  
  224. if (retry == 0) // If we just failed [maxRetriesPerGame] times for joining this game
  225. fullretry--; // Update flag
  226.  
  227. sendEventToOOG(D2NT_MGR_PRINT_LOG, COLOR_1 + "Game doesn't exist or its full. " + retry + " retries left.", 0);
  228.  
  229. inGameAt = Random(gameDoesNotExistDelayMin, gameDoesNotExistDelayMax);
  230. setNextGameMake();
  231. chatActionsDone = true; // Undo the flag set by setNextGameMake()
  232.  
  233. locationTimeout(gameDoesNotExistTimeout, location);
  234. }
  235. break;
  236. }
  237.  
  238. if (!chatActionsDone || GetTickCount() > nextGameMake + 180000) // Need to join channel or its been 3 minutes and no action, possibly kicked from channel, rejoin
  239. {
  240. if (GetTickCount() > nextGameMake + 180000) // Reset this so that we don't keep joining channel over and over
  241. nextGameMake = GetTickCount();
  242.  
  243. chatActionsDone = true;
  244. Delay(Random(waitInChatBeforeActionsMin, waitInChatBeforeActionsMax));
  245.  
  246. Say("/join " + joinChannelInChat);
  247. Delay(1000);
  248. sendEventToOOG(D2NT_MGR_PRINT_LOG, "Looking for games..", 0);
  249. }
  250.  
  251. // Time for action?
  252. if (GetTickCount() >= nextGameMake)
  253. {
  254. sendEventToOOG(D2NT_MGR_PRINT_STATUS, location.name, 0);
  255.  
  256. // If we can retry and there is a game to retry
  257. if (retry > 0 && game != "" && lastGameStatus == 2)
  258. {
  259. controlData.click(controlData.controls.lobby.button.join);
  260. retry--;
  261. break;
  262. }
  263.  
  264. var chat = controlData.get(controlData.controls.lobby.chat.textBox.channelText);
  265. if (chat)
  266. {
  267. var linewithname = "";
  268. lines = chat.GetText();
  269. if (lines)
  270. {
  271. for (var line = 0; line < lines.length; line++)
  272. {
  273. linewithname = lines[line].toLowerCase();
  274.  
  275. for (var _filterindex = 0; _filterindex < extrafilters.length; _filterindex += 2)
  276. {
  277. // Check filters
  278. if (linewithname.lastIndexOf(extrafilters[_filterindex]) != -1 && linewithname.lastIndexOf(extrafilters[_filterindex+1]) != -1)
  279. {
  280. for (var _gameindex = 0; _gameindex < gamenames.length; _gameindex++)
  281. {
  282. // Check for game name
  283. var index = linewithname.lastIndexOf(gamenames[_gameindex]);
  284. //sendEventToOOG(D2NT_MGR_PRINT_LOG, "1:" + index + "," + gamenames[_gameindex], 0);
  285. //sendEventToOOG(D2NT_MGR_PRINT_LOG, "2:" + linewithname.substring(index), 0);
  286.  
  287. if (index > -1)
  288. {
  289. var gamenumber = "";
  290. var regexp;
  291. if(hasDash){
  292. regexp = /^.*\-(\d+).*$/g;
  293. }
  294. else{
  295. regexp = /^.*?-*(\d+).*$/g;
  296. }
  297. //if (linewithname.substring(index).match(regexp)) {
  298. gamenumber = linewithname.substring(index).replace(regexp,"$1");
  299.  
  300. //sendEventToOOG(D2NT_MGR_PRINT_LOG, "3:" + gamenumber + "," + linewithname.substring(index), 0);
  301. //game = linewithname.substring(index, linewithname.lastIndexOf(extrafilters[_filterindex+1]));
  302. gamepassword = gamepasswords[_gameindex];
  303. game = gamenames[_gameindex] + gamenumber;
  304.  
  305. if (gamenumber != "" && game != samegame[_gameindex])
  306. {
  307. sendEventToOOG(D2NT_MGR_PRINT_LOG, "Joining " + game + "..", 0);
  308. samegame[_gameindex] = game;
  309. if (UseCDKeyChangeTrick)
  310. {
  311. // increasing the number of runs in the OOG (for CDKey switch)
  312. // Problem : the gamename/gamepassword set in your OOG will be typed in the lobbyChat
  313. // this may output a red message in the lobby chat if you have a password set (not a real problem)
  314. // I recommend to set a gamename like "Run" and no gamepassword in your OOG. -> this will output "Run-01", "Run-02", etc in lobby chat before joining games
  315. sendEventToOOG(D2NT_MGR_CREATE_GAME, location.name, 0);
  316. Delay(1000);
  317. }
  318. controlData.click(controlData.controls.lobby.button.join);
  319. retry = maxRetriesPerGame;
  320. break;
  321. }
  322. }
  323. }
  324. }
  325. }
  326. }
  327. }
  328. }
  329. }
  330. else
  331. sendEventToOOG(D2NT_MGR_PRINT_STATUS, location.name + " (" + parseInt((nextGameMake-GetTickCount())/1000) + "s)", 0);
  332. break;
  333.  
  334. case 1: // Lobby
  335. //ftjcheck
  336. if (ftjcount >= 10 && lastGameStatus == 1) {
  337. sendEventToOOG(D2NT_MGR_PRINT_LOG, COLOR_1 + "FTJ detected...........Idling", 0);
  338. ftjcount = 0;
  339. inGameAt = 0;
  340. lastGameStatus = 0;
  341. setNextGameMake();
  342. locationTimeout(ftjDelay*1000, location);
  343. }
  344. // Enter chat
  345. Delay(Random(waitBeforeEnterChatMin, waitBeforeEnterChatMax));
  346. controlData.click(controlData.controls.lobby.button.enterChat);
  347. break;
  348.  
  349. case 2: // Waiting In Line
  350. if (GetTickCount()-lastGameMade > waitInLineTimeout)
  351. controlData.click(controlData.controls.lobby.inLine.button.cancel);
  352. break;
  353.  
  354. case 4: // Create Game
  355. break;
  356.  
  357. case 5: // Join Game
  358. if (game != "")
  359. {
  360. sendEventToOOG(D2NT_MGR_PRINT_STATUS, location.name, 0);
  361.  
  362. controlData.setText( controlData.controls.lobby.join.editBox.gameName, game);
  363. Delay (1250);
  364. controlData.setText( controlData.controls.lobby.join.editBox.password, gamepassword);
  365. Delay (1250);
  366. controlData.click(controlData.controls.lobby.join.button.joinGame);
  367. }
  368.  
  369. lastGameMade = GetTickCount();
  370. lastGameStatus = 1;
  371. break;
  372.  
  373. case 6: // Ladder
  374. break;
  375.  
  376. case 7: // Channel List
  377. break;
  378.  
  379. case 8: // Main Menu
  380. if (controlData.getCurrentRealmIndex() == me.gatewayid)
  381. {
  382. outputGameLength();
  383. controlData.click(controlData.gameTypes[me.playtype]);
  384. }
  385. else
  386. controlData.click(controlData.controls.mainMenu.button.gateway);
  387. break;
  388.  
  389. case 9: // Login
  390. sendEventToOOG(D2NT_MGR_PRINT_STATUS, location.name, 0);
  391. Delay(loginDelay);
  392.  
  393. controlData.setText(controlData.controls.login.editBox.accountName, me.account);
  394.  
  395. sendEventToOOG(D2NT_MGR_LOGIN, location.name, 0);
  396.  
  397. locationTimeout(5000, location);
  398. break;
  399.  
  400. case 10: // Login Error (this is a fatal error, so stop)
  401. sendEventToOOG(D2NT_MGR_RESTART, location.name, 10);
  402. Delay(3500);
  403. break;
  404.  
  405. case 11: // Unable To Connect
  406. timeoutDelay(unableToConnectRetry*60*1000, location)
  407. controlData.click(controlData.controls.login.unableToConnect.button.ok);
  408. break;
  409.  
  410. case 12: // Character Select
  411. var _time, _control;
  412.  
  413. sendEventToOOG(D2NT_MGR_PRINT_STATUS, location.name, 0);
  414.  
  415. for (_time = 0; _time < characterScreenTimeout; _time += 500)
  416. {
  417. _control = controlData.get(controlData.controls.characterSelect.textBox.characterInfo[me.charloc]);
  418. if (_control && _control.GetText() != undefined)
  419. break;
  420.  
  421. Delay(500);
  422. }
  423.  
  424. if (_time < characterScreenTimeout)
  425. {
  426. Delay(characterSelectDelay);
  427.  
  428. controlData.click(controlData.controls.characterSelect.textBox.characters[me.charloc], 0, 0, 1);
  429. controlData.click(controlData.controls.characterSelect.textBox.characterInfo[me.charloc], 0, 0, 1);
  430.  
  431. // reset last game made, so it doesnt make a game immediately
  432. inGameAt = 0;
  433. setNextGameMake();
  434. }
  435. else
  436. {
  437. controlData.click(controlData.controls.characterSelect.button.exit);
  438. timeoutDelay(realmDownRetry*60*1000, location);
  439. }
  440. break;
  441.  
  442. case 13: // Realm Down - Character Select screen
  443. controlData.click(controlData.controls.characterSelect.button.exit);
  444. timeoutDelay(realmDownRetry*60*1000, location);
  445. break;
  446.  
  447. case 14: // Character Select - Disconnected
  448. timeoutDelay(disconnectedRetry*60*1000, location);
  449. controlData.click(controlData.controls.characterSelect.disconnected.button.ok);
  450. break;
  451.  
  452. case 15: // New Character
  453. break;
  454.  
  455. case 16: // Character Select - Please Wait popup
  456. if (!locationTimeout(pleaseWaitTimeout, location))
  457. controlData.click(controlData.controls.characterSelect.pleaseWait.button.cancel);
  458. break;
  459.  
  460. case 17: // Lobby - Lost Connection - just click okay, since we're toast anyway
  461. controlData.click(controlData.controls.lobby.lostConnection.button.ok);
  462. break;
  463.  
  464. case 18: // D2 Splash
  465. controlData.click(controlData.controls.d2Splash.textBox.copyright);
  466. break;
  467.  
  468. case 19: // Login - Cdkey In Use
  469. timeoutDelay(cdkeyInUseRetry*60*1000, location);
  470. controlData.click(controlData.controls.login.cdkeyInUse.button.ok);
  471. break;
  472.  
  473. case 20: // Single Player - Select Difficulty
  474. controlData.click(controlData.singlePlayerDifficulties[me.diff]);
  475. break;
  476.  
  477. case 21: // Main Menu - Connecting
  478. if (!locationTimeout(connectingToBnetTimeout, location))
  479. controlData.click(controlData.controls.mainMenu.connecting.button.cancel);
  480. break;
  481.  
  482. case 22: // Login - Invalid Cdkey (classic or xpac)
  483. sendEventToOOG(D2NT_MGR_RESTART, location.name, 3600);
  484. Delay(3500);
  485. break;
  486.  
  487. case 23: // Character Select - Connecting
  488. if (!locationTimeout(characterScreenTimeout, location))
  489. controlData.click(controlData.controls.characterSelect.button.exit);
  490. break;
  491.  
  492. case 24: // Server Down - not much to do but wait..
  493. break;
  494.  
  495. case 25: // Lobby - Please Wait
  496. if (!locationTimeout(pleaseWaitTimeout, location))
  497. controlData.click(controlData.controls.lobby.pleaseWait.button.cancel);
  498. break;
  499.  
  500. case 26: // Lobby - Game Name Exists
  501. sendEventToOOG(D2NT_MGR_PRINT_LOG, COLOR_1 + "Game already exists.", 0);
  502.  
  503. inGameAt = 0;
  504. setNextGameMake();
  505.  
  506. locationTimeout(15000, location);
  507. break;
  508.  
  509. case 27: // Gateway Select
  510. controlData.clickRealmEntry(me.gatewayid);
  511. controlData.click(controlData.controls.gateway.button.ok);
  512. break;
  513.  
  514. case 28: // Lobby - Game Does Not Exist
  515. // This is handled in the lobby chat
  516.  
  517. // Get rid of the message (DNE/Full game) so the lobby chat can handle
  518. controlData.click(controlData.controls.lobby.button.create);
  519. Delay(200);
  520. controlData.click(controlData.controls.lobby.create.button.cancel);
  521. break;
  522.  
  523. default:
  524. if (lastGameStatus == 1) {
  525. sendEventToOOG(D2NT_MGR_PRINT_STATUS, "Please Wait...", 0);
  526. for (ftjcount=0; ftjcount<=10 && !me.ingame; ftjcount++) {
  527. Delay(1000)
  528. }
  529. }
  530. break;
  531. }
  532. }
  533.  
  534. function sendEventToOOG(locationId, statusString, pendingTime)
  535. {
  536. return SendCopyData("D2NT Manager", null, (locationId<<16)|pendingTime, statusString);
  537. }
  538.  
  539. function setNextGameMake()
  540. {
  541. lastGameMade = GetTickCount();
  542. nextGameMake = lastGameMade + createGameThreshold + Random(0-createGameThresholdRandom, createGameThresholdRandom) + inGameAt;
  543. inGameAt = 0;
  544. chatActionsDone = false;
  545. }
  546.  
  547. function outputGameLength()
  548. {
  549. if (inGameAt)
  550. {
  551. duration = GetTickCount() - inGameAt;
  552.  
  553. inGameAt = (duration < gameMinLength ? gameMinLength - duration : 0);
  554. }
  555. }
  556.  
  557. function locationTimeout(time, location)
  558. {
  559. endtime = GetTickCount() + time;
  560.  
  561. while (controlData.getLocation().id == location.id && endtime > GetTickCount())
  562. {
  563. sendEventToOOG(D2NT_MGR_PRINT_STATUS, location.name + " (" + parseInt((endtime-GetTickCount())/1000) + "s)", 0);
  564. Delay(500);
  565. }
  566.  
  567. return (controlData.getLocation().id != location.id);
  568. }
  569.  
  570. function timeoutDelay(time, location)
  571. {
  572. endtime = GetTickCount() + time;
  573.  
  574. while (endtime > GetTickCount())
  575. {
  576. sendEventToOOG(D2NT_MGR_PRINT_STATUS, location.name + " (" + parseInt((endtime-GetTickCount())/1000) + "s)", 0);
  577. Delay(1000);
  578. if(me.ingame){ Delay(1000); return;}
  579. }
  580. }
  581.  
  582. function getRandomString(_length)
  583. {
  584. _retString = "";
  585. _charSet = "0123456789abcdefghijklmnopqrstuvwxyz";
  586.  
  587. while (_length--)
  588. {
  589. _retString += _charSet.charAt(Random(0, _charSet.length-1));
  590. Delay(1);
  591. }
  592.  
  593. return _retString;
  594. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement