Lottery.java 19 KB

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