Lottery.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. /*
  2. * This program is free software: you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation, either version 3 of the License, or (at your option) any later
  5. * version.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT
  8. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. * details.
  11. *
  12. * You should have received a copy of the GNU General Public License along with
  13. * this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. package com.l2jserver.gameserver.instancemanager.games;
  16. import java.sql.Connection;
  17. import java.sql.PreparedStatement;
  18. import java.sql.ResultSet;
  19. import java.sql.SQLException;
  20. import java.sql.Statement;
  21. import java.util.Calendar;
  22. import java.util.logging.Level;
  23. import java.util.logging.Logger;
  24. import com.l2jserver.Config;
  25. import com.l2jserver.L2DatabaseFactory;
  26. import com.l2jserver.gameserver.Announcements;
  27. import com.l2jserver.gameserver.ThreadPoolManager;
  28. import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
  29. import com.l2jserver.gameserver.network.SystemMessageId;
  30. import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
  31. import com.l2jserver.util.Rnd;
  32. public class Lottery
  33. {
  34. public static final long SECOND = 1000;
  35. public static final long MINUTE = 60000;
  36. protected static final Logger _log = Logger.getLogger(Lottery.class.getName());
  37. private static final String INSERT_LOTTERY = "INSERT INTO games(id, idnr, enddate, prize, newprize) VALUES (?, ?, ?, ?, ?)";
  38. private static final String UPDATE_PRICE = "UPDATE games SET prize=?, newprize=? WHERE id = 1 AND idnr = ?";
  39. private static final String UPDATE_LOTTERY = "UPDATE games SET finished=1, prize=?, newprize=?, number1=?, number2=?, prize1=?, prize2=?, prize3=? WHERE id=1 AND idnr=?";
  40. private static final String SELECT_LAST_LOTTERY = "SELECT idnr, prize, newprize, enddate, finished FROM games WHERE id = 1 ORDER BY idnr DESC LIMIT 1";
  41. private static final String SELECT_LOTTERY_ITEM = "SELECT enchant_level, custom_type2 FROM items WHERE item_id = 4442 AND custom_type1 = ?";
  42. private static final String SELECT_LOTTERY_TICKET = "SELECT number1, number2, prize1, prize2, prize3 FROM games WHERE id = 1 and idnr = ?";
  43. protected int _number;
  44. protected long _prize;
  45. protected boolean _isSellingTickets;
  46. protected boolean _isStarted;
  47. protected long _enddate;
  48. protected Lottery()
  49. {
  50. _number = 1;
  51. _prize = Config.ALT_LOTTERY_PRIZE;
  52. _isSellingTickets = false;
  53. _isStarted = false;
  54. _enddate = System.currentTimeMillis();
  55. if (Config.ALLOW_LOTTERY)
  56. (new startLottery()).run();
  57. }
  58. public static Lottery getInstance()
  59. {
  60. return SingletonHolder._instance;
  61. }
  62. public int getId()
  63. {
  64. return _number;
  65. }
  66. public long getPrize()
  67. {
  68. return _prize;
  69. }
  70. public long getEndDate()
  71. {
  72. return _enddate;
  73. }
  74. public void increasePrize(long count)
  75. {
  76. _prize += count;
  77. try (Connection con = L2DatabaseFactory.getInstance().getConnection())
  78. {
  79. PreparedStatement statement = con.prepareStatement(UPDATE_PRICE);
  80. statement.setLong(1, getPrize());
  81. statement.setLong(2, getPrize());
  82. statement.setInt(3, getId());
  83. statement.execute();
  84. statement.close();
  85. }
  86. catch (SQLException e)
  87. {
  88. _log.log(Level.WARNING, "Lottery: Could not increase current lottery prize: " + e.getMessage(), e);
  89. }
  90. }
  91. public boolean isSellableTickets()
  92. {
  93. return _isSellingTickets;
  94. }
  95. public boolean isStarted()
  96. {
  97. return _isStarted;
  98. }
  99. private class startLottery implements Runnable
  100. {
  101. protected startLottery()
  102. {
  103. // Do nothing
  104. }
  105. @Override
  106. public void run()
  107. {
  108. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  109. Statement statement = con.createStatement();
  110. ResultSet rset = statement.executeQuery(SELECT_LAST_LOTTERY))
  111. {
  112. if (rset.next())
  113. {
  114. _number = rset.getInt("idnr");
  115. if (rset.getInt("finished") == 1)
  116. {
  117. _number++;
  118. _prize = rset.getLong("newprize");
  119. }
  120. else
  121. {
  122. _prize = rset.getLong("prize");
  123. _enddate = rset.getLong("enddate");
  124. if (_enddate <= System.currentTimeMillis() + 2 * MINUTE)
  125. {
  126. (new finishLottery()).run();
  127. return;
  128. }
  129. if (_enddate > System.currentTimeMillis())
  130. {
  131. _isStarted = true;
  132. ThreadPoolManager.getInstance().scheduleGeneral(new finishLottery(), _enddate - System.currentTimeMillis());
  133. if (_enddate > System.currentTimeMillis() + 12 * MINUTE)
  134. {
  135. _isSellingTickets = true;
  136. ThreadPoolManager.getInstance().scheduleGeneral(new stopSellingTickets(), _enddate - System.currentTimeMillis() - 10 * MINUTE);
  137. }
  138. return;
  139. }
  140. }
  141. }
  142. }
  143. catch (SQLException e)
  144. {
  145. _log.log(Level.WARNING, "Lottery: Could not restore lottery data: " + e.getMessage(), e);
  146. }
  147. if (Config.DEBUG)
  148. _log.info("Lottery: Starting ticket sell for lottery #" + getId() + ".");
  149. _isSellingTickets = true;
  150. _isStarted = true;
  151. Announcements.getInstance().announceToAll("Lottery tickets are now available for Lucky Lottery #" + getId() + ".");
  152. Calendar finishtime = Calendar.getInstance();
  153. finishtime.setTimeInMillis(_enddate);
  154. finishtime.set(Calendar.MINUTE, 0);
  155. finishtime.set(Calendar.SECOND, 0);
  156. if (finishtime.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)
  157. {
  158. finishtime.set(Calendar.HOUR_OF_DAY, 19);
  159. _enddate = finishtime.getTimeInMillis();
  160. _enddate += 604800000;
  161. }
  162. else
  163. {
  164. finishtime.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
  165. finishtime.set(Calendar.HOUR_OF_DAY, 19);
  166. _enddate = finishtime.getTimeInMillis();
  167. }
  168. ThreadPoolManager.getInstance().scheduleGeneral(new stopSellingTickets(), _enddate - System.currentTimeMillis() - 10 * MINUTE);
  169. ThreadPoolManager.getInstance().scheduleGeneral(new finishLottery(), _enddate - System.currentTimeMillis());
  170. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  171. PreparedStatement statement = con.prepareStatement(INSERT_LOTTERY))
  172. {
  173. statement.setInt(1, 1);
  174. statement.setInt(2, getId());
  175. statement.setLong(3, getEndDate());
  176. statement.setLong(4, getPrize());
  177. statement.setLong(5, getPrize());
  178. statement.execute();
  179. }
  180. catch (SQLException e)
  181. {
  182. _log.log(Level.WARNING, "Lottery: Could not store new lottery data: " + e.getMessage(), e);
  183. }
  184. }
  185. }
  186. private class stopSellingTickets implements Runnable
  187. {
  188. protected stopSellingTickets()
  189. {
  190. // Do nothing
  191. }
  192. @Override
  193. public void run()
  194. {
  195. if (Config.DEBUG)
  196. _log.info("Lottery: Stopping ticket sell for lottery #" + getId() + ".");
  197. _isSellingTickets = false;
  198. Announcements.getInstance().announceToAll(SystemMessage.getSystemMessage(SystemMessageId.LOTTERY_TICKET_SALES_TEMP_SUSPENDED));
  199. }
  200. }
  201. private class finishLottery implements Runnable
  202. {
  203. protected finishLottery()
  204. {
  205. // Do nothing
  206. }
  207. @Override
  208. public void run()
  209. {
  210. if (Config.DEBUG)
  211. _log.info("Lottery: Ending lottery #" + getId() + ".");
  212. int[] luckynums = new int[5];
  213. int luckynum = 0;
  214. for (int i = 0; i < 5; i++)
  215. {
  216. boolean found = true;
  217. while (found)
  218. {
  219. luckynum = Rnd.get(20) + 1;
  220. found = false;
  221. for (int j = 0; j < i; j++)
  222. if (luckynums[j] == luckynum)
  223. found = true;
  224. }
  225. luckynums[i] = luckynum;
  226. }
  227. if (Config.DEBUG)
  228. _log.info("Lottery: The lucky numbers are " + luckynums[0] + ", " + luckynums[1] + ", " + luckynums[2] + ", " + luckynums[3] + ", " + luckynums[4] + ".");
  229. int enchant = 0;
  230. int type2 = 0;
  231. for (int i = 0; i < 5; i++)
  232. {
  233. if (luckynums[i] < 17)
  234. enchant += Math.pow(2, luckynums[i] - 1);
  235. else
  236. type2 += Math.pow(2, luckynums[i] - 17);
  237. }
  238. if (Config.DEBUG)
  239. _log.info("Lottery: Encoded lucky numbers are " + enchant + ", " + type2);
  240. int count1 = 0;
  241. int count2 = 0;
  242. int count3 = 0;
  243. int count4 = 0;
  244. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  245. PreparedStatement statement = con.prepareStatement(SELECT_LOTTERY_ITEM))
  246. {
  247. statement.setInt(1, getId());
  248. try (ResultSet rset = statement.executeQuery())
  249. {
  250. while (rset.next())
  251. {
  252. int curenchant = rset.getInt("enchant_level") & enchant;
  253. int curtype2 = rset.getInt("custom_type2") & type2;
  254. if (curenchant == 0 && curtype2 == 0)
  255. continue;
  256. int count = 0;
  257. for (int i = 1; i <= 16; i++)
  258. {
  259. int val = curenchant / 2;
  260. if (val != Math.round((double) curenchant / 2))
  261. count++;
  262. int val2 = curtype2 / 2;
  263. if (val2 != (double) curtype2 / 2)
  264. count++;
  265. curenchant = val;
  266. curtype2 = val2;
  267. }
  268. if (count == 5)
  269. count1++;
  270. else if (count == 4)
  271. count2++;
  272. else if (count == 3)
  273. count3++;
  274. else if (count > 0)
  275. count4++;
  276. }
  277. }
  278. }
  279. catch (SQLException e)
  280. {
  281. _log.log(Level.WARNING, "Lottery: Could restore lottery data: " + e.getMessage(), e);
  282. }
  283. long prize4 = count4 * Config.ALT_LOTTERY_2_AND_1_NUMBER_PRIZE;
  284. long prize1 = 0;
  285. long prize2 = 0;
  286. long prize3 = 0;
  287. if (count1 > 0)
  288. prize1 = (long) ((getPrize() - prize4) * Config.ALT_LOTTERY_5_NUMBER_RATE / count1);
  289. if (count2 > 0)
  290. prize2 = (long) ((getPrize() - prize4) * Config.ALT_LOTTERY_4_NUMBER_RATE / count2);
  291. if (count3 > 0)
  292. prize3 = (long) ((getPrize() - prize4) * Config.ALT_LOTTERY_3_NUMBER_RATE / count3);
  293. if (Config.DEBUG)
  294. {
  295. _log.info("Lottery: " + count1 + " players with all FIVE numbers each win " + prize1 + ".");
  296. _log.info("Lottery: " + count2 + " players with FOUR numbers each win " + prize2 + ".");
  297. _log.info("Lottery: " + count3 + " players with THREE numbers each win " + prize3 + ".");
  298. _log.info("Lottery: " + count4 + " players with ONE or TWO numbers each win " + prize4 + ".");
  299. }
  300. long newprize = getPrize() - (prize1 + prize2 + prize3 + prize4);
  301. if (Config.DEBUG)
  302. _log.info("Lottery: Jackpot for next lottery is " + newprize + ".");
  303. SystemMessage sm;
  304. if (count1 > 0)
  305. {
  306. // There are winners.
  307. sm = SystemMessage.getSystemMessage(SystemMessageId.AMOUNT_FOR_WINNER_S1_IS_S2_ADENA_WE_HAVE_S3_PRIZE_WINNER);
  308. sm.addNumber(getId());
  309. sm.addItemNumber(getPrize());
  310. sm.addItemNumber(count1);
  311. Announcements.getInstance().announceToAll(sm);
  312. }
  313. else
  314. {
  315. // There are no winners.
  316. sm = SystemMessage.getSystemMessage(SystemMessageId.AMOUNT_FOR_LOTTERY_S1_IS_S2_ADENA_NO_WINNER);
  317. sm.addNumber(getId());
  318. sm.addItemNumber(getPrize());
  319. Announcements.getInstance().announceToAll(sm);
  320. }
  321. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  322. PreparedStatement statement = con.prepareStatement(UPDATE_LOTTERY))
  323. {
  324. statement.setLong(1, getPrize());
  325. statement.setLong(2, newprize);
  326. statement.setInt(3, enchant);
  327. statement.setInt(4, type2);
  328. statement.setLong(5, prize1);
  329. statement.setLong(6, prize2);
  330. statement.setLong(7, prize3);
  331. statement.setInt(8, getId());
  332. statement.execute();
  333. }
  334. catch (SQLException e)
  335. {
  336. _log.log(Level.WARNING, "Lottery: Could not store finished lottery data: " + e.getMessage(), e);
  337. }
  338. ThreadPoolManager.getInstance().scheduleGeneral(new startLottery(), MINUTE);
  339. _number++;
  340. _isStarted = false;
  341. }
  342. }
  343. public int[] decodeNumbers(int enchant, int type2)
  344. {
  345. int res[] = new int[5];
  346. int id = 0;
  347. int nr = 1;
  348. while (enchant > 0)
  349. {
  350. int val = enchant / 2;
  351. if (val != Math.round((double) enchant / 2))
  352. {
  353. res[id++] = nr;
  354. }
  355. enchant /= 2;
  356. nr++;
  357. }
  358. nr = 17;
  359. while (type2 > 0)
  360. {
  361. int val = type2 / 2;
  362. if (val != (double) type2 / 2)
  363. {
  364. res[id++] = nr;
  365. }
  366. type2 /= 2;
  367. nr++;
  368. }
  369. return res;
  370. }
  371. public long[] checkTicket(L2ItemInstance item)
  372. {
  373. return checkTicket(item.getCustomType1(), item.getEnchantLevel(), item.getCustomType2());
  374. }
  375. public long[] checkTicket(int id, int enchant, int type2)
  376. {
  377. long res[] = { 0, 0 };
  378. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  379. PreparedStatement statement = con.prepareStatement(SELECT_LOTTERY_TICKET))
  380. {
  381. statement.setInt(1, id);
  382. try (ResultSet rset = statement.executeQuery())
  383. {
  384. if (rset.next())
  385. {
  386. int curenchant = rset.getInt("number1") & enchant;
  387. int curtype2 = rset.getInt("number2") & type2;
  388. if (curenchant == 0 && curtype2 == 0)
  389. {
  390. return res;
  391. }
  392. int count = 0;
  393. for (int i = 1; i <= 16; i++)
  394. {
  395. int val = curenchant / 2;
  396. if (val != Math.round((double) curenchant / 2))
  397. count++;
  398. int val2 = curtype2 / 2;
  399. if (val2 != (double) curtype2 / 2)
  400. count++;
  401. curenchant = val;
  402. curtype2 = val2;
  403. }
  404. switch (count)
  405. {
  406. case 0:
  407. break;
  408. case 5:
  409. res[0] = 1;
  410. res[1] = rset.getLong("prize1");
  411. break;
  412. case 4:
  413. res[0] = 2;
  414. res[1] = rset.getLong("prize2");
  415. break;
  416. case 3:
  417. res[0] = 3;
  418. res[1] = rset.getLong("prize3");
  419. break;
  420. default:
  421. res[0] = 4;
  422. res[1] = 200;
  423. }
  424. if (Config.DEBUG)
  425. _log.warning("count: " + count + ", id: " + id + ", enchant: " + enchant + ", type2: " + type2);
  426. }
  427. }
  428. }
  429. catch (SQLException e)
  430. {
  431. _log.log(Level.WARNING, "Lottery: Could not check lottery ticket #" + id + ": " + e.getMessage(), e);
  432. }
  433. return res;
  434. }
  435. private static class SingletonHolder
  436. {
  437. protected static final Lottery _instance = new Lottery();
  438. }
  439. }