SevenSigns.java 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410
  1. /*
  2. * Copyright © 2004-2019 L2J Server
  3. *
  4. * This file is part of L2J Server.
  5. *
  6. * L2J Server is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * L2J Server is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.l2jserver.gameserver;
  20. import static com.l2jserver.gameserver.network.SystemMessageId.COMPETITION_PERIOD_BEGUN;
  21. import static com.l2jserver.gameserver.network.SystemMessageId.DAWN_OBTAINED_AVARICE;
  22. import static com.l2jserver.gameserver.network.SystemMessageId.DAWN_OBTAINED_GNOSIS;
  23. import static com.l2jserver.gameserver.network.SystemMessageId.DAWN_OBTAINED_STRIFE;
  24. import static com.l2jserver.gameserver.network.SystemMessageId.DAWN_WON;
  25. import static com.l2jserver.gameserver.network.SystemMessageId.DUSK_OBTAINED_AVARICE;
  26. import static com.l2jserver.gameserver.network.SystemMessageId.DUSK_OBTAINED_GNOSIS;
  27. import static com.l2jserver.gameserver.network.SystemMessageId.DUSK_OBTAINED_STRIFE;
  28. import static com.l2jserver.gameserver.network.SystemMessageId.DUSK_WON;
  29. import static com.l2jserver.gameserver.network.SystemMessageId.PREPARATIONS_PERIOD_BEGUN;
  30. import static com.l2jserver.gameserver.network.SystemMessageId.QUEST_EVENT_PERIOD_BEGUN;
  31. import static com.l2jserver.gameserver.network.SystemMessageId.QUEST_EVENT_PERIOD_ENDED;
  32. import static com.l2jserver.gameserver.network.SystemMessageId.RESULTS_PERIOD_BEGUN;
  33. import static com.l2jserver.gameserver.network.SystemMessageId.SEAL_OF_STRIFE_FORBIDS_SUMMONING;
  34. import static com.l2jserver.gameserver.network.SystemMessageId.SEAL_VALIDATION_PERIOD_BEGUN;
  35. import static com.l2jserver.gameserver.network.SystemMessageId.SEAL_VALIDATION_PERIOD_ENDED;
  36. import static com.l2jserver.gameserver.network.SystemMessageId.VALIDATION_PERIOD_BEGUN;
  37. import java.sql.SQLException;
  38. import java.util.Calendar;
  39. import java.util.LinkedHashMap;
  40. import java.util.List;
  41. import java.util.Map;
  42. import java.util.Map.Entry;
  43. import org.slf4j.Logger;
  44. import org.slf4j.LoggerFactory;
  45. import com.l2jserver.commons.database.ConnectionFactory;
  46. import com.l2jserver.gameserver.config.Config;
  47. import com.l2jserver.gameserver.instancemanager.CastleManager;
  48. import com.l2jserver.gameserver.model.AutoSpawnHandler;
  49. import com.l2jserver.gameserver.model.AutoSpawnHandler.AutoSpawnInstance;
  50. import com.l2jserver.gameserver.model.L2World;
  51. import com.l2jserver.gameserver.model.StatsSet;
  52. import com.l2jserver.gameserver.model.TeleportWhereType;
  53. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  54. import com.l2jserver.gameserver.model.entity.Castle;
  55. import com.l2jserver.gameserver.model.skills.CommonSkill;
  56. import com.l2jserver.gameserver.network.SystemMessageId;
  57. import com.l2jserver.gameserver.network.serverpackets.SSQInfo;
  58. import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
  59. import com.l2jserver.gameserver.util.Broadcast;
  60. /**
  61. * Seven Signs engine.
  62. * @author Tempy
  63. */
  64. public class SevenSigns {
  65. private static final Logger LOG = LoggerFactory.getLogger(SevenSigns.class);
  66. public static final String SEVEN_SIGNS_HTML_PATH = "data/html/seven_signs/";
  67. public static final int CABAL_NULL = 0;
  68. public static final int CABAL_DUSK = 1;
  69. public static final int CABAL_DAWN = 2;
  70. public static final int SEAL_NULL = 0;
  71. public static final int SEAL_AVARICE = 1;
  72. public static final int SEAL_GNOSIS = 2;
  73. public static final int SEAL_STRIFE = 3;
  74. public static final int PERIOD_COMP_RECRUITING = 0;
  75. public static final int PERIOD_COMPETITION = 1;
  76. public static final int PERIOD_COMP_RESULTS = 2;
  77. public static final int PERIOD_SEAL_VALIDATION = 3;
  78. public static final int PERIOD_START_HOUR = 18;
  79. public static final int PERIOD_START_MINS = 00;
  80. public static final int PERIOD_START_DAY = Calendar.MONDAY;
  81. // The quest event and seal validation periods last for approximately one week
  82. // with a 15 minutes "interval" period sandwiched between them.
  83. public static final int PERIOD_MINOR_LENGTH = 900000;
  84. public static final int PERIOD_MAJOR_LENGTH = 604800000 - PERIOD_MINOR_LENGTH;
  85. public static final int RECORD_SEVEN_SIGNS_ID = 5707;
  86. public static final int RECORD_SEVEN_SIGNS_COST = 500;
  87. // NPC Related Constants \\
  88. public static final int ORATOR_NPC_ID = 31094;
  89. public static final int PREACHER_NPC_ID = 31093;
  90. public static final int MAMMON_MERCHANT_ID = 31113;
  91. public static final int MAMMON_BLACKSMITH_ID = 31126;
  92. public static final int MAMMON_MARKETEER_ID = 31092;
  93. public static final int LILITH_NPC_ID = 25283;
  94. public static final int ANAKIM_NPC_ID = 25286;
  95. public static final int CREST_OF_DAWN_ID = 31170;
  96. public static final int CREST_OF_DUSK_ID = 31171;
  97. // Seal Stone Related Constants
  98. public static final int SEAL_STONE_BLUE_ID = 6360;
  99. public static final int SEAL_STONE_GREEN_ID = 6361;
  100. public static final int SEAL_STONE_RED_ID = 6362;
  101. public static final int[] SEAL_STONE_IDS = {
  102. SEAL_STONE_BLUE_ID,
  103. SEAL_STONE_GREEN_ID,
  104. SEAL_STONE_RED_ID
  105. };
  106. public static final int SEAL_STONE_BLUE_VALUE = 3;
  107. public static final int SEAL_STONE_GREEN_VALUE = 5;
  108. public static final int SEAL_STONE_RED_VALUE = 10;
  109. public static final int BLUE_CONTRIB_POINTS = 3;
  110. public static final int GREEN_CONTRIB_POINTS = 5;
  111. public static final int RED_CONTRIB_POINTS = 10;
  112. private final Calendar _nextPeriodChange = Calendar.getInstance();
  113. protected int _activePeriod;
  114. protected int _currentCycle;
  115. protected double _dawnStoneScore;
  116. protected double _duskStoneScore;
  117. protected int _dawnFestivalScore;
  118. protected int _duskFestivalScore;
  119. protected int _compWinner;
  120. protected int _previousWinner;
  121. protected Calendar _lastSave = Calendar.getInstance();
  122. protected Map<Integer, StatsSet> _signsPlayerData = new LinkedHashMap<>();
  123. private final Map<Integer, Integer> _signsSealOwners = new LinkedHashMap<>();
  124. private final Map<Integer, Integer> _signsDuskSealTotals = new LinkedHashMap<>();
  125. private final Map<Integer, Integer> _signsDawnSealTotals = new LinkedHashMap<>();
  126. private static final String LOAD_DATA = "SELECT charId, cabal, seal, red_stones, green_stones, blue_stones, " + "ancient_adena_amount, contribution_score FROM seven_signs";
  127. private static final String LOAD_STATUS = "SELECT * FROM seven_signs_status WHERE id=0";
  128. private static final String INSERT_PLAYER = "INSERT INTO seven_signs (charId, cabal, seal) VALUES (?,?,?)";
  129. private static final String UPDATE_PLAYER = "UPDATE seven_signs SET cabal=?, seal=?, red_stones=?, green_stones=?, blue_stones=?, " + "ancient_adena_amount=?, contribution_score=? WHERE charId=?";
  130. private static final String UPDATE_STATUS = "UPDATE seven_signs_status SET current_cycle=?, active_period=?, previous_winner=?, " + "dawn_stone_score=?, dawn_festival_score=?, dusk_stone_score=?, dusk_festival_score=?, "
  131. + "avarice_owner=?, gnosis_owner=?, strife_owner=?, avarice_dawn_score=?, gnosis_dawn_score=?, " + "strife_dawn_score=?, avarice_dusk_score=?, gnosis_dusk_score=?, strife_dusk_score=?, " + "festival_cycle=?, accumulated_bonus0=?, accumulated_bonus1=?, accumulated_bonus2=?,"
  132. + "accumulated_bonus3=?, accumulated_bonus4=?, date=? WHERE id=0";
  133. protected SevenSigns() {
  134. try {
  135. restoreSevenSignsData();
  136. } catch (Exception ex) {
  137. LOG.error("Failed to load configuration!", ex);
  138. }
  139. LOG.info("Currently in the {} period.", getCurrentPeriodName());
  140. initializeSeals();
  141. if (isSealValidationPeriod()) {
  142. if (getCabalHighestScore() == CABAL_NULL) {
  143. LOG.info("The competition ended with a tie last week.");
  144. } else {
  145. LOG.info("The {} were victorious last week.", getCabalName(getCabalHighestScore()));
  146. }
  147. } else if (getCabalHighestScore() == CABAL_NULL) {
  148. LOG.info("The competition, if the current trend continues, will end in a tie this week.");
  149. } else {
  150. LOG.info("The {} are in the lead this week.", getCabalName(getCabalHighestScore()));
  151. }
  152. long milliToChange = 0;
  153. if (isNextPeriodChangeInPast()) {
  154. LOG.info("Next period change was in the past (server was offline), changing periods now!");
  155. } else {
  156. setCalendarForNextPeriodChange();
  157. milliToChange = getMilliToPeriodChange();
  158. }
  159. // Schedule a time for the next period change.
  160. SevenSignsPeriodChange sspc = new SevenSignsPeriodChange();
  161. ThreadPoolManager.getInstance().scheduleGeneral(sspc, milliToChange);
  162. // Thanks to http://rainbow.arch.scriptmania.com/scripts/timezone_countdown.html for help with this.
  163. double numSecs = (milliToChange / 1000) % 60;
  164. double countDown = ((milliToChange / 1000.0) - numSecs) / 60;
  165. int numMins = (int) Math.floor(countDown % 60);
  166. countDown = (countDown - numMins) / 60;
  167. int numHours = (int) Math.floor(countDown % 24);
  168. int numDays = (int) Math.floor((countDown - numHours) / 24);
  169. LOG.info("Next period begins in {} days, {} hours and {} mins.", numDays, numHours, numMins);
  170. }
  171. private boolean isNextPeriodChangeInPast() {
  172. Calendar lastPeriodChange = Calendar.getInstance();
  173. switch (getCurrentPeriod()) {
  174. case PERIOD_SEAL_VALIDATION:
  175. case PERIOD_COMPETITION:
  176. lastPeriodChange.set(Calendar.DAY_OF_WEEK, PERIOD_START_DAY);
  177. lastPeriodChange.set(Calendar.HOUR_OF_DAY, PERIOD_START_HOUR);
  178. lastPeriodChange.set(Calendar.MINUTE, PERIOD_START_MINS);
  179. lastPeriodChange.set(Calendar.SECOND, 0);
  180. // if we hit next week, just turn back 1 week
  181. if (Calendar.getInstance().before(lastPeriodChange)) {
  182. lastPeriodChange.add(Calendar.HOUR, -24 * 7);
  183. }
  184. break;
  185. case PERIOD_COMP_RECRUITING:
  186. case PERIOD_COMP_RESULTS:
  187. // because of the short duration of this period, just check it from last save
  188. lastPeriodChange.setTimeInMillis(_lastSave.getTimeInMillis() + PERIOD_MINOR_LENGTH);
  189. break;
  190. }
  191. // because of previous "date" column usage, check only if it already contains usable data for us
  192. if ((_lastSave.getTimeInMillis() > 7) && _lastSave.before(lastPeriodChange)) {
  193. return true;
  194. }
  195. return false;
  196. }
  197. /**
  198. * Registers all random spawns and auto-chats for Seven Signs NPCs, along with spawns for the Preachers of Doom and Orators of Revelations at the beginning of the Seal Validation period.
  199. */
  200. public void spawnSevenSignsNPC() {
  201. final AutoSpawnInstance merchantSpawn = AutoSpawnHandler.getInstance().getAutoSpawnInstance(MAMMON_MERCHANT_ID, false);
  202. final AutoSpawnInstance blacksmithSpawn = AutoSpawnHandler.getInstance().getAutoSpawnInstance(MAMMON_BLACKSMITH_ID, false);
  203. final AutoSpawnInstance lilithSpawn = AutoSpawnHandler.getInstance().getAutoSpawnInstance(LILITH_NPC_ID, false);
  204. final AutoSpawnInstance anakimSpawn = AutoSpawnHandler.getInstance().getAutoSpawnInstance(ANAKIM_NPC_ID, false);
  205. final List<AutoSpawnInstance> crestOfDawnSpawns = AutoSpawnHandler.getInstance().getAutoSpawnInstances(CREST_OF_DAWN_ID);
  206. final List<AutoSpawnInstance> crestOfDuskSpawns = AutoSpawnHandler.getInstance().getAutoSpawnInstances(CREST_OF_DUSK_ID);
  207. final List<AutoSpawnInstance> oratorSpawns = AutoSpawnHandler.getInstance().getAutoSpawnInstances(ORATOR_NPC_ID);
  208. final List<AutoSpawnInstance> preacherSpawns = AutoSpawnHandler.getInstance().getAutoSpawnInstances(PREACHER_NPC_ID);
  209. final List<AutoSpawnInstance> marketeerSpawns = AutoSpawnHandler.getInstance().getAutoSpawnInstances(MAMMON_MARKETEER_ID);
  210. if (isSealValidationPeriod() || isCompResultsPeriod()) {
  211. for (AutoSpawnInstance spawnInst : marketeerSpawns) {
  212. AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, true);
  213. }
  214. if ((getSealOwner(SEAL_GNOSIS) == getCabalHighestScore()) && (getSealOwner(SEAL_GNOSIS) != CABAL_NULL)) {
  215. if (!Config.ANNOUNCE_MAMMON_SPAWN) {
  216. blacksmithSpawn.setBroadcast(false);
  217. }
  218. if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(blacksmithSpawn.getObjectId(), true).isSpawnActive()) {
  219. AutoSpawnHandler.getInstance().setSpawnActive(blacksmithSpawn, true);
  220. }
  221. for (AutoSpawnInstance spawnInst : oratorSpawns) {
  222. if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(spawnInst.getObjectId(), true).isSpawnActive()) {
  223. AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, true);
  224. }
  225. }
  226. for (AutoSpawnInstance spawnInst : preacherSpawns) {
  227. if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(spawnInst.getObjectId(), true).isSpawnActive()) {
  228. AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, true);
  229. }
  230. }
  231. } else {
  232. AutoSpawnHandler.getInstance().setSpawnActive(blacksmithSpawn, false);
  233. for (AutoSpawnInstance spawnInst : oratorSpawns) {
  234. AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, false);
  235. }
  236. for (AutoSpawnInstance spawnInst : preacherSpawns) {
  237. AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, false);
  238. }
  239. }
  240. if ((getSealOwner(SEAL_AVARICE) == getCabalHighestScore()) && (getSealOwner(SEAL_AVARICE) != CABAL_NULL)) {
  241. if (!Config.ANNOUNCE_MAMMON_SPAWN) {
  242. merchantSpawn.setBroadcast(false);
  243. }
  244. if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(merchantSpawn.getObjectId(), true).isSpawnActive()) {
  245. AutoSpawnHandler.getInstance().setSpawnActive(merchantSpawn, true);
  246. }
  247. switch (getCabalHighestScore()) {
  248. case CABAL_DAWN:
  249. if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(lilithSpawn.getObjectId(), true).isSpawnActive()) {
  250. AutoSpawnHandler.getInstance().setSpawnActive(lilithSpawn, true);
  251. }
  252. AutoSpawnHandler.getInstance().setSpawnActive(anakimSpawn, false);
  253. for (AutoSpawnInstance dawnCrest : crestOfDawnSpawns) {
  254. if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(dawnCrest.getObjectId(), true).isSpawnActive()) {
  255. AutoSpawnHandler.getInstance().setSpawnActive(dawnCrest, true);
  256. }
  257. }
  258. for (AutoSpawnInstance duskCrest : crestOfDuskSpawns) {
  259. AutoSpawnHandler.getInstance().setSpawnActive(duskCrest, false);
  260. }
  261. break;
  262. case CABAL_DUSK:
  263. if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(anakimSpawn.getObjectId(), true).isSpawnActive()) {
  264. AutoSpawnHandler.getInstance().setSpawnActive(anakimSpawn, true);
  265. }
  266. AutoSpawnHandler.getInstance().setSpawnActive(lilithSpawn, false);
  267. for (AutoSpawnInstance duskCrest : crestOfDuskSpawns) {
  268. if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(duskCrest.getObjectId(), true).isSpawnActive()) {
  269. AutoSpawnHandler.getInstance().setSpawnActive(duskCrest, true);
  270. }
  271. }
  272. for (AutoSpawnInstance dawnCrest : crestOfDawnSpawns) {
  273. AutoSpawnHandler.getInstance().setSpawnActive(dawnCrest, false);
  274. }
  275. break;
  276. }
  277. } else {
  278. AutoSpawnHandler.getInstance().setSpawnActive(merchantSpawn, false);
  279. AutoSpawnHandler.getInstance().setSpawnActive(lilithSpawn, false);
  280. AutoSpawnHandler.getInstance().setSpawnActive(anakimSpawn, false);
  281. for (AutoSpawnInstance dawnCrest : crestOfDawnSpawns) {
  282. AutoSpawnHandler.getInstance().setSpawnActive(dawnCrest, false);
  283. }
  284. for (AutoSpawnInstance duskCrest : crestOfDuskSpawns) {
  285. AutoSpawnHandler.getInstance().setSpawnActive(duskCrest, false);
  286. }
  287. }
  288. } else {
  289. AutoSpawnHandler.getInstance().setSpawnActive(merchantSpawn, false);
  290. AutoSpawnHandler.getInstance().setSpawnActive(blacksmithSpawn, false);
  291. AutoSpawnHandler.getInstance().setSpawnActive(lilithSpawn, false);
  292. AutoSpawnHandler.getInstance().setSpawnActive(anakimSpawn, false);
  293. for (AutoSpawnInstance dawnCrest : crestOfDawnSpawns) {
  294. AutoSpawnHandler.getInstance().setSpawnActive(dawnCrest, false);
  295. }
  296. for (AutoSpawnInstance duskCrest : crestOfDuskSpawns) {
  297. AutoSpawnHandler.getInstance().setSpawnActive(duskCrest, false);
  298. }
  299. for (AutoSpawnInstance spawnInst : oratorSpawns) {
  300. AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, false);
  301. }
  302. for (AutoSpawnInstance spawnInst : preacherSpawns) {
  303. AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, false);
  304. }
  305. for (AutoSpawnInstance spawnInst : marketeerSpawns) {
  306. AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, false);
  307. }
  308. }
  309. }
  310. public static long calcContributionScore(long blueCount, long greenCount, long redCount) {
  311. long contrib = blueCount * BLUE_CONTRIB_POINTS;
  312. contrib += greenCount * GREEN_CONTRIB_POINTS;
  313. contrib += redCount * RED_CONTRIB_POINTS;
  314. return contrib;
  315. }
  316. public static long calcAncientAdenaReward(long blueCount, long greenCount, long redCount) {
  317. long reward = blueCount * SEAL_STONE_BLUE_VALUE;
  318. reward += greenCount * SEAL_STONE_GREEN_VALUE;
  319. reward += redCount * SEAL_STONE_RED_VALUE;
  320. return reward;
  321. }
  322. public static final String getCabalShortName(int cabal) {
  323. switch (cabal) {
  324. case CABAL_DAWN:
  325. return "dawn";
  326. case CABAL_DUSK:
  327. return "dusk";
  328. }
  329. return "No Cabal";
  330. }
  331. public static final String getCabalName(int cabal) {
  332. switch (cabal) {
  333. case CABAL_DAWN:
  334. return "Lords of Dawn";
  335. case CABAL_DUSK:
  336. return "Revolutionaries of Dusk";
  337. }
  338. return "No Cabal";
  339. }
  340. public static final String getSealName(int seal, boolean shortName) {
  341. String sealName = (!shortName) ? "Seal of " : "";
  342. switch (seal) {
  343. case SEAL_AVARICE:
  344. sealName += "Avarice";
  345. break;
  346. case SEAL_GNOSIS:
  347. sealName += "Gnosis";
  348. break;
  349. case SEAL_STRIFE:
  350. sealName += "Strife";
  351. break;
  352. }
  353. return sealName;
  354. }
  355. public final int getCurrentCycle() {
  356. return _currentCycle;
  357. }
  358. public final int getCurrentPeriod() {
  359. return _activePeriod;
  360. }
  361. private final int getDaysToPeriodChange() {
  362. int numDays = _nextPeriodChange.get(Calendar.DAY_OF_WEEK) - PERIOD_START_DAY;
  363. if (numDays < 0) {
  364. return 0 - numDays;
  365. }
  366. return 7 - numDays;
  367. }
  368. public final long getMilliToPeriodChange() {
  369. return (_nextPeriodChange.getTimeInMillis() - System.currentTimeMillis());
  370. }
  371. protected void setCalendarForNextPeriodChange() {
  372. // Calculate the number of days until the next period
  373. // A period starts at 18:00 pm (local time), like on official servers.
  374. switch (getCurrentPeriod()) {
  375. case PERIOD_SEAL_VALIDATION:
  376. case PERIOD_COMPETITION:
  377. int daysToChange = getDaysToPeriodChange();
  378. if (daysToChange == 7) {
  379. if (_nextPeriodChange.get(Calendar.HOUR_OF_DAY) < PERIOD_START_HOUR) {
  380. daysToChange = 0;
  381. } else if ((_nextPeriodChange.get(Calendar.HOUR_OF_DAY) == PERIOD_START_HOUR) && (_nextPeriodChange.get(Calendar.MINUTE) < PERIOD_START_MINS)) {
  382. daysToChange = 0;
  383. }
  384. }
  385. // Otherwise...
  386. if (daysToChange > 0) {
  387. _nextPeriodChange.add(Calendar.DATE, daysToChange);
  388. }
  389. _nextPeriodChange.set(Calendar.HOUR_OF_DAY, PERIOD_START_HOUR);
  390. _nextPeriodChange.set(Calendar.MINUTE, PERIOD_START_MINS);
  391. break;
  392. case PERIOD_COMP_RECRUITING:
  393. case PERIOD_COMP_RESULTS:
  394. _nextPeriodChange.add(Calendar.MILLISECOND, PERIOD_MINOR_LENGTH);
  395. break;
  396. }
  397. LOG.info("Next period change set to {}.", _nextPeriodChange.getTime());
  398. }
  399. public final String getCurrentPeriodName() {
  400. String periodName = null;
  401. switch (_activePeriod) {
  402. case PERIOD_COMP_RECRUITING:
  403. periodName = "Quest Event Initialization";
  404. break;
  405. case PERIOD_COMPETITION:
  406. periodName = "Competition (Quest Event)";
  407. break;
  408. case PERIOD_COMP_RESULTS:
  409. periodName = "Quest Event Results";
  410. break;
  411. case PERIOD_SEAL_VALIDATION:
  412. periodName = "Seal Validation";
  413. break;
  414. }
  415. return periodName;
  416. }
  417. /**
  418. * @return {@code true} if it's competition period, {@code false} otherwise
  419. */
  420. public final boolean isCompetitionPeriod() {
  421. return (_activePeriod == PERIOD_COMPETITION);
  422. }
  423. public final boolean isSealValidationPeriod() {
  424. return (_activePeriod == PERIOD_SEAL_VALIDATION);
  425. }
  426. public final boolean isCompResultsPeriod() {
  427. return (_activePeriod == PERIOD_COMP_RESULTS);
  428. }
  429. /**
  430. * returns true if the given date is in Seal Validation or in Quest Event Results period
  431. * @param date
  432. * @return
  433. */
  434. public boolean isDateInSealValidPeriod(Calendar date) {
  435. long nextPeriodChange = getMilliToPeriodChange();
  436. long nextQuestStart = 0;
  437. long nextValidStart = 0;
  438. long tillDate = date.getTimeInMillis() - Calendar.getInstance().getTimeInMillis();
  439. while (((2 * PERIOD_MAJOR_LENGTH) + (2 * PERIOD_MINOR_LENGTH)) < tillDate) {
  440. tillDate -= ((2 * PERIOD_MAJOR_LENGTH) + (2 * PERIOD_MINOR_LENGTH));
  441. }
  442. while (tillDate < 0) {
  443. tillDate += ((2 * PERIOD_MAJOR_LENGTH) + (2 * PERIOD_MINOR_LENGTH));
  444. }
  445. switch (getCurrentPeriod()) {
  446. case PERIOD_COMP_RECRUITING:
  447. nextValidStart = nextPeriodChange + PERIOD_MAJOR_LENGTH;
  448. nextQuestStart = nextValidStart + PERIOD_MAJOR_LENGTH + PERIOD_MINOR_LENGTH;
  449. break;
  450. case PERIOD_COMPETITION:
  451. nextValidStart = nextPeriodChange;
  452. nextQuestStart = nextPeriodChange + PERIOD_MAJOR_LENGTH + PERIOD_MINOR_LENGTH;
  453. break;
  454. case PERIOD_COMP_RESULTS:
  455. nextQuestStart = nextPeriodChange + PERIOD_MAJOR_LENGTH;
  456. nextValidStart = nextQuestStart + PERIOD_MAJOR_LENGTH + PERIOD_MINOR_LENGTH;
  457. break;
  458. case PERIOD_SEAL_VALIDATION:
  459. nextQuestStart = nextPeriodChange;
  460. nextValidStart = nextPeriodChange + PERIOD_MAJOR_LENGTH + PERIOD_MINOR_LENGTH;
  461. break;
  462. }
  463. if (((nextQuestStart < tillDate) && (tillDate < nextValidStart)) || ((nextValidStart < nextQuestStart) && ((tillDate < nextValidStart) || (nextQuestStart < tillDate)))) {
  464. return false;
  465. }
  466. return true;
  467. }
  468. public final int getCurrentScore(int cabal) {
  469. double totalStoneScore = _dawnStoneScore + _duskStoneScore;
  470. switch (cabal) {
  471. case CABAL_NULL:
  472. return 0;
  473. case CABAL_DAWN:
  474. return Math.round((float) (_dawnStoneScore / ((float) totalStoneScore == 0 ? 1 : totalStoneScore)) * 500) + _dawnFestivalScore;
  475. case CABAL_DUSK:
  476. return Math.round((float) (_duskStoneScore / ((float) totalStoneScore == 0 ? 1 : totalStoneScore)) * 500) + _duskFestivalScore;
  477. }
  478. return 0;
  479. }
  480. public final double getCurrentStoneScore(int cabal) {
  481. switch (cabal) {
  482. case CABAL_NULL:
  483. return 0;
  484. case CABAL_DAWN:
  485. return _dawnStoneScore;
  486. case CABAL_DUSK:
  487. return _duskStoneScore;
  488. }
  489. return 0;
  490. }
  491. public final int getCurrentFestivalScore(int cabal) {
  492. switch (cabal) {
  493. case CABAL_NULL:
  494. return 0;
  495. case CABAL_DAWN:
  496. return _dawnFestivalScore;
  497. case CABAL_DUSK:
  498. return _duskFestivalScore;
  499. }
  500. return 0;
  501. }
  502. public final int getCabalHighestScore() {
  503. if (getCurrentScore(CABAL_DUSK) == getCurrentScore(CABAL_DAWN)) {
  504. return CABAL_NULL;
  505. } else if (getCurrentScore(CABAL_DUSK) > getCurrentScore(CABAL_DAWN)) {
  506. return CABAL_DUSK;
  507. } else {
  508. return CABAL_DAWN;
  509. }
  510. }
  511. public final int getSealOwner(int seal) {
  512. return _signsSealOwners.get(seal);
  513. }
  514. public final int getSealProportion(int seal, int cabal) {
  515. if (cabal == CABAL_NULL) {
  516. return 0;
  517. } else if (cabal == CABAL_DUSK) {
  518. return _signsDuskSealTotals.get(seal);
  519. } else {
  520. return _signsDawnSealTotals.get(seal);
  521. }
  522. }
  523. public final int getTotalMembers(int cabal) {
  524. int cabalMembers = 0;
  525. String cabalName = getCabalShortName(cabal);
  526. for (StatsSet sevenDat : _signsPlayerData.values()) {
  527. if (sevenDat.getString("cabal").equals(cabalName)) {
  528. cabalMembers++;
  529. }
  530. }
  531. return cabalMembers;
  532. }
  533. public int getPlayerStoneContrib(int objectId) {
  534. final StatsSet currPlayer = _signsPlayerData.get(objectId);
  535. if (currPlayer == null) {
  536. return 0;
  537. }
  538. int stoneCount = 0;
  539. stoneCount += currPlayer.getInt("red_stones");
  540. stoneCount += currPlayer.getInt("green_stones");
  541. stoneCount += currPlayer.getInt("blue_stones");
  542. return stoneCount;
  543. }
  544. public int getPlayerContribScore(int objectId) {
  545. final StatsSet currPlayer = _signsPlayerData.get(objectId);
  546. if (currPlayer == null) {
  547. return 0;
  548. }
  549. return currPlayer.getInt("contribution_score");
  550. }
  551. public int getPlayerAdenaCollect(int objectId) {
  552. final StatsSet currPlayer = _signsPlayerData.get(objectId);
  553. if (currPlayer == null) {
  554. return 0;
  555. }
  556. return currPlayer.getInt("ancient_adena_amount");
  557. }
  558. public int getPlayerSeal(int objectId) {
  559. final StatsSet currPlayer = _signsPlayerData.get(objectId);
  560. if (currPlayer == null) {
  561. return SEAL_NULL;
  562. }
  563. return currPlayer.getInt("seal");
  564. }
  565. public int getPlayerCabal(int objectId) {
  566. final StatsSet currPlayer = _signsPlayerData.get(objectId);
  567. if (currPlayer == null) {
  568. return CABAL_NULL;
  569. }
  570. String playerCabal = currPlayer.getString("cabal");
  571. if (playerCabal.equalsIgnoreCase("dawn")) {
  572. return CABAL_DAWN;
  573. } else if (playerCabal.equalsIgnoreCase("dusk")) {
  574. return CABAL_DUSK;
  575. } else {
  576. return CABAL_NULL;
  577. }
  578. }
  579. /**
  580. * Restores all Seven Signs data and settings, usually called at server startup.
  581. */
  582. protected void restoreSevenSignsData() {
  583. try (var con = ConnectionFactory.getInstance().getConnection()) {
  584. try (var s = con.createStatement();
  585. var rs = s.executeQuery(LOAD_DATA)) {
  586. StatsSet sevenDat = null;
  587. int charObjId;
  588. while (rs.next()) {
  589. charObjId = rs.getInt("charId");
  590. sevenDat = new StatsSet();
  591. sevenDat.set("charId", charObjId);
  592. sevenDat.set("cabal", rs.getString("cabal"));
  593. sevenDat.set("seal", rs.getInt("seal"));
  594. sevenDat.set("red_stones", rs.getInt("red_stones"));
  595. sevenDat.set("green_stones", rs.getInt("green_stones"));
  596. sevenDat.set("blue_stones", rs.getInt("blue_stones"));
  597. sevenDat.set("ancient_adena_amount", rs.getDouble("ancient_adena_amount"));
  598. sevenDat.set("contribution_score", rs.getDouble("contribution_score"));
  599. _signsPlayerData.put(charObjId, sevenDat);
  600. }
  601. }
  602. try (var s = con.createStatement();
  603. var rs = s.executeQuery(LOAD_STATUS)) {
  604. while (rs.next()) {
  605. _currentCycle = rs.getInt("current_cycle");
  606. _activePeriod = rs.getInt("active_period");
  607. _previousWinner = rs.getInt("previous_winner");
  608. _dawnStoneScore = rs.getDouble("dawn_stone_score");
  609. _dawnFestivalScore = rs.getInt("dawn_festival_score");
  610. _duskStoneScore = rs.getDouble("dusk_stone_score");
  611. _duskFestivalScore = rs.getInt("dusk_festival_score");
  612. _signsSealOwners.put(SEAL_AVARICE, rs.getInt("avarice_owner"));
  613. _signsSealOwners.put(SEAL_GNOSIS, rs.getInt("gnosis_owner"));
  614. _signsSealOwners.put(SEAL_STRIFE, rs.getInt("strife_owner"));
  615. _signsDawnSealTotals.put(SEAL_AVARICE, rs.getInt("avarice_dawn_score"));
  616. _signsDawnSealTotals.put(SEAL_GNOSIS, rs.getInt("gnosis_dawn_score"));
  617. _signsDawnSealTotals.put(SEAL_STRIFE, rs.getInt("strife_dawn_score"));
  618. _signsDuskSealTotals.put(SEAL_AVARICE, rs.getInt("avarice_dusk_score"));
  619. _signsDuskSealTotals.put(SEAL_GNOSIS, rs.getInt("gnosis_dusk_score"));
  620. _signsDuskSealTotals.put(SEAL_STRIFE, rs.getInt("strife_dusk_score"));
  621. _lastSave.setTimeInMillis(rs.getLong("date"));
  622. }
  623. }
  624. } catch (SQLException ex) {
  625. LOG.warn("Unable to load Seven Signs data from database!", ex);
  626. }
  627. }
  628. /**
  629. * Saves all Seven Signs player data.<br>
  630. * Should be called on period change and shutdown only.
  631. */
  632. public void saveSevenSignsData() {
  633. try (var con = ConnectionFactory.getInstance().getConnection();
  634. var ps = con.prepareStatement(UPDATE_PLAYER)) {
  635. for (StatsSet sevenDat : _signsPlayerData.values()) {
  636. ps.setString(1, sevenDat.getString("cabal"));
  637. ps.setInt(2, sevenDat.getInt("seal"));
  638. ps.setInt(3, sevenDat.getInt("red_stones"));
  639. ps.setInt(4, sevenDat.getInt("green_stones"));
  640. ps.setInt(5, sevenDat.getInt("blue_stones"));
  641. ps.setDouble(6, sevenDat.getDouble("ancient_adena_amount"));
  642. ps.setDouble(7, sevenDat.getDouble("contribution_score"));
  643. ps.setInt(8, sevenDat.getInt("charId"));
  644. ps.addBatch();
  645. }
  646. ps.executeBatch();
  647. } catch (SQLException ex) {
  648. LOG.warn("Unable to save data to database!", ex);
  649. }
  650. }
  651. public final void saveSevenSignsData(int objectId) {
  652. StatsSet sevenDat = _signsPlayerData.get(objectId);
  653. if (sevenDat == null) {
  654. return;
  655. }
  656. try (var con = ConnectionFactory.getInstance().getConnection();
  657. var ps = con.prepareStatement(UPDATE_PLAYER)) {
  658. ps.setString(1, sevenDat.getString("cabal"));
  659. ps.setInt(2, sevenDat.getInt("seal"));
  660. ps.setInt(3, sevenDat.getInt("red_stones"));
  661. ps.setInt(4, sevenDat.getInt("green_stones"));
  662. ps.setInt(5, sevenDat.getInt("blue_stones"));
  663. ps.setDouble(6, sevenDat.getDouble("ancient_adena_amount"));
  664. ps.setDouble(7, sevenDat.getDouble("contribution_score"));
  665. ps.setInt(8, sevenDat.getInt("charId"));
  666. ps.execute();
  667. } catch (SQLException ex) {
  668. LOG.error("Unable to save data to database!", ex);
  669. }
  670. }
  671. public final void saveSevenSignsStatus() {
  672. try (var con = ConnectionFactory.getInstance().getConnection();
  673. var ps = con.prepareStatement(UPDATE_STATUS)) {
  674. ps.setInt(1, _currentCycle);
  675. ps.setInt(2, _activePeriod);
  676. ps.setInt(3, _previousWinner);
  677. ps.setDouble(4, _dawnStoneScore);
  678. ps.setInt(5, _dawnFestivalScore);
  679. ps.setDouble(6, _duskStoneScore);
  680. ps.setInt(7, _duskFestivalScore);
  681. ps.setInt(8, _signsSealOwners.get(SEAL_AVARICE));
  682. ps.setInt(9, _signsSealOwners.get(SEAL_GNOSIS));
  683. ps.setInt(10, _signsSealOwners.get(SEAL_STRIFE));
  684. ps.setInt(11, _signsDawnSealTotals.get(SEAL_AVARICE));
  685. ps.setInt(12, _signsDawnSealTotals.get(SEAL_GNOSIS));
  686. ps.setInt(13, _signsDawnSealTotals.get(SEAL_STRIFE));
  687. ps.setInt(14, _signsDuskSealTotals.get(SEAL_AVARICE));
  688. ps.setInt(15, _signsDuskSealTotals.get(SEAL_GNOSIS));
  689. ps.setInt(16, _signsDuskSealTotals.get(SEAL_STRIFE));
  690. ps.setInt(17, SevenSignsFestival.getInstance().getCurrentFestivalCycle());
  691. for (int i = 0; i < SevenSignsFestival.FESTIVAL_COUNT; i++) {
  692. ps.setInt(18 + i, SevenSignsFestival.getInstance().getAccumulatedBonus(i));
  693. }
  694. _lastSave = Calendar.getInstance();
  695. ps.setLong(18 + SevenSignsFestival.FESTIVAL_COUNT, _lastSave.getTimeInMillis());
  696. ps.execute();
  697. } catch (SQLException ex) {
  698. LOG.warn("Unable to save data to database!", ex);
  699. }
  700. }
  701. /**
  702. * Used to reset the cabal details of all players, and update the database.<BR>
  703. * Primarily used when beginning a new cycle, and should otherwise never be called.
  704. */
  705. protected void resetPlayerData() {
  706. // Reset each player's contribution data as well as seal and cabal.
  707. for (StatsSet sevenDat : _signsPlayerData.values()) {
  708. // Reset the player's cabal and seal information
  709. sevenDat.set("cabal", "");
  710. sevenDat.set("seal", SEAL_NULL);
  711. sevenDat.set("contribution_score", 0);
  712. }
  713. }
  714. /**
  715. * Used to specify cabal-related details for the specified player.<br>
  716. * This method checks to see if the player has registered before and will update the database if necessary.
  717. * @param objectId
  718. * @param chosenCabal
  719. * @param chosenSeal
  720. * @return the cabal ID the player has joined.
  721. */
  722. public int setPlayerInfo(int objectId, int chosenCabal, int chosenSeal) {
  723. StatsSet currPlayerData = _signsPlayerData.get(objectId);
  724. if (currPlayerData != null) {
  725. // If the seal validation period has passed,
  726. // cabal information was removed and so "re-register" player
  727. currPlayerData.set("cabal", getCabalShortName(chosenCabal));
  728. currPlayerData.set("seal", chosenSeal);
  729. _signsPlayerData.put(objectId, currPlayerData);
  730. } else {
  731. currPlayerData = new StatsSet();
  732. currPlayerData.set("charId", objectId);
  733. currPlayerData.set("cabal", getCabalShortName(chosenCabal));
  734. currPlayerData.set("seal", chosenSeal);
  735. currPlayerData.set("red_stones", 0);
  736. currPlayerData.set("green_stones", 0);
  737. currPlayerData.set("blue_stones", 0);
  738. currPlayerData.set("ancient_adena_amount", 0);
  739. currPlayerData.set("contribution_score", 0);
  740. _signsPlayerData.put(objectId, currPlayerData);
  741. // Update data in database, as we have a new player signing up.
  742. try (var con = ConnectionFactory.getInstance().getConnection();
  743. var ps = con.prepareStatement(INSERT_PLAYER)) {
  744. ps.setInt(1, objectId);
  745. ps.setString(2, getCabalShortName(chosenCabal));
  746. ps.setInt(3, chosenSeal);
  747. ps.execute();
  748. } catch (SQLException ex) {
  749. LOG.warn("Failed to save data!", ex);
  750. }
  751. }
  752. // Increasing Seal total score for the player chosen Seal.
  753. if ("dawn".equals(currPlayerData.getString("cabal"))) {
  754. _signsDawnSealTotals.put(chosenSeal, _signsDawnSealTotals.get(chosenSeal) + 1);
  755. } else {
  756. _signsDuskSealTotals.put(chosenSeal, _signsDuskSealTotals.get(chosenSeal) + 1);
  757. }
  758. if (!Config.ALT_SEVENSIGNS_LAZY_UPDATE) {
  759. saveSevenSignsStatus();
  760. }
  761. return chosenCabal;
  762. }
  763. /**
  764. * Returns the amount of ancient adena the specified player can claim, if any.<BR>
  765. * If removeReward = True, all the ancient adena owed to them is removed, then DB is updated.
  766. * @param objectId
  767. * @param removeReward
  768. * @return
  769. */
  770. public int getAncientAdenaReward(int objectId, boolean removeReward) {
  771. StatsSet currPlayer = _signsPlayerData.get(objectId);
  772. int rewardAmount = currPlayer.getInt("ancient_adena_amount");
  773. currPlayer.set("red_stones", 0);
  774. currPlayer.set("green_stones", 0);
  775. currPlayer.set("blue_stones", 0);
  776. currPlayer.set("ancient_adena_amount", 0);
  777. if (removeReward) {
  778. _signsPlayerData.put(objectId, currPlayer);
  779. if (!Config.ALT_SEVENSIGNS_LAZY_UPDATE) {
  780. saveSevenSignsData(objectId);
  781. saveSevenSignsStatus();
  782. }
  783. }
  784. return rewardAmount;
  785. }
  786. /**
  787. * Used to add the specified player's seal stone contribution points to the current total for their cabal.<br>
  788. * Returns the point score the contribution was worth.<br>
  789. * Each stone count <B>must be</B> broken down and specified by the stone's color.
  790. * @param objectId
  791. * @param blueCount
  792. * @param greenCount
  793. * @param redCount
  794. * @return
  795. */
  796. public long addPlayerStoneContrib(int objectId, long blueCount, long greenCount, long redCount) {
  797. StatsSet currPlayer = _signsPlayerData.get(objectId);
  798. long contribScore = calcContributionScore(blueCount, greenCount, redCount);
  799. long totalAncientAdena = currPlayer.getLong("ancient_adena_amount") + calcAncientAdenaReward(blueCount, greenCount, redCount);
  800. long totalContribScore = currPlayer.getLong("contribution_score") + contribScore;
  801. if (totalContribScore > Config.ALT_MAXIMUM_PLAYER_CONTRIB) {
  802. return -1;
  803. }
  804. currPlayer.set("red_stones", currPlayer.getInt("red_stones") + redCount);
  805. currPlayer.set("green_stones", currPlayer.getInt("green_stones") + greenCount);
  806. currPlayer.set("blue_stones", currPlayer.getInt("blue_stones") + blueCount);
  807. currPlayer.set("ancient_adena_amount", totalAncientAdena);
  808. currPlayer.set("contribution_score", totalContribScore);
  809. _signsPlayerData.put(objectId, currPlayer);
  810. switch (getPlayerCabal(objectId)) {
  811. case CABAL_DAWN:
  812. _dawnStoneScore += contribScore;
  813. break;
  814. case CABAL_DUSK:
  815. _duskStoneScore += contribScore;
  816. break;
  817. }
  818. if (!Config.ALT_SEVENSIGNS_LAZY_UPDATE) {
  819. saveSevenSignsData(objectId);
  820. saveSevenSignsStatus();
  821. }
  822. return contribScore;
  823. }
  824. /**
  825. * Adds the specified number of festival points to the specified cabal.<br>
  826. * Remember, the same number of points are <b>deducted from the rival cabal</b> to maintain proportionality.
  827. * @param cabal
  828. * @param amount
  829. */
  830. public void addFestivalScore(int cabal, int amount) {
  831. if (cabal == CABAL_DUSK) {
  832. _duskFestivalScore += amount;
  833. // To prevent negative scores!
  834. if (_dawnFestivalScore >= amount) {
  835. _dawnFestivalScore -= amount;
  836. }
  837. } else {
  838. _dawnFestivalScore += amount;
  839. if (_duskFestivalScore >= amount) {
  840. _duskFestivalScore -= amount;
  841. }
  842. }
  843. }
  844. /**
  845. * Send info on the current Seven Signs period to the specified player.
  846. * @param player
  847. */
  848. public void sendCurrentPeriodMsg(L2PcInstance player) {
  849. SystemMessage sm = null;
  850. switch (getCurrentPeriod()) {
  851. case PERIOD_COMP_RECRUITING:
  852. sm = SystemMessage.getSystemMessage(PREPARATIONS_PERIOD_BEGUN);
  853. break;
  854. case PERIOD_COMPETITION:
  855. sm = SystemMessage.getSystemMessage(COMPETITION_PERIOD_BEGUN);
  856. break;
  857. case PERIOD_COMP_RESULTS:
  858. sm = SystemMessage.getSystemMessage(RESULTS_PERIOD_BEGUN);
  859. break;
  860. case PERIOD_SEAL_VALIDATION:
  861. sm = SystemMessage.getSystemMessage(VALIDATION_PERIOD_BEGUN);
  862. break;
  863. }
  864. player.sendPacket(sm);
  865. }
  866. /**
  867. * Sends the built-in system message specified by sysMsgId to all online players.
  868. * @param sysMsgId
  869. */
  870. public void sendMessageToAll(SystemMessageId sysMsgId) {
  871. Broadcast.toAllOnlinePlayers(SystemMessage.getSystemMessage(sysMsgId));
  872. }
  873. /**
  874. * Used to initialize the seals for each cabal.<bR>
  875. * (Used at startup or at beginning of a new cycle).<br>
  876. * This method should be called after <b>resetSeals()</b> and <b>calcNewSealOwners()</b> on a new cycle.
  877. */
  878. protected void initializeSeals() {
  879. for (Entry<Integer, Integer> e : _signsSealOwners.entrySet()) {
  880. if (e.getValue() != CABAL_NULL) {
  881. if (isSealValidationPeriod()) {
  882. LOG.info("The {} have won the {}.", getCabalName(e.getValue()), getSealName(e.getKey(), false));
  883. } else {
  884. LOG.info("The {} is currently owned by {}.", getSealName(e.getKey(), false), getCabalName(e.getValue()));
  885. }
  886. } else {
  887. LOG.info("The {} remains unclaimed.", getSealName(e.getKey(), false));
  888. }
  889. }
  890. }
  891. /**
  892. * Only really used at the beginning of a new cycle, this method resets all seal-related data.
  893. */
  894. protected void resetSeals() {
  895. _signsDawnSealTotals.put(SEAL_AVARICE, 0);
  896. _signsDawnSealTotals.put(SEAL_GNOSIS, 0);
  897. _signsDawnSealTotals.put(SEAL_STRIFE, 0);
  898. _signsDuskSealTotals.put(SEAL_AVARICE, 0);
  899. _signsDuskSealTotals.put(SEAL_GNOSIS, 0);
  900. _signsDuskSealTotals.put(SEAL_STRIFE, 0);
  901. }
  902. /**
  903. * Calculates the ownership of the three Seals of the Seven Signs, based on various criterion. <BR>
  904. * <BR>
  905. * Should only ever called at the beginning of a new cycle.
  906. */
  907. protected void calcNewSealOwners() {
  908. for (Integer currSeal : _signsDawnSealTotals.keySet()) {
  909. int prevSealOwner = _signsSealOwners.get(currSeal);
  910. int newSealOwner = CABAL_NULL;
  911. int dawnProportion = getSealProportion(currSeal, CABAL_DAWN);
  912. int totalDawnMembers = getTotalMembers(CABAL_DAWN) == 0 ? 1 : getTotalMembers(CABAL_DAWN);
  913. int dawnPercent = Math.round(((float) dawnProportion / (float) totalDawnMembers) * 100);
  914. int duskProportion = getSealProportion(currSeal, CABAL_DUSK);
  915. int totalDuskMembers = getTotalMembers(CABAL_DUSK) == 0 ? 1 : getTotalMembers(CABAL_DUSK);
  916. int duskPercent = Math.round(((float) duskProportion / (float) totalDuskMembers) * 100);
  917. /**
  918. * If a Seal was already closed or owned by the opponent and the new winner wants to assume ownership of the Seal, 35% or more of the members of the Cabal must have chosen the Seal.<br>
  919. * If they chose less than 35%, they cannot own the Seal.<br>
  920. * If the Seal was owned by the winner in the previous Seven Signs, they can retain that seal if 10% or more members have chosen it.<br>
  921. * If they want to possess a new Seal, at least 35% of the members of the Cabal must have chosen the new Seal.
  922. */
  923. switch (prevSealOwner) {
  924. case CABAL_NULL:
  925. switch (getCabalHighestScore()) {
  926. case CABAL_NULL:
  927. newSealOwner = CABAL_NULL;
  928. break;
  929. case CABAL_DAWN:
  930. if (dawnPercent >= 35) {
  931. newSealOwner = CABAL_DAWN;
  932. } else {
  933. newSealOwner = CABAL_NULL;
  934. }
  935. break;
  936. case CABAL_DUSK:
  937. if (duskPercent >= 35) {
  938. newSealOwner = CABAL_DUSK;
  939. } else {
  940. newSealOwner = CABAL_NULL;
  941. }
  942. break;
  943. }
  944. break;
  945. case CABAL_DAWN:
  946. switch (getCabalHighestScore()) {
  947. case CABAL_NULL:
  948. if (dawnPercent >= 10) {
  949. newSealOwner = CABAL_DAWN;
  950. } else {
  951. newSealOwner = CABAL_NULL;
  952. }
  953. break;
  954. case CABAL_DAWN:
  955. if (dawnPercent >= 10) {
  956. newSealOwner = CABAL_DAWN;
  957. } else {
  958. newSealOwner = CABAL_NULL;
  959. }
  960. break;
  961. case CABAL_DUSK:
  962. if (duskPercent >= 35) {
  963. newSealOwner = CABAL_DUSK;
  964. } else if (dawnPercent >= 10) {
  965. newSealOwner = CABAL_DAWN;
  966. } else {
  967. newSealOwner = CABAL_NULL;
  968. }
  969. break;
  970. }
  971. break;
  972. case CABAL_DUSK:
  973. switch (getCabalHighestScore()) {
  974. case CABAL_NULL:
  975. if (duskPercent >= 10) {
  976. newSealOwner = CABAL_DUSK;
  977. } else {
  978. newSealOwner = CABAL_NULL;
  979. }
  980. break;
  981. case CABAL_DAWN:
  982. if (dawnPercent >= 35) {
  983. newSealOwner = CABAL_DAWN;
  984. } else if (duskPercent >= 10) {
  985. newSealOwner = CABAL_DUSK;
  986. } else {
  987. newSealOwner = CABAL_NULL;
  988. }
  989. break;
  990. case CABAL_DUSK:
  991. if (duskPercent >= 10) {
  992. newSealOwner = CABAL_DUSK;
  993. } else {
  994. newSealOwner = CABAL_NULL;
  995. }
  996. break;
  997. }
  998. break;
  999. }
  1000. _signsSealOwners.put(currSeal, newSealOwner);
  1001. // Alert all online players to new seal status.
  1002. switch (currSeal) {
  1003. case SEAL_AVARICE:
  1004. if (newSealOwner == CABAL_DAWN) {
  1005. sendMessageToAll(DAWN_OBTAINED_AVARICE);
  1006. } else if (newSealOwner == CABAL_DUSK) {
  1007. sendMessageToAll(DUSK_OBTAINED_AVARICE);
  1008. }
  1009. break;
  1010. case SEAL_GNOSIS:
  1011. if (newSealOwner == CABAL_DAWN) {
  1012. sendMessageToAll(DAWN_OBTAINED_GNOSIS);
  1013. } else if (newSealOwner == CABAL_DUSK) {
  1014. sendMessageToAll(DUSK_OBTAINED_GNOSIS);
  1015. }
  1016. break;
  1017. case SEAL_STRIFE:
  1018. if (newSealOwner == CABAL_DAWN) {
  1019. sendMessageToAll(DAWN_OBTAINED_STRIFE);
  1020. } else if (newSealOwner == CABAL_DUSK) {
  1021. sendMessageToAll(DUSK_OBTAINED_STRIFE);
  1022. }
  1023. CastleManager.getInstance().validateTaxes(newSealOwner);
  1024. break;
  1025. }
  1026. }
  1027. }
  1028. /**
  1029. * This method is called to remove all players from catacombs and necropolises, who belong to the losing cabal.<br>
  1030. * Should only ever called at the beginning of Seal Validation.
  1031. * @param compWinner
  1032. */
  1033. protected void teleLosingCabalFromDungeons(String compWinner) {
  1034. for (L2PcInstance player : L2World.getInstance().getPlayers()) {
  1035. StatsSet currPlayer = _signsPlayerData.get(player.getObjectId());
  1036. if (isSealValidationPeriod() || isCompResultsPeriod()) {
  1037. if (!player.isGM() && player.isIn7sDungeon() && ((currPlayer == null) || !currPlayer.getString("cabal").equals(compWinner))) {
  1038. player.teleToLocation(TeleportWhereType.TOWN);
  1039. player.setIsIn7sDungeon(false);
  1040. player.sendMessage("You have been teleported to the nearest town due to the beginning of the Seal Validation period.");
  1041. }
  1042. } else {
  1043. if (!player.isGM() && player.isIn7sDungeon() && ((currPlayer == null) || !currPlayer.getString("cabal").isEmpty())) {
  1044. player.teleToLocation(TeleportWhereType.TOWN);
  1045. player.setIsIn7sDungeon(false);
  1046. player.sendMessage("You have been teleported to the nearest town because you have not signed for any cabal.");
  1047. }
  1048. }
  1049. }
  1050. }
  1051. /**
  1052. * The primary controller of period change of the Seven Signs system.<br>
  1053. * This runs all related tasks depending on the period that is about to begin.
  1054. * @author Tempy
  1055. */
  1056. protected class SevenSignsPeriodChange implements Runnable {
  1057. @Override
  1058. public void run() {
  1059. // Remember the period check here refers to the period just ENDED!
  1060. final int periodEnded = getCurrentPeriod();
  1061. _activePeriod++;
  1062. switch (periodEnded) {
  1063. case PERIOD_COMP_RECRUITING: // Initialization
  1064. // Start the Festival of Darkness cycle.
  1065. SevenSignsFestival.getInstance().startFestivalManager();
  1066. // Send message that Competition has begun.
  1067. sendMessageToAll(QUEST_EVENT_PERIOD_BEGUN);
  1068. break;
  1069. case PERIOD_COMPETITION: // Results Calculation
  1070. // Send message that Competition has ended.
  1071. sendMessageToAll(QUEST_EVENT_PERIOD_ENDED);
  1072. int compWinner = getCabalHighestScore();
  1073. // Schedule a stop of the festival engine and reward highest ranking members from cycle
  1074. SevenSignsFestival.getInstance().getFestivalManagerSchedule().cancel(false);
  1075. SevenSignsFestival.getInstance().rewardHighestRanked();
  1076. calcNewSealOwners();
  1077. switch (compWinner) {
  1078. case CABAL_DAWN:
  1079. sendMessageToAll(DAWN_WON);
  1080. break;
  1081. case CABAL_DUSK:
  1082. sendMessageToAll(DUSK_WON);
  1083. break;
  1084. }
  1085. _previousWinner = compWinner;
  1086. // Reset Castle ticket buy count
  1087. List<Castle> castles = CastleManager.getInstance().getCastles();
  1088. for (Castle castle : castles) {
  1089. castle.setTicketBuyCount(0);
  1090. }
  1091. break;
  1092. case PERIOD_COMP_RESULTS: // Seal Validation
  1093. // Perform initial Seal Validation set up.
  1094. initializeSeals();
  1095. // Buff/Debuff members of the event when Seal of Strife captured.
  1096. giveCPMult(getSealOwner(SEAL_STRIFE));
  1097. // Send message that Seal Validation has begun.
  1098. sendMessageToAll(SEAL_VALIDATION_PERIOD_BEGUN);
  1099. LOG.info("The {} have won the competition with {} points!", getCabalName(_previousWinner), getCurrentScore(_previousWinner));
  1100. break;
  1101. case PERIOD_SEAL_VALIDATION: // Reset for New Cycle
  1102. // Ensure a cycle restart when this period ends.
  1103. _activePeriod = PERIOD_COMP_RECRUITING;
  1104. // Send message that Seal Validation has ended.
  1105. sendMessageToAll(SEAL_VALIDATION_PERIOD_ENDED);
  1106. // Clear Seal of Strife influence.
  1107. removeCPMult();
  1108. // Reset all data
  1109. resetPlayerData();
  1110. resetSeals();
  1111. _currentCycle++;
  1112. // Reset all Festival-related data and remove any unused blood offerings.
  1113. // NOTE: A full update of Festival data in the database is also performed.
  1114. SevenSignsFestival.getInstance().resetFestivalData(false);
  1115. _dawnStoneScore = 0;
  1116. _duskStoneScore = 0;
  1117. _dawnFestivalScore = 0;
  1118. _duskFestivalScore = 0;
  1119. break;
  1120. }
  1121. // Make sure all Seven Signs data is saved for future use.
  1122. saveSevenSignsData();
  1123. saveSevenSignsStatus();
  1124. teleLosingCabalFromDungeons(getCabalShortName(getCabalHighestScore()));
  1125. SSQInfo ss = new SSQInfo();
  1126. Broadcast.toAllOnlinePlayers(ss);
  1127. spawnSevenSignsNPC();
  1128. LOG.info("The {} period has begun!", getCurrentPeriodName());
  1129. setCalendarForNextPeriodChange();
  1130. SevenSignsPeriodChange sspc = new SevenSignsPeriodChange();
  1131. ThreadPoolManager.getInstance().scheduleGeneral(sspc, getMilliToPeriodChange());
  1132. }
  1133. }
  1134. public boolean checkIsDawnPostingTicket(int itemId) {
  1135. // TODO: I think it should be some kind of a list in the datapack for compare.
  1136. if ((itemId > 6114) && (itemId < 6175)) {
  1137. return true;
  1138. }
  1139. if ((itemId > 6801) && (itemId < 6812)) {
  1140. return true;
  1141. }
  1142. if ((itemId > 7997) && (itemId < 8008)) {
  1143. return true;
  1144. }
  1145. if ((itemId > 7940) && (itemId < 7951)) {
  1146. return true;
  1147. }
  1148. if ((itemId > 6294) && (itemId < 6307)) {
  1149. return true;
  1150. }
  1151. if ((itemId > 6831) && (itemId < 6834)) {
  1152. return true;
  1153. }
  1154. if ((itemId > 8027) && (itemId < 8030)) {
  1155. return true;
  1156. }
  1157. if ((itemId > 7970) && (itemId < 7973)) {
  1158. return true;
  1159. }
  1160. return false;
  1161. }
  1162. public boolean checkIsRookiePostingTicket(int itemId) {
  1163. // TODO: I think it should be some kind of a list in the datapack for compare.
  1164. if ((itemId > 6174) && (itemId < 6295)) {
  1165. return true;
  1166. }
  1167. if ((itemId > 6811) && (itemId < 6832)) {
  1168. return true;
  1169. }
  1170. if ((itemId > 7950) && (itemId < 7971)) {
  1171. return true;
  1172. }
  1173. if ((itemId > 8007) && (itemId < 8028)) {
  1174. return true;
  1175. }
  1176. return false;
  1177. }
  1178. public void giveCPMult(int strifeOwner) {
  1179. for (L2PcInstance player : L2World.getInstance().getPlayers()) {
  1180. // Gives "Victor of War" passive skill to all online characters with Cabal, which controls Seal of Strife
  1181. int cabal = getPlayerCabal(player.getObjectId());
  1182. if (cabal != SevenSigns.CABAL_NULL) {
  1183. if (cabal == strifeOwner) {
  1184. player.addSkill(CommonSkill.THE_VICTOR_OF_WAR.getSkill());
  1185. } else {
  1186. // Gives "The Vanquished of War" passive skill to all online characters with Cabal, which does not control Seal of Strife
  1187. player.addSkill(CommonSkill.THE_VANQUISHED_OF_WAR.getSkill());
  1188. }
  1189. }
  1190. }
  1191. }
  1192. public void removeCPMult() {
  1193. // Remove SevenSigns' buffs/debuffs.
  1194. for (L2PcInstance player : L2World.getInstance().getPlayers()) {
  1195. player.removeSkill(CommonSkill.THE_VICTOR_OF_WAR.getSkill());
  1196. player.removeSkill(CommonSkill.THE_VANQUISHED_OF_WAR.getSkill());
  1197. }
  1198. }
  1199. public boolean checkSummonConditions(L2PcInstance activeChar) {
  1200. if (activeChar == null) {
  1201. return true;
  1202. }
  1203. // Golems cannot be summoned by Dusk when the Seal of Strife is controlled by the Dawn
  1204. if (isSealValidationPeriod()) {
  1205. if (getSealOwner(SEAL_STRIFE) == CABAL_DAWN) {
  1206. if (getPlayerCabal(activeChar.getObjectId()) == CABAL_DUSK) {
  1207. activeChar.sendPacket(SEAL_OF_STRIFE_FORBIDS_SUMMONING);
  1208. return true;
  1209. }
  1210. }
  1211. }
  1212. return false;
  1213. }
  1214. public static SevenSigns getInstance() {
  1215. return SingletonHolder.INSTANCE;
  1216. }
  1217. private static class SingletonHolder {
  1218. protected static final SevenSigns INSTANCE = new SevenSigns();
  1219. }
  1220. }