Valkazaar Ikariam Developer Tools V0.5.0+

Base scripting tools and data tracking utilities to simplify

此脚本不应直接安装,它是供其他脚本使用的外部库。如果你需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/459367/1145227/Valkazaar%20Ikariam%20Developer%20Tools%20V050%2B.js

  1. // ==UserScript==
  2. // @name Ikariam Developer Tools V0.5.0+
  3. // @namespace AubergineAnodyne
  4. // @description Base scripting tools and data tracking utilities to simplify
  5. // writing Ikariam Greasemonkey scripts.
  6. // @author AubergineAnodyne
  7. // (very loosely based on Ikariam Developer Tools by PhasmaExMachina)
  8. //
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // @grant GM_addStyle
  12. // @grant GM_xmlhttpRequest
  13. //
  14. // @version 0.30
  15. //
  16. // @history 0.30 Added turkish translations and Fixed blackmarket and sea chart archive buildins.
  17. // @history 0.29 Fixed pillage of crystal not showing up.
  18. // @history 0.28 Added COLONIZE constant.
  19. // @history 0.27 Added helper methods for scripts to load/read Ikariam pages in the background.
  20. // @history 0.27 Added support for spy data (IkaTools.EmpireData.EspionageData).
  21. // @history 0.27 Added Romanian translation (by Peta).
  22. // @history 0.26 Added Hungarian translation (by Toroco).
  23. // @history 0.26 Updated for Ikariam changes in 5.3.2.
  24. // @history 0.25 Fix bug in parsing training batches for military.
  25. // @history 0.25 Fix bug in parsing returning colonization mission. Also correct resource calculation for colonization mission.
  26. // @history 0.25 Fix bug in parsing missions when pirate raid is in progress.
  27. // @history 0.24 Added support for Pirate Fortress (v0.5.3 new building).
  28. // @history 0.23 Added resetData function.
  29. // @history 0.23 Fixed a bug that future research levels >= 9 would not be parsed.
  30. // @history 0.22 Fixed a bug that stopped the script from running in some browser configurations.
  31. // @history 0.22 Added some debugging features.
  32. // @history 0.21 Added resizing of settings tab.
  33. // @history 0.20 Added building icon data.
  34. // @history 0.20 Added HTML setting type.
  35. // @history 0.20 Added some movement type data.
  36. // @history 0.20 Fixed a bug computing number of transports for transport missions.
  37. // @history 0.19 Added Polish translation (from pitmm).
  38. // @history 0.19 Fixed temple build resource requirements showing up incorrectly (marble instead of crystal).
  39. // @history 0.19 Changed how transition to resource and mine views works.
  40. // @history 0.18 Hopefully fixed population calculation crash when running the theology government.
  41. // @history 0.18 Fix for transport form on test server. (Probably coming to other servers with 0.5.1).
  42. // @history 0.17 Added German localization (translation by Cherry).
  43. // @history 0.17 Fixed date display bug with yesterday/today/tomorrow being incorrectly assigned due to timezone issue.
  44. // @history 0.16 Reworked how initial ajax response is determined so it works in Chrome.
  45. // @history 0.15 Removed CDATA section (hopefully will work in Chrome).
  46. // @history 0.14 UI support for script settings.
  47. // @history 0.14 Fixed corruption calculation for cities with no palace.
  48. // @history 0.14 Corrected loading time for deploy on the same island.
  49. // @history 0.13 Another tweak to work with TamperMonkey.
  50. // @history 0.12 Another tweak to work with TamperMonkey.
  51. // @history 0.11 Small tweak to work with TamperMonkey in Google Chrome.
  52. // @history 0.10 Initial version.
  53. // @require http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js
  54. // ==/UserScript==
  55.  
  56. if (typeof IkaTools == 'undefined') {
  57. IkaTools = (function() {
  58. var ikaToolsVersion = 0;
  59. /**
  60. * Support functions for logging, profiling, and debugging.
  61. */
  62. var Logging = (function() {
  63. var exceptionLog = [];
  64. var options = {
  65. debug: false,
  66. timings: false,
  67. profile: false,
  68. };
  69. function getExceptionLog() {
  70. return exceptionLog;
  71. }
  72. /**
  73. * Analogous to console.log, but may have been disabled through options.
  74. */
  75. function debug() {
  76. if (options.debug && console && console.log) {
  77. // console.log is not a true javascript function. In some browsers we can't call
  78. // it with console.log.apply syntax. Instead we just manually support up to 6
  79. // arguments.
  80. switch (arguments.length) {
  81. case 0:
  82. console.log();
  83. break;
  84. case 1:
  85. console.log(arguments[0]);
  86. break;
  87. case 2:
  88. console.log(arguments[0], arguments[1]);
  89. break;
  90. case 3:
  91. console.log(arguments[0], arguments[1], arguments[2]);
  92. break;
  93. case 4:
  94. console.log(arguments[0], arguments[1], arguments[2], arguments[3]);
  95. break;
  96. case 5:
  97. console.log(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
  98. break;
  99. default:
  100. console.log(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4],
  101. arguments[5]);
  102. break;
  103. }
  104. }
  105. }
  106. /**
  107. * Debug a caught exception.
  108. */
  109. function debugException(text, ex) {
  110. exceptionLog.push({text: text, stack: ex.stack, message: ex.message});
  111. if (console && console.error) {
  112. console.error('IkaTools.debugException (in %s)\n%s\n\n%s\n', text, ex, ex.stack);
  113. }
  114. }
  115. /**
  116. * Wrap a function to catch and log an exception.
  117. * If the time property is true in options (or absent) then time the function call.
  118. * (May set alwaysTime option to output time regardless of disabled logging settings.)
  119. * If the group property is true in options (or absent) then group the function call.
  120. */
  121. function debuggable(debugOptions, func) {
  122. if (typeof(debugOptions) == 'string') {
  123. debugOptions = { label: debugOptions };
  124. }
  125. debugOptions = $.extend({ time: true,
  126. group: true,
  127. profile: false,
  128. swallowException: false,
  129. }, debugOptions);
  130. if (!debugOptions.label) {
  131. debugOptions.label = Utils.nextId('_debuggable');
  132. }
  133. return function debuggableWrapper() {
  134. var time = ((options.timings && debugOptions.time) || debugOptions.alwaysTime) &&
  135. console && console.time;
  136. var group = debugOptions.group && (options.group) && console && console.group;
  137. var profile = debugOptions.profile && options.profile && console && console.profile;
  138. try {
  139. if (profile) {
  140. console.profile(debugOptions.label);
  141. }
  142. if (group) {
  143. console.group(debugOptions.label);
  144. }
  145. if (time) {
  146. console.time(debugOptions.label);
  147. }
  148. return func.apply(this, arguments);
  149. } catch (ex) {
  150. Logging.debugException(debugOptions.label, ex);
  151. if (!debugOptions.swallowException) {
  152. throw ex;
  153. }
  154. } finally {
  155. if (time) {
  156. console.timeEnd(debugOptions.label);
  157. }
  158. if (group) {
  159. console.groupEnd(debugOptions.label);
  160. }
  161. if (profile) {
  162. console.profileEnd(debugOptions.label);
  163. }
  164. }
  165. };
  166. }
  167. /**
  168. * Wrap a console.time/timeEnd pair around a function. May be disabled
  169. * through options.
  170. */
  171. function time(timeOptions, func) {
  172. return function time() {
  173. var time = (options.timings || timeOptions.alwaysTime) && console && console.time;
  174. try {
  175. if (time) {
  176. console.time(timeOptions.label);
  177. }
  178. return func.apply(this, arguments);
  179. } finally {
  180. if (time) {
  181. console.timeEnd(timeOptions.label);
  182. }
  183. }
  184. }
  185. }
  186. /**
  187. * Wrap a console.group/groupEnd pair around a function. May be disabled
  188. * through options.
  189. */
  190. function group(groupOptions, func) {
  191. var label;
  192. var collapsed = false;
  193. return function group() {
  194. var group = (options.timings || options.debug) && console && console.group;
  195. try {
  196. if (group) {
  197. if (groupOptions.collapsed && console.groupCollapsed) {
  198. console.groupCollapsed(groupOptions.label);
  199. } else {
  200. console.group(groupOptions.label);
  201. }
  202. }
  203. return func.apply(this, arguments);
  204. } finally {
  205. if (group) {
  206. console.groupEnd(groupOptions.label);
  207. }
  208. }
  209. }
  210. }
  211. /**
  212. * Sets logging options. Properties are debug, timings, and profile.
  213. */
  214. function setOptions(newOptions) {
  215. $.extend(options, newOptions);
  216. if (console && console.log) {
  217. //console.log("Set logging options to: ", options);
  218. }
  219. }
  220. /**
  221. * Allows logging options to be configured by browsing to various anchors
  222. * (and persisted for future page views).
  223. */
  224. function setAndSaveOptionsFromPageAnchor() {
  225. var savedOptions = new Data.Value(
  226. 'debugOptions',
  227. { debug: false, timings: false, profile: false , group: false},
  228. { useDomain: false, version: 0 });
  229. savedOptions.get();
  230. var anchor = window.location.hash;
  231. if (anchor.substring(0, 15) == '#ikaScriptTools') {
  232. if (anchor == '#ikaScriptToolsDebugAll') {
  233. savedOptions.get().debug = true;
  234. savedOptions.get().timings = true;
  235. savedOptions.get().profile = true;
  236. } else if (anchor == '#ikaScriptToolsDebugNone') {
  237. savedOptions.get().debug = false;
  238. savedOptions.get().timings = false;
  239. savedOptions.get().profile = false;
  240. } else if (anchor == '#ikaScriptToolsDebugOn') {
  241. savedOptions.get().debug = true;
  242. } else if (anchor == '#ikaScriptToolsDebugOff') {
  243. savedOptions.get().debug = false;
  244. } else if (anchor == '#ikaScriptToolsGroupOn') {
  245. savedOptions.get().group = true;
  246. } else if (anchor == '#ikaScriptToolsGroupOff') {
  247. savedOptions.get().group = false;
  248. } else if (anchor == '#ikaScriptToolsTimingsOn') {
  249. savedOptions.get().timings = true;
  250. } else if (anchor == '#ikaScriptToolsTimingsOff') {
  251. savedOptions.get().timings = false;
  252. } else if (anchor == '#ikaScriptToolsProfilesOn') {
  253. savedOptions.get().profile = true;
  254. } else if (anchor == '#ikaScriptToolsProfilesOff') {
  255. savedOptions.get().profile = false;
  256. }
  257. savedOptions.saveAsync();
  258. }
  259. setOptions(savedOptions.get());
  260. }
  261. return {
  262. debug: debug,
  263. debugException: debugException,
  264. debuggable: debuggable,
  265. time: time,
  266. group: group,
  267. setAndSaveOptionsFromPageAnchor: setAndSaveOptionsFromPageAnchor,
  268. getExceptionLog: getExceptionLog,
  269. };
  270. })();
  271. /**
  272. * Random utils that don't belong anywhere else.
  273. */
  274. var Utils = function() {
  275. function thunk(func) {
  276. var computed = false;
  277. var value;
  278. function thunker() {
  279. if (!computed) {
  280. value = func();
  281. computed = true;
  282. }
  283. return value;
  284. }
  285. return thunker;
  286. }
  287. function resettable(func) {
  288. var value = func();
  289. function getValue() {
  290. return value;
  291. }
  292. getValue.reset = function() {
  293. var ret = value;
  294. value = func();
  295. return ret;
  296. }
  297. getValue.set = function(v) {
  298. value = v;
  299. }
  300. return getValue;
  301. }
  302. function fixedFunction(value) {
  303. return function() {
  304. return value;
  305. }
  306. }
  307. var nextId = function() {
  308. var id = 10000;
  309. function nextId(prefix) {
  310. id++;
  311. if (prefix) {
  312. return prefix + id.toString(16);
  313. } else {
  314. return id;
  315. }
  316. };
  317. return nextId;
  318. }();
  319. var nextIntegerId = function() {
  320. var id = 100000;
  321. return function nextIntegerId() {
  322. return id++;
  323. };
  324. }();
  325. function EventDispatcher(name) {
  326. this.name = name;
  327. this.listeners = [];
  328. }
  329. $.extend(EventDispatcher.prototype, {
  330. addListener: function addListener(l) {
  331. var listener = Logging.debuggable(
  332. {
  333. label: 'EventDispatcher[' + this.name + '].ListenerWrapper',
  334. swallowException: true,
  335. },
  336. function() {
  337. l.apply(null, arguments)
  338. });
  339. this.listeners.push(listener);
  340. return {
  341. cancel: this._cancelListener.bind(this, listener),
  342. };
  343. },
  344. _cancelListener: function(listener) {
  345. for (var i = 0, len = this.listeners.length; i < len; i++) {
  346. if (this.listeners[i] === listener) {
  347. this.listeners.splice(i, 1);
  348. return;
  349. }
  350. }
  351. },
  352. /*bindEventArgs: function bindEventArgs() {
  353. var that = this;
  354. var args = Array.prototype.slice.call(arguments);
  355. return {
  356. bindEventArgs: function boundBindEventArgs() {
  357. return that.bindEventArgs.apply(that,
  358. args.concat(Array.prototype.slice.call(arguments)));
  359. },
  360. send: function boundSend() {
  361. that.send.apply(that, args.concat(Array.prototype.slice.call(arguments)));
  362. },
  363. scheduleSend: function boundScheduleSend(name, delay, callback) {
  364. return that.scheduleSend.apply(that,
  365. [name, delay, callback].concat(args).concat(Array.prototype.slice.call(arguments, 3)));
  366. },
  367. toJSON: function toJSON() {
  368. return undefined;
  369. },
  370. };
  371. },*/
  372. send: function send() {
  373. var listeners = this.listeners.slice();
  374. for (var i = 0, len = listeners.length; i < len; i++) {
  375. listeners[i].apply(null, arguments);
  376. }
  377. },
  378. scheduleSend: function(name, delay, callback) {
  379. var that = this;
  380. var sendArgs = [];
  381. for (var i = 3; i < arguments.length; i++) {
  382. sendArgs.push(arguments[i]);
  383. }
  384. return clearTimeout.bind(null, setTimeout(
  385. function scheduledSend() {
  386. callback();
  387. that.send.apply(that, sendArgs);
  388. },
  389. Math.max(delay, 10)));
  390. },
  391. /*startIntervalSend: function startIntervalSend(name, initialDelay, interval) {
  392. this.cancelIntervalSend();
  393. var sendCall = this.send.bind(this);
  394. var sendArgs = [];
  395. for (var i = 2; i < arguments.length; i++) {
  396. sendArgs.push(arguments[i]);
  397. }
  398. this.cancelInterval = clearInterval.bind(null, setInterval(
  399. IkaTools.Logging.debuggable(
  400. 'IkaTools.Utils.EventDispatcher.intervalSend[' + name + ']',
  401. function() {
  402. sendCall.apply(null, sendArgs);
  403. }),
  404. interval));
  405. },
  406. cancelIntervalSend: function cancelIntevalSend() {
  407. if (this.cancelInterval) {
  408. this.cancelInterval();
  409. }
  410. },*/
  411. toJSON: function toJSON() {
  412. return undefined;
  413. },
  414. });
  415. function getVersion() {
  416. var parts = $.map(
  417. $('#GF_toolbar li.version span').text().match(/[0-9]+/g),
  418. function(x) {
  419. return parseInt(x);
  420. }).concat([0,0,0,0,0,0,0]);
  421. return {
  422. greaterThanOrEqual: function() {
  423. for (var i = 0; i < arguments.length; i++) {
  424. if (parts[i] != arguments[i]) {
  425. return parts[i] >= arguments[i];
  426. }
  427. }
  428. return true;
  429. },
  430. lessThan: function() {
  431. for (var i = 0; i < arguments.length; i++) {
  432. if (parts[i] != arguments[i]) {
  433. return parts[i] < arguments[i];
  434. }
  435. }
  436. return false;
  437. },
  438. };
  439. }
  440. function isChrome() {
  441. return navigator.vendor.match(/Google/) || navigator.userAgent.match(/Chrome/);
  442. }
  443. function iterateIkariamAjaxResponse(response, f) {
  444. $.each(response, function iterateIkariamAjaxResponseItem(index, item) {
  445. f(index, item[0], item[1]);
  446. });
  447. }
  448. function forEachIkariamAjaxResponseFunction(f) {
  449. return function forEachIkariamResponse(response) {
  450. iterateIkariamAjaxResponse(response, f);
  451. }
  452. }
  453. function backgroundFetchPage(url, callback, options) {
  454. options = $.extend({method: 'GET', data: ''}, options);
  455. var headers = {
  456. 'User-agent': navigator.userAgent,
  457. 'Cookie': document.cookie,
  458. 'Referer': 'http://' + document.domain + '/index.php',
  459. };
  460. if(options.method == 'POST') {
  461. headers['Content-type'] = 'application/x-www-form-urlencoded';
  462. }
  463. setTimeout(function() {
  464. GM_xmlhttpRequest ({
  465. method: options.method,
  466. url: url,
  467. data: options.data,
  468. headers: headers,
  469. onload: Logging.debuggable('IkaTools.Utils.backgroundGetIkariamPage[' + url + ']', callback)
  470. });
  471. }, 0);
  472. }
  473. function backgroundFetchIkariamAjaxPage(url, callback, options) {
  474. backgroundFetchPage(url, function(response) {
  475. callback(JSON.parse(response.responseText));
  476. }, options);
  477. }
  478. var jsonResponseRegex = /ikariam.getClass\(ajax.Responder, (.*?)\);$/m;
  479. function backgroundFetchIkariamFullPage(url, callback, options) {
  480. backgroundFetchPage(url, function(response) {
  481. var match = jsonResponseRegex.exec(response.responseText);
  482. jsonResponse = [];
  483. if (match) {
  484. jsonResponse = JSON.parse(match[1]);
  485. }
  486. callback(response, jsonResponse);
  487. }, options);
  488. }
  489. var urlParamsRegex = /\?([^\#]*)(#|$)/;
  490. function parseUrlParams(url) {
  491. var paramsList = url.match(urlParamsRegex)[1].split('&');
  492. var params = {};
  493. $.each(paramsList, function(index, item) {
  494. var paramParts = item.split('=');
  495. if (paramParts.length == 2) {
  496. params[paramParts[0]] = decodeURIComponent(paramParts[1]);
  497. }
  498. });
  499. return params;
  500. }
  501. var timestampRegex = /(\d+)\.(\d+)\.(\d+)\s+(\d+):(\d+):(\d+)/i;
  502. function parseIkariamTimestamp(timestamp){
  503. var d = new Date();
  504. //Get the local GMT offset in hours
  505. //Attempt to match the constituent parts of the timestamp
  506. var match = timestamp.match(timestampRegex);
  507. if (match){
  508. d.setTime(0);
  509. d.setDate(parseInt(match[1], 10));
  510. d.setMonth(parseInt(match[2], 10)-1);
  511. d.setFullYear(parseInt(match[3], 10));
  512. d.setHours(parseInt(match[4], 10));
  513. d.setMinutes(parseInt(match[5], 10));
  514. d.setSeconds(parseInt(match[6], 10));
  515. //Adjust the time to get its TZ correct
  516. d.setTime(d.getTime() - d.getTimezoneOffset() * Constants.Time.MILLIS_PER_MINUTE -
  517. Constants.Time.MILLIS_PER_HOUR); // Server time is german = GMT+1
  518. }
  519. return d;
  520. }
  521. return {
  522. thunk: thunk,
  523. resettable: resettable,
  524. fixedFunction: fixedFunction,
  525. EventDispatcher: EventDispatcher,
  526. nextId: nextId,
  527. nextIntegerId: nextIntegerId,
  528. getVersion: thunk(getVersion),
  529. isChrome: thunk(isChrome),
  530. iterateIkariamAjaxResponse: iterateIkariamAjaxResponse,
  531. forEachIkariamAjaxResponseFunction: forEachIkariamAjaxResponseFunction,
  532. backgroundFetchIkariamAjaxPage: backgroundFetchIkariamAjaxPage,
  533. backgroundFetchIkariamFullPage: backgroundFetchIkariamFullPage,
  534. parseUrlParams: parseUrlParams,
  535. parseIkariamTimestamp: parseIkariamTimestamp,
  536. };
  537. }();
  538. /**
  539. * Internationalization and localization routines.
  540. */
  541. var Intl = function() {
  542. function Localizer(allLanguages) {
  543. this.allLanguages = allLanguages;
  544. this.languages = ['en'];
  545. }
  546. $.extend(Localizer.prototype, {
  547. localize: function localize(identifier) {
  548. var identifierParts = identifier.split('.');
  549. if (arguments.length > 1) {
  550. identifierParts = $.makeArray(arguments);
  551. }
  552. for (var i = 0; i < this.languages.length; i++) {
  553. var languageConfig = this.allLanguages[this.languages[i]];
  554. if (languageConfig !== undefined) {
  555. var translation = this.find(languageConfig, identifierParts);
  556. if (translation !== undefined) {
  557. return translation;
  558. }
  559. }
  560. }
  561. return '?#!' + identifierParts.join(',') + '!#?';
  562. },
  563. delayedLocalize: function delayedLocalize() {
  564. var args = arguments;
  565. var t = this;
  566. return function doLocalize() {
  567. return t.localize.apply(t, args);
  568. };
  569. },
  570. find: function find(config, parts) {
  571. for (var i = 0; i < parts.length; i++) {
  572. config = config[parts[i]];
  573. if (config === undefined) {
  574. return undefined;
  575. }
  576. }
  577. return config;
  578. },
  579. setPreferredLanguage: function setPreferredLanguage(language) {
  580. this.languages.unshift(language);
  581. },
  582. });
  583. var baseLocalizer = new Localizer({
  584. en: {
  585. formatting: {
  586. "thousandsSeparator": ",",
  587. "unknown": "?",
  588. hourFormatAMPM: true,
  589. },
  590. timeunits: {
  591. long: {
  592. day: 'Day',
  593. hour: 'Hour',
  594. week: 'Week',
  595. },
  596. short: {
  597. day: 'D',
  598. hour: 'h',
  599. minute: 'm',
  600. second: 's',
  601. },
  602. complete: '-',
  603. yesterday: 'yesterday',
  604. tomorrow: 'tomorrow',
  605. today: 'today',
  606. am: 'AM',
  607. pm: 'PM',
  608. },
  609. settings: {
  610. script_settings: 'Script Options',
  611. settings: 'Settings',
  612. save: 'Save',
  613. },
  614. },
  615. de: {
  616. formatting: {
  617. "thousandsSeparator": ".",
  618. "unknown": "?",
  619. hourFormatAMPM: false,
  620. },
  621. timeunits: {
  622. long: {
  623. day: 'Tag',
  624. hour: 'Stunde.',
  625. week: 'Woche',
  626. },
  627. short: {
  628. day: 'T',
  629. hour: 'h',
  630. minute: 'm',
  631. second: 's',
  632. },
  633. complete: '-',
  634. yesterday: 'gestern',
  635. tomorrow: 'morgen',
  636. today: 'heute',
  637. am: 'AM',
  638. pm: 'PM',
  639. },
  640. settings: {
  641. script_settings: 'Script Optionen',
  642. settings: 'Einstellungen',
  643. save: 'speichern',
  644. },
  645. },
  646. fr: {
  647. formatting: {
  648. "thousandsSeparator": ".",
  649. "unknown": "?",
  650. hourFormatAMPM: false,
  651. },
  652. timeunits: {
  653. long: {
  654. day: 'Jour',
  655. hour: 'Heure',
  656. week: 'Semaine',
  657. },
  658. short: {
  659. day: 'J',
  660. hour: 'h',
  661. minute: 'm',
  662. second: 's',
  663. },
  664. complete: '-',
  665. yesterday: 'hier',
  666. tomorrow: 'demain',
  667. today: 'aujourd\'hui',
  668. am: 'AM',
  669. pm: 'PM',
  670. },
  671. settings: {
  672. script_settings: 'Script Optionen',
  673. settings: 'Einstellungen',
  674. save: 'speichern',
  675. },
  676. },
  677. hu: {
  678. formatting: {
  679. "thousandsSeparator": ",",
  680. "unknown": "?",
  681. hourFormatAMPM: true,
  682. },
  683. timeunits: {
  684. long: {
  685. day: 'Nap',
  686. hour: 'Óra',
  687. week: 'Hét',
  688. },
  689. short: {
  690. day: 'n',
  691. hour: 'ó',
  692. minute: 'p',
  693. second: 'mp',
  694. },
  695. complete: '-',
  696. yesterday: 'tegnap',
  697. tomorrow: 'holnap',
  698. today: 'ma',
  699. am: 'Délelőtt',
  700. pm: 'Délután',
  701. },
  702. settings: {
  703. script_settings: 'Script Beállítások',
  704. settings: 'Beállítások',
  705. save: 'Mentés',
  706. },
  707. },
  708. pl: {
  709. formatting: {
  710. "thousandsSeparator": ",",
  711. "unknown": "?",
  712. hourFormatAMPM: true,
  713. },
  714. timeunits: {
  715. long: {
  716. day: 'Dzien',
  717. hour: 'Godzina',
  718. week: 'Tydzien',
  719. },
  720. short: {
  721. day: 'D',
  722. hour: 'h',
  723. minute: 'm',
  724. second: 's',
  725. },
  726. complete: '-',
  727. yesterday: 'wczoraj',
  728. tomorrow: 'jutro',
  729. today: 'dzis',
  730. am: 'AM',
  731. pm: 'PM',
  732. },
  733. settings: {
  734. script_settings: 'Opcje Skryptu',
  735. settings: 'Ustawienia',
  736. save: 'Zapisz',
  737. },
  738. },
  739. ro: {
  740. formatting: {
  741. "thousandsSeparator": ",",
  742. "unknown": "?",
  743. hourFormatAMPM: false,
  744. },
  745. timeunits: {
  746. long: {
  747. day: 'Zi',
  748. hour: 'Ora',
  749. week: 'Saptamana',
  750. },
  751. short: {
  752. day: 'D',
  753. hour: 'h',
  754. minute: 'm',
  755. second: 's',
  756. },
  757. complete: '-',
  758. yesterday: 'ieri',
  759. tomorrow: 'maine',
  760. today: 'azi',
  761. am: 'AM',
  762. pm: 'PM',
  763. },
  764. settings: {
  765. script_settings: 'Optiuni Script',
  766. settings: 'Setari',
  767. save: 'Salveaza',
  768. },
  769. },
  770. tr: {
  771. formatting: {
  772. "thousandsSeparator": ",",
  773. "unknown": "?",
  774. hourFormatAMPM: false,
  775. },
  776. timeunits: {
  777. long: {
  778. day: 'Gün',
  779. hour: 'Saat',
  780. week: 'Hafta',
  781. },
  782. short: {
  783. day: 'Gn',
  784. hour: 'Sa',
  785. minute: 'Dk',
  786. second: 'Sn',
  787. },
  788. complete: '-',
  789. yesterday: 'dün',
  790. tomorrow: 'yarın',
  791. today: 'bugün',
  792. am: 'AM',
  793. pm: 'PM',
  794. },
  795. settings: {
  796. script_settings: 'Script Seçenekleri',
  797. settings: 'Ayarlar',
  798. save: 'Kaydet',
  799. },
  800. },
  801. });
  802. function formatInteger(number, forceShowSign) {
  803. if (number === undefined || isNaN(number)) {
  804. return baseLocalizer.localize('formatting.unknown');
  805. }
  806. number = Math.floor(number);
  807. var separator = baseLocalizer.localize('formatting.thousandsSeparator');
  808. var s = number.toString();
  809. var sign = forceShowSign ? '+' : '';
  810. if (number === 0) {
  811. sign = '';
  812. } else if (number < 0) {
  813. sign = '-';
  814. s = s.substring(1);
  815. }
  816. var i = s.length - 3;
  817. while (i > 0) {
  818. s = s.substring(0, i) + separator + s.substring(i);
  819. i -= 3;
  820. }
  821. return sign + s;
  822. };
  823. function formatDecimal(number, digits, forceShowSign) {
  824. if (number === undefined || isNaN(number)) {
  825. return baseLocalizer.localize('formatting.unknown');
  826. }
  827. var value = number.toFixed(digits);
  828. if (forceShowSign && number >= 0) { // .5 * Math.pow(10, -digits)) {
  829. value = '+' + value;
  830. }
  831. return value;
  832. }
  833. function formatRemainingTime(millis, complete, maxResolution) {
  834. maxResolution = maxResolution || 4;
  835. if (millis == Number.POSITIVE_INFINITY) {
  836. return '&infin;';
  837. }
  838. var seconds = Math.ceil(millis / Constants.Time.MILLIS_PER_SECOND);
  839. if (seconds <= 0) {
  840. return complete || baseLocalizer.localize('timeunits','complete');
  841. }
  842. var parts = [];
  843. if (maxResolution >= 1) {
  844. parts.push({value: Math.floor((seconds / 60 / 60 / 24)), label: 'day'});
  845. }
  846. if (maxResolution >= 2) {
  847. parts.push({value: Math.floor((seconds / 60/ 60) % 24), label: 'hour' });
  848. }
  849. if (maxResolution >= 3) {
  850. parts.push({value: Math.floor((seconds / 60) % 60), label: 'minute' });
  851. }
  852. if (maxResolution >= 4) {
  853. parts.push({value: Math.floor((seconds) % 60), label: 'second' });
  854. }
  855. while (parts[0] && !parts[0].value) {
  856. parts.shift();
  857. }
  858. parts.splice(2);
  859. return $.map(parts, function(part) {
  860. return '%s%s'.format(part.value,
  861. baseLocalizer.localize('timeunits','short',part.label));
  862. }).join(' ');
  863. }
  864. function padLeft(s, length, char) {
  865. while (s.length < length) {
  866. s = char + s;
  867. }
  868. return s;
  869. }
  870. function formatAbsoluteTime(date) {
  871. var now = new Date();
  872. var dateDay = Math.floor(date.valueOf() / Constants.Time.MILLIS_PER_DAY -
  873. date.getTimezoneOffset() / Constants.Time.MINUTES_PER_DAY);
  874. var nowDay = Math.floor(now.valueOf() / Constants.Time.MILLIS_PER_DAY -
  875. now.getTimezoneOffset() / Constants.Time.MINUTES_PER_DAY);
  876. var dayString = '';
  877. if (dateDay == nowDay + 1) {
  878. dayString = baseLocalizer.localize('timeunits','tomorrow');
  879. } else if (dateDay == nowDay - 1) {
  880. dayString = baseLocalizer.localize('timeunits','yesterday');
  881. } else if (dateDay == nowDay) {
  882. dayString = baseLocalizer.localize('timeunits','today');
  883. } else {
  884. dayString = date.toLocaleDateString();
  885. }
  886. var timeString = '';
  887. if (baseLocalizer.localize('formatting', 'hourFormatAMPM')) {
  888. var m = '';
  889. if (date.getHours() == 0) {
  890. timeString = '12';
  891. m = baseLocalizer.localize('timeunits','am');
  892. } else if (date.getHours() == 12) {
  893. timeString = '12';
  894. m = baseLocalizer.localize('timeunits','pm');
  895. } else if (date.getHours() > 12) {
  896. timeString = (date.getHours() - 12).toString();
  897. m = baseLocalizer.localize('timeunits','pm');
  898. } else {
  899. timeString = date.getHours().toString();
  900. m = baseLocalizer.localize('timeunits','am');
  901. }
  902. timeString = timeString + ':' +
  903. padLeft(date.getMinutes().toString(), 2, 0) + ' ' + m;
  904. } else {
  905. timeString = date.getHours().toString() + ':' +
  906. padLeft(date.getMinutes().toString(), 2, '0');
  907. }
  908. return dayString + ' ' + timeString;
  909. }
  910. return {
  911. Localizer: Localizer,
  912. localizer: baseLocalizer,
  913. formatInteger: formatInteger,
  914. formatDecimal: formatDecimal,
  915. formatRemainingTime: formatRemainingTime,
  916. formatAbsoluteTime: formatAbsoluteTime,
  917. };
  918. }();
  919. var View = function() {
  920. function getDomain() {
  921. return document.domain;
  922. }
  923. function getCurrentCityId() {
  924. var relatedCityData = unsafeWindow.ikariam.model.relatedCityData;
  925. return relatedCityData[relatedCityData.selectedCity].id;
  926. }
  927. function getCurrentCity() {
  928. return EmpireData.getCity(getCurrentCityId());
  929. }
  930. function isActiveCity(city) {
  931. return city.getId() == getCurrentCityId();
  932. }
  933. function getCurrentIslandId() {
  934. // This is available in javascript data in island and city views (unfortunately at
  935. // different locations). It does not appear to be anywhere in world view data.
  936. return parseInt($("#js_islandBread").attr("href").match(/islandId=(\d+)/)[1]);
  937. };
  938. function getViewType() {
  939. return unsafeWindow.ikariam.backgroundView.id;
  940. }
  941. function viewIsIsland() {
  942. return getViewType() == 'island';
  943. }
  944. function viewIsCity() {
  945. return getViewType() == 'city';
  946. }
  947. function getIkariamBaseViewParams() {
  948. var mainboxX = unsafeWindow.ikariam.mainbox_x;
  949. var mainboxY = unsafeWindow.ikariam.mainbox_y;
  950. var mainboxZ = unsafeWindow.ikariam.mainbox_z;
  951. if (mainboxX || mainboxY || mainboxZ) {
  952. return {
  953. mainbox_x: mainboxX,
  954. mainbox_y: mainboxY,
  955. mainbox_z: mainboxZ,
  956. };
  957. }
  958. return {};
  959. }
  960. function makeFullIkariamUrl(params, anchor) {
  961. return 'http://' + getDomain() + '/index.php?' +
  962. $.map(params, function(value, key) {
  963. return encodeURIComponent(key) + '=' +
  964. encodeURIComponent(value);
  965. }).join('&') +
  966. (anchor ? '#' + anchor : '');
  967. }
  968. function makeLocalIkariamUrl(params) {
  969. return '?' + $.map(params, function(value, key) { return key + '=' + value; }).join('&');
  970. }
  971. function loadLocalIkariamUrl(url) {
  972. Logging.debug("loadLocalIkariamUrl: ", url);
  973. // This is an odd way to make the ajaxHandlerCall rather than just calling it directly.
  974. // It is done this way so that in Chrome the actions run when the response is recieved
  975. // are run in the page's javascript context instead of the javascript context of the
  976. // TamperMonkey extension. This is necessary to properly evaluate script actions in
  977. // returned ikariam pages or stuff like transport sliders simply will not work.
  978. document.location = 'javascript:ajaxHandlerCall(' + JSON.stringify(url) + '); void(0);';
  979. }
  980. function goToIkariamPage(city, mainView, mainParams, view, viewParams, anchor) {
  981. var changeParams = {
  982. // Whacked up logic I don't really understand that makes transitioning to
  983. // island mine/mill pages work. Yes, it's completely incomprehensible how Ikariam
  984. // developers could screw this up, but somehow they can make the mill go to the mine
  985. // if you say the old view is island when you want to go to an island page.
  986. // Truly incredible!
  987. oldView: mainView == 'island' ? getViewType() : mainView,
  988. action: 'header',
  989. function: 'changeCurrentCity',
  990. cityId: city.getId(),
  991. actionRequest: unsafeWindow.ikariam.model.actionRequest,
  992. };
  993. $.extend(changeParams, getIkariamBaseViewParams(), mainParams);
  994. if (view) {
  995. $.extend(changeParams, viewParams);
  996. changeParams.templateView = view;
  997. }
  998. $.extend(changeParams, { backgroundView: mainView } );
  999. if (mainView == 'island' && view && !anchor) {
  1000. // Stupid ikariam developers still include the city preselect when we ask for a
  1001. // specific view. Which will overwrite whatever view (mine/mill/wonder) we
  1002. // actually want to see. Set this hack to suppress that transition when the page
  1003. // loads.
  1004. anchor = 'ikaScriptToolsSuppressCityPreselect';
  1005. }
  1006. if (getViewType() == mainView) {
  1007. loadLocalIkariamUrl(makeLocalIkariamUrl(changeParams));
  1008. return;
  1009. }
  1010. var url = makeFullIkariamUrl(changeParams, anchor);
  1011. Logging.debug('goToIkariamPage: ', url);
  1012. window.location.assign(url);
  1013. }
  1014. function goToLocalView(view, params) {
  1015. loadLocalIkariamUrl(makeLocalIkariamUrl($.extend({view: view}, params)));
  1016. }
  1017. function goToCitysIslandView(city, view, params) {
  1018. if (isActiveCity(city) && viewIsIsland() &&
  1019. getCurrentIslandId() == city.getIslandId()) {
  1020. if (view) {
  1021. loadLocalIkariamUrl(makeLocalIkariamUrl($.extend({view: view}, params)));
  1022. }
  1023. } else if (viewIsIsland()) {
  1024. goToIkariamPage(city, 'island', null, view, params);
  1025. } else {
  1026. goToIkariamPage(city, 'island', null, null, null,
  1027. makeIkariamLoadLocalPageAnchor($.extend({view: view}, params)));
  1028. }
  1029. }
  1030. function goToIslandView(city, islandId, view, params) {
  1031. if (isActiveCity(city) && viewIsIsland() &&
  1032. getCurrentIslandId() == islandId) {
  1033. if (view) {
  1034. loadLocalIkariamUrl(makeLocalIkariamUrl($.extend({view: view}, params)));
  1035. }
  1036. } else {
  1037. goToIkariamPage(city, 'island', { currentIslandId: islandId }, view, params);
  1038. }
  1039. }
  1040. function goToIkariamFullPage(params, anchor) {
  1041. url = makeFullIkariamUrl(params, anchor);
  1042. Logging.debug('goToIkariamFullPage: ', url);
  1043. window.location.replace(url);
  1044. }
  1045. function makeIkariamLoadLocalPageAnchor(params, doNotSuppressFirstCityInfo) {
  1046. if (doNotSuppressFirstCityInfo) {
  1047. return 'ikaScriptToolsLoadLocalIkariamUrl_DoNotSuppressFirstCityInfo=' +
  1048. encodeURIComponent(makeLocalIkariamUrl(params));
  1049. } else {
  1050. return 'ikaScriptToolsLoadLocalIkariamUrl=' +
  1051. encodeURIComponent(makeLocalIkariamUrl(params));
  1052. }
  1053. }
  1054. function goToCityView(city, view, params) {
  1055. if (isActiveCity(city) && viewIsCity()) {
  1056. if (view) {
  1057. loadLocalIkariamUrl(makeLocalIkariamUrl($.extend({view: view}, params)));
  1058. }
  1059. } else {
  1060. goToIkariamPage(city, 'city', null, view, params);
  1061. }
  1062. }
  1063. function activateCity(city) {
  1064. if (!isActiveCity(city)) {
  1065. goToIkariamPage(city, getViewType());
  1066. }
  1067. }
  1068. var suppressingChangeView = false;
  1069. var superSuppressChangeView = Utils.resettable(Utils.fixedFunction(false));
  1070. var initialPageAjaxResponse = Utils.thunk(function findInitialPageAjaxResponse() {
  1071. var regex = /ikariam.getClass\(ajax.Responder, (.*)\);/;
  1072. var response = [];
  1073. $('script').each(function findInitialPageAjaxResponse(index, script) {
  1074. var match = regex.exec(script.innerHTML);
  1075. if (match) {
  1076. response = JSON.parse(match[1]);
  1077. }
  1078. });
  1079. return response;
  1080. });
  1081. unsafeWindow.ajax.Responder.changeView = function(changeView) {
  1082. return Logging.debuggable(
  1083. 'IkaTools.View.changeViewReplacement',
  1084. function customChangeView(params) {
  1085. if (suppressingChangeView && suppressingChangeView == params[0]) {
  1086. Logging.debug("Suppressing change to view: ", params[0]);
  1087. } else if (superSuppressChangeView() == params[0]) {
  1088. superSuppressChangeView.reset();
  1089. Logging.debug("Super suppressing change to view", params[0]);
  1090. } else {
  1091. changeView.apply(this, arguments);
  1092. }
  1093. });
  1094. }(unsafeWindow.ajax.Responder.changeView);
  1095. var ajaxResponseEvent = new Utils.EventDispatcher();
  1096. function registerIkariamAjaxResponseCallback(f, fireInitialPageView) {
  1097. var canceller = ajaxResponseEvent.addListener(f);
  1098. if (fireInitialPageView) {
  1099. f(initialPageAjaxResponse());
  1100. }
  1101. return canceller;
  1102. }
  1103. var suppressNextAjaxChangeView = Utils.resettable(Utils.fixedFunction(null));
  1104. function suppressChangeViewOfNextAjaxResponse(type) {
  1105. suppressNextAjaxChangeView.set(type);
  1106. }
  1107. function suppressFirstChangeViewOfType(type) {
  1108. superSuppressChangeView.set(type);
  1109. }
  1110. var nextAjaxResponseEvent =
  1111. Utils.resettable(function() { return new Utils.EventDispatcher(); });
  1112. function registerNextIkariamAjaxRequestCallback(f) {
  1113. return nextAjaxResponseEvent().addListener(f);
  1114. }
  1115. function replaceExecuteAjaxRequest(executeAjaxRequest) {
  1116. return function customExecuteAjaxRequest() {
  1117. var ajaxEvent = nextAjaxResponseEvent.reset();
  1118. var suppressChangeView = suppressNextAjaxChangeView.reset();
  1119. var args = $.makeArray(arguments);
  1120. args.push(undefined);
  1121. if (!args[1]) {
  1122. args[1] = function customAjaxCallback(responseText) {
  1123. suppressingChangeView = suppressChangeView;
  1124. var responder = unsafeWindow.ikariam.getClass(
  1125. unsafeWindow.ajax.Responder, responseText);
  1126. unsafeWindow.ikariam.controller.ajaxResponder = responder;
  1127. suppressingChangeView = null;
  1128. ajaxResponseEvent.send(responder.responseArray);
  1129. ajaxEvent.send(responder.responseArray);
  1130. }
  1131. args[1].isScriptInterceptor = true;
  1132. } else if (args[1].isScriptInterceptor) {
  1133. // Allows multiple instances of this script to work
  1134. var func = args[1];
  1135. args[1] = function customAjaxCallbackWrapper() {
  1136. suppressingChangeView = suppressChangeView;
  1137. func.apply(this, arguments);
  1138. suppressingChangeView = null;
  1139. var responseArray = unsafeWindow.ikariam.controller.ajaxResponder.responseArray;
  1140. ajaxResponseEvent.send(responseArray);
  1141. ajaxEvent.send(responseArray);
  1142. }
  1143. }
  1144. var ret = executeAjaxRequest.apply(this, args);
  1145. };
  1146. }
  1147. if (unsafeWindow.ikariam.controller) {
  1148. unsafeWindow.ikariam.controller.executeAjaxRequest =
  1149. replaceExecuteAjaxRequest(unsafeWindow.ikariam.controller.executeAjaxRequest);
  1150. } else {
  1151. unsafeWindow.ikariam.Controller.executeAjaxRequest =
  1152. replaceExecuteAjaxRequest(unsafeWindow.ikariam.Controller.executeAjaxRequest);
  1153. }
  1154. var ajaxFormEvent = new Utils.EventDispatcher();
  1155. unsafeWindow.ajaxHandlerCallFromForm = function(ajaxHandlerCallFromForm) {
  1156. return function customerAjaxHandlerCallFromForm(form) {
  1157. ajaxFormEvent.send(form);
  1158. return ajaxHandlerCallFromForm.apply(this, arguments);
  1159. };
  1160. }(unsafeWindow.ajaxHandlerCallFromForm);
  1161. function registerAjaxFormSubmitCallback(f) {
  1162. return ajaxFormEvent.addListener(f);
  1163. }
  1164. var gameTimeDifference = 0;
  1165. function setGameTimeDifference(value) {
  1166. Logging.debug("Game time difference: ", value);
  1167. gameTimeDifference = value;
  1168. }
  1169. function getGameTimeDifference() {
  1170. return gameTimeDifference;
  1171. }
  1172. function gameTimeNow() {
  1173. return new Date().getTime() - gameTimeDifference;
  1174. }
  1175. return {
  1176. getDomain: getDomain,
  1177. getCurrentCityId: getCurrentCityId,
  1178. getCurrentCity: getCurrentCity,
  1179. isActiveCity: isActiveCity,
  1180. getCurrentIslandId: getCurrentIslandId,
  1181. getViewType: getViewType,
  1182. viewIsIsland: viewIsIsland,
  1183. viewIsCity: viewIsCity,
  1184. goToCitysIslandView: goToCitysIslandView,
  1185. goToCityView: goToCityView,
  1186. goToIslandView: goToIslandView,
  1187. goToLocalView: goToLocalView,
  1188. activateCity: activateCity,
  1189. goToIkariamFullPage: goToIkariamFullPage,
  1190. makeIkariamLoadLocalPageAnchor: makeIkariamLoadLocalPageAnchor,
  1191. //registerViewChangedListener: registerViewChangedListener,
  1192. suppressChangeViewOfNextAjaxResponse: suppressChangeViewOfNextAjaxResponse,
  1193. suppressFirstChangeViewOfType: suppressFirstChangeViewOfType,
  1194. registerNextIkariamAjaxRequestCallback: registerNextIkariamAjaxRequestCallback,
  1195. registerIkariamAjaxResponseCallback: registerIkariamAjaxResponseCallback,
  1196. registerAjaxFormSubmitCallback: registerAjaxFormSubmitCallback,
  1197. loadLocalIkariamUrl: loadLocalIkariamUrl,
  1198. setGameTimeDifference: setGameTimeDifference,
  1199. getGameTimeDifference: getGameTimeDifference,
  1200. gameTimeNow: gameTimeNow,
  1201. };
  1202. }();
  1203. /**
  1204. * Data value class for encapsulating GM_getValue/GM_setValue access and
  1205. * serialization/deserialization.
  1206. */
  1207. var Data = function() {
  1208. function Value(key, defaultValue, options) {
  1209. this.options = $.extend({ useDomain: true, loadCallback: function() {} }, options);
  1210. if (this.options.useDomain) {
  1211. this.key = View.getDomain() + "/" + key + "/" +
  1212. (options.version || ikaToolsVersion) + '-' + ikaToolsVersion;
  1213. } else {
  1214. this.key = key + "/" + ikaToolsVersion;
  1215. }
  1216. this.defaultValue = defaultValue;
  1217. this.data = defaultValue;
  1218. this.needsSave = false;
  1219. }
  1220. $.extend(Value.prototype, {
  1221. load: function load() {
  1222. var rawValue = GM_getValue(this.key, "null");
  1223. if (rawValue !== undefined) {
  1224. var data = JSON.parse(rawValue, this.options.reviver);
  1225. Logging.debug('Loaded data "%s": %s -> %o', this.key, rawValue, data);
  1226. if (data !== null) {
  1227. this.data = data;
  1228. }
  1229. } else {
  1230. }
  1231. this.loaded = true;
  1232. this.options.loadCallback(this.data);
  1233. return this.data;
  1234. },
  1235. save: function save() {
  1236. return this.doSave(true);
  1237. },
  1238. doSave: function doSave(force) {
  1239. if (this.needsSave || force) {
  1240. var value = JSON.stringify(this.data, this.options.stringifier);
  1241. Logging.debug('Saved data "%s": %o -> %s', this.key, this.data, value);
  1242. GM_setValue(this.key, value);
  1243. this.needsSave = false;
  1244. }
  1245. return this.data;
  1246. },
  1247. saveAsync: function saveAsync() {
  1248. this.needsSave = true;
  1249. setTimeout(Logging.debuggable('IkaTools.Data.Value[' + this.key + ']',
  1250. this.doSave.bind(this, false)), 0);
  1251. },
  1252. get: function get() {
  1253. if (!this.loaded) {
  1254. var value = this.load();
  1255. return value;
  1256. }
  1257. return this.data;
  1258. },
  1259. set: function set(data) {
  1260. this.data = data;
  1261. return data;
  1262. },
  1263. reset: function reset() {
  1264. this.set(this.defaultValue);
  1265. this.save();
  1266. },
  1267. });
  1268. return {
  1269. Value: Value,
  1270. };
  1271. }();
  1272. var UI = function() {
  1273. function ToolTipHandler(toolTipClass, toolTipContainer, options) {
  1274. this.toolTips = {};
  1275. this.options = $.extend({
  1276. delay: 200,
  1277. activeClass: 'active',
  1278. offsetX: 0,
  1279. offsetY: 20,
  1280. toolTipClass: toolTipClass,
  1281. toolTipContainer: toolTipContainer || $('<div/>'),
  1282. }, options);
  1283. this.toolTipContainer =
  1284. $('<div style="position: absolute; z-index: 100000; display:none;"/>');
  1285. this.toolTipContainer.append(this.options.toolTipContainer);
  1286. this.activeToolTipElement = null;
  1287. this.pendingShowEvent = null;
  1288. this.activeToolTip = null;
  1289. var body = $('body');
  1290. body.append(this.toolTipContainer);
  1291. }
  1292. $.extend(ToolTipHandler.prototype, {
  1293. _getCurrentToolTip: function _getCurrentToolTip() {
  1294. if (this.activeToolTipElement) {
  1295. var id = this.activeToolTipElement.id;
  1296. if (id) {
  1297. return this.toolTips[id];
  1298. }
  1299. }
  1300. },
  1301. _reset: function _reset() {
  1302. clearTimeout(this.pendingShowEvent);
  1303. this.toolTipContainer.hide();
  1304. var toolTipInfo = this._getCurrentToolTip();
  1305. if (this.activeToolTip && this.activeToolTip.deactivated) {
  1306. this.activeToolTip.deactivated($(this.activeToolTipElement));
  1307. }
  1308. this.activeToolTip = null;
  1309. this.options.toolTipContainer.empty();
  1310. this.activeToolTipElement = null;
  1311. },
  1312. _showToolTip: function _showToolTip() {
  1313. var toolTipInfo = this._getCurrentToolTip();
  1314. if (toolTipInfo) {
  1315. this.activeToolTip = toolTipInfo.contentCreator($(this.activeToolTipElement));
  1316. this.options.toolTipContainer.append(this.activeToolTip);
  1317. this.toolTipContainer.show();
  1318. }
  1319. },
  1320. _mouseOver: function _mouseOver(e) {
  1321. var toolTipElement = $(e.target).closest('.' + this.options.toolTipClass);
  1322. if (toolTipElement.get(0) == this.activeToolTipElement) {
  1323. return;
  1324. }
  1325. this._reset();
  1326. if (toolTipElement.length > 0) {
  1327. this.activeToolTipElement = toolTipElement[0];
  1328. this.toolTipContainer.css({
  1329. left: (e.pageX + this.options.offsetX) + 'px',
  1330. top: (e.pageY + this.options.offsetY) + 'px',
  1331. });
  1332. this.pendingShowEvent = setTimeout(IkaTools.Logging.debuggable(
  1333. 'IkaTools.UI.ToolTipHandler.showToolTip[' + this.options.toolTipClass + ']',
  1334. this._showToolTip.bind(this)), this.options.delay);
  1335. }
  1336. },
  1337. _mouseOut: function _mouseOut(e) {
  1338. if (this.activeToolTipElement) {
  1339. var target = $(e.relatedTarget).closest('.' + this.options.toolTipClass);
  1340. if (target.get(0) != this.activeToolTipElement) {
  1341. this._reset();
  1342. return;
  1343. }
  1344. }
  1345. },
  1346. _mouseMove: function _mouseMove(e) {
  1347. if (this.activeToolTipElement && !this.activeToolTip) {
  1348. this.toolTipContainer.css({
  1349. left: (e.pageX + this.options.offsetX)+ 'px',
  1350. top: (e.pageY + this.options.offsetY) + 'px',
  1351. });
  1352. }
  1353. },
  1354. register: function registerToolTip(id, contentCreator) {
  1355. this.toolTips[id] = {
  1356. contentCreator: contentCreator,
  1357. };
  1358. },
  1359. registerSimple: function registerSimpleToolTip(id, content) {
  1360. this.register(id, function() {
  1361. return $(content);
  1362. });
  1363. },
  1364. registerRefreshable: function registerRefreshableToolTip(id, contentGenerator) {
  1365. var toolTip = this;
  1366. this.register(id, function() {
  1367. var id = Utils.nextId();
  1368. var interval = setInterval(Logging.debuggable('IkaTools.ToolTip.refresh[' + id + ']',
  1369. function refreshToolTip() {
  1370. toolTip.options.toolTipContainer.html(contentGenerator());
  1371. }), Constants.Time.MILLIS_PER_SECOND);
  1372. var tip = $(contentGenerator());
  1373. tip.deactivated = function() {
  1374. clearInterval(interval);
  1375. };
  1376. return tip;
  1377. });
  1378. },
  1379. deregister: function deregister(id) {
  1380. delete this.toolTips[id];
  1381. },
  1382. startHandling: function startHandling(element) {
  1383. element.on('mouseover'/*, '.' + this.options.toolTipClass*/, Logging.debuggable(
  1384. 'IkaTools.UI.ToolTipHandler.mouseOver[' + this.options.toolTipClass + ']',
  1385. this._mouseOver.bind(this)));
  1386. element.on('mouseout'/*, '.' + this.options.toolTipClass*/, Logging.debuggable(
  1387. 'IkaTools.UI.ToolTipHandler.mouseOut[' + this.options.toolTipClass + ']',
  1388. this._mouseOut.bind(this)))
  1389. element.on('mousemove', '.' + this.options.toolTipClass, Logging.debuggable(
  1390. 'IkaTools.UI.ToolTipHandler.mouseMove[' + this.options.toolTipClass + ']',
  1391. this._mouseMove.bind(this)))
  1392. }
  1393. });
  1394. function LeftMenu(items, options) {
  1395. this.items = items;
  1396. this.active = false;
  1397. this.options = $.extend({atTop: false }, options);
  1398. }
  1399. $.extend(LeftMenu.prototype, {
  1400. ITEM_CONTRACTED_WIDTH: 53,
  1401. ITEM_EXPANDED_WIDTH: 199,
  1402. ITEM_Z_INDEX_EXPANDED: 120000,
  1403. ITEM_Z_INDEX_CONTRACTED: 65,
  1404. ANIMATION_DURATION: 300,
  1405. display: function display() {
  1406. // Add leftMenu div and "standard" contents if we are in a
  1407. // view where it is not already present
  1408. var leftMenuDiv = $('#leftMenu');
  1409. if (!leftMenuDiv.length) {
  1410. leftMenuDiv = $('<div id="leftMenu" >' +
  1411. '<div class="slot_menu city_menu" style="z-index: 65; ">' +
  1412. '<ul class="menu_slots"/>' +
  1413. '</div>' +
  1414. '</div>');
  1415. $('#container').append(leftMenuDiv);
  1416. }
  1417. // Setup event handlers
  1418. for (var i = 0; i < this.items.length; i++) {
  1419. var item = this.items[i];
  1420. item.element.width(this.ITEM_COLLAPSED_WIDTH + 'px');
  1421. item.element.mouseenter(this.expand.bind(this, item));
  1422. item.element.mouseleave(this.contract.bind(this, item));
  1423. }
  1424. this.holderDiv = $('.slot_menu', leftMenuDiv);
  1425. this.holderDiv.hover(this.menuActivated.bind(this),
  1426. this.menuPassivated.bind(this));
  1427. // Add elements to ui
  1428. var menuSlots = $('ul.menu_slots', leftMenuDiv);
  1429. if (this.options.atTop) {
  1430. for (var i = this.items.length - 1; i >= 0; i--) {
  1431. menuSlots.prepend(this.items[i].element);
  1432. }
  1433. } else {
  1434. for (var i = 0; i < this.items.length; i++) {
  1435. menuSlots.append(this.items[i].element);
  1436. }
  1437. }
  1438. },
  1439. menuActivated: function menuActivated() {
  1440. this.active = true;
  1441. this.holderDiv.css('z-index', this.ITEM_Z_INDEX_EXPANDED);
  1442. },
  1443. menuPassivated: function menuPassivated() {
  1444. this.active = false;
  1445. },
  1446. contract: function contract(item) {
  1447. var holder = item.element.parent().parent();
  1448. item.element.animate(
  1449. { width: this.ITEM_CONTRACTED_WIDTH },
  1450. 300,
  1451. 'swing',
  1452. this.contractComplete.bind(this)
  1453. );
  1454. },
  1455. contractComplete: function contractComplete() {
  1456. if (!this.active) {
  1457. this.holderDiv.css('z-index', this.ITEM_Z_INDEX_CONTRACTED);
  1458. }
  1459. },
  1460. expand: function expand(item) {
  1461. item.element.animate(
  1462. { width: this.ITEM_EXPANDED_WIDTH },
  1463. 300,
  1464. 'swing');
  1465. this.holderDiv.css('z-index', this.ITEM_Z_INDEX_EXPANDED);
  1466. },
  1467. });
  1468. LeftMenu.Item = function LeftMenu_Item(element) {
  1469. this.element = element;
  1470. }
  1471. function resizePopup() {
  1472. unsafeWindow.ikariam.controller.adjustSizes();
  1473. }
  1474. var destroyedTemplateViewEvent = Utils.thunk(function() {
  1475. var dispatcher = new Utils.EventDispatcher();
  1476. var oldDestroyTemplateView = unsafeWindow.ikariam.TemplateView.destroyTemplateView;
  1477. unsafeWindow.ikariam.TemplateView.destroyTemplateView =
  1478. function customDestroyTemplateView() {
  1479. oldDestroyTemplateView.apply(this, arguments);
  1480. dispatcher.send();
  1481. };
  1482. return dispatcher;
  1483. });
  1484. function PopupWindow(id, header, content, options) {
  1485. this.id = id;
  1486. this.headerElement = $(header);
  1487. this.contentElement = $(content);
  1488. this.options = $.extend({
  1489. sidebars: [],
  1490. oversized: false,
  1491. activatedCallback: function() {},
  1492. deactivatedCallback: function() {},
  1493. }, options);
  1494. this.isActive = false;
  1495. destroyedTemplateViewEvent().addListener(this._popupDestroyed.bind(this));
  1496. }
  1497. $.extend(PopupWindow.prototype, {
  1498. _popupDestroyed: function _popupDestroyed() {
  1499. if (this.isActive) {
  1500. this.options.deactivatedCallback(this);
  1501. }
  1502. this.isActive = false;
  1503. },
  1504. display: function display(skipResize) {
  1505. // Always display it. There's no good way to track if it is
  1506. // already displayed because there is no callback when it is destroyed.
  1507. // (One can replace unsafeWindow.ikariam.destroyTemplateView, but there
  1508. // are still some issues with quickly switching between views that can
  1509. // mess things up and will still be considered "active" when its not visible.
  1510. templateViewArg = {
  1511. boxId: this.id,
  1512. headerElem: this.headerElement.html(),
  1513. contentElem: '<div><div id="ikaPopupTempHolder"></div></div>',
  1514. sidebarEls: this.options.sidebars,
  1515. oversized: this.options.oversized,
  1516. replaceBox: true,
  1517. keepSidebars: false
  1518. };
  1519. this.isActive = true;
  1520. this.activePopup = unsafeWindow.ikariam.createTemplateView(templateViewArg);
  1521. // Null out the id or submitting the change city form will send it as the
  1522. // templateView
  1523. unsafeWindow.ikariam.templateView.id = null;
  1524. unsafeWindow.ikariam.model.viewParams = null;
  1525. $('#ikaPopupTempHolder').replaceWith(this.contentElement);
  1526. this.options.activatedCallback(this);
  1527. if (skipResize) {
  1528. unsafeWindow.ikariam.controller.adjustSizes();
  1529. }
  1530. },
  1531. close: function close() {
  1532. if (this.isActive) {
  1533. unsafeWindow.ikariam.TemplateView.destroyTemplateView();
  1534. }
  1535. }
  1536. });
  1537. function TabPane(tabs, options) {
  1538. this.tabs = tabs;
  1539. this.options = $.extend({ tabActivatedCallback: function() {} }, options);
  1540. this.currentTab = null;
  1541. this.container = $("<div/>");
  1542. var tabsContainer = $('<ul class="tabmenu"/>');
  1543. this.container.append(tabsContainer);
  1544. for (var i = 0; i < tabs.length; i++) {
  1545. var tab = tabs[i];
  1546. tab.tabPane = this;
  1547. tabsContainer.append(tab.tabHolder)
  1548. this.container.append(tab.contentHolder);
  1549. tab.contentHolder.hide();
  1550. tab.tabHolder.click(
  1551. IkaTools.Logging.debuggable('IkaTools.TabPane.Tab.click',
  1552. this.activate.bind(this, tab)));
  1553. }
  1554. }
  1555. $.extend(TabPane.prototype, {
  1556. getContainer: function getContainer() {
  1557. return this.container;
  1558. },
  1559. activate: function activate(tab) {
  1560. if (this.currentTab !== tab) {
  1561. if (this.currentTab) {
  1562. this.currentTab.contentHolder.hide();
  1563. this.currentTab.tabHolder.removeClass('selected');
  1564. this.currentTab.deactivated();
  1565. }
  1566. tab.contentHolder.show();
  1567. tab.tabHolder.addClass('selected');
  1568. }
  1569. tab.activated();
  1570. this.options.tabActivatedCallback(this.currentTab, tab);
  1571. this.currentTab = tab;
  1572. }
  1573. });
  1574. TabPane.Tab = function Tab(tab, content, options) {
  1575. this.tab = $(tab);
  1576. this.content = $(content);
  1577. this.options = $.extend({ activatedCallback: function() {},
  1578. deactivatedCallback: function() {} },
  1579. options);
  1580. this.contentHolder = $('<div/>');
  1581. this.contentHolder.append(this.content);
  1582. this.tabHolder = $('<li class="tab"/>');
  1583. this.tabHolder.append(this.tab);
  1584. }
  1585. $.extend(TabPane.Tab.prototype, {
  1586. activate: function activate() {
  1587. this.tabPane.activate(this);
  1588. },
  1589. activated: function activated() {
  1590. this.options.activatedCallback(this);
  1591. },
  1592. deactivated: function deactivated() {
  1593. this.options.deactivatedCallback(this);
  1594. },
  1595. });
  1596. function SettingsWindow(id, scriptName, settings, settingGroups) {
  1597. this.id = id;
  1598. this.settings = settings;
  1599. this.scriptName = scriptName;
  1600. this.settingGroups = settingGroups;
  1601. this.saveEvent = new Utils.EventDispatcher();
  1602. }
  1603. $.extend(SettingsWindow.prototype, {
  1604. show: function show() {
  1605. var tabs = $.map(this.settingGroups, function makeTab(group, index) {
  1606. var content = $.map(group.getSettings(), this.renderSetting.bind(this)).join('');
  1607. content = '<div class="contentBox01h">' +
  1608. '<h3 class="header">' +
  1609. Intl.localizer.localize('settings','settings') +
  1610. '</h3>' +
  1611. '<div class="content">' +
  1612. '<table class="table01"><tbody>' + content + '</tbody></table>' +
  1613. '</div>' +
  1614. '<div class="footer"/>' +
  1615. '</div>' +
  1616. '<div class="centerButton">' +
  1617. '<a class="ikaScriptToolsSaveOptions button">' +
  1618. Intl.localizer.localize('settings','save') +
  1619. '</a>' +
  1620. '</div>';
  1621. return new TabPane.Tab('<b>' + group.getName() + '</b>', content, {});
  1622. }.bind(this));
  1623. var tabPane = new TabPane(tabs, {
  1624. tabActivatedCallback: function() {
  1625. IkaTools.UI.resizePopup();
  1626. },
  1627. });
  1628. var popup = new PopupWindow(
  1629. 'options',
  1630. $('<div>' + Intl.localizer.localize('settings','script_settings') + ': ' +
  1631. this.scriptName + '</div>'),
  1632. tabPane.getContainer());
  1633. tabs[0].activate();
  1634. popup.display();
  1635. $.each(this.settingGroups, function postRenderSettingsGroup(index, group) {
  1636. $.each(group.getSettings(), this.postRenderSetting.bind(this));
  1637. }.bind(this));
  1638. $('.ikaScriptToolsSaveOptions').click(Logging.debuggable(
  1639. 'IkaTools.UI.SettingsWindow.saveSettings',
  1640. this._save.bind(this)));
  1641. },
  1642. renderSetting: function renderSetting(setting, index) {
  1643. var type = setting.getType();
  1644. var html = '<tr><td>' + setting.getLabel()() + '</td><td class="left">';
  1645. if (type == Settings.Type.BOOLEAN) {
  1646. html += '<input id="ikaScriptToolsSettingInput_' + setting.getName() +
  1647. '" type="checkbox" ' + (setting.isEnabled() ? 'checked' : '' ) + '/>';
  1648. } else if (type == Settings.Type.CHOICE) {
  1649. html += '<select id="ikaScriptToolsSettingInput_' + setting.getName() + '">';
  1650. $.each(setting.getChoices(), function renderOption(key, value) {
  1651. html += '<option value="' + value + '"' +
  1652. (setting.getValue() == value ? 'selected="selected"' : '') + '>' + key + '</option>';
  1653. })
  1654. html += '</select>';
  1655. } else if (type == Settings.Type.HTML) {
  1656. html += setting.getHtml()();
  1657. } else if (type == Settings.Type.TEXT) {
  1658. html += '<input id="ikaScriptToolsSettingInput_' + setting.getName()
  1659. + '" value="' + setting.getValue() + '"/>';
  1660. }
  1661. html += '</td>';
  1662. return html;
  1663. },
  1664. postRenderSetting: function postRenderSetting(index, setting) {
  1665. var type = setting.getType();
  1666. if (type == Settings.Type.HTML) {
  1667. setting.getPostRender()();
  1668. }
  1669. },
  1670. _save: function save() {
  1671. $.each(this.settingGroups, function saveGroup(index, group) {
  1672. $.each(group.getSettings(), this._saveSetting.bind(this));
  1673. }.bind(this));
  1674. this.settings.save();
  1675. this.saveEvent.send(this);
  1676. },
  1677. _saveSetting: function saveSetting(index, setting) {
  1678. var type = setting.getType();
  1679. if (type == Settings.Type.BOOLEAN) {
  1680. setting.setEnabled(
  1681. $('#ikaScriptToolsSettingInput_' + setting.getName()).is(':checked'));
  1682. } else if (type == Settings.Type.CHOICE) {
  1683. setting.setValue(
  1684. $('#ikaScriptToolsSettingInput_' + setting.getName()).val());
  1685. } else if (type == Settings.Type.TEXT) {
  1686. setting.setValue(
  1687. $('#ikaScriptToolsSettingInput_' + setting.getName()).val());
  1688. }
  1689. },
  1690. registerSavedSettingsHandler: function registerSavedSettingsHandler(f) {
  1691. return this.saveEvent.addListener(f);
  1692. },
  1693. addAsScriptOptionsLink: function addAsScriptOptionsLink() {
  1694. if($('#IkaScriptToolSettingsDropdown').size() == 0) {
  1695. GM_addStyle(
  1696. '#IkaScriptToolSettingsDropdown { ' +
  1697. ' position:absolute; ' +
  1698. '}' +
  1699. '#IkaScriptToolSettingsDropdown:hover {' +
  1700. ' padding-bottom:20px;' +
  1701. '}' +
  1702. '#IkaScriptToolSettingsDropdown #IkaScriptToolsSettingsDropdownLinks { ' +
  1703. ' display:none;' +
  1704. '}' +
  1705. '#IkaScriptToolSettingsDropdown:hover #IkaScriptToolsSettingsDropdownLinks {' +
  1706. ' display:block;' +
  1707. '}' +
  1708. '#IkaScriptToolsSettingsDropdownLinks { ' +
  1709. ' background-color:#FFF5E1; ' +
  1710. ' padding:.5em; ' +
  1711. ' padding-bottom:0; ' +
  1712. ' border:1px solid #666; ' +
  1713. ' position:absolute; ' +
  1714. ' right:-80px; ' +
  1715. ' margin-top:2px; ' +
  1716. ' width:170px;' +
  1717. '}' +
  1718. '#IkaScriptToolsSettingsDropdownLinks a { ' +
  1719. ' color:#666; ' +
  1720. ' cursor:pointer; ' +
  1721. ' margin-left:0; ' +
  1722. ' padding-left:.2em; ' +
  1723. ' display:block; ' +
  1724. ' margin-bottom:.5em;' +
  1725. '}'
  1726. );
  1727. var li = document.createElement('li');
  1728. li.id = 'IkaOptionsDropdown';
  1729. $('#GF_toolbar ul').append($(
  1730. '<li id="IkaScriptToolSettingsDropdown">' +
  1731. '<a href="javascript:void(0);">' +
  1732. Intl.localizer.localize('settings','script_settings') + '</a>' +
  1733. '<div id="IkaScriptToolsSettingsDropdownLinks">' +
  1734. '</li>'));
  1735. }
  1736. var link = $('<a>' + this.scriptName + '</a>');
  1737. link.click(Logging.debuggable('IkaTools.UI.SettingsWindow.showSettings',
  1738. this.show.bind(this)));
  1739. $('#IkaScriptToolsSettingsDropdownLinks').append(link);
  1740. },
  1741. });
  1742. SettingsWindow.Group = function(name, settings) {
  1743. this.name = name;
  1744. this.settings = settings;
  1745. }
  1746. $.extend(SettingsWindow.Group.prototype, {
  1747. getName: function getName() {
  1748. return this.name;
  1749. },
  1750. getSettings: function getSettings() {
  1751. return this.settings;
  1752. },
  1753. });
  1754. return {
  1755. ToolTipHandler: ToolTipHandler,
  1756. LeftMenu: LeftMenu,
  1757. resizePopup: resizePopup,
  1758. PopupWindow: PopupWindow,
  1759. TabPane: TabPane,
  1760. SettingsWindow: SettingsWindow,
  1761. };
  1762. }();
  1763. var Settings = function() {
  1764. var Type = {
  1765. BOOLEAN: 1,
  1766. CHOICE: 2,
  1767. HTML: 3,
  1768. TEXT: 4,
  1769. }
  1770. function Settings(name) {
  1771. this.name = name;
  1772. this.data = new Data.Value('scriptOptions_' + name, { }, { version: 1 });
  1773. }
  1774. $.extend(Settings.prototype, {
  1775. _getValue: function getValue(name, defaultValue) {
  1776. var value = this.data.get()[name];
  1777. return value === undefined ? defaultValue : value;
  1778. },
  1779. _setValue: function setValue(name, value) {
  1780. this.data.get()[name] = value;
  1781. },
  1782. save: function save() {
  1783. this.data.save();
  1784. },
  1785. boolean: function boolean(name, enabled, labelFunc) {
  1786. return new Boolean(this, name, this._getValue(name, enabled), labelFunc);
  1787. },
  1788. choice: function choice(name, value, choices, labelFunc) {
  1789. return new Choice(this, name, this._getValue(name, value), choices, labelFunc);
  1790. },
  1791. html: function html(htmlFunc, postRender, labelFunc) {
  1792. return new Html(htmlFunc, postRender, labelFunc);
  1793. },
  1794. text: function text(name, value, labelFunc) {
  1795. return new Text(this, name, this._getValue(name, value), labelFunc);
  1796. }
  1797. });
  1798. function Boolean(settings, name, enabled, labelFunc) {
  1799. this.settings = settings;
  1800. this.name = name;
  1801. this.enabled = enabled;
  1802. this.labelFunc= labelFunc;
  1803. }
  1804. $.extend(Boolean.prototype, {
  1805. isEnabled: function isEnabled() {
  1806. return this.enabled;
  1807. },
  1808. setEnabled: function(enabled) {
  1809. this.enabled = enabled;
  1810. this.settings._setValue(this.name, enabled);
  1811. },
  1812. getName: function getName() {
  1813. return this.name;
  1814. },
  1815. getType: function getType() {
  1816. return Type.BOOLEAN;
  1817. },
  1818. getLabel: function getLabel() {
  1819. return this.labelFunc;
  1820. },
  1821. });
  1822. function Choice(settings, name, value, choices, labelFunc) {
  1823. this.settings = settings;
  1824. this.name = name;
  1825. this.value = value;
  1826. this.choices = choices;
  1827. this.labelFunc = labelFunc;
  1828. }
  1829. $.extend(Choice.prototype, {
  1830. getValue: function getValue() {
  1831. return this.value;
  1832. },
  1833. setValue: function setValue(value) {
  1834. this.value = value;
  1835. this.settings._setValue(this.name, value);
  1836. },
  1837. getChoices: function getChoices() {
  1838. return this.choices;
  1839. },
  1840. getName: function getName() {
  1841. return this.name;
  1842. },
  1843. getType: function getType() {
  1844. return Type.CHOICE;
  1845. },
  1846. getLabel: function getLabel() {
  1847. return this.labelFunc;
  1848. },
  1849. });
  1850. function Html(htmlFunc, postRender, labelFunc) {
  1851. this.labelFunc = labelFunc;
  1852. this.htmlFunc = htmlFunc;
  1853. this.postRender = postRender;
  1854. }
  1855. $.extend(Html.prototype, {
  1856. getHtml: function getHtml() {
  1857. return this.htmlFunc;
  1858. },
  1859. getPostRender: function getPostRender() {
  1860. return this.postRender;
  1861. },
  1862. getType: function getType() {
  1863. return Type.HTML;
  1864. },
  1865. getLabel: function getLabel() {
  1866. return this.labelFunc;
  1867. },
  1868. });
  1869. function Text(settings, name, value, labelFunc) {
  1870. this.settings = settings;
  1871. this.name = name;
  1872. this.value = value;
  1873. this.labelFunc = labelFunc;
  1874. }
  1875. $.extend(Text.prototype, {
  1876. getValue: function getValue() {
  1877. return this.value;
  1878. },
  1879. setValue: function setValue(value) {
  1880. this.value = value;
  1881. this.settings._setValue(this.name, value);
  1882. },
  1883. getName: function getName() {
  1884. return this.name;
  1885. },
  1886. getType: function getType() {
  1887. return Type.TEXT;
  1888. },
  1889. getLabel: function getLabel() {
  1890. return this.labelFunc;
  1891. },
  1892. });
  1893. return {
  1894. Settings: Settings,
  1895. Type: Type,
  1896. };
  1897. }();
  1898. var Constants = {
  1899. Resources: {
  1900. WOOD: 'wood',
  1901. WINE: 'wine',
  1902. MARBLE: 'marble',
  1903. GLASS: 'glass',
  1904. SULFUR: 'sulfur',
  1905. POPULATION: 'population',
  1906. CITIZENS: 'citizens',
  1907. SCIENTISTS: 'scientists',
  1908. ACTION_POINTS: 'actionPoints',
  1909. CULTURAL_GOODS: 'culturalGoods',
  1910. TAVERN_WINE_LEVEL: 'tavernWineLevel',
  1911. PRIESTS: 'priests',
  1912. },
  1913. CivilizationData: {
  1914. GOVERNMENT: 'government',
  1915. RESEARCH: 'research',
  1916. MOVEMENT: 'movement',
  1917. PREMIUM_FEATURE: 'premiumFeature',
  1918. },
  1919. PremiumFeatures: {
  1920. DOUBLED_STORAGE_CAPACITY: 'doubledStorageCapacity',
  1921. DOUBLED_SAFE_CAPACITY: 'doubledSafeCapacity',
  1922. },
  1923. Movements: {
  1924. Mission: {
  1925. TRANSPORT: 'transport',
  1926. DEPLOY_ARMY: 'deployarmy',
  1927. DEPLOY_NAVY: 'deployfleet',
  1928. PLUNDER: 'plunder',
  1929. },
  1930. Stage: {
  1931. LOADING: 'loading',
  1932. EN_ROUTE: 'en_route',
  1933. RETURNING: 'returning',
  1934. },
  1935. EventType: {
  1936. DATA_UPDATED: 'dataUpdated',
  1937. STAGE_CHANGED: 'stageChanged',
  1938. CANCELLED: 'cancelled',
  1939. COMPLETED: 'completed',
  1940. },
  1941. MissionData: {
  1942. transport: {
  1943. icon: '/cdn/all/both/actions/transport.jpg',
  1944. },
  1945. deployarmy: {
  1946. icon: '/cdn/all/both/actions/occupy.jpg',
  1947. },
  1948. deployfleet: {
  1949. icon: '/cdn/all/both/actions/blockade.jpg',
  1950. },
  1951. plunder: {
  1952. icon: '/cdn/all/both/actions/plunder.jpg',
  1953. },
  1954. piracyRaid: {
  1955. icon: '/cdn/all/both/actions/piracyRaid.jpg',
  1956. },
  1957. }
  1958. },
  1959. BuildingEventType: {
  1960. DATA_REFRESH: 'dataRefresh',
  1961. UPGRADE_COMPLETE: 'upgradeComplete',
  1962. },
  1963. Buildings: {
  1964. TOWN_HALL: 'townHall',
  1965. PALACE: 'palace',
  1966. GOVERNORS_RESIDENCE: 'palaceColony',
  1967. TAVERN: 'tavern',
  1968. MUSEUM: 'museum',
  1969. ACADEMY: 'academy',
  1970. WORKSHOP: 'workshop',
  1971. TEMPLE: 'temple',
  1972. EMBASSY: 'embassy',
  1973. WAREHOUSE: 'warehouse',
  1974. DUMP: 'dump',
  1975. TRADING_PORT: 'port',
  1976. TRADING_POST: 'branchOffice',
  1977. BLACK_MARKET: 'blackMarket',
  1978. MARINE_CHART_ARCHIVE: 'marineChartArchive',
  1979. WALL: 'wall',
  1980. HIDEOUT: 'safehouse',
  1981. BARRACKS: 'barracks',
  1982. SHIPYARD: 'shipyard',
  1983. PIRATE_FORTRESS: 'pirateFortress',
  1984. FORESTER: 'forester',
  1985. CARPENTER: 'carpentering',
  1986. WINERY: 'winegrower',
  1987. WINE_PRESS: 'vineyard',
  1988. STONEMASON: 'stonemason',
  1989. ARCHITECT: 'architect',
  1990. GLASSBLOWER: 'glassblowing',
  1991. OPTICIAN: 'optician',
  1992. ALCHEMISTS_TOWER: 'alchemist',
  1993. FIREWORK_TEST_AREA: 'fireworker',
  1994. },
  1995. // Time data from http://ikariam.wikia.com/wiki/User_blog:Warrior_fr/Actual_building_Time_formula
  1996. // Rest of data from http://ikariam.wikia.com/ building pages.
  1997. BuildingData: {
  1998. academy: {
  1999. maxlevel:50,
  2000. wood:[55,58,98,226,328,538,844,1143,1723,2291,3367,4434,6403,8387,10965,1562,20374,28767,37471,48786,63496,88974,124014,150549,209779,272798,378372,461226,639658,883624,1081231,1493547,2063095,2849833,3936586,5437761,7511392,10375779,14332469,19797999,27347750,37776516,52182177,72081279,99568686,137538116,189986771,262436146,362513298,500753777],
  2001. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2002. marble:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2003. glass:[0,0,0,0,193,368,639,936,1503,211,3255,4485,6761,9226,12555,18599,25216,36997,50063,67702,91516,133177,192765,243011,351634,474841,683916,865717,1246777,1788499,2272591,3259901,4676140,6707654,9621744,13801839,19797943,28399010,40736746,58434518,83820952,120236331,172472097,247401296,354882919,509059121,730216007,1047452831,1502510795,2155265251],
  2004. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2005. time :{a:1440, b:1, c:1.2, d:720},
  2006. icon :'/cdn/all/both/img/city/academy_l.png',
  2007. maxScientists: [0, 8, 12, 16, 22, 28, 35, 43, 51, 60, 69, 79, 89, 100, 111, 122, 134, 146, 159, 172, 185, 198, 212, 227, 241, 256, 271, 287, 302, 318, 335, 351, 368 ],
  2008. },
  2009. alchemist: {
  2010. maxlevel:61,
  2011. wood:[235,401,617,898,1263,1738,2354,3157,4199,5554,7316,9607,12585,16456,21488,28030,36535,47591,61963,80649,104938,136516,177565,230931,300306,390494,507737,660154,858296,1115880,1450740,1886057,2451998,3187758,4144296,5387857,7004569,9106400,11838919,15391374,20009798,26014054,33819980,43968198,57161548,74313771,96612788,125602976,163292127,212290500,275991607,358807234,466472992,606445556,788419092,1024996652,1332563034,1732419548,2252259306,2928085165,3806703211],
  2012. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2013. marble:[0,99,219,374,577,840,1182,1627,2205,2955,3931,5202,6852,8997,11786,15412,20125,26253,34219,44574,58037,75538,98289,127865,166315,216300,281279,365753,475568,618329,803918,1045183,1358855,1766663,2296859,2986173,3882359,5047500,6562314,8531740,11092214,14421117,18749061,24375871,31691353,41202296,53567581,69643833,90544754,117718283,153046903,198978051,258693667,336330631,437267348,568496341,739108674,960923743,1249308082,1624239900,2111693096],
  2014. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2015. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2016. time :{a:72000, b:11, c:1.1, d:6120},
  2017. icon :'/cdn/all/both/img/city/alchemist_l.png',
  2018. },
  2019. architect: {
  2020. maxlevel:50,
  2021. wood:[159,250,355,477,619,783,974,1195,1452,1750,2095,2495,2960,3500,4125,4850,5692,6668,7800,9114,10637,12404,14454,16832,19591,22791,26503,30809,35804,41599,48319,56116,65170,75686,87899,102082,118554,137684,159900,185702,215666,250465,290880,337815,392325,455629,529148,614530,713689,828848],
  2022. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2023. marble:[91,137,190,253,325,408,504,615,743,890,1060,1255,1480,1739,2037,2379,2774,3227,3748,4348,5037,5829,6738,7784,8986,10367,11953,13774,15867,18271,21031,24201,27848,32046,36876,42434,48830,56190,64659,74406,85621,98526,113377,130466,150131,172760,198799,228763,263244,302922],
  2024. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2025. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2026. time :{a:125660, b:37, c:1.06, d:2628},
  2027. icon :'/cdn/all/both/img/city/architect_l.png',
  2028. },
  2029. barracks: {
  2030. maxlevel:49,
  2031. wood:[42,98,167,254,361,493,658,862,1115,1429,1818,2301,2899,3641,4561,5701,7116,887,11044,13741,17086,21233,26375,32751,40658,50461,62618,77693,96385,119564,148305,183944,228137,282936,350886,435146,539626,669183,829833,1029039,1276055,1582354,1962165,2433131,3017129,3741286,4639240,5752704,7133399],
  2032. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2033. marble:[0,0,0,0,0,0,0,0,153,370,640,975,1389,1904,2542,3332,4312,5528,7037,8907,11224,14099,17664,22084,27566,34363,42791,53241,662,82268,102193,126901,157539,195528,242636,30105,373483,4633,574672,712774,884021,1096368,1359677,1686180,2091044,2593076,3215595,3987519,4944704],
  2034. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2035. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2036. time :{a:25200, b:11, c:1.1, d:1728},
  2037. icon :'/cdn/all/both/img/city/barracks_l.png',
  2038. },
  2039. branchOffice: {
  2040. maxlevel:72,
  2041. wood:[41,148,297,499,770,113,1602,2218,3017,4047,5367,7054,9201,11924,15369,19716,25185,32054,40663,51434,64885,81661,10255,12853,1608,200835,250454,311885,387872,481778,597732,740796,917337,1135187,1404015,1735749,2145109,2650258,3273613,4043584,4994655,6169425,7620506,9412889,11626849,14361545,17739456,21911869,27065655,33431640,41294936,51007721,63005003,77824109,96128747,118738731,146666701,181163475,223774071,276406902,341419250,421722842,520914258,643436013,794775525,981710881,1212614410,1497827655,1850124544,2285283504,2822794124,3486730050],
  2042. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2043. marble:[0,0,0,0,464,681,965,1337,1818,2439,3235,4252,5547,7188,9265,11885,15182,19323,24512,31004,39114,49226,61819,77479,96933,121067,150978,188009,233815,290424,360322,446564,552986,684311,846366,1046343,1293113,1597627,1973398,2437552,3010878,3719054,4593796,5674284,7008907,8657442,10693723,13208947,16315766,20153328,24893505,30748599,37980844,46914155,57948631,71578478,88414142,109209651,134896382,166624778,205815871,254224933,314020081,387879387,479110822,591800408,730995225,902929453,1115303588,1377629325,1701655566,2101894617],
  2044. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2045. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2046. capacity:[400,1600,3600,6400,10000,14400,19600,25600,32400,40000,48400,57600,67600,78400,90000,102400,115600,129600,144400,160000,176400,193600,211600,230400,250000,270400,291600,313600,336400,360000,384400,409600,435600,462400,490000,518400,547600,577600,608400,640842,675014,711009,748923,788858,830924,875232,921903,971062,1022844,1077386,1134836,1195351,1259092,1326232,1396952,1471443,1549906,1632554,1719608,1811305,1907891,2009628,2116789,2229665,2348560,2473795,2605708,2744655,2891011,3045172,3207553,3378593],
  2047. time :{a:108000, b:11, c:1.1, d:9360},
  2048. icon :'/cdn/all/both/img/city/branchoffice_l.png',
  2049. },
  2050. blackMarket: {
  2051. maxlevel:25,
  2052. wood:[378,762,1169,1625,2163,2827,3666,4734,6093,7813,9967,12634,15900,19855,24596,30222,36841,44565,53507,63790,75540,88886,103963,120912,139876],
  2053. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2054. marble:[223,451,694,968,1297,1709,2236,2915,3786,4895,6290,8024,10154,12738,15841,19528,23871,28942,34817,41579,49307,58089,68014,79175,91664],
  2055. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2056. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2057. time :{a:0, b:0, c:0, d:0},
  2058. icon :'/cdn/all/both/img/city/blackmarket_l.png',
  2059. },
  2060. marineChartArchive: {
  2061. maxlevel:40,
  2062. wood:[497,1116,1834,2667,3634,4755,6056,7564,9314,11344,13698,16431,19599,23275,27538,32484,38221,44877,52596,61551,71939,83990,97968,114183,132992,154810,180120,209478,243534,283039,328865,382024,443687,515217,598191,694442,806092,935607,1085844,1260119],
  2063. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2064. marble:[297,916,1647,2509,3526,4727,6143,7815,9787,12115,14861,18103,21926,26438,31764,38047,45461,54210,64533,76715,91089,108051,128066,151684,179552,212438,251242,297031,351062,414819,490052,578827,683582,807192,953052,1125168,1328263,1567917,1850707,2184400],
  2065. glass:[138,525,982,1521,2156,2906,3792,4837,6069,7525,9241,11266,13656,16476,19804,23731,28366,33834,40285,47899,56883,67484,79993,94754,112173,132726,156978,185596,219366,259214,306235,361719,427191,504447,595611,703182,830117,979901,1156644,1365203],
  2066. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2067. time :{a:0, b:0, c:0, d:0},
  2068. icon :'/cdn/all/both/img/city/marinechartarchive_l.png',
  2069. },
  2070. carpentering: {
  2071. maxlevel:50,
  2072. wood:[54,104,165,235,319,417,533,668,827,1013,1231,1487,1787,2137,2549,3030,3593,4252,5023,5925,6980,8213,9656,11343,13316,15621,18317,21468,25150,29454,34482,40359,47238,55289,64713,75743,88653,103763,121449,142149,166377,194734,227925,266773,312242,365461,427751,500657,585990,685867],
  2073. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2074. marble:[0,0,0,0,0,0,0,308,381,469,575,701,853,1036,1254,1517,1832,2211,2664,3208,3862,4645,5586,6715,8070,9696,11646,13987,16796,20167,24212,29067,34894,41891,50291,60374,72479,87013,104459,125403,150548,180733,216971,260475,312702,375400,450669,541030,649509,779739],
  2075. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2076. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2077. time :{a:125660, b:37, c:1.06, d:2808},
  2078. icon :'/cdn/all/both/img/city/carpentering_l.png',
  2079. },
  2080. dump: {
  2081. maxlevel:80,
  2082. wood:[550,990,1518,2153,2913,3827,4922,6237,7815,9708,11980,14706,17978,21904,26615,32268,39052,47193,56962,68685,82752,99633,119890,144198,173369,208372,250377,300784,361270,433855,520956,625478,750903,901415,1082027,1298764,1558847,1870946,2245466,2694889,3234264,3881593,4658484,5590867,6709864,8052825,9664577,11598917,13920408,16706541,20050310,24063326,28879536,34659697,41596742,49922218,59914014,71905643,86297363,103569548,124298713,149176766,179034093,214867283,257872389,309484852,371427409,445767602,534986785,642062945,770570110,924797636,1109893386,1332035549,1598638868,1918602120,2302605153,2763465357,3316565485,3980367111],
  2083. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2084. marble:[427,801,1242,1763,2375,3103,3959,4969,6161,7567,9226,11184,13494,16221,19437,23233,27713,32999,39235,46595,55279,65527,77620,91888,108725,128594,152037,179702,212345,250864,296317,349952,413240,487921,576043,680028,802731,947520,1118371,1319975,1557920,1838759,2170225,2561442,3023182,3568157,4211372,4970538,5866554,6924092,8172266,9645444,11384185,13436361,15858473,18717208,22091275,26073570,30773734,36321177,42868631,50596365,59717144,70482083,83187570,98183417,115882499,136772114,161427405,190527194,224872670,265409450,313253612,369722426,436370616,515033175,607875879,717454917,846787273,999433788],
  2085. glass:[602,985,1434,1959,2572,3032,4130,5113,6263,7608,9183,11024,13179,15701,18650,22102,26139,30864,36391,42859,50426,59279,69637,81756,95936,112525,131936,154646,173476,212303,248675,291232,341021,399276,467434,547178,640478,749641,877361,1026793,1201676,1406345,1645873,1926197,2254267,2638213,3087553,3613423,4228860,4949118,5792050,6778550,7933070,9284227,10865512,12716122,14881927,17416611,20383001,23854625,27917535,32672439,38237196,44749739,52371496,61291388,71730512,83947623,98245548,114978689,134561813,157480325,184302310,215692604,252429279,295422930,345739242,404625407,473541040,554194356],
  2086. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2087. capacity:[32000,65401,101073,139585,181437,227119,277128,331991,392268,458564,531535,611896,700427,797983,905498,1024000,1154614,1298578,1457248,1632119,1824830,2037185,2271165,2528951,2812939,3125764,3470326,3849813,4267731,4727939,5234678,5792619,6406896,7083160,7827629,8647143,9549229,10542172,11635086,12838003,14161964,15619122,17222851,18987875,20930401,23068269,25421121,28010583,30860463,33996977,37448993,41248299,45429902,50032358,55098129,60673986,66811447,73567262,81003948,89190382,98202448,108123754,119046431,131072000,144312338,158890744,174943109,192619216,212084166,233519956,257127222,283127160,311763649,343305589,378049493,416322336,458484710,504934306,556109751,612494861],
  2088. time :{a:32000, b:13, c:1.17, d:2160},
  2089. icon :'/cdn/all/both/img/city/dump_l.png',
  2090. },
  2091. embassy: {
  2092. maxlevel:78,
  2093. wood:[208,356,535,750,1008,1317,1689,2134,2668,331,4078,5002,611,7439,9036,1095,13247,16004,19313,23283,28048,33764,40625,48857,58737,70592,84817,101888,122373,146955,176454,211853,254352,305378,366639,440191,528497,634519,761809,914635,1098120,1318413,1582899,1900444,2281690,2739419,3288971,3948770,4740930,5692004,6833873,8204811,9850772,11826928,14199519,17048074,20468075,24574160,29503963,35422730,42528856,51060537,61303752,73601851,88367060,106094308,127377805,152930967,183610329,220444254,264667405,317762127,381508140,458042191,549929678,660250643,792703010,951726544],
  2094. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2095. marble:[133,294,491,731,1023,1381,1816,2347,2996,3787,4753,593,7366,9119,11257,13865,17048,20931,25667,31446,38497,47097,57591,70393,86012,105066,128312,156673,191273,233485,284984,347812,424491,518075,632291,771688,941816,1149450,1402860,1712137,2089598,2550275,3112514,3798706,4636175,5658276,6905710,8428156,10286243,12553968,15321640,18699477,22822000,27853382,33993991,41488371,50634975,61798058,75422175,92049891,112343384,137110819,167338529,204230298,249255296,304206590,371272550,453123999,553020573,674940534,823739201,1005342303,1226981968,1497484733,1827623048,2230544282,2722294292,3322456440],
  2096. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2097. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2098. time :{a:96000, b:7, c:1.05, d:10080},
  2099. icon :'/cdn/all/both/img/city/embassy_l.png',
  2100. },
  2101. fireworker: {
  2102. maxlevel:50,
  2103. wood:[234,303,382,473,578,699,837,996,1180,1391,1633,1911,2232,2601,3024,3512,4072,4717,5458,6311,7291,8419,9715,11206,12921,14893,17161,19768,22767,26216,30182,34744,39994,46038,52996,61005,70225,80839,93056,107119,123308,141943,163395,188088,216514,249236,286902,330262,380174,437629],
  2104. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2105. marble:[116,182,259,348,452,571,711,872,1060,1277,1529,1823,2162,2555,3012,3542,4157,4869,5696,6655,7768,9059,10556,12292,14307,16644,19356,22500,26148,30379,35288,40981,47593,55271,64188,74543,86571,100537,116757,135594,157471,182876,212380,246644,286436,332648,386316,448642,521023,605081],
  2106. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2107. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2108. time :{a:125660, b:37, c:1.06, d:2628},
  2109. icon :'/cdn/all/both/img/city/fireworker_l.png',
  2110. },
  2111. forester: {
  2112. maxlevel:61,
  2113. wood:[215,369,571,832,1173,1615,2189,2936,3907,5171,6812,8946,1172,15327,20015,26111,34034,44334,57725,75133,97764,127184,165429,215148,279783,36381,473043,615046,79965,1039635,1351616,1757192,2284467,2969962,3861151,5019757,6526023,8484269,11030122,14339902,18642840,24236948,31509665,40964688,53256856,69237501,90013417,117023507,152138445,197790230,257140627,334300142,434612711,565025809,734571624,954992608,1241554740,1614104820,2098445027,2728119932,3546739737],
  2114. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2115. marble:[0,89,203,352,546,798,1125,155,2103,2822,3756,4971,6550,8603,11272,14742,19252,25115,32738,42647,55529,72276,94047,122348,159140,206971,269149,349982,455064,591671,769260,1000126,1300278,1690511,2197858,2857466,3715034,4829968,6279511,8164084,10614242,13799729,17941227,23325646,30326008,39427279,51259973,66643826,86644597,112647886,146455135,190408425,247552729,321846860,418437727,544016901,707284190,919550337,1195520604,1554313513,2020785330],
  2116. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2117. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2118. time :{a:72000, b:11, c:1.1, d:6120},
  2119. icon :'/cdn/all/both/img/city/forester_l.png',
  2120. },
  2121. glassblowing: {
  2122. maxlevel:61,
  2123. wood:[235,401,617,898,1263,1738,2354,3157,4199,5554,7316,9607,12585,16456,21488,2803,36535,47591,61963,80649,104938,136516,177565,230931,300306,390494,507737,660154,858296,1115880,1450740,1886057,2451998,3187758,4144296,5387857,7004569,9106400,11838919,15391374,20009798,26014054,33819980,43968198,57161548,74313771,96612788,125602976,163292127,212290500,275991607,358807234,466472992,606445556,788419092,1024996652,1332563034,1732419548,2252259306,2928085165,3806703211],
  2124. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2125. marble:[0,99,219,374,577,840,1182,1627,2205,2955,3931,5202,6852,8997,11786,15412,20125,26253,34219,44574,58037,75538,98289,127865,166315,216300,281279,365753,475568,618329,803918,1045183,1358855,1766663,2296859,2986173,3882359,5047500,6562314,8531740,11092214,14421117,18749061,24375871,31691353,41202296,53567581,69643833,90544754,117718283,153046903,198978051,258693667,336330631,437267348,568496341,739108674,960923743,1249308082,1624239900,2111693096],
  2126. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2127. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2128. time :{a:72000, b:11, c:1.1, d:6120},
  2129. icon :'/cdn/all/both/img/city/glassblowing_l.png',
  2130. },
  2131. museum: {
  2132. maxlevel:36,
  2133. wood:[481,1234,2363,4055,6595,10405,16119,2469,37548,56833,85762,129155,194244,291878,438329,658006,987521,1481794,2223204,3335317,5003487,7505999,11260150,16891952,25340520,38014669,57027835,85550503,128338880,192528010,288821553,433276641,649981434,975071871,1462757402,2194360517],
  2134. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2135. marble:[240,1023,2212,4021,6769,10946,17296,26948,41618,63917,97812,149332,227642,346674,527603,802613,1220630,1856015,2821801,4289796,6521148,9913144,15069498,22907948,34823590,52937193,80472645,122330751,185961486,282689952,429732042,653258546,993053083,1509592842,2294812419,3488466488],
  2136. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2137. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2138. satisfaction:[20,41,63,88,114,144,176,211,250,294,341,395,453,518,590,670,759,857,965,1086,1219,1367,1530,1711,1912,2134,2380,2652,2953,3286,3655,4064,4516,5016,5569,6182],
  2139. maxBonus:[50,100,150,200,250,300,350,400,450,500,550,600,650,700,750,800,850,900,950,1000,1050,1100,1150,1200,1250,1300,1350,1400,1450,1500,1550,1600,1650,1700,1750,1800],
  2140. time :{a:18000, b:1, c:1.1, d:14040},
  2141. icon :'/cdn/all/both/img/city/museum_r.png',
  2142. },
  2143. optician: {
  2144. maxlevel:50,
  2145. wood:[102,161,231,311,405,513,638,784,952,1148,1376,1639,1944,2298,2710,3187,3741,4382,5127,5990,6992,8154,9503,11066,12881,14984,17426,20257,23541,27352,31771,36898,42851,49765,57795,67121,77951,90528,105135,122100,141801,164681,191253,222113,257950,299572,347908,404045,469238,544951],
  2146. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2147. marble:[0,30,82,143,214,296,391,502,630,778,951,1150,1382,1652,1963,2325,2745,3232,3797,4453,5213,6094,7117,8304,9681,11277,13129,15277,17770,20661,24014,27905,32425,37679,43783,50877,59119,68698,79827,92761,107789,125252,145545,169125,196525,228365,265363,308354,358312,416362],
  2148. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2149. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2150. time :{a:125660, b:37, c:1.06, d:2772},
  2151. icon :'/cdn/all/both/img/city/optician_l.png',
  2152. },
  2153. palace: {
  2154. maxlevel:20,
  2155. wood:[612,5008,13801,31386,66557,136898,27758,558944,1121673,2247131,4079425,7405756,13444352,24406772,44307863,80436146,146023147,265089275,481240988,873641108],
  2156. wine:[0,0,0,9372,19014,38299,76868,154007,308284,616838,1233946,2468433,4937948,9878058,19760441,39529536,79076377,158187373,316443997,633026529],
  2157. marble:[0,1233,3909,9262,19967,41378,84199,169841,341125,683694,1368832,2740554,5486893,10985369,21993924,44034272,88161489,176509062,353390684,707527273],
  2158. glass:[0,0,0,0,18221,36464,72948,145917,291856,583733,1167487,2335016,4670116,9340398,18681126,37362913,74727147,149456937,298919160,597848890],
  2159. sulfur:[0,0,2656,8858,21263,46072,95691,194928,393402,790351,1584248,3175603,6365454,12759465,25576175,51267094,102764189,205989415,412902967,827658356],
  2160. time :{a:11520, b:1, c:1.4, d:0},
  2161. icon :'/cdn/all/both/img/city/palace_l.png',
  2162. },
  2163. palaceColony:{
  2164. maxlevel:20,
  2165. wood:[612,5008,13801,31386,66557,136898,27758,558944,1121673,2247131,4079425,7405756,13444352,24406772,44307863,80436146,146023147,265089275,481240988,873641108],
  2166. wine:[0,0,0,9372,19014,38299,76868,154007,308284,616838,1233946,2468433,4937948,9878058,19760441,39529536,79076377,158187373,316443997,633026529],
  2167. marble:[0,1233,3909,9262,19967,41378,84199,169841,341125,683694,1368832,2740554,5486893,10985369,21993924,44034272,88161489,176509062,353390684,707527273],
  2168. glass:[0,0,0,0,18221,36464,72948,145917,291856,583733,1167487,2335016,4670116,9340398,18681126,37362913,74727147,149456937,298919160,597848890],
  2169. sulfur:[0,0,2656,8858,21263,46072,95691,194928,393402,790351,1584248,3175603,6365454,12759465,25576175,51267094,102764189,205989415,412902967,827658356],
  2170. time :{a:11520, b:1, c:1.4, d:0},
  2171. icon :'/cdn/all/both/img/city/palaceColony_l.png',
  2172. },
  2173. pirateFortress:{
  2174. maxlevel:30,
  2175. wood:[387,779,1194,1664,2229,2947,3883,5117,6737,8844,11549,14976,19258,24539,30972,38724,47969,58894,71694,86577,103757,123463,145929,171405,200146,232419,268500,308676,353243,402507],
  2176. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2177. marble:[215,434,673,956,1319,1808,2479,3396,4633,6274,8412,11149,14594,18866,24096,30418,37979,46932,57441,69677,83818,100053,118579,139599,163326,189984,219798,253009,289861,330608],
  2178. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2179. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2180. time :{a:1550, b:1, c:1.2, d:1800},
  2181. icon :'/cdn/all/both/img/city/pirateFortress_l.png',
  2182. },
  2183. port: {
  2184. maxlevel:74,
  2185. wood:[51,129,235,368,547,768,1038,1414,1811,2352,3041,3863,4892,6108,7611,954,11808,14673,18143,22329,27356,33703,41278,50493,61881,75359,92107,112468,136757,166786,20283,246402,299897,364631,441994,537638,652033,790936,959771,1164024,1412356,1711592,2073513,2513976,3045758,3690710,4471434,5417311,6563277,7951657,9633732,11671630,14140619,17131894,20755934,25146596,30466049,36910766,44718784,54178492,65639285,79524466,96346887,116727883,141420228,171335934,207579939,251490917,304690720,369144286,447232209,541838670,656457962,795323552],
  2186. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2187. marble:[0,0,0,0,0,151,280,464,680,978,1374,1871,2518,3318,4343,57,7366,9536,12267,15687,19949,25492,32366,41024,52078,65675,83109,105055,132228,166917,210098,264169,332782,418793,525453,6616,830582,1043000,1310271,1645242,2066850,2593492,3253373,4084635,5124763,6431252,8069741,10125666,12705379,15942324,20003946,25100346,31495153,39519164,49587450,62220829,78072811,97963398,122921504,154238178,193533392,242839836,304708068,382338449,479746700,601971622,755335750,947772410,1189236100,1492217420,1872389200,2349417230,2947977544,3699032889],
  2188. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2189. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2190. loadingSpeed:[30,60,93,129,169,213,261,315,373,437,508,586,672,766,869,983,1108,1246,1398,1565,1748,195,2172,2416,2685,298,3305,3663,4056,4489,4965,5488,6064,6698,7394,8161,9004,9931,10951,12073,13308,14666,16159,17802,19609,21597,23784,2616,288,3174,3498,3852,4242,4668,5142,5664,624,687,7566,8334,9174,10104,1113,12258,13494,14862,16368,1803,19854,21864,24078,26514,29202,3216],
  2191. time :{a:50400, b:23, c:1.15, d:1512},
  2192. icon :'/cdn/all/both/img/city/port_r.png',
  2193. },
  2194. safehouse: {
  2195. maxlevel:60,
  2196. wood:[97,213,345,497,669,866,1089,1345,1636,1967,2346,2777,3268,3829,4467,5196,6026,6972,8052,9281,10683,12282,14104,16181,1855,21249,24327,27836,31836,36396,41593,47519,54288,62022,70857,80952,92484,105659,120712,137908,157555,180,205642,234938,268407,306644,350328,400235,457252,522392,596811,681832,778964,889934,1016714,1161553,1327026,1516072,1732050,1978795],
  2197. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2198. marble:[0,0,0,110,169,236,314,405,509,632,774,937,1128,1349,1604,1902,2247,2647,3110,3648,4272,4996,5836,6810,7940,9251,10772,12536,14582,16955,19708,22902,26613,30927,35939,41764,48532,56397,65538,76159,88501,102844,119511,13888,161387,187542,217936,253256,294299,341995,39742,461827,536672,623647,724717,842167,978652,1137256,1321562,1535739],
  2199. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2200. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2201. time :{a:96000, b:7, c:1.05, d:12960},
  2202. icon :'/cdn/all/both/img/city/safehouse_l.png',
  2203. },
  2204. shipyard: {
  2205. maxlevel:38,
  2206. wood:[90,173,278,410,577,786,105,1383,1802,2331,2997,3835,4892,6224,7903,10017,12681,16039,20268,25597,32312,40774,51434,64868,81792,103119,129989,163847,206506,260258,327985,41332,520843,656322,827026,1042112,1313121,1654593],
  2207. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2208. marble:[0,0,0,0,0,669,904,1201,1575,2047,2641,339,4332,5521,7018,8904,11281,14276,1805,22804,28796,36344,45856,5784,7294,91966,11594,146145,184205,23216,292584,368717,464645,585515,737811,929703,1171488,1476136],
  2209. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2210. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2211. time :{a:64800, b:7, c:1.05, d:7128},
  2212. icon :'/cdn/all/both/img/city/shipyard_l.png',
  2213. },
  2214. stonemason: {
  2215. maxlevel:61,
  2216. wood:[235,401,617,898,1263,1738,2354,3157,4199,5554,7316,9607,12585,16456,21488,28030,36535,47591,61963,80649,104938,136516,177565,230931,300306,390494,507737,660154,858296,1115880,1450740,1886057,2451998,3187758,4144296,5387857,7004569,9106400,11838919,15391374,20009798,26014054,33819980,43968198,57161548,74313771,96612788,125602976,163292127,212290500,275991607,358807234,466472992,606445556,788419092,1024996652,1332563034,1732419548,2252259306,2928085165,3806703211],
  2217. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2218. marble:[0,99,219,374,577,840,1182,1627,2205,2955,3931,5202,6852,8997,11786,15412,20125,26253,34219,44574,58037,75538,98289,127865,166315,216300,281279,365753,475568,618329,803918,1045183,1358855,1766663,2296859,2986173,3882359,5047500,6562314,8531740,11092214,14421117,18749061,24375871,31691353,41202296,53567581,69643833,90544754,117718283,153046903,198978051,258693667,336330631,437267348,568496341,739108674,960923743,1249308082,1624239900,2111693096],
  2219. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2220. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2221. time :{a:72000, b:11, c:1.1, d:6120},
  2222. icon :'/cdn/all/both/img/city/stonemason_l.png',
  2223. },
  2224. temple: {
  2225. maxlevel:56,
  2226. wood:[185,196,286,399,514,653,823,1029,1231,1524,1816,2160,2650,3143,3833,4408,5359,6163,7471,8812,10134,12236,14407,16568,19939,22931,27543,31674,37201,43672,51248,58934,69131,82618,93217,111324,128023,149952,175636,205722,240960,282234,330578,387204,453528,531214,622206,728784,853617,999835,1171097,1371695,1606653,1881858,2204204,2581763],
  2227. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2228. marble:[148,163,249,363,487,646,850,1109,1384,1788,2223,2760,3533,4372,5565,6677,8471,10166,12858,15825,18990,23928,29398,35277,44302,53162,66630,79955,97989,120036,146983,176379,215889,269226,316976,395001,474001,579334,708075,865425,1057743,1292797,1580085,1931216,2360375,2884904,3525994,4309550,5267229,6437726,7868332,9616852,11753933,14365921,17558351,21460211],
  2229. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2230. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2231. time :{a:2160, b:1, c:1.1, d:0},
  2232. icon :'/cdn/all/both/img/city/temple_l.png',
  2233. },
  2234. tavern: {
  2235. maxlevel:70,
  2236. wood:[86,190,315,465,645,860,1119,143,1803,225,2787,3431,4203,5131,6244,758,9183,11106,13414,16183,19507,23495,28281,34023,40915,49185,59108,71017,85306,102455,123032,147725,177357,212916,255585,306789,368233,441967,530448,636624,764036,916929,1100402,1320569,1584770,1901810,2282259,2738814,3286701,3944191,4733208,5680066,6816336,8179914,9816267,11779966,14136495,16964436,20358093,24430637,29317873,35182779,42220933,50667036,60802744,72966054,87562577,105079069,126099653,151325310],
  2237. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2238. marble:[0,0,0,80,104,135,177,229,299,388,504,657,853,1109,1442,1875,2438,3169,412,5356,6963,9052,11768,15298,19887,25854,3361,43693,56801,73841,95994,124792,16223,210899,274168,356419,463345,602349,783054,1017969,1323361,1720368,2236479,2907424,3779650,4913547,6387610,8303891,10795057,14033573,18243642,23716732,30831747,40081266,52105638,67737320,88058503,114476037,148818827,193464446,251503743,326954818,425041200,552553478,718319416,933815102,1213959454,1578147056,2051590869,2667067736],
  2239. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2240. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2241. maxWine:[4,8,13,18,24,30,37,44,51,60,68,78,88,99,110,122,136,150,165,180,197,216,235,255,277,300,325,351,378,408,439,472,507,544,584,626,670,717,766,818,874,933,995,1060,1129,1203,1280,1361,1449,1541,1640,1745,1857,1976,2102,2237,2380,2532,2694,2867,3050,3246,3453,3675,3910,4160,4426,4710,5011,5332],
  2242. satisfaction:[12,24,36,48,61,73,86,99,112,125,138,152,165,179,193,207,222,236,251,266,282,297,313,329,345,361,378,395,412,430,448,466,484,502,521,540,560,580,600,620,641,662,683,705,727,749,772,795,819,843,867,891,916,942,968,994,1021,1048,1075,1103,1131,1160,1189,1219,1249,1280,1311,1343,1375,1408],
  2243. maxBonus:[60,120,181,242,304,367,430,494,559,624,691,758,826,896,966,1037,1109,1182,1256,1332,1408,1485,1564,1644,1725,1807,1891,1975,2061,2149,2238,2328,2419,2512,2606,2702,2800,2898,2999,3101,3204,3310,3416,3525,3635,3747,3861,3976,4094,4213,4334,4457,4582,4709,4838,4969,5103,5238,5375,5515,5657,5801,5947,6096,6247,6400,6556,6714,6875,7038],
  2244. time :{a:10800, b:1, c:1.06, d:10440},
  2245. icon :'/cdn/all/both/img/city/taverne_r.png',
  2246. wineUse: [0, 4, 8, 13, 18, 24, 30, 37, 44, 51, 60, 68, 78, 88, 99, 110, 122, 136, 150, 165, 180, 197, 216, 235, 255, 277, 300, 325, 351, 378, 408, 439, 472, 507, 544, 584, 626, 670, 717, 766, 818, 874, 933, 995, 1060, 1129, 1202, 1280, 1362],
  2247. },
  2248. townHall: {
  2249. maxlevel:66,
  2250. wood:[0,135,288,535,793,1195,1732,2327,3148,4107,5308,6943,8841,11199,14124,18047,21863,27765,34599,42385,52638,64331,80802,9721,12177,146383,180609,222632,270815,333385,405226,492419,59823,735066,892521,1095676,1315122,1613532,1957605,2374710,2880686,3494469,4239031,5142235,6237884,7566981,9179268,11135082,13507617,16385666,19876937,24112088,29249616,35481790,43041845,52212708,63337596,76832847,93203512,113062251,137152263,166375099,201824404,244826841,296991745,360271352],
  2251. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2252. marble:[0,0,0,0,245,473,804,1213,1798,2532,3501,4871,6567,8784,11674,15698,19995,26678,34915,44905,58539,75091,98986,12498,164305,207293,26843,347289,443409,572956,731026,932491,1189231,1534000,1955370,2520083,3175628,4090537,5210432,6636074,8451790,10764309,13709563,17460677,22238144,28322787,36072268,45942107,58512461,74522227,94912471,120881750,153956559,196081062,249731375,318061105,405086733,515923697,657087086,836874602,1065854305,1357485815,1728911472,2201963988,2804449784,3571783476],
  2253. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2254. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2255. time :{a:1800, b:1, c:1.17, d:-1080},
  2256. icon :'/cdn/all/both/img/city/townhall_l.png',
  2257. },
  2258. vineyard: {
  2259. maxlevel:50,
  2260. wood:[291,363,447,542,651,778,923,1091,1283,1504,1758,2050,2386,2773,3217,3728,4316,4992,5769,6664,7691,8874,10234,11797,13595,15664,18041,20776,23921,27538,31697,36480,41984,48318,55608,63998,73654,84767,97556,112275,129215,148711,171148,196970,226689,260891,300253,345554,397690,457692],
  2261. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2262. marble:[105,170,245,332,433,550,686,843,1026,1238,1484,1769,2100,2484,2930,3446,4046,4741,5547,6482,7568,8826,10286,11979,13944,16223,18866,21932,25489,29615,34401,39953,46401,53891,62588,72689,84421,98046,113870,132247,153591,178380,207168,240604,279436,324534,376912,437743,508391,590441],
  2263. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2264. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2265. time :{a:125660, b:37, c:1.06, d:2232},
  2266. icon :'/cdn/all/both/img/city/vineyard_l.png',
  2267. },
  2268. wall: {
  2269. maxlevel:48,
  2270. wood:[98,310,565,870,1237,1677,2205,2839,3599,4512,5608,6922,8498,10391,12662,15387,18657,22581,2729,32941,39722,47859,57623,6934,83401,100275,120522,144819,173976,208964,250949,301332,361791,434343,521404,625877,751246,901687,1082217,1298853,1558817,1870773,2245120,2694337,3233397,3880269,4656516,5588011],
  2271. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2272. marble:[0,174,443,767,1155,1621,218,285,3655,4621,578,717,8839,10842,13245,16129,19589,23742,28725,34705,41881,50491,60824,73223,88103,105958,127384,153096,18395,220975,265404,31872,382698,459473,551602,662157,794823,954022,1145061,1374308,1649405,1979520,2375658,2851025,3421465,4105993,4927426,5913145],
  2273. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2274. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2275. time :{a:57600, b:11, c:1.1, d:3240},
  2276. icon :'/cdn/all/both/img/city/wall.png',
  2277. },
  2278. warehouse: {
  2279. maxlevel:85,
  2280. wood:[137,247,380,538,728,957,123,1559,1953,2426,2995,3676,4494,5476,6653,8066,9763,11798,1424,17171,20688,24908,29972,36049,43342,52093,62594,75195,90318,108464,130239,156369,187725,225353,270506,324691,389711,467736,561366,673722,808565,970396,1164618,1397712,1677459,2013197,2416131,2899711,3480078,4176604,5012536,6015778,7219815,8664836,10399072,12480410,14978319,17976177,21574046,25892014,31074209,37293603,44757785,53715898,64466945,77369775,92855062,111439673,133743930,160512304,192638273,231194140,277466828,333000831,399649768,479638253,575636150,690847686,829118402,995063513,1194221950,1433241242,1720099398,2064371199,2477547780],
  2281. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2282. marble:[0,0,0,82,181,300,442,614,819,1066,1362,1717,2143,2653,3268,4004,4887,5946,7218,8745,10577,12775,15412,18577,22376,26934,32403,38966,46842,56293,67634,81245,97576,117174,140691,168911,202776,243415,29218,350699,420938,505244,606436,727895,87368,1048662,1258691,1510786,1813370,2176555,2612483,3135717,3763747,4517560,5422349,6508351,7811859,9376439,11254377,13508432,16213936,19461305,23359065,28037479,33652897,40392985,48482997,58193295,69848398,83837814,100629067,120783314,144974107,174009893,208861041,250692266,300901556,361166892,433502324,520325282,624537363,749621309,899757388,1079963105,1296260886],
  2283. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2284. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2285. capacity:[8000,16401,25455,35331,46181,58159,71421,86138,102493,120687,140942,163502,188637,216646,24786,282647,321416,364622,412768,466416,526189,592779,666959,749584,841609,944094,1058219,1185297,1326787,1484315,1659690,1854922,2072252,2314171,2583453,2883186,3216807,3588142,4001450,4461476,4973499,5543400,6177729,6883779,7669673,8544460,9518219,10602179,11808851,13152172,14647676,16312668,18166439,20230485,22528769,25088000,27937955,31111829,34646637,38583648,42968887,47853679,53295269,59357506,66111616,73637056,82022473,91366775,101780329,113386298,126322135,140741251,156814887,174734197,194712581,216988297,241827374,269526873,300418536,334872863,373303675,416173213,463997848,517354466,576887609],
  2286. time :{a:2880, b:1, c:1.14, d:2160},
  2287. icon :'/cdn/all/both/img/city/warehouse_l.png',
  2288. },
  2289. winegrower: {
  2290. maxlevel:61,
  2291. wood:[235,401,617,898,1263,1738,2354,3157,4199,5554,7316,9607,12585,16456,21488,28030,36535,47591,61963,80649,104938,136516,177565,230931,300306,390494,507737,660154,858296,1115880,1450740,1886057,2451998,3187758,4144296,5387857,7004569,9106400,11838919,15391374,20009798,26014054,33819980,43968198,57161548,74313771,96612788,125602976,163292127,212290500,275991607,358807234,466472992,606445556,788419092,1024996652,1332563034,1732419548,2252259306,2928085165,3806703211],
  2292. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2293. marble:[0,99,219,374,577,840,1182,1627,2205,2955,3931,5202,6852,8997,11786,15412,20125,26253,34219,44574,58037,75538,98289,127865,166315,216300,281279,365753,475568,618329,803918,1045183,1358855,1766663,2296859,2986173,3882359,5047500,6562314,8531740,11092214,14421117,18749061,24375871,31691353,41202296,53567581,69643833,90544754,117718283,153046903,198978051,258693667,336330631,437267348,568496341,739108674,960923743,1249308082,1624239900,2111693096],
  2294. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2295. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2296. time :{a:72000, b:11, c:1.1, d:6120},
  2297. icon :'/cdn/all/both/img/city/winegrower_l.png',
  2298. },
  2299. workshop: {
  2300. maxlevel:32,
  2301. wood:[189,329,489,671,879,1117,1387,1695,2046,2447,2904,3424,4017,4693,5465,6344,7346,8488,9791,11275,12967,14896,17096,19604,22462,25721,29436,33671,38498,44002,50277,57429],
  2302. wine:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2303. marble:[81,143,215,300,396,509,639,791,967,1171,1407,1682,2000,2369,2797,3294,3870,4539,5314,6214,7257,8468,9871,11500,13390,15581,18123,21072,24493,28461,33064,38404],
  2304. glass:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2305. sulfur:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  2306. time :{a:96000, b:7, c:1.05, d:11880},
  2307. icon :'/cdn/all/both/img/city/workshop_l.png',
  2308. },
  2309. },
  2310. Research: {
  2311. Seafaring: {
  2312. CARPENTRY: 2150,
  2313. DECK_WEAPONS: 1010,
  2314. PIRACY: 1170,
  2315. SHIP_MAINTENANCE: 1020,
  2316. DRAFT: 1130,
  2317. EXPANSION: 1030,
  2318. FOREIGN_CULTURES: 1040,
  2319. PITCH: 1050,
  2320. MARKET: 2070,
  2321. GREEK_FIRE: 1060,
  2322. COUNTERWEIGHT: 1070,
  2323. DIPLOMACY: 1080,
  2324. SEA_MAPS: 1090,
  2325. PADDLE_WHEEL_ENGINE: 1100,
  2326. CAULKING: 1140,
  2327. MORTAR_ATTACHMENT: 1110,
  2328. MASSIVE_RAM: 1150,
  2329. OFFSHORE_BASE: 1160,
  2330. SEAFARING_FUTURE: 1999,
  2331. },
  2332. Economy: {
  2333. CONSERVATION: 2010,
  2334. PULLEY: 2020,
  2335. WEALTH: 2030,
  2336. WINE_CULTURE: 2040,
  2337. IMPROVED_RESOURCE_GATHERING: 2130,
  2338. GEOMETRY: 2060,
  2339. ARCHITECTURE: 1120,
  2340. HOLIDAY: 2080,
  2341. LEGISLATION: 2170,
  2342. CULINARY_SPECIALITIES: 2050,
  2343. HELPING_HANDS: 2090,
  2344. SPIRIT_LEVEL: 2100,
  2345. WINE_PRESS: 2140,
  2346. DEPOT: 2160,
  2347. BUREACRACY: 2110,
  2348. UTOPIA: 2120,
  2349. ECONOMIC_FUTURE: 2999,
  2350. },
  2351. Science: {
  2352. WELL_CONSTRUCTION: 3010,
  2353. PAPER: 3020,
  2354. ESPIONAGE: 3030,
  2355. POLYTHEISM: 3040,
  2356. INK: 3050,
  2357. GOVERNMENT_FORMATION: 3150,
  2358. INVENTION: 3140,
  2359. CULTURAL_EXCHANGE: 3060,
  2360. ANATOMY: 3070,
  2361. OPTICS: 3080,
  2362. EXPERIMENTS: 3081,
  2363. MECHANICAL_PEN: 3090,
  2364. BIRDS_FLIGHT: 3100,
  2365. LETTER_CHUTE: 3110,
  2366. STATE_RELIGION: 3160,
  2367. PRESSURE_CHAMBER: 3120,
  2368. ARCHIMEDEAN_PRINCIPLE: 3130,
  2369. SCIENTIFIC_FUTURE: 3999,
  2370. },
  2371. Military: {
  2372. DRY_DOCKS: 4010,
  2373. MAPS: 4020,
  2374. PROFESSIONAL_ARMY: 4030,
  2375. SEIGE: 4040,
  2376. CODE_OF_HONOR: 4050,
  2377. BALLISTICS: 4060,
  2378. LAW_OF_THE_LEVEL: 4070,
  2379. GOVERNOR: 4080,
  2380. PYROTECHNICS: 4130,
  2381. LOGISTICS: 4090,
  2382. GUNPOWDER: 4100,
  2383. ROBOTICS: 4110,
  2384. CANNON_CASTING: 4120,
  2385. MILITARISTIC_FUTURE: 4999,
  2386. },
  2387. },
  2388. Government: {
  2389. ANARCHY: 'anarchie',
  2390. IKACRACY: 'ikakratie',
  2391. ARISTOCRACY: 'aristokratie',
  2392. DICTATORSHIP: 'diktatur',
  2393. DEMOCRACY: 'demokratie',
  2394. NOMOCRACY: 'nomokratie',
  2395. OLIGARCHY: 'oligarchie',
  2396. TECHNOCRACY: 'technokratie',
  2397. THEOCRACY: 'theokratie',
  2398. },
  2399. TradeGoodOrdinals: {
  2400. WINE: 1,
  2401. MARBLE: 2,
  2402. GLASS: 3,
  2403. SULFUR: 4,
  2404. },
  2405. Time: {
  2406. SECONDS_PER_HOUR: 3600,
  2407. SECONDS_PER_MINUTE: 60,
  2408. MILLIS_PER_DAY: 1000 * 60 * 60 * 24,
  2409. MILLIS_PER_HOUR: 1000 * 60 * 60,
  2410. MILLIS_PER_SECOND: 1000,
  2411. MILLIS_PER_MINUTE: 60000,
  2412. MINUTES_PER_DAY: 24 * 60,
  2413. MINUTES_PER_HOUR: 60,
  2414. HOURS_PER_DAY: 24,
  2415. HOURS_PER_WEEK: 24 * 7,
  2416. SAFE_TIME_DELTA: 1000,
  2417. INITIAL_PAGE_LOAD_DELTA: 2000,
  2418. },
  2419. GamePlay: {
  2420. BUILDING_SPOTS: 24,
  2421. HAPPINESS_PER_CULTURAL_GOOD: 50,
  2422. HAPPINESS_PER_WINE_SERVING_LEVEL: 60,
  2423. BASE_RESOURCE_PROTECTION: 100,
  2424. RESOURCES_PER_TRANSPORT: 500,
  2425. RESOURCE_PROTECTION_WAREHOUSE: 480,
  2426. RESOURCE_PROTECTION_WAREHOUSE_INACTIVE: 80,
  2427. },
  2428. BaseView: {
  2429. ISLAND: 'island',
  2430. CITY: 'city',
  2431. WORLD: 'worldview',
  2432. },
  2433. Military: {
  2434. // Army
  2435. HOPLITE: 'phalanx',
  2436. STEAM_GIANT: 'steamgiant',
  2437. SPEARMAN: 'spearman',
  2438. SWORDSMAN: 'swordsman',
  2439. SLINGER: 'slinger',
  2440. ARCHER: 'archer',
  2441. GUNNER: 'marksman',
  2442. BATTERING_RAM: 'ram',
  2443. CATAPULT: 'catapult',
  2444. MORTAR: 'mortar',
  2445. GYROCOPTER: 'gyrocopter',
  2446. BALLOON_BOMBADIER: 'bombardier',
  2447. COOK: 'cook',
  2448. DOCTOR: 'medic',
  2449. ARMY: 'army',
  2450. // Navy
  2451. RAM_SHIP: 'ship_ram',
  2452. FLAME_THROWER: 'ship_flamethrower',
  2453. STEAM_RAM: 'ship_steamboat',
  2454. BALLISTA_SHIP: 'ship_ballista',
  2455. CATAPULT_SHIP: 'ship_catapult',
  2456. MORTAR_SHIP: 'ship_mortar',
  2457. SUBMARINE: 'ship_submarine',
  2458. PADDLE_SPEED_SHIP: 'ship_paddlespeedship',
  2459. BALLOON_CARRIER: 'ship_ballooncarrier',
  2460. TENDER: 'ship_tender',
  2461. ROCKET_SHIP: 'ship_rocketship',
  2462. NAVY: 'navy',
  2463. },
  2464. UnitData: {
  2465. spearman: {
  2466. minimumBuildingLevelToBuild: 1,
  2467. baseBuildTime: 60,
  2468. isArmy: true,
  2469. speed: 60,
  2470. cargoSize: 3,
  2471. },
  2472. slinger: {
  2473. minimumBuildingLevelToBuild: 2,
  2474. baseBuildTime: 90,
  2475. isArmy: true,
  2476. speed: 60,
  2477. cargoSize: 3,
  2478. },
  2479. ram: {
  2480. minimumBuildingLevelToBuild: 3,
  2481. baseBuildTime: 600,
  2482. isArmy: true,
  2483. speed: 40,
  2484. cargoSize: 30,
  2485. },
  2486. phalanx: {
  2487. minimumBuildingLevelToBuild: 4,
  2488. baseBuildTime: 300,
  2489. isArmy: true,
  2490. speed: 60,
  2491. cargoSize: 5,
  2492. },
  2493. cook: {
  2494. minimumBuildingLevelToBuild: 5,
  2495. baseBuildTime: 1200,
  2496. isArmy: true,
  2497. speed: 40,
  2498. cargoSize: 20,
  2499. },
  2500. swordsman: {
  2501. minimumBuildingLevelToBuild: 6,
  2502. baseBuildTime: 180,
  2503. isArmy: true,
  2504. speed: 60,
  2505. cargoSize: 3,
  2506. },
  2507. archer: {
  2508. minimumBuildingLevelToBuild: 7,
  2509. baseBuildTime: 240,
  2510. isArmy: true,
  2511. speed: 60,
  2512. cargoSize: 5,
  2513. },
  2514. catapult: {
  2515. minimumBuildingLevelToBuild: 8,
  2516. baseBuildTime: 1800,
  2517. isArmy: true,
  2518. speed: 40,
  2519. cargoSize: 30,
  2520. },
  2521. medic: {
  2522. minimumBuildingLevelToBuild: 9,
  2523. baseBuildTime: 1200,
  2524. isArmy: true,
  2525. speed: 60,
  2526. cargoSize: 10,
  2527. },
  2528. gyrocopter: {
  2529. minimumBuildingLevelToBuild: 10,
  2530. baseBuildTime: 900,
  2531. isArmy: true,
  2532. speed: 80,
  2533. cargoSize: 15,
  2534. },
  2535. bombardier: {
  2536. minimumBuildingLevelToBuild: 11,
  2537. baseBuildTime: 1800,
  2538. isArmy: true,
  2539. speed: 20,
  2540. cargoSize: 30,
  2541. },
  2542. steamgiant: {
  2543. minimumBuildingLevelToBuild: 12,
  2544. baseBuildTime: 900,
  2545. isArmy: true,
  2546. speed: 40,
  2547. cargoSize: 15,
  2548. },
  2549. marksman: {
  2550. minimumBuildingLevelToBuild: 13,
  2551. baseBuildTime: 600,
  2552. isArmy: true,
  2553. speed: 60,
  2554. cargoSize: 5,
  2555. },
  2556. mortar: {
  2557. minimumBuildingLevelToBuild: 14,
  2558. baseBuildTime: 2400,
  2559. isArmy: true,
  2560. speed: 40,
  2561. cargoSize: 30,
  2562. },
  2563. barbarian: {
  2564. minimumBuildingLevelToBuild: 1,
  2565. baseBuildTime: 1,
  2566. isArmy: true,
  2567. speed: 40,
  2568. cargoSize: 5,
  2569. },
  2570. ship_ram: {
  2571. minimumBuildingLevelToBuild: 1,
  2572. baseBuildTime: 2400,
  2573. isArmy: false,
  2574. speed: 40,
  2575. cargoSize: 0,
  2576. },
  2577. ship_flamethrower: {
  2578. minimumBuildingLevelToBuild: 4,
  2579. baseBuildTime: 1800,
  2580. isArmy: false,
  2581. speed: 40,
  2582. cargoSize: 0,
  2583. },
  2584. ship_submarine: {
  2585. minimumBuildingLevelToBuild: 19,
  2586. baseBuildTime: 3600,
  2587. isArmy: false,
  2588. speed: 40,
  2589. cargoSize: 0,
  2590. },
  2591. ship_ballista: {
  2592. minimumBuildingLevelToBuild: 3,
  2593. baseBuildTime: 3000,
  2594. isArmy: false,
  2595. speed: 40,
  2596. cargoSize: 0,
  2597. },
  2598. ship_catapult: {
  2599. minimumBuildingLevelToBuild: 3,
  2600. baseBuildTime: 3000,
  2601. isArmy: false,
  2602. speed: 40,
  2603. cargoSize: 0,
  2604. },
  2605. ship_mortar: {
  2606. minimumBuildingLevelToBuild: 17,
  2607. baseBuildTime: 3000,
  2608. isArmy: false,
  2609. speed: 30,
  2610. cargoSize: 0,
  2611. },
  2612. ship_steamboat: {
  2613. minimumBuildingLevelToBuild: 15,
  2614. baseBuildTime: 2400,
  2615. isArmy: false,
  2616. speed: 40,
  2617. cargoSize: 0,
  2618. },
  2619. ship_rocketship: {
  2620. minimumBuildingLevelToBuild: 11,
  2621. baseBuildTime: 3600,
  2622. isArmy: false,
  2623. speed: 30,
  2624. cargoSize: 0,
  2625. },
  2626. ship_paddlespeedship: {
  2627. minimumBuildingLevelToBuild: 13,
  2628. baseBuildTime: 1800,
  2629. isArmy: false,
  2630. speed: 60,
  2631. cargoSize: 0,
  2632. },
  2633. ship_ballooncarrier: {
  2634. minimumBuildingLevelToBuild: 7,
  2635. baseBuildTime: 3900,
  2636. isArmy: false,
  2637. speed: 20,
  2638. cargoSize: 0,
  2639. },
  2640. ship_tender: {
  2641. minimumBuildingLevelToBuild: 9,
  2642. baseBuildTime: 2400,
  2643. isArmy: false,
  2644. speed: 30,
  2645. cargoSize: 0,
  2646. },
  2647. },
  2648. UnitIds: {
  2649. 301: 'slinger',
  2650. 302: 'swordsman',
  2651. 303: 'phalanx',
  2652. 304: 'marksman',
  2653. 305: 'mortar',
  2654. 306: 'catapult',
  2655. 307: 'ram',
  2656. 308: 'steamgiant',
  2657. 309: 'bombardier',
  2658. 310: 'cook',
  2659. 311: 'medic',
  2660. 312: 'gyrocopter',
  2661. 313: 'archer',
  2662. 315: 'spearman',
  2663. 316: 'barbarian',
  2664. 210: 'ship_ram',
  2665. 211: 'ship_flamethrower',
  2666. 212: 'ship_submarine',
  2667. 213: 'ship_ballista',
  2668. 214: 'ship_catapult',
  2669. 215: 'ship_mortar',
  2670. 216: 'ship_steamboat',
  2671. 217: 'ship_rocketship',
  2672. 218: 'ship_paddlespeedship',
  2673. 219: 'ship_ballooncarrier',
  2674. 220: 'ship_tender',
  2675. },
  2676. IkariamAjaxResponseType: {
  2677. RELOAD: 'reload',
  2678. PROVIDE_FEEDBACK: 'provideFeedback',
  2679. QUEST_DATA: 'questData',
  2680. UPDATE_GLOBAL_DATA: 'updateGlobalData',
  2681. UPDATE_TEMPLATE_DATA: 'updateTemplateData',
  2682. UPDATE_BACKGROUND_DATA: 'updateBackgroundData',
  2683. CLOSE_VIEW: 'closeView',
  2684. CHANGE_VIEW: 'changeView',
  2685. ADD_WINDOW: 'addWindow',
  2686. CREATE_VIEW: 'createView',
  2687. EVAL_SCRIPT: 'evalScript',
  2688. },
  2689. CityType: {
  2690. OWN: 'ownCity',
  2691. DEPLOYMENT: 'deployedCities',
  2692. OCCUPATION: 'occupiedCities',
  2693. },
  2694. View: {
  2695. CITY_DETAIL: 'cityDetails',
  2696. CITY_MILITARY: 'cityMilitary',
  2697. RELATED_CITIES: 'relatedCities',
  2698. ACADEMY: 'academy',
  2699. PALACE: 'palace',
  2700. MUSEUM: 'museum',
  2701. ASSIGN_CULTURAL_POSSESSIONS: 'culturalPossessions_assign',
  2702. TOWN_HALL: 'townHall',
  2703. TEMPLE: 'temple',
  2704. RESEARCH_ADVISOR: 'researchAdvisor',
  2705. FINANCES: 'finances',
  2706. BARRACKS: 'barracks',
  2707. SHIPYARD: 'shipyard',
  2708. PIRATE_FORTRESS: 'pirateFortress',
  2709. MILITARY_ADVISOR: 'militaryAdvisor',
  2710. MILITARY_ADVISOR_REPORT: 'militaryAdvisorReportView',
  2711. PREMIUM: 'premium',
  2712. TRANSPORT: 'transport',
  2713. DEPLOY: 'deployment',
  2714. BRANCH_OFFICE: 'branchOffice',
  2715. BLACK_MARKET: 'blackMarket',
  2716. TAKE_OFFER: 'takeOffer',
  2717. RESOURCE: 'resource',
  2718. TRADE_GOOD: 'tradegood',
  2719. ABOLISH_CITY: 'abolishCity',
  2720. HIDEOUT: 'safehouse',
  2721. PILLAGE: 'plunder',
  2722. BLOCKADE: 'blockade',
  2723. SEND_SPY: 'sendSpy',
  2724. SPY_MISSION: 'spyMissions',
  2725. HIGH_SCORE: 'highscore',
  2726. ALLIANCE_PAGE: 'allyPage',
  2727. OCCUPY: 'occupy',
  2728. COLONIZE: 'colonize',
  2729. },
  2730. PlayerState: {
  2731. INACTIVE: 'inactive',
  2732. NORMAL: '',
  2733. VACATION: 'vacation',
  2734. NEW: 'noob',
  2735. },
  2736. CombatType: {
  2737. BLOCKADE: 'blockade',
  2738. PILLAGE: 'plunder',
  2739. },
  2740. };
  2741. var EmpireData = function() {
  2742. function Military(city) {
  2743. this._ikaToolsType = 'military';
  2744. this.lastArmyUpdate = 0;
  2745. this.lastNavyUpdate = 0;
  2746. this.present = new MilitaryUnits();
  2747. this.armyTrainingBatches = [];
  2748. this.navyTrainingBatches = [];
  2749. this._setCity(city);
  2750. }
  2751. $.extend(Military.prototype, {
  2752. _setCity: function setCity(city) {
  2753. if (city) {
  2754. this.city = Utils.fixedFunction(city);
  2755. this._startArmyTrainingTimers();
  2756. this._startNavyTrainingTimers();
  2757. }
  2758. },
  2759. _startTrainingTimers: function startTrainingTimers(batches) {
  2760. var military = this;
  2761. while (batches.length > 0 && batches[0].completionTime <= View.gameTimeNow()) {
  2762. var batch = batches.shift();
  2763. if ((batch.type == Constants.Military.ARMY &&
  2764. batch.getCompletionTime() > this.lastArmyUpdate) ||
  2765. (batch.type == Constants.Military.NAVY &&
  2766. batch.getCompletionTime() > this.lastNavyUpdate)) {
  2767. military.present._increment(batch.getUnits());
  2768. }
  2769. }
  2770. $.each(batches, function startTrainingBatchTimers(index, batch) {
  2771. if (batch.completionEvent) {
  2772. batch.completionEvent();
  2773. }
  2774. batch.completionEvent = militaryChangedEvent().scheduleSend(
  2775. 'MilitaryTrainingComplete[' + military.city().getId() + ']',
  2776. batch.getCompletionTime() - View.gameTimeNow() +
  2777. Constants.Time.SAFE_TIME_DELTA,
  2778. function militaryTrainingComplete() {
  2779. military.present._increment(batches.shift().getUnits());
  2780. empireData.saveAsync();
  2781. },
  2782. [{
  2783. city: military.city(),
  2784. military: military,
  2785. type: 'training_complete',
  2786. }]);
  2787. });
  2788. },
  2789. _startArmyTrainingTimers: function startArmyTrainingTimers() {
  2790. this._startTrainingTimers(this.armyTrainingBatches);
  2791. },
  2792. _startNavyTrainingTimers: function startNavyTrainingTimers() {
  2793. this._startTrainingTimers(this.navyTrainingBatches);
  2794. },
  2795. _updatePresent: function updatePresent(unit, count) {
  2796. return this.present._setCount(unit, count);
  2797. },
  2798. _markPresentUpdated: function markPresentUpdated(army, navy) {
  2799. if (army || army === undefined) {
  2800. this.lastArmyUpdate = View.gameTimeNow();
  2801. }
  2802. if (navy || navy === undefined) {
  2803. this.lastNavyUpdate = View.gameTimeNow();
  2804. }
  2805. },
  2806. _setArmyTrainingBatches: function setArmyTrainingBatches(batches) {
  2807. $.each(this.armyTrainingBatches, function cancelTrainingBatchTimer(index, batch) {
  2808. if (batch.completionEvent) {
  2809. batch.completionEvent();
  2810. }
  2811. });
  2812. this.armyTrainingBatches = batches;
  2813. this._startArmyTrainingTimers();
  2814. },
  2815. _setNavyTrainingBatches: function setNavyTrainingBatches(batches) {
  2816. $.each(this.navyTrainingBatches, function cancelTrainingBatchTimer(index, batch) {
  2817. if (batch.completionEvent) {
  2818. batch.completionEvent();
  2819. }
  2820. });
  2821. this.navyTrainingBatches = batches;
  2822. this._startNavyTrainingTimers();
  2823. },
  2824. _increment: function increment(units) {
  2825. this.present._increment(units);
  2826. },
  2827. _decrement: function decrement(units) {
  2828. this.present._decrement(units);
  2829. },
  2830. _clear: function clear() {
  2831. this.present._clear();
  2832. this.armyTrainingBatches = [];
  2833. this.navyTrainingBatches = [];
  2834. },
  2835. getTrainingBatches: function getTrainingBatches(batches) {
  2836. return $.merge($.merge([], this.armyTrainingBatches), this.navyTrainingBatches);
  2837. },
  2838. getPresent: function getPresent() {
  2839. return this.present;
  2840. },
  2841. });
  2842. function MilitaryUnits() {
  2843. this._ikaToolsType = 'militaryUnits';
  2844. this.units = {};
  2845. }
  2846. $.extend(MilitaryUnits.prototype, {
  2847. _setCount: function setCount(unit, count) {
  2848. var oldCount = this.units[unit];
  2849. this.units[unit] = count;
  2850. return oldCount != count;
  2851. },
  2852. _increment: function increment(units) {
  2853. $.each(units.units, function(unit, count) {
  2854. this._setCount(unit, (this.getCount(unit) || 0) + count);
  2855. }.bind(this));
  2856. },
  2857. _decrement: function decrement(units) {
  2858. $.each(units.units, function(unit, count) {
  2859. this._setCount(unit, Math.max(0, (this.getCount(unit) || 0) - count));
  2860. }.bind(this));
  2861. },
  2862. _clear: function clear() {
  2863. this.units = {};
  2864. },
  2865. getCount: function getCount(unit) {
  2866. return this.units[unit];
  2867. },
  2868. getCounts: function getCounts() {
  2869. return this.units;
  2870. },
  2871. getCargoSize: function getCargoSize() {
  2872. var cargoSize = 0;
  2873. $.each(this.units, function addCargoSize(unit, count) {
  2874. cargoSize += Constants.UnitData[unit].cargoSize * count;
  2875. });
  2876. return cargoSize;
  2877. },
  2878. });
  2879. function TrainingBatch(type, completionTime, units) {
  2880. this._ikaToolsType = 'trainingBatch';
  2881. this.type = type;
  2882. this.units = units;
  2883. this.completionTime = completionTime;
  2884. }
  2885. $.extend(TrainingBatch.prototype, {
  2886. getUnits: function getUnits() {
  2887. return this.units;
  2888. },
  2889. getCompletionTime: function getCompletionTime() {
  2890. return this.completionTime;
  2891. },
  2892. _getType: function getType() {
  2893. return this.type;
  2894. },
  2895. });
  2896. function City(id, type) {
  2897. this._ikaToolsType = 'city';
  2898. if (id) {
  2899. this.id = id;
  2900. this.type = type;
  2901. this.level = 0;
  2902. this.resources = {
  2903. wood: new City.Resource(this),
  2904. wine: new City.Resource(this),
  2905. marble: new City.Resource(this),
  2906. glass: new City.Resource(this),
  2907. sulfur: new City.Resource(this),
  2908. };
  2909. this.buildings = new Array(Constants.GamePlay.BUILDING_SPOTS);
  2910. for (var i = 0; i < Constants.GamePlay.BUILDING_SPOTS; i++) {
  2911. this.buildings[i] = new City.Building(this);
  2912. }
  2913. this.military = new Military();
  2914. this.actionPoints = 0;
  2915. this.scientists = 0;
  2916. this.culturalGoods = 0;
  2917. this.priests = 0;
  2918. this.tavernWineLevel = 0;
  2919. this.population = undefined;
  2920. this.resourceUpdateTime = 0;
  2921. this.islandCoordinates = undefined;
  2922. }
  2923. }
  2924. $.extend(City.prototype, {
  2925. _postLoad: function postLoad() {
  2926. while (this.buildings.length < Constants.GamePlay.BUILDING_SPOTS) {
  2927. this.buildings.push(new City.Building(this));
  2928. }
  2929. for (var i = 0; i < Constants.GamePlay.BUILDING_SPOTS; i++) {
  2930. this.buildings[i]._setCity(this);
  2931. }
  2932. if (this.isOwn()) {
  2933. this.resources[Constants.Resources.WOOD]._setCity(this);
  2934. this.resources[Constants.Resources.WINE]._setCity(this);
  2935. this.resources[Constants.Resources.MARBLE]._setCity(this);
  2936. this.resources[Constants.Resources.GLASS]._setCity(this);
  2937. this.resources[Constants.Resources.SULFUR]._setCity(this);
  2938. }
  2939. this.military._setCity(this);
  2940. },
  2941. _updateFromGlobalData: function _updateFromGlobalData(data, correctWineConsumption) {
  2942. var changes = [];
  2943. this._updateActionPoints(data.maxActionPoints, changes);
  2944. if (this.isOwn()) {
  2945. var wineConsumption = data.wineSpendings;
  2946. var baseWineConsumption = data.wineSpendings;
  2947. var winePress = this.getBuildingByType(Constants.Buildings.WINE_PRESS);
  2948. if (correctWineConsumption) {
  2949. if (winePress) {
  2950. wineConsumption = wineConsumption * (100 - winePress.getLevel()) / 100;
  2951. }
  2952. } else {
  2953. if (winePress) {
  2954. baseWineConsumption = Math.floor(
  2955. wineConsumption / (100 - winePress.getLevel()) * 100);
  2956. }
  2957. }
  2958. var use = Constants.BuildingData[Constants.Buildings.TAVERN].wineUse;
  2959. for (var i = 0; i < 48; i++) {
  2960. if (use[i] >= baseWineConsumption) {
  2961. this._updateTavernWineLevel(i, changes);
  2962. break;
  2963. }
  2964. }
  2965. this._updateResources(data.currentResources,
  2966. data.maxResources,
  2967. data.resourceProduction,
  2968. data.tradegoodProduction,
  2969. wineConsumption / Constants.Time.SECONDS_PER_HOUR,
  2970. changes);
  2971. }
  2972. raiseResourcesChanged(changes);
  2973. },
  2974. _updateFromBackgroundData: function _updateFromBackgroundData(data) {
  2975. this.islandId = parseInt(data.islandId);
  2976. if (this.isOwn()) {
  2977. this._updateBuildings(data.position);
  2978. }
  2979. },
  2980. _updateBuildings: function _updateBuildings(buildingsData) {
  2981. var changes = [];
  2982. for (var i = 0; i < Constants.GamePlay.BUILDING_SPOTS; i++) {
  2983. var building = this.buildings[i];
  2984. if (buildingsData[i]) {
  2985. if (building._update(i, buildingsData[i])) {
  2986. changes.push({
  2987. city: this,
  2988. building: building,
  2989. type: Constants.BuildingEventType.DATA_REFRESH,
  2990. });
  2991. }
  2992. }
  2993. }
  2994. this.level = this.buildings[0].getLevel();
  2995. raiseBuildingsChanged(changes);
  2996. },
  2997. _updateResources: function updateResources(
  2998. currentInfo, maxInfo, woodProduction, resourceProduction, wineConsumption,
  2999. changedAccumulator) {
  3000. this._updateResource(Constants.Resources.WOOD,
  3001. changedAccumulator,
  3002. currentInfo["resource"],
  3003. woodProduction);
  3004. this._updateResource(Constants.Resources.WINE,
  3005. changedAccumulator,
  3006. currentInfo["1"],
  3007. this.getTradeGoodType() == Constants.Resources.WINE ?
  3008. resourceProduction : undefined,
  3009. wineConsumption);
  3010. this._updateResource(Constants.Resources.MARBLE,
  3011. changedAccumulator,
  3012. currentInfo["2"],
  3013. this.getTradeGoodType() == Constants.Resources.MARBLE ?
  3014. resourceProduction : undefined);
  3015. this._updateResource(Constants.Resources.GLASS,
  3016. changedAccumulator,
  3017. currentInfo["3"],
  3018. this.getTradeGoodType() == Constants.Resources.GLASS ?
  3019. resourceProduction : undefined);
  3020. this._updateResource(Constants.Resources.SULFUR,
  3021. changedAccumulator,
  3022. currentInfo["4"],
  3023. this.getTradeGoodType() == Constants.Resources.SULFUR ?
  3024. resourceProduction : undefined);
  3025. this._updatePopulation(currentInfo.population, changedAccumulator);
  3026. this.resourceUpdateTime = View.gameTimeNow();
  3027. },
  3028. _updateResource: function updateResource(
  3029. name, changedAccumulator, current, max, production, consumption) {
  3030. var resource = this.resources[name];
  3031. if (resource._update(current, max, production, consumption)) {
  3032. changedAccumulator.push({
  3033. city: this,
  3034. type: name,
  3035. value: resource,
  3036. });
  3037. }
  3038. },
  3039. _incrementResource: function incrementResource(name, changedAccumulator, delta) {
  3040. var resource = this.resources[name];
  3041. if (delta) {
  3042. resource._increment(delta);
  3043. changedAccumulator.push({
  3044. city: this,
  3045. type: name,
  3046. value: resource,
  3047. });
  3048. }
  3049. },
  3050. _updateActionPoints: function updateActionPoints(actionPoints, changedAccumulator) {
  3051. if (this.actionPoints != actionPoints) {
  3052. changedAccumulator.push({
  3053. city: this,
  3054. type: Constants.Resources.ACTION_POINTS,
  3055. value: actionPoints,
  3056. });
  3057. }
  3058. this.actionPoints = actionPoints;
  3059. },
  3060. _updateActionPointsBy: function updateActionPointsBy(delta, changedAccumulator) {
  3061. this._updateActionPoints(
  3062. Math.max(0, Math.min(this.actionPoints + delta, this.getMaxActionPoints())),
  3063. changedAccumulator);
  3064. },
  3065. _updateScientists: function updateScientists(scientists, changedAccumulator) {
  3066. if (this.scientists != scientists) {
  3067. changedAccumulator.push({
  3068. city: this,
  3069. type: Constants.Resources.SCIENTISTS,
  3070. value: scientists,
  3071. });
  3072. }
  3073. this.scientists = scientists;
  3074. },
  3075. _updateTavernWineLevel: function updateTavernWineLevel(level, changedAccumulator) {
  3076. if (this.tavernWineLevel != level) {
  3077. changedAccumulator.push({
  3078. city: this,
  3079. type: Constants.Resources.TAVERN_WINE_LEVEL,
  3080. value: level,
  3081. });
  3082. }
  3083. this.tavernWineLevel = level;
  3084. },
  3085. _updateCulturalGoods: function updateCulturalGoods(culturalGoods, changedAccumulator) {
  3086. if (this.culturalGoods != culturalGoods) {
  3087. changedAccumulator.push({
  3088. city: this,
  3089. type: Constants.Resources.CULTURAL_GOODS,
  3090. value: culturalGoods,
  3091. });
  3092. }
  3093. this.culturalGoods = culturalGoods;
  3094. },
  3095. _updatePopulation: function updatePopulation(population, changedAccumulator) {
  3096. if (Math.abs(this.getPopulationData().population - population) >= 1) {
  3097. changedAccumulator.push({
  3098. city: this,
  3099. type: Constants.Resources.POPULATION,
  3100. value: population,
  3101. });
  3102. }
  3103. this.population = population;
  3104. },
  3105. _updatePriests: function updatePriests(priests, changedAccumulator) {
  3106. if (this.priests != priests) {
  3107. changedAccumulator.push({
  3108. city: this,
  3109. type: Constants.Resources.PRIESTS,
  3110. value: priests,
  3111. });
  3112. }
  3113. this.priests = priests;
  3114. },
  3115. getLastResourceUpdate: function getLastResourceUpdate() {
  3116. return this.resourceUpdateTime;
  3117. },
  3118. getTimeSinceResourceUpdate: function getTimeSinceLastResourceUpdate() {
  3119. return View.gameTimeNow() - this.resourceUpdateTime;
  3120. },
  3121. isOwn: function isOwn() {
  3122. return this.type == Constants.CityType.OWN;
  3123. },
  3124. isDeployment: function isDeployment() {
  3125. return this.type == Constants.CityType.DEPLOYMENT;
  3126. },
  3127. isOccupation: function isOccupation() {
  3128. return this.type == Constants.CityType.OCCUPATION;
  3129. },
  3130. getType: function getType() {
  3131. return this.type;
  3132. },
  3133. getBuildingByPosition: function getBuildingByPosition(position) {
  3134. return this.buildings[position];
  3135. },
  3136. getBuildingsByType: function getBuildingsByType(type) {
  3137. return this.buildings.filter(function buildingsFilter(building) {
  3138. return building.getType() == type;
  3139. });
  3140. },
  3141. getBuildingByType: function getBuildingByType(type) {
  3142. for (var i = 0; i < Constants.GamePlay.BUILDING_SPOTS; i++) {
  3143. var building = this.buildings[i];
  3144. if (building && building.getType() == type) {
  3145. return building;
  3146. }
  3147. }
  3148. return null;
  3149. },
  3150. getBuildings: function getBuildings() {
  3151. return this.buildings;
  3152. },
  3153. getMilitary: function getMilitary() {
  3154. return this.military;
  3155. },
  3156. getId: function getId() {
  3157. return this.id;
  3158. },
  3159. getName: function getName() {
  3160. return this.name;
  3161. },
  3162. getIslandId: function getIslandId() {
  3163. return this.islandId;
  3164. },
  3165. getTradeGoodType: function getTradeGoodType() {
  3166. return this.tradeGoodType;
  3167. },
  3168. getActionPoints: function getActionPoints() {
  3169. return this.actionPoints;
  3170. },
  3171. getMaxActionPoints: function getMaxActionPoints() {
  3172. return 3 + Math.floor(this.level / 4) - (this.isOwn() ? 0 : 2);
  3173. },
  3174. getCulturalGoods: function getCulturalGoods() {
  3175. return this.culturalGoods;
  3176. },
  3177. getTavernWineLevel: function getTavernWineLevel() {
  3178. return this.tavernWineLevel;
  3179. },
  3180. getPopulationData: function getPopulationData() {
  3181. var max = 0;
  3182. var happiness = 196;
  3183. var townHall = this.getBuildingByType(Constants.Buildings.TOWN_HALL);
  3184. var temple = this.getBuildingByType(Constants.Buildings.TEMPLE);
  3185. var palace = this.getBuildingByType(Constants.Buildings.PALACE);
  3186. var tavern = this.getBuildingByType(Constants.Buildings.TAVERN);
  3187. var museum = this.getBuildingByType(Constants.Buildings.MUSEUM);
  3188. var civData = getCivilizationData();
  3189. if (townHall) {
  3190. // Formula from http://ikariam.wikia.com/wiki/Citizen
  3191. max += Math.floor(10 * Math.pow(townHall.getLevel(), 1.5)) * 2 + 40;
  3192. }
  3193. if (civData.hasResearched(Constants.Research.Economy.HOLIDAY)) {
  3194. max += 50;
  3195. happiness += 25;
  3196. }
  3197. if (civData.hasResearched(Constants.Research.Economy.ECONOMIC_FUTURE)) {
  3198. var level = civData.getResearchLevel(Constants.Research.Economy.ECONOMIC_FUTURE);
  3199. max += 20 * level;
  3200. happiness += 10 * level;
  3201. }
  3202. if (palace) {
  3203. if (civData.hasResearched(Constants.Research.Science.WELL_CONSTRUCTION)) {
  3204. max += 50;
  3205. happiness += 50;
  3206. }
  3207. if (civData.hasResearched(Constants.Research.Economy.UTOPIA)) {
  3208. max += 200;
  3209. happiness += 200;
  3210. }
  3211. }
  3212. if (tavern) {
  3213. happiness += Constants.BuildingData.tavern.satisfaction[tavern.getLevel()];//12 * tavern.getLevel();
  3214. }
  3215. happiness += Constants.BuildingData.tavern.maxBonus[tavern.getLevel()];//Constants.GamePlay.HAPPINESS_PER_WINE_SERVING_LEVEL * this.getTavernWineLevel();
  3216. if (museum) {
  3217. happiness += Constants.BuildingData.museum.satisfaction[museum.getLevel()];//20 * museum.getLevel();
  3218. }
  3219. happiness += Constants.BuildingData.museum.maxBonus[this.getCulturalGoods()];//Constants.GamePlay.HAPPINESS_PER_CULTURAL_GOOD * this.getCulturalGoods();
  3220. var government = civData.getGovernment();
  3221. if (government == Constants.Government.DEMOCRACY) {
  3222. happiness += 75;
  3223. } else if (government == Constants.Government.DICTATORSHIP) {
  3224. happiness -= 75;
  3225. } else if (government == Constants.Government.THEOCRACY) {
  3226. if (temple) {
  3227. happiness += Math.min(150, this.getPriests() * 5 / max * 100 * 2);
  3228. } else {
  3229. happiness -= 20;
  3230. }
  3231. }
  3232. happiness = happiness * (1 - this.getCorruption());
  3233. var happinessDelta = happiness - this.population;
  3234. var currentPopulation = this.population +
  3235. happinessDelta * (1 - Math.pow(Math.E,
  3236. -(this.getTimeSinceResourceUpdate() / 50 / Constants.Time.MILLIS_PER_HOUR)));
  3237. var population = Math.min(currentPopulation, max);
  3238. return {
  3239. population: population,
  3240. max: max,
  3241. happiness: happiness - population,
  3242. growth: max == population && happiness > (population - 1)
  3243. ? 0 : (happiness - population) / 50,
  3244. };
  3245. },
  3246. getCorruption: function getCorruption() {
  3247. var palace = this.getBuildingByType(Constants.Buildings.GOVERNORS_RESIDENCE) ||
  3248. this.getBuildingByType(Constants.Buildings.PALACE);
  3249. var level = palace ? palace.getLevel() : 0;
  3250. var corruption = 1 - (level + 1) / getOwnCities().length;
  3251. var government = getCivilizationData().getGovernment();
  3252. if (government == Constants.Government.ARISTOCRACY ||
  3253. government == Constants.Government.OLIGARCHY) {
  3254. corruption += .03;
  3255. } else if (government == Constants.Government.NOMOCRACY) {
  3256. corruption -= .05;
  3257. } else if (government == Constants.Government.ANARCHY) {
  3258. corruption += .25;
  3259. }
  3260. return Math.min(Math.max(corruption, 0), 1);
  3261. },
  3262. getScientists: function getScientists() {
  3263. return this.scientists;
  3264. },
  3265. getResearch: function getResearch() {
  3266. var civData = getCivilizationData();
  3267. var multiplier = 1.0;
  3268. multiplier += civData.hasResearched(
  3269. IkaTools.Constants.Research.Science.PAPER) ? .02 : 0;
  3270. multiplier += civData.hasResearched(
  3271. IkaTools.Constants.Research.Science.INK) ? .04 : 0;
  3272. multiplier += civData.hasResearched(
  3273. IkaTools.Constants.Research.Science.MECHANICAL_PEN) ? .08 : 0;
  3274. multiplier += (civData.getResearchLevel(
  3275. IkaTools.Constants.Research.Science.SCIENTIFIC_FUTURE) || 0) * .02;
  3276. multiplier -= this.getCorruption();
  3277. return this.scientists * multiplier;
  3278. },
  3279. getResource: function getResource(resourceName) {
  3280. return this.resources[resourceName];
  3281. },
  3282. getResourceCapacity: function getResourceMaximumCapacity() {
  3283. var total = 1500;
  3284. var safe = 100;
  3285. $.each(this.getBuildingsByType(Constants.Buildings.WAREHOUSE), function(i, building) {
  3286. total += Constants.BuildingData.warehouse.capacity[building.getLevel()]//8000 * building.getLevel();
  3287. safe += 480 * building.getLevel();
  3288. });
  3289. $.each(this.getBuildingsByType(Constants.Buildings.DUMP), function(i, building) {
  3290. total += Constants.BuildingData.dump.capacity[building.getLevel()]//8000 * building.getLevel();
  3291. });
  3292. var civilizationData = getCivilizationData();
  3293. if (civilizationData.isPremiumFeatureEnabled(
  3294. Constants.PremiumFeatures.DOUBLED_STORAGE_CAPACITY)) {
  3295. total *= 2;
  3296. }
  3297. if (civilizationData.isPremiumFeatureEnabled(
  3298. Constants.PremiumFeatures.DOUBLED_SAFE_CAPACITY)) {
  3299. safe *= 2;
  3300. }
  3301. return {
  3302. maximum: total,
  3303. safe: safe,
  3304. };
  3305. },
  3306. getIslandCoordinates: function getIslandCoordinates() {
  3307. return this.islandCoordinates;
  3308. },
  3309. getLoadingSpeed: function getLoadingSpeed() {
  3310. var speed = 10;
  3311. var ports = this.getBuildingsByType(Constants.Buildings.TRADING_PORT);
  3312. if (ports[0]) {
  3313. speed = Constants.BuildingData[Constants.Buildings.TRADING_PORT]
  3314. .loadingSpeed[ports[0].getLevel()];
  3315. }
  3316. if (ports[1]) {
  3317. speed += Constants.BuildingData[Constants.Buildings.TRADING_PORT]
  3318. .loadingSpeed[ports[1].getLevel()];
  3319. }
  3320. return speed / Constants.Time.SECONDS_PER_MINUTE;
  3321. },
  3322. getPriests: function getPriests() {
  3323. return this.priests;
  3324. },
  3325. });
  3326. City.Resource = function City_Resource(city) {
  3327. this._ikaToolsType = 'cityResource';
  3328. this._setCity(city);
  3329. }
  3330. $.extend(City.Resource.prototype, {
  3331. _setCity: function setCity(city) {
  3332. if (city) {
  3333. this.city = Utils.fixedFunction(city);
  3334. }
  3335. },
  3336. _update: function _update(current, production, consumption) {
  3337. var changed =
  3338. Math.abs(current - this.getCurrent()) > 3 ||
  3339. this.production != production ||
  3340. this.consumption != consumption;
  3341. this.current = current;
  3342. this.production = production;
  3343. this.consumption = consumption;
  3344. return changed;
  3345. },
  3346. _increment: function _increment(delta) {
  3347. this.current = Math.max(this.current + delta, 0);
  3348. },
  3349. getCurrent: function getCurrent() {
  3350. if (this.current === undefined) {
  3351. return undefined;
  3352. }
  3353. var current = this.current;
  3354. var now = View.gameTimeNow();
  3355. var max = this.city().getResourceCapacity().maximum;
  3356. var lastUpdate = this.city().getLastResourceUpdate();
  3357. if (this.production) {
  3358. current += this.production * (now - lastUpdate) /
  3359. Constants.Time.MILLIS_PER_SECOND;
  3360. }
  3361. if (this.consumption) {
  3362. // Wine use takes place on the hour.
  3363. var startHour = Math.floor(lastUpdate / Constants.Time.MILLIS_PER_HOUR);
  3364. var nowHour = Math.floor(now / Constants.Time.MILLIS_PER_HOUR);
  3365. current -=
  3366. this.consumption * Constants.Time.SECONDS_PER_HOUR * (nowHour - startHour);
  3367. }
  3368. return Math.max(0, Math.min(max, current));
  3369. },
  3370. /**
  3371. * In milliseconds.
  3372. */
  3373. getTimeUntilFull: function getTimeUntilFull() {
  3374. var overallProduction = (this.production || 0) - (this.consumption || 0);
  3375. if (this.current === undefined) {
  3376. return Number.POSITIVE_INFINITY;
  3377. } else {
  3378. var current = this.getCurrent();
  3379. var max = this.city().getResourceCapacity().maximum;
  3380. var production = this.production;
  3381. if (overallProduction > 0) {
  3382. var secondsToNextHour = (Constants.Time.MILLIS_PER_HOUR -
  3383. (View.gameTimeNow() % Constants.Time.MILLIS_PER_HOUR)) /
  3384. Constants.Time.MILLIS_PER_SECOND;
  3385. var atNextHour = current + secondsToNextHour * production;
  3386. if (atNextHour >= max) {
  3387. return (max - current) / production * Constants.Time.MILLIS_PER_SECOND;
  3388. } else {
  3389. var hours = Math.floor(
  3390. (max - atNextHour) / overallProduction / Constants.Time.SECONDS_PER_HOUR);
  3391. var atHours = atNextHour + overallProduction * hours * Constants.Time.SECONDS_PER_HOUR;
  3392. return (secondsToNextHour +
  3393. hours * Constants.Time.SECONDS_PER_HOUR +
  3394. (max - atHours) / production) * Constants.Time.MILLIS_PER_SECOND;
  3395. }
  3396. } else if (this.current && this.getCurrent() == max) {
  3397. // No production, but filled exactly to capacity
  3398. return 0;
  3399. } else {
  3400. return Number.POSITIVE_INFINITY;
  3401. }
  3402. }
  3403. },
  3404. /**
  3405. * In milliseconds.
  3406. */
  3407. getTimeUntilEmpty: function getTimeUntilEmpty() {
  3408. if (this.current === undefined) {
  3409. return Number.POSITIVE_INFINITY;
  3410. } else if (this.consumption) {
  3411. if (this.production > this.consumption) {
  3412. // Could run out in first hour, but nobody is going to run their empire that
  3413. // way so it's not worth the effort of calculating.
  3414. return Number.POSITIVE_INFINITY;
  3415. } else {
  3416. // Wine use takes place on the hour.
  3417. var current = this.getCurrent();
  3418. var production = this.production || 0;
  3419. var secondsToNextHour = (Constants.Time.MILLIS_PER_HOUR -
  3420. (View.gameTimeNow() % Constants.Time.MILLIS_PER_HOUR)) /
  3421. Constants.Time.MILLIS_PER_SECOND;
  3422. // Compute to end of next hour
  3423. var atNextHour = current - this.consumption * Constants.Time.SECONDS_PER_HOUR +
  3424. production * secondsToNextHour;
  3425. if (atNextHour <= 0) {
  3426. return secondsToNextHour * Constants.Time.MILLIS_PER_SECOND;
  3427. } else {
  3428. var hourlyDiff =
  3429. (this.consumption - production) * Constants.Time.SECONDS_PER_HOUR;
  3430. return Constants.Time.MILLIS_PER_SECOND * (secondsToNextHour +
  3431. Math.ceil(atNextHour / hourlyDiff) * Constants.Time.SECONDS_PER_HOUR);
  3432. }
  3433. }
  3434. }
  3435. return Number.POSITIVE_INFINITY;
  3436. },
  3437. getCapacity: function getCapacity() {
  3438. return this.city().getResourceCapacity();
  3439. },
  3440. getProduction: function getProduction() {
  3441. return this.production;
  3442. },
  3443. getConsumption: function getConsumption() {
  3444. return this.consumption;
  3445. },
  3446. });
  3447. City.Building = function City_Building(city) {
  3448. this._ikaToolsType = 'building';
  3449. this._setCity(city);
  3450. this.position = null;
  3451. this.type = null;
  3452. this.level = 0;
  3453. }
  3454. $.extend(City.Building.prototype, {
  3455. _setCity: function setCity(city) {
  3456. if (city) {
  3457. this.city = Utils.fixedFunction(city);
  3458. this._scheduleUpgradeComplete();
  3459. }
  3460. },
  3461. _update: function _update(position, data) {
  3462. this.position = position;
  3463. var changed = false;
  3464. if (data.building.indexOf('buildingGround') >= 0) {
  3465. changed = !this.isEmpty();
  3466. this.type = '';
  3467. delete this.level;
  3468. delete this.completionTime;
  3469. } else {
  3470. var type = data.building.split(' ')[0];
  3471. var level = parseInt(data.level);
  3472. var isUpgrading = 'completed' in data;
  3473. changed = (type != this.getType() ||
  3474. level != this.getLevel() ||
  3475. isUpgrading != this.isUpgrading());
  3476. this.type = type;
  3477. this.level = level;
  3478. if (isUpgrading) {
  3479. var completionTime = parseInt(data.completed) * 1000;
  3480. if (this.completionTime != completionTime) {
  3481. this.completionTime = completionTime;
  3482. this._scheduleUpgradeComplete();
  3483. }
  3484. } else {
  3485. delete this.completionTime;
  3486. }
  3487. }
  3488. return changed;
  3489. },
  3490. _scheduleUpgradeComplete: function _scheduleUpgradeComplete() {
  3491. if (this.upgradeEvent) {
  3492. this.upgradeEvent();
  3493. }
  3494. if (this.completionTime) {
  3495. if (this.completionTime <= View.gameTimeNow()) {
  3496. this.level = this.level + 1;
  3497. delete this.completionTime;
  3498. raiseBuildingsChanged([{
  3499. city: this.city(),
  3500. building: this,
  3501. type: Constants.BuildingEventType.UPGRADE_COMPLETE,
  3502. }]);
  3503. } else {
  3504. this.upgradeEvent = buildingsChangedEvent().scheduleSend(
  3505. this.type + "->" + (this.level + 1),
  3506. // 0.5.0 still does a full page refresh if you're viewing the city when the
  3507. // building completes. So we cheat a bit and send this event a few seconds
  3508. // before it actually takes place so it doesn't get lost as part of a page
  3509. // refresh that just sees it as a normal "info_refresh" event.
  3510. this.completionTime - View.gameTimeNow() + Constants.Time.SAFE_TIME_DELTA,
  3511. function() {
  3512. this.level = this.level + 1;
  3513. delete this.completionTime;
  3514. empireData.saveAsync();
  3515. }.bind(this),
  3516. [{
  3517. city: this.city(),
  3518. building: this,
  3519. type: Constants.BuildingEventType.UPGRADE_COMPLETE,
  3520. }]);
  3521. }
  3522. }
  3523. },
  3524. getPosition: function getPosition() {
  3525. return this.position;
  3526. },
  3527. isEmpty: function isEmpty() {
  3528. return !this.type;
  3529. },
  3530. getType: function getType() {
  3531. return this.type;
  3532. },
  3533. getLevel: function getLevel() {
  3534. return this.level;
  3535. },
  3536. isUpgrading: function isUpgrading() {
  3537. return (this.completionTime > View.gameTimeNow());
  3538. },
  3539. getRemainingUpgradeTime: function getRemainingUpgradeTime() {
  3540. var diff = this.completionTime - View.gameTimeNow();
  3541. return Math.max(diff, 0);
  3542. },
  3543. getCompletionTime: function getCompletionTime() {
  3544. return new Date(this.completionTime);
  3545. },
  3546. getUpgradeCosts: function getUpgradeCost() {
  3547. var city = this.city();
  3548. var civData = getCivilizationData();
  3549. var buildingCostData = Constants.BuildingData[this.getType()];
  3550. var level = this.getLevel() + (this.isUpgrading() ? 1 : 0);
  3551. var timeParams = buildingCostData.time;
  3552. var costs = {
  3553. wood: buildingCostData.wood[level] || 0,
  3554. wine: buildingCostData.wine[level] || 0,
  3555. marble: buildingCostData.marble[level] || 0,
  3556. glass: buildingCostData.glass[level] || 0,
  3557. sulfur: buildingCostData.sulfur[level] || 0,
  3558. time: Math.round(timeParams.a / timeParams.b *
  3559. Math.pow(timeParams.c, level+1) - timeParams.d) * 1000,
  3560. };
  3561. var multiplier = 1.0;
  3562. multiplier -= civData.hasResearched(Constants.Research.Economy.PULLEY) ? .02 : 0;
  3563. multiplier -= civData.hasResearched(Constants.Research.Economy.GEOMETRY) ? .04 : 0;
  3564. multiplier -= civData.hasResearched(Constants.Research.Economy.SPIRIT_LEVEL) ? .08 : 0;
  3565. var carpenter = city.getBuildingByType(Constants.Buildings.CARPENTER);
  3566. var winePress = city.getBuildingByType(Constants.Buildings.WINE_PRESS);
  3567. var architect = city.getBuildingByType(Constants.Buildings.ARCHITECT);
  3568. var optician = city.getBuildingByType(Constants.Buildings.OPTICIAN);
  3569. var fireworker = city.getBuildingByType(Constants.Buildings.FIREWORK_TEST_AREA);
  3570. return {
  3571. wood: costs.wood * (multiplier - (carpenter ? carpenter.getLevel() / 100 : 0)),
  3572. wine: costs.wine * (multiplier - (winePress ? winePress.getLevel() / 100 : 0)),
  3573. marble: costs.marble * (multiplier - (architect ? architect.getLevel() / 100 : 0)),
  3574. glass: costs.glass * (multiplier - (optician ? optician.getLevel() / 100 : 0)),
  3575. sulfur: costs.sulfur * (multiplier - (fireworker ? fireworker.getLevel() / 100 : 0)),
  3576. time: costs.time,
  3577. };
  3578. },
  3579. isMaxLevel: function isMaxLevel() {
  3580. return (this.getLevel() + (this.isUpgrading() ? 1 : 0)) >=
  3581. Constants.BuildingData[this.getType()].maxLevel;
  3582. },
  3583. });
  3584. CivilizationData = function CivilizationData() {
  3585. this._ikaToolsType = 'civilizationData';
  3586. this.research = {};
  3587. this.government = 'ikakratie';
  3588. this.movements = {};
  3589. this.premiumFeatures = {};
  3590. }
  3591. $.extend(CivilizationData.prototype, {
  3592. _startMovementTimers: function _startMovementTimers() {
  3593. $.each(this.movements, function updateMovementsOnLoad(id, movement) {
  3594. if (movement._updateAndStartTimer()) {
  3595. delete this.movements[id];
  3596. }
  3597. }.bind(this));
  3598. },
  3599. _updateGovernment: function updateGovernment(government, changedAccumulator) {
  3600. if (this.government != government) {
  3601. changedAccumulator.push({
  3602. type: Constants.CivilizationData.GOVERNMENT,
  3603. government: government,
  3604. });
  3605. }
  3606. this.government = government;
  3607. },
  3608. _updateResearch: function updateResearch(researchId, level, changedAccumulator) {
  3609. var oldResearch = this.research[researchId];
  3610. if (!oldResearch || oldResearch.level != level) {
  3611. changedAccumulator.push({
  3612. type: Constants.CivilizationData.RESEARCH,
  3613. id: researchId,
  3614. level: level,
  3615. });
  3616. }
  3617. this.research[researchId] = { level: level };
  3618. },
  3619. _updateMovement: function updateMovement(movement, changedAccumulator) {
  3620. var existing = this.movements[movement.getId()];
  3621. if (existing) {
  3622. existing._cancelTimer();
  3623. }
  3624. this.movements[movement.getId()] = movement;
  3625. movement._updateAndStartTimer();
  3626. if (!existing || (movement.getCompletionTime() != existing.getCompletionTime())) {
  3627. changedAccumulator.push({
  3628. movement: movement,
  3629. type: Constants.Movements.EventType.DATA_UPDATED,
  3630. });
  3631. }
  3632. },
  3633. _removeMovement: function removeMovement(movementId, changedAccumulator) {
  3634. var movement = this.movements[movementId];
  3635. if (movement) {
  3636. if (movement.stage == Constants.Movements.Stage.LOADING && movementId >= 0) {
  3637. var originCity = movement.getOriginCity();
  3638. movement._updateCity(originCity, originCity);
  3639. }
  3640. movement._cancelTimer();
  3641. delete this.movements[movementId];
  3642. changedAccumulator.push({
  3643. movement: movement,
  3644. type: Constants.Movements.EventType.CANCELLED,
  3645. });
  3646. }
  3647. },
  3648. _updatePremiumFeature: function updatePremiumFeature(
  3649. changedAccumulator, feature, enabled) {
  3650. var currentlyEnabled = this.premiumFeatures[feature] || false;
  3651. if (currentlyEnabled != enabled) {
  3652. changedAccumulator.push({
  3653. type: Constants.CivilizationData.PREMIUM_FEATURE,
  3654. feature: feature,
  3655. enabled: enabled,
  3656. });
  3657. }
  3658. this.premiumFeatures[feature] = enabled;
  3659. },
  3660. hasResearched: function hasResearched(researchId) {
  3661. var research = this.research[researchId];
  3662. return research ? research.level > 0 : undefined;
  3663. },
  3664. getResearchLevel: function getResearchLevel(researchId) {
  3665. var research = this.research[researchId];
  3666. return research ? research.level : undefined;
  3667. },
  3668. getGovernment: function getGovernment() {
  3669. return this.government;
  3670. },
  3671. getMovements: function getMovements() {
  3672. var movements = [];
  3673. $.each(this.movements, function(id, movement) {
  3674. movements.push(movement);
  3675. });
  3676. movements.sort(function compareMovements(m1, m2) {
  3677. return m1.getArrivalTime() - m2.getArrivalTime();
  3678. });
  3679. return movements;
  3680. },
  3681. getMovement: function getMovement(movementId) {
  3682. return this.movements[movementId];
  3683. },
  3684. isPremiumFeatureEnabled: function isPremiumFeatureEnabled(feature) {
  3685. return this.premiumFeatures[feature];
  3686. },
  3687. });
  3688. function calculateTravelTime(island1Coords, island2Coords, units, transporters) {
  3689. // same island
  3690. if (island1Coords[0] == island2Coords[0] &&
  3691. island1Coords[1] == island2Coords[1]) {
  3692. var baseTime = 10 * Constants.Time.MILLIS_PER_MINUTE;
  3693. var multiplier = transporters ? 60 : 80; // fastest unit
  3694. if (units) {
  3695. $.each(units.getCounts(), function applyUnitSpeed(type, count) {
  3696. if (count) {
  3697. var data = Constants.UnitData[type];
  3698. multiplier = Math.min(multiplier, data.speed);
  3699. }
  3700. });
  3701. }
  3702. return baseTime * 60 / multiplier;
  3703. } else {
  3704. var baseTime = 20 * Math.sqrt(Math.pow(island1Coords[0] - island2Coords[0], 2) +
  3705. Math.pow(island1Coords[1] - island2Coords[1], 2)) *
  3706. Constants.Time.MILLIS_PER_MINUTE;
  3707. var multiplier = 60; // fastest ship
  3708. if (units) {
  3709. $.each(units.getCounts(), function applyUnitSpeed(type, count) {
  3710. if (count) {
  3711. var data = Constants.UnitData[type];
  3712. if (!data.isArmy) {
  3713. multiplier = Math.min(multiplier, data.speed);
  3714. }
  3715. }
  3716. });
  3717. }
  3718. return baseTime * 60 / multiplier;
  3719. }
  3720. }
  3721. function Movement(
  3722. id, type, completionTime, mission, stage, originCityId, targetCityId,
  3723. transports, units, resources, transportTime) {
  3724. this._ikaToolsType = 'movement';
  3725. if (id) {
  3726. this.id = id;
  3727. this.completionTime = completionTime;
  3728. this.mission = mission;
  3729. this.stage = stage;
  3730. this.originCityId = originCityId;
  3731. this.targetCityId = targetCityId;
  3732. this.units = units;
  3733. this.transports = transports;
  3734. this.resources = resources;
  3735. this.transportTime = transportTime;
  3736. this.type = type;
  3737. var originCity = this.getOriginCity();
  3738. var targetCity = this.getTargetCity();
  3739. if (originCity && targetCity) {
  3740. var originCoords = originCity.getIslandCoordinates();
  3741. var targetCoords = targetCity.getIslandCoordinates();
  3742. if (originCoords && targetCoords) {
  3743. this.transportTime = calculateTravelTime(
  3744. originCoords, targetCoords, this.units, this.transports);
  3745. }
  3746. }
  3747. if (!this.transportTime) {
  3748. this.transportTime = Number.POSITIVE_INFINITY;
  3749. }
  3750. if (this.completionTime <= new Date().getTime()) {
  3751. this._toNextStage();
  3752. }
  3753. }
  3754. }
  3755. $.extend(Movement.prototype, {
  3756. _cancelTimer: function cancelTimer() {
  3757. if (this.completionEvent) {
  3758. this.completionEvent();
  3759. }
  3760. },
  3761. _startTimer: function startTimer() {
  3762. var remainingTime = this.getTimeRemaining();
  3763. if (isFinite(remainingTime)) {
  3764. this.completionEvent = movementsChangedEvent().scheduleSend(
  3765. 'Movement[' + this.id + ']',
  3766. remainingTime + Constants.Time.SAFE_TIME_DELTA,
  3767. function moveToNextStage() {
  3768. if (this._toNextStage()) {
  3769. getCivilizationData()._removeMovement(this.id, []);
  3770. }
  3771. empireData.saveAsync();
  3772. }.bind(this),
  3773. [{
  3774. previousStage: this.getStage(),
  3775. movement: this,
  3776. type: this._isFinalStage() ?
  3777. Constants.Movements.EventType.COMPLETED :
  3778. Constants.Movements.EventType.STAGE_CHANGED,
  3779. }]);
  3780. }
  3781. },
  3782. _updateCity: function updateCity(city, originCity) {
  3783. var resourceChanges = [];
  3784. if (city) {
  3785. if (originCity) {
  3786. originCity._updateActionPointsBy(1, resourceChanges);
  3787. }
  3788. if (city.isOwn() &&
  3789. (this.mission == Constants.Movements.Mission.TRANSPORT ||
  3790. this.mission == Constants.Movements.Mission.PLUNDER)) {
  3791. $.each(this.resources, function updateCityResource(name, value) {
  3792. city._incrementResource(name, resourceChanges, value);
  3793. });
  3794. }
  3795. raiseResourcesChanged(resourceChanges);
  3796. if (this.mission == Constants.Movements.Mission.DEPLOY_ARMY ||
  3797. this.mission == Constants.Movements.Mission.DEPLOY_NAVY) {
  3798. var military = city.getMilitary();
  3799. military._increment(this.units);
  3800. raiseMilitaryChanged([{
  3801. military: military,
  3802. city: city,
  3803. type: 'deployment_arrived',
  3804. }]);
  3805. }
  3806. }
  3807. },
  3808. _updateAndStartTimer: function updateAndStartTimer() {
  3809. this._cancelTimer();
  3810. if (this.completionTime <= View.gameTimeNow()) {
  3811. return this._toNextStage();
  3812. } else {
  3813. this._startTimer();
  3814. }
  3815. },
  3816. _toNextStage: function toNextStage() {
  3817. var isFinalStage = this._isFinalStage();
  3818. if (this.stage == Constants.Movements.Stage.LOADING) {
  3819. this.stage = Constants.Movements.Stage.EN_ROUTE;
  3820. this.completionTime += this.transportTime;
  3821. this._startTimer();
  3822. } else if (this.stage == Constants.Movements.Stage.EN_ROUTE) {
  3823. if (isFinalStage) {
  3824. this._updateCity(this.getTargetCity(), this.getOriginCity());
  3825. }
  3826. } else if (this.stage == Constants.Movements.Stage.RETURNING) {
  3827. if (isFinalStage) {
  3828. var originCity = this.getOriginCity();
  3829. this._updateCity(originCity, originCity);
  3830. }
  3831. }
  3832. return isFinalStage;
  3833. },
  3834. _isFinalStage: function isFinalStage() {
  3835. if (this.stage == Constants.Movements.Stage.LOADING) {
  3836. return false;
  3837. } else if (this.stage == Constants.Movements.Stage.EN_ROUTE) {
  3838. if (this.mission == Constants.Movements.Mission.TRANSPORT) {
  3839. var city = getCity(this.targetCityId);
  3840. return city && city.isOwn();
  3841. } else if (this.mission == Constants.Movements.Mission.DEPLOY_ARMY ||
  3842. this.mission == Constants.Movements.Mission.DEPLOY_NAVY) {
  3843. return true;
  3844. }
  3845. } else {
  3846. return true;
  3847. }
  3848. },
  3849. getId: function getId() {
  3850. return this.id;
  3851. },
  3852. getMission: function getMission() {
  3853. return this.mission;
  3854. },
  3855. getStage: function getStage() {
  3856. return this.stage;
  3857. },
  3858. getOriginCityId: function getOriginCityId() {
  3859. return this.originCityId;
  3860. },
  3861. getTargetCityId: function getTargetCityId() {
  3862. return this.targetCityId;
  3863. },
  3864. getOriginCity: function getOriginCity() {
  3865. return this.originCityId && getCity(this.originCityId);
  3866. },
  3867. getTargetCity: function getTargetCity() {
  3868. return this.targetCityId && getCity(this.targetCityId);
  3869. },
  3870. getCompletionTime: function getCompletionTime() {
  3871. return this.completionTime;
  3872. },
  3873. getTimeRemaining: function getTimeRemaining() {
  3874. return this.completionTime - View.gameTimeNow();
  3875. },
  3876. getArrivalTime: function() {
  3877. var time = this.getCompletionTime();
  3878. if (this.stage == Constants.Movements.Stage.LOADING) {
  3879. time += this.transportTime;
  3880. }
  3881. return time;
  3882. },
  3883. getUnits: function getUnits() {
  3884. return this.units;
  3885. },
  3886. getResource: function getResource(resourceName) {
  3887. return this.resources[resourceName];
  3888. },
  3889. isHostile: function isHostile() {
  3890. return this.type.indexOf('hostile') >= 0;
  3891. },
  3892. isOwn: function isOwn() {
  3893. return this.type.indexOf('own') >= 0;
  3894. }
  3895. });
  3896. var empireData = new Data.Value(
  3897. 'empireData',
  3898. {
  3899. cities: {},
  3900. cityOrder: [],
  3901. civilizationData: new CivilizationData(),
  3902. },
  3903. {
  3904. reviver: function empireDataReviver(key, value) {
  3905. if (value && value._ikaToolsType) {
  3906. var obj;
  3907. switch(value._ikaToolsType) {
  3908. case 'city': obj = new City(); break;
  3909. case 'building': obj = new City.Building; break;
  3910. case 'cityResource': obj = new City.Resource(); break;
  3911. case 'military': obj = new Military(); break;
  3912. case 'militaryUnits': obj = new MilitaryUnits(); break;
  3913. case 'trainingBatch': obj = new TrainingBatch(); break;
  3914. case 'civilizationData': obj = new CivilizationData(); break;
  3915. case 'movement': obj = new Movement(); break;
  3916. }
  3917. $.extend(obj, value);
  3918. if (obj._postLoad) {
  3919. obj._postLoad();
  3920. }
  3921. return obj;
  3922. }
  3923. return value;
  3924. },
  3925. version: 28,
  3926. loadCallback: function empireDataLoaded() {
  3927. getCivilizationData()._startMovementTimers();
  3928. },
  3929. });
  3930. function raiseCivilizationDataChanged(changes) {
  3931. if (changes.length) {
  3932. civilizationDataChangedEvent().send(changes);
  3933. }
  3934. }
  3935. var civilizationDataChangedEvent = Utils.thunk(function() {
  3936. return new Utils.EventDispatcher();
  3937. });
  3938. function registerCivilizationDataChangedHandler(callback) {
  3939. return civilizationDataChangedEvent().addListener(callback);
  3940. }
  3941. function raiseMovementsChanged(changes) {
  3942. if (changes.length) {
  3943. movementsChangedEvent().send(changes);
  3944. }
  3945. }
  3946. var movementsChangedEvent = Utils.thunk(function() {
  3947. return new Utils.EventDispatcher();
  3948. });
  3949. function registerMovementsChangedHandler(callback) {
  3950. return movementsChangedEvent().addListener(callback);
  3951. }
  3952. function raiseResourcesChanged(changes) {
  3953. if (changes.length) {
  3954. resourcesChangedEvent().send(changes);
  3955. }
  3956. }
  3957. var resourcesChangedEvent = Utils.thunk(function() {
  3958. return new Utils.EventDispatcher();
  3959. });
  3960. function registerResourcesChangedHandler(callback) {
  3961. return resourcesChangedEvent().addListener(callback);
  3962. }
  3963. var buildingsChangedEvent = Utils.thunk(function() {
  3964. var dispatcher = new Utils.EventDispatcher();
  3965. return dispatcher;
  3966. });
  3967. function raiseBuildingsChanged(changes) {
  3968. if (changes.length) {
  3969. buildingsChangedEvent().send(changes);
  3970. }
  3971. }
  3972. function registerBuildingsChangedHandler(callback) {
  3973. return buildingsChangedEvent().addListener(callback);
  3974. }
  3975. var militaryChangedEvent = Utils.thunk(function() {
  3976. var dispatcher = new Utils.EventDispatcher();
  3977. return dispatcher;
  3978. });
  3979. function raiseMilitaryChanged(changes) {
  3980. if (changes.length) {
  3981. militaryChangedEvent().send(changes);
  3982. }
  3983. }
  3984. function registerMilitaryChangedHandler(callback) {
  3985. return militaryChangedEvent().addListener(callback);
  3986. };
  3987. var TRADE_GOOD_LOOKUP = {
  3988. "1": Constants.Resources.WINE,
  3989. "2": Constants.Resources.MARBLE,
  3990. "3": Constants.Resources.GLASS,
  3991. "4": Constants.Resources.SULFUR,
  3992. };
  3993. function getCity(id) {
  3994. return empireData.get().cities[id];
  3995. };
  3996. var coordsRegex = /\[(\d+):(\d+)\]/;
  3997. function parseCoordinates(coords) {
  3998. var match = coords.match(coordsRegex);
  3999. return [parseInt(match[1]), parseInt(match[2])];
  4000. }
  4001. function processTransportForm(form) {
  4002. var city = View.getCurrentCity();
  4003. var transports = parseInt($('#transporterCount').val()) || 0;
  4004. var resources = {};
  4005. resources[Constants.Resources.WOOD] =
  4006. parseInt($('#textfield_wood').val()) || 0;
  4007. resources[Constants.Resources.WINE] =
  4008. parseInt($('#textfield_wine').val()) || 0;
  4009. resources[Constants.Resources.MARBLE] =
  4010. parseInt($('#textfield_marble').val()) || 0;
  4011. resources[Constants.Resources.GLASS] =
  4012. parseInt($('#textfield_glass').val()) || 0;
  4013. resources[Constants.Resources.SULFUR] =
  4014. parseInt($('#textfield_sulfur').val()) || 0;
  4015. if ($('#createColony').length) {
  4016. resources[Constants.Resources.WOOD] += 1250;
  4017. }
  4018. var destinationCityId = parseInt($(form.elements['destinationCityId']).val());
  4019. var totalResources =
  4020. resources[Constants.Resources.WOOD] +
  4021. resources[Constants.Resources.WINE] +
  4022. resources[Constants.Resources.MARBLE] +
  4023. resources[Constants.Resources.GLASS] +
  4024. resources[Constants.Resources.SULFUR];
  4025. var loadingCompletion = View.gameTimeNow() +
  4026. (totalResources / city.getLoadingSpeed() * Constants.Time.MILLIS_PER_SECOND);
  4027. var movement = new Movement(
  4028. -(new Date().getTime()),
  4029. 'own',
  4030. loadingCompletion, //TODO: multiple loads from same town (if own) stack
  4031. Constants.Movements.Mission.TRANSPORT,
  4032. Constants.Movements.Stage.LOADING,
  4033. city.getId(),
  4034. destinationCityId,
  4035. transports,
  4036. new MilitaryUnits(),
  4037. resources
  4038. // TODO: transport time for towns other than one's that are tracked
  4039. );
  4040. View.registerNextIkariamAjaxRequestCallback(function saveTransportData(response) {
  4041. Utils.iterateIkariamAjaxResponse(response,
  4042. function lookForSuccessFeedback(index, name, data) {
  4043. if (name == Constants.IkariamAjaxResponseType.PROVIDE_FEEDBACK &&
  4044. data[0].type == 10) {
  4045. var changes = [];
  4046. getCivilizationData()._updateMovement(movement, changes);
  4047. raiseMovementsChanged(changes);
  4048. }
  4049. });
  4050. });
  4051. }
  4052. function coordinatesEqual(coordinates1, coordinates2) {
  4053. if (!coordinates1 || !coordinates2) {
  4054. return false;
  4055. }
  4056. return coordinates1[0] == coordinates2[0] &&
  4057. coordinates1[1] == coordinates2[1];
  4058. }
  4059. function processDeploymentForm(form) {
  4060. var city = View.getCurrentCity();
  4061. var transports = parseInt($('#transporterCount').html()) || 0;
  4062. var mission = $(form.elements['function']).val() == 'deployArmy' ?
  4063. Constants.Movements.Mission.DEPLOY_ARMY : Constants.Movements.Mission.DEPLOY_NAVY;
  4064. var destinationCityId = parseInt($(form.elements['destinationCityId']).val());
  4065. var destinationCity = getCity(destinationCityId);
  4066. var units = new MilitaryUnits();
  4067. $.each(Constants.UnitIds, function countDeployingUnits(id, type) {
  4068. var elementId;
  4069. if (Constants.UnitData[type].isArmy) {
  4070. elementId = '#cargo_army_' + id;
  4071. } else {
  4072. elementId = '#cargo_fleet_' + id;
  4073. }
  4074. units._setCount(type, parseInt($(elementId).val()) || 0);
  4075. });
  4076. var cargoSize = units.getCargoSize();
  4077. var loadingCompletion = new Date().getTime() +
  4078. (units.getCargoSize() / city.getLoadingSpeed() * Constants.Time.MILLIS_PER_SECOND);
  4079. if (destinationCity &&
  4080. coordinatesEqual(destinationCity.getIslandCoordinates(),
  4081. city.getIslandCoordinates())) {
  4082. loadingCompletion = new Date().getTime();
  4083. }
  4084. var movement = new Movement(
  4085. -(new Date().getTime()),
  4086. 'own',
  4087. loadingCompletion, //TODO: multiple loads from same town (if own) stack
  4088. mission,
  4089. Constants.Movements.Stage.LOADING,
  4090. city.getId(),
  4091. destinationCityId,
  4092. transports,
  4093. units,
  4094. {}
  4095. // TODO: transport time for towns other than one's that are tracked
  4096. );
  4097. View.registerNextIkariamAjaxRequestCallback(function saveDeploymentData(response) {
  4098. Utils.iterateIkariamAjaxResponse(response,
  4099. function lookForSuccessFeedback(index, name, data) {
  4100. if (name == Constants.IkariamAjaxResponseType.PROVIDE_FEEDBACK &&
  4101. data[0].type == 10) {
  4102. var military = city.getMilitary();
  4103. military._decrement(units);
  4104. raiseMilitaryChanged([{
  4105. military: military,
  4106. city: city,
  4107. type: 'movement_started',
  4108. }]);
  4109. var changes = [];
  4110. getCivilizationData()._updateMovement(movement, changes);
  4111. raiseMovementsChanged(changes);
  4112. }
  4113. });
  4114. });
  4115. }
  4116. function processPlunderForm(form) {
  4117. var city = View.getCurrentCity();
  4118. var transports = parseInt($('#transporterCount').html()) || 0;
  4119. var destinationCityId = parseInt($(form.elements['destinationCityId']).val());
  4120. var destinationCity = getCity(destinationCityId);
  4121. var units = new MilitaryUnits();
  4122. $.each(Constants.UnitIds, function countDeployingUnits(id, type) {
  4123. units._setCount(type, parseInt($('#cargo_army_' + id).val()) || 0);
  4124. });
  4125. var cargoSize = units.getCargoSize();
  4126. var loadingCompletion = new Date().getTime() +
  4127. (units.getCargoSize() / city.getLoadingSpeed() * Constants.Time.MILLIS_PER_SECOND);
  4128. if (destinationCity &&
  4129. coordinatesEqual(destinationCity.getIslandCoordinates(),
  4130. city.getIslandCoordinates())) {
  4131. loadingCompletion = new Date().getTime();
  4132. }
  4133. var movement = new Movement(
  4134. -(new Date().getTime()),
  4135. 'own',
  4136. loadingCompletion, //TODO: multiple loads from same town (if own) stack
  4137. Constants.Movements.Mission.PLUNDER,
  4138. Constants.Movements.Stage.LOADING,
  4139. city.getId(),
  4140. destinationCityId,
  4141. transports,
  4142. units,
  4143. {}
  4144. // TODO: transport time for towns other than one's that are tracked
  4145. );
  4146. View.registerNextIkariamAjaxRequestCallback(function savePlunderData(response) {
  4147. Utils.iterateIkariamAjaxResponse(response,
  4148. function lookForSuccessFeedback(index, name, data) {
  4149. if (name == Constants.IkariamAjaxResponseType.PROVIDE_FEEDBACK &&
  4150. data[0].type == 10) {
  4151. var military = city.getMilitary();
  4152. military._decrement(units);
  4153. raiseMilitaryChanged([{
  4154. military: military,
  4155. city: city,
  4156. type: 'movement_started',
  4157. }]);
  4158. var changes = [];
  4159. getCivilizationData()._updateMovement(movement, changes);
  4160. raiseMovementsChanged(changes);
  4161. }
  4162. });
  4163. });
  4164. }
  4165. function processCityMilitaryView(data) {
  4166. var city = View.getCurrentCity();
  4167. var military = city.getMilitary();
  4168. var armyTds = $('#tabUnits').find('tr.count td');
  4169. var e = false;
  4170. e |= military._updatePresent(Constants.Military.HOPLITE, parseInt(armyTds[0].innerHTML));
  4171. e |= military._updatePresent(Constants.Military.STEAM_GIANT, parseInt(armyTds[1].innerHTML));
  4172. e |= military._updatePresent(Constants.Military.SPEARMAN, parseInt(armyTds[2].innerHTML));
  4173. e |= military._updatePresent(Constants.Military.SWORDSMAN, parseInt(armyTds[3].innerHTML));
  4174. e |= military._updatePresent(Constants.Military.SLINGER, parseInt(armyTds[4].innerHTML));
  4175. e |= military._updatePresent(Constants.Military.ARCHER, parseInt(armyTds[5].innerHTML));
  4176. e |= military._updatePresent(Constants.Military.GUNNER, parseInt(armyTds[6].innerHTML));
  4177. e |= military._updatePresent(Constants.Military.BATTERING_RAM, parseInt(armyTds[7].innerHTML));
  4178. e |= military._updatePresent(Constants.Military.CATAPULT, parseInt(armyTds[8].innerHTML));
  4179. e |= military._updatePresent(Constants.Military.MORTAR, parseInt(armyTds[9].innerHTML));
  4180. e |= military._updatePresent(Constants.Military.GYROCOPTER, parseInt(armyTds[10].innerHTML));
  4181. e |= military._updatePresent(Constants.Military.BALLOON_BOMBADIER, parseInt(armyTds[11].innerHTML));
  4182. e |= military._updatePresent(Constants.Military.COOK, parseInt(armyTds[12].innerHTML));
  4183. e |= military._updatePresent(Constants.Military.DOCTOR, parseInt(armyTds[13].innerHTML));
  4184. var navyTds = $('#tabShips').find('tr.count td');
  4185. e |= military._updatePresent(Constants.Military.RAM_SHIP, parseInt(navyTds[2].innerHTML));
  4186. e |= military._updatePresent(Constants.Military.FLAME_THROWER, parseInt(navyTds[0].innerHTML));
  4187. e |= military._updatePresent(Constants.Military.STEAM_RAM, parseInt(navyTds[1].innerHTML));
  4188. e |= military._updatePresent(Constants.Military.BALLISTA_SHIP, parseInt(navyTds[4].innerHTML));
  4189. e |= military._updatePresent(Constants.Military.CATAPULT_SHIP, parseInt(navyTds[3].innerHTML));
  4190. e |= military._updatePresent(Constants.Military.MORTAR_SHIP, parseInt(navyTds[5].innerHTML));
  4191. e |= military._updatePresent(Constants.Military.SUBMARINE, parseInt(navyTds[7].innerHTML));
  4192. e |= military._updatePresent(Constants.Military.PADDLE_SPEED_SHIP, parseInt(navyTds[8].innerHTML));
  4193. e |= military._updatePresent(Constants.Military.BALLOON_CARRIER, parseInt(navyTds[9].innerHTML));
  4194. e |= military._updatePresent(Constants.Military.TENDER, parseInt(navyTds[10].innerHTML));
  4195. e |= military._updatePresent(Constants.Military.ROCKET_SHIP, parseInt(navyTds[6].innerHTML));
  4196. military._markPresentUpdated();
  4197. if (e) {
  4198. raiseMilitaryChanged([{
  4199. city: city,
  4200. military: military,
  4201. type: 'data_updated',
  4202. }]);
  4203. }
  4204. }
  4205. function processRelatedCitiesView(data) {
  4206. var city = View.getCurrentCity();
  4207. var military = city.getMilitary();
  4208. military._clear();
  4209. var changed = false;
  4210. var info = $('#relatedCities .contentBox01h:eq(0)');
  4211. var whitespace = /\s+/;
  4212. info.find('.troops .armybutton').each(function(i, element) {
  4213. var type = element.className.split(whitespace)[1];
  4214. changed |= military._updatePresent(type, parseInt(element.innerHTML));
  4215. });
  4216. info.find('.troops .fleetbutton').each(function(i, element) {
  4217. var type = element.className.split(whitespace)[1];
  4218. changed |= military._updatePresent(type, parseInt(element.innerHTML));
  4219. });
  4220. military._markPresentUpdated();
  4221. if (changed) {
  4222. raiseMilitaryChanged([{
  4223. city: city,
  4224. military: military,
  4225. type: 'data_updated',
  4226. }]);
  4227. }
  4228. }
  4229. function parseMilitaryUnitsFromPending(container) {
  4230. var idRegex = /\d+/;
  4231. var units = new MilitaryUnits();
  4232. container.children('.army_wrapper').each(function(index, unitNode) {
  4233. var unitNode = $(unitNode);
  4234. var type = Constants.UnitIds[
  4235. parseInt(unitNode.find('.army').attr('class').match(idRegex)[0])];
  4236. var count = parseInt(unitNode.find('.unitcounttextlabel').html());
  4237. units._setCount(type, count);
  4238. });
  4239. return units;
  4240. }
  4241. function parseTrainingBatches(viewHtmlText, type, buildingLevel) {
  4242. var trainingBatches = [];
  4243. var constructionList = $('#unitConstructionList');
  4244. if (constructionList.length) {
  4245. var completionTime = parseInt(viewHtmlText.match(
  4246. /showUnitCountdown.'buildCountDown', 'buildProgress', (\d+)/)[1]) * 1000;
  4247. trainingBatches.push(new TrainingBatch(type,
  4248. completionTime, parseMilitaryUnitsFromPending(constructionList)));
  4249. constructionList.children('.constructionBlock').each(function() {
  4250. var units = parseMilitaryUnitsFromPending($(this));
  4251. completionTime += computeTrainingTime(buildingLevel, units);
  4252. trainingBatches.push(new TrainingBatch(type, completionTime, units));
  4253. });
  4254. }
  4255. return trainingBatches;
  4256. }
  4257. function computeTrainingTime(barracksLevel, units) {
  4258. var time = 0;
  4259. $.each(units.getCounts(), function(type, count) {
  4260. var data = Constants.UnitData[type];
  4261. time += count * Math.pow(0.95, barracksLevel - data.minimumBuildingLevelToBuild) *
  4262. data.baseBuildTime;
  4263. });
  4264. return time * Constants.Time.MILLIS_PER_SECOND;
  4265. }
  4266. function processBarracksView(viewHtmlText, data) {
  4267. var city = View.getCurrentCity();
  4268. var military = city.getMilitary();
  4269. var barracks = city.getBuildingByType(Constants.Buildings.BARRACKS);
  4270. var changed = false;
  4271. function update(type, dataName) {
  4272. if (data[dataName]) {
  4273. changed = military._updatePresent(type, parseInt(data[dataName].text));
  4274. }
  4275. }
  4276. update(Constants.Military.HOPLITE, 'js_barracksUnitUnitsAvailable1');
  4277. update(Constants.Military.STEAM_GIANT, 'js_barracksUnitUnitsAvailable2');
  4278. update(Constants.Military.SPEARMAN, 'js_barracksUnitUnitsAvailable3');
  4279. update(Constants.Military.SWORDSMAN, 'js_barracksUnitUnitsAvailable4');
  4280. update(Constants.Military.SLINGER, 'js_barracksUnitUnitsAvailable5');
  4281. update(Constants.Military.ARCHER, 'js_barracksUnitUnitsAvailable6');
  4282. update(Constants.Military.GUNNER, 'js_barracksUnitUnitsAvailable7');
  4283. update(Constants.Military.BATTERING_RAM, 'js_barracksUnitUnitsAvailable8');
  4284. update(Constants.Military.CATAPULT, 'js_barracksUnitUnitsAvailable9');
  4285. update(Constants.Military.MORTAR, 'js_barracksUnitUnitsAvailable10');
  4286. update(Constants.Military.GYROCOPTER, 'js_barracksUnitUnitsAvailable11');
  4287. update(Constants.Military.BALLOON_BOMBADIER, 'js_barracksUnitUnitsAvailable12');
  4288. update(Constants.Military.COOK, 'js_barracksUnitUnitsAvailable13');
  4289. update(Constants.Military.DOCTOR, 'js_barracksUnitUnitsAvailable14');
  4290. military._markPresentUpdated(true, false);
  4291. military._setArmyTrainingBatches(
  4292. parseTrainingBatches(viewHtmlText, Constants.Military.ARMY, barracks.getLevel()));
  4293. raiseMilitaryChanged([{
  4294. city: city,
  4295. military: military,
  4296. type: 'data_updated',
  4297. }]);
  4298. }
  4299. function processShipyardView(viewHtmlText, data) {
  4300. var city = View.getCurrentCity();
  4301. var military = city.getMilitary();
  4302. var shipyard = city.getBuildingByType(Constants.Buildings.SHIPYARD);
  4303. var change = false;
  4304. function update(type, dataName) {
  4305. if (data[dataName]) {
  4306. changed = military._updatePresent(type, parseInt(data[dataName].text));
  4307. }
  4308. }
  4309. update(Constants.Military.FLAME_THROWER, 'js_barracksUnitUnitsAvailable1');
  4310. update(Constants.Military.STEAM_RAM, 'js_barracksUnitUnitsAvailable2');
  4311. update(Constants.Military.RAM_SHIP, 'js_barracksUnitUnitsAvailable3');
  4312. update(Constants.Military.CATAPULT_SHIP, 'js_barracksUnitUnitsAvailable4');
  4313. update(Constants.Military.BALLISTA_SHIP, 'js_barracksUnitUnitsAvailable5');
  4314. update(Constants.Military.MORTAR_SHIP, 'js_barracksUnitUnitsAvailable6');
  4315. update(Constants.Military.ROCKET_SHIP, 'js_barracksUnitUnitsAvailable7');
  4316. update(Constants.Military.SUBMARINE, 'js_barracksUnitUnitsAvailable8');
  4317. update(Constants.Military.PADDLE_SPEED_SHIP, 'js_barracksUnitUnitsAvailable9');
  4318. update(Constants.Military.BALLOON_CARRIER, 'js_barracksUnitUnitsAvailable10');
  4319. update(Constants.Military.TENDER, 'js_barracksUnitUnitsAvailable11');
  4320. military._markPresentUpdated(false, true);
  4321. military._setNavyTrainingBatches(
  4322. parseTrainingBatches(viewHtmlText, Constants.Military.NAVY, shipyard.getLevel()));
  4323. raiseMilitaryChanged([{
  4324. city: city,
  4325. military: military,
  4326. type: 'data_updated',
  4327. }]);
  4328. }
  4329. function processAcademyView(data) {
  4330. var changes = [];
  4331. View.getCurrentCity()._updateScientists(
  4332. parseInt(data['js_academy_research_tooltip_basic_production'].text),
  4333. changes);
  4334. raiseResourcesChanged(changes);
  4335. }
  4336. function processSetScientistsForm(form) {
  4337. var scientists = parseInt($('#inputScientists').val());
  4338. View.registerNextIkariamAjaxRequestCallback(function saveScientstsData(response) {
  4339. Utils.iterateIkariamAjaxResponse(response,
  4340. function lookForSuccessFeedback(index, name, data) {
  4341. if (name == Constants.IkariamAjaxResponseType.PROVIDE_FEEDBACK &&
  4342. data[0].type == 10) {
  4343. var changes = [];
  4344. View.getCurrentCity()._updateScientists(scientists, changes);
  4345. raiseResourcesChanged(changes);
  4346. }
  4347. });
  4348. });
  4349. }
  4350. function processPalaceView(data) {
  4351. var changes = [];
  4352. getCivilizationData()._updateGovernment(
  4353. $('.government_pic img').attr('src').slice(16, -8), changes);
  4354. raiseCivilizationDataChanged(changes);
  4355. }
  4356. function processMuseumView(data) {
  4357. var changes = [];
  4358. View.getCurrentCity()._updateCulturalGoods(
  4359. parseInt(/\d+/.exec($('#val_culturalGoodsDeposit').parent().text())[0]),
  4360. changes);
  4361. raiseResourcesChanged(changes);
  4362. }
  4363. function processCulturalPossessionsAssignView(data) {
  4364. // Have to delay this because the script elements in the changed view
  4365. // need to run before we can access the cultural good information.
  4366. // There is no feasible way to extract the data at this point.
  4367. setTimeout(function() {
  4368. var cityIdRegex = /textfield_city_(\d+)/
  4369. var changes = [];
  4370. $('#moveCulturalGoods ul li input').each(function (index, item) {
  4371. item = $(item);
  4372. var city = getCity(
  4373. parseInt(cityIdRegex.exec(item.attr('id'))[1]));
  4374. city._updateCulturalGoods(parseInt(item.val()), changes);
  4375. });
  4376. raiseResourcesChanged(changes);
  4377. empireData.saveAsync();
  4378. }, 0);
  4379. }
  4380. function processTownHallView(data) {
  4381. var changes = [];
  4382. var city = View.getCurrentCity();
  4383. city._updatePriests(
  4384. parseInt(data['js_TownHallPopulationGraphPriestCount'].text), changes);
  4385. city._updateCulturalGoods(
  4386. parseInt(
  4387. data['js_TownHallSatisfactionOverviewCultureBoniTreatyBonusValue'].text) / 50,
  4388. changes);
  4389. city._updateTavernWineLevel(
  4390. parseInt(data['js_TownHallSatisfactionOverviewWineBoniServeBonusValue'].text) / 60,
  4391. changes);
  4392. raiseResourcesChanged(changes);
  4393. }
  4394. function processTempleView(data) {
  4395. var changes = [];
  4396. View.getCurrentCity()._updatePriests(
  4397. parseInt(data['js_TempleSlider'].slider.ini_value), changes);
  4398. raiseResourcesChanged(changes);
  4399. }
  4400. function processResearchAdvisorView(data) {
  4401. var civData = getCivilizationData();
  4402. var idRegex = /researchId=([0-9]+)/i
  4403. var levelRegex = /\((\d+)\)/;
  4404. var researches =
  4405. JSON.parse(data['new_js_params'] || data['load_js'].params).currResearchType;
  4406. var changes = [];
  4407. $.each(researches, function (name, researchData) {
  4408. var id = parseInt(idRegex.exec(researchData.aHref)[1]);
  4409. var levelMatch = levelRegex.exec(name);
  4410. var level = levelMatch
  4411. ? parseInt(levelMatch[1]) - 1
  4412. : (researchData.liClass == 'explored' ? 1 : 0);
  4413. civData._updateResearch(id, level, changes);
  4414. });
  4415. raiseCivilizationDataChanged(changes);
  4416. }
  4417. function processFinancesView(data) {
  4418. var cities = getOwnCities();
  4419. var scientistCost = 6;
  4420. if (getCivilizationData().hasResearched(Constants.Research.Science.LETTER_CHUTE)) {
  4421. scientistCost = 3;
  4422. }
  4423. var changes = []
  4424. $('#finances .table01:eq(1) tr').slice(1, -1).each(function(index, row) {
  4425. var tds = $(row).children('td');
  4426. var city = cities[index];
  4427. if ($(tds[0]).text() == city.getName()) {
  4428. city._updateScientists(
  4429. Math.round(-parseInt($(tds[2]).text().replace(',', '')) / scientistCost), changes);
  4430. }
  4431. });
  4432. raiseResourcesChanged(changes);
  4433. }
  4434. function processMilitaryAdvisorView(data) {
  4435. var civilizationData = getCivilizationData();
  4436. var movementIds = {};
  4437. var changes = [];
  4438. $.each(civilizationData.getMovements(), function(index, movement) {
  4439. movementIds[movement.getId()] = movement;
  4440. });
  4441. var movementMainValueRegex = /^js_MilitaryMovementsEventRow(\d+)$/;
  4442. var cityIdRegex = /cityId=(\d+)/;
  4443. $.each(data, function(key, value) {
  4444. var match = movementMainValueRegex.exec(key);
  4445. if (match /*&& value.class.indexOf('own') > 0*/) {
  4446. var movementId = parseInt(match[1]);
  4447. delete movementIds[movementId];
  4448. var type = value.class;
  4449. var completionTime =
  4450. data['js_MilitaryMovementsEventRow' + movementId + 'ArrivalTime']
  4451. .countdown.enddate * Constants.Time.MILLIS_PER_SECOND;
  4452. var originCityId = parseInt(
  4453. data['js_MilitaryMovementsEventRow' + movementId + 'OriginLink'].href
  4454. .match(cityIdRegex)[1]);
  4455. var targetCityId = data['js_MilitaryMovementsEventRow' + movementId + 'TargetLink'].href
  4456. ? parseInt(data['js_MilitaryMovementsEventRow' + movementId + 'TargetLink'].href
  4457. .match(cityIdRegex)[1]) : 0;
  4458. var mission = data['js_MilitaryMovementsEventRow' + movementId + 'MissionIcon']
  4459. .class.split(' ')[1];
  4460. var stage = Constants.Movements.Stage.LOADING;
  4461. var statusClass =
  4462. data['js_MilitaryMovementsEventRow' + movementId + 'Mission'].class;
  4463. if (statusClass && statusClass.indexOf('arrow_right_green') >= 0) {
  4464. stage = Constants.Movements.Stage.EN_ROUTE;
  4465. } else if (statusClass && statusClass.indexOf('arrow_left_green') >= 0) {
  4466. stage = Constants.Movements.Stage.RETURNING;
  4467. }
  4468. var transports = 0;
  4469. var resources = {};
  4470. var units = new MilitaryUnits();
  4471. $.each(
  4472. data['js_MilitaryMovementsEventRow' + movementId + 'UnitDetails']
  4473. .appendElement || [],
  4474. function processUnit(index, item) {
  4475. var count = parseInt(item.text);
  4476. if (item.class.indexOf('ship_transport') >= 0) {
  4477. transports = count;
  4478. }
  4479. if (item.class.indexOf(Constants.Resources.WOOD) >= 0) {
  4480. resources[Constants.Resources.WOOD] = count;
  4481. } else if (item.class.indexOf(Constants.Resources.WINE) >= 0) {
  4482. resources[Constants.Resources.WINE] = count;
  4483. } else if (item.class.indexOf(Constants.Resources.MARBLE) >= 0) {
  4484. resources[Constants.Resources.MARBLE] = count;
  4485. } else if (item.class.indexOf(Constants.Resources.GLASS) >= 0) {
  4486. resources[Constants.Resources.GLASS] = count;
  4487. } else if (item.class.indexOf(Constants.Resources.SULFUR) >= 0) {
  4488. resources[Constants.Resources.SULFUR] = count;
  4489. }
  4490. $.each(Constants.Military, function findIsUnit(key, type) {
  4491. if (item.class.indexOf(' ' + type) >= 0) {
  4492. units._setCount(type, count);
  4493. return false;
  4494. }
  4495. });
  4496. });
  4497. var movement = new Movement(
  4498. movementId, type, completionTime, mission, stage, originCityId, targetCityId,
  4499. transports, units, resources);
  4500. civilizationData._updateMovement(movement, changes);
  4501. }
  4502. });
  4503. $.each(movementIds, function removeMissingMovements(id, value) {
  4504. civilizationData._removeMovement(id, changes);
  4505. });
  4506. raiseMovementsChanged(changes);
  4507. }
  4508. function processPremiumView(data) {
  4509. var civilizationData = getCivilizationData();
  4510. var changes = [];
  4511. civilizationData._updatePremiumFeature(changes,
  4512. Constants.PremiumFeatures.DOUBLED_SAFE_CAPACITY,
  4513. $('#js_buySafecapacityBonusActiveTime').hasClass('green'));
  4514. civilizationData._updatePremiumFeature(changes,
  4515. Constants.PremiumFeatures.DOUBLED_STORAGE_CAPACITY,
  4516. $('#js_buyStoragecapacityBonusActiveTime').hasClass('green'));
  4517. raiseCivilizationDataChanged(changes);
  4518. }
  4519. function updateAndStartTracking() {
  4520. // Process all known cities that show up in the dropdown.
  4521. // Drop any cities that are no longer there.
  4522. var cities = { };
  4523. var cityOrder = [];
  4524. function updateCurrentCity(globalData, backgroundData, correctWineConsumption) {
  4525. var currentCity = View.getCurrentCity();
  4526. if (View.viewIsCity() && currentCity.getId() == parseInt(backgroundData.id)) {
  4527. currentCity._updateFromBackgroundData(backgroundData);
  4528. }
  4529. currentCity._updateFromGlobalData(globalData, correctWineConsumption);
  4530. Logging.debug("Current city %s[%s]: ",
  4531. currentCity.name, currentCity.id, currentCity);
  4532. }
  4533. $.each(unsafeWindow.dataSetForView.relatedCityData,
  4534. function updateFromPage_Each(key, value) {
  4535. if (key.substring(0, 5) == "city_") {
  4536. var city = empireData.get().cities[value.id] ||
  4537. new City(value.id, value.relationship);
  4538. city.type = value.relationship;
  4539. city.name = value.name;
  4540. city.islandCoordinates = parseCoordinates(value.coords);
  4541. if (value.tradegood) {
  4542. city.tradeGoodType = TRADE_GOOD_LOOKUP[value.tradegood];
  4543. }
  4544. empireData.get().cities[city.id] = city;
  4545. cityOrder.push(city.id);
  4546. Logging.debug("City %s[%s]: %o", city.name, city.id, city);
  4547. }
  4548. });
  4549. empireData.get().cityOrder = cityOrder;
  4550. var globalData = {
  4551. maxActionPoints: parseInt($('#js_GlobalMenu_maxActionPoints').text()),
  4552. };
  4553. updateCurrentCity($.extend(globalData, unsafeWindow.dataSetForView),
  4554. unsafeWindow.ikariam.backgroundView.screen.data,
  4555. true);
  4556. empireData.saveAsync();
  4557. function updateEmpireDataFromGlobalData(data) {
  4558. View.setGameTimeDifference(new Date().getTime() - (data['time'] || data[1]) *
  4559. Constants.Time.MILLIS_PER_SECOND);
  4560. updateCurrentCity(data['headerData'] || data[10], data['backgroundData'] || data[11]);
  4561. }
  4562. View.registerIkariamAjaxResponseCallback(
  4563. function updateEmpireDataFromAjaxResponse(response) {
  4564. var globalData;
  4565. var view;
  4566. var viewHtml;
  4567. var templateData;
  4568. Utils.iterateIkariamAjaxResponse(response, function(index, name, data) {
  4569. if (name == Constants.IkariamAjaxResponseType.UPDATE_GLOBAL_DATA) {
  4570. globalData = data;
  4571. } else if (name == Constants.IkariamAjaxResponseType.CHANGE_VIEW) {
  4572. view = data[0];
  4573. viewHtml = data[1];
  4574. } else if (name == Constants.IkariamAjaxResponseType.UPDATE_TEMPLATE_DATA) {
  4575. templateData = data;
  4576. }
  4577. });
  4578. if (globalData) {
  4579. updateEmpireDataFromGlobalData(globalData);
  4580. }
  4581. if (view == Constants.View.CITY_MILITARY) {
  4582. processCityMilitaryView(templateData);
  4583. } else if (view == Constants.View.RELATED_CITIES) {
  4584. processRelatedCitiesView(templateData);
  4585. } else if (view == Constants.View.ACADEMY) {
  4586. processAcademyView(templateData);
  4587. } else if (view == Constants.View.PALACE) {
  4588. processPalaceView(templateData);
  4589. } else if (view == Constants.View.MUSEUM) {
  4590. processMuseumView(templateData);
  4591. } else if (view == Constants.View.ASSIGN_CULTURAL_POSSESSIONS) {
  4592. processCulturalPossessionsAssignView(templateData);
  4593. } else if (view == Constants.View.TOWN_HALL) {
  4594. processTownHallView(templateData);
  4595. } else if (view == Constants.View.TEMPLE) {
  4596. processTempleView(templateData);
  4597. } else if (view == Constants.View.RESEARCH_ADVISOR) {
  4598. processResearchAdvisorView(templateData);
  4599. } else if (view == Constants.View.FINANCES) {
  4600. processFinancesView(templateData);
  4601. } else if (view == Constants.View.BARRACKS) {
  4602. processBarracksView(viewHtml, templateData);
  4603. } else if (view == Constants.View.SHIPYARD) {
  4604. processShipyardView(viewHtml, templateData);
  4605. } else if (view == Constants.View.MILITARY_ADVISOR) {
  4606. processMilitaryAdvisorView(templateData);
  4607. } else if (view == Constants.View.PREMIUM) {
  4608. processPremiumView(templateData);
  4609. }
  4610. if (unsafeWindow.ikariam.templateView) {
  4611. if (unsafeWindow.ikariam.templateView.id == Constants.View.RESEARCH_ADVISOR) {
  4612. processResearchAdvisorView(templateData);
  4613. }
  4614. }
  4615. empireData.saveAsync();
  4616. }, true);
  4617. View.registerAjaxFormSubmitCallback(
  4618. function ajaxHandlerCallFromFormEmpireDataUpdate(form) {
  4619. if (form.id == 'transport' || form.id == 'transportForm') {
  4620. processTransportForm(form);
  4621. } else if (form.id == 'deploymentForm') {
  4622. processDeploymentForm(form);
  4623. } else if (form.id == 'plunderForm') {
  4624. processPlunderForm(form);
  4625. } else if (form.id == 'setScientists') {
  4626. processSetScientistsForm(form);
  4627. }
  4628. });
  4629. }
  4630. function updateMovements(callback) {
  4631. View.backgroundGetIkariamPage(
  4632. 'http://' + document.domain + '/index.php?view=militaryAdvisor&activeTab=militaryMovements&ajax=1',
  4633. function updateMovementsCallback(response) {
  4634. var dataResponse = JSON.parse(response.responseText);
  4635. Utils.iterateIkariamAjaxResponse(dataResponse, function(index, name, data) {
  4636. if (name == Constants.IkariamAjaxResponseType.UPDATE_TEMPLATE_DATA) {
  4637. processMilitaryAdvisorView(data);
  4638. }
  4639. });
  4640. empireData.saveAsync();
  4641. callback(dataResponse);
  4642. },
  4643. 'POST');
  4644. }
  4645. function getCities() {
  4646. var data = empireData.get();
  4647. var cities = [];
  4648. for (var i = 0; i < data.cityOrder.length; i++) {
  4649. cities.push(data.cities[data.cityOrder[i]]);
  4650. }
  4651. return cities;
  4652. }
  4653. function getOwnCities() {
  4654. return getCities().filter(function(city) {
  4655. return city.isOwn();
  4656. });
  4657. }
  4658. function getCivilizationData() {
  4659. return empireData.get().civilizationData;
  4660. }
  4661. function getDebugString(includePrivateData) {
  4662. return JSON.stringify(empireData.get(), function debugStringify(name, value) {
  4663. if (name === 'name' || name === 'islandCoordinates') {
  4664. return undefined;
  4665. }
  4666. return value;
  4667. });
  4668. }
  4669. function resetData() {
  4670. empireData.reset();
  4671. }
  4672. var Espionage = function() {
  4673. function Target(id) {
  4674. this._ikaToolsType = 'target';
  4675. if (id) {
  4676. this.id = id;
  4677. this.playerId = undefined;
  4678. this.allianceId = undefined;
  4679. this.townLevel = undefined;
  4680. this.wallLevel = undefined;
  4681. this.warehouseLevel = undefined;
  4682. this.islandId = undefined;
  4683. this.coords = undefined;
  4684. this.occupierId = undefined;
  4685. this.blockaderId = undefined;
  4686. this.tradeGoodType = undefined;
  4687. this.lastUpdateTime = 0;
  4688. this.military = new MilitaryUnits();
  4689. this.otherMilitary = new MilitaryUnits();
  4690. this.militaryLastSpyMessageId = 0;
  4691. this.militaryLastSpyTime = 0;
  4692. this.resources = {
  4693. wood: 0,
  4694. wine: 0,
  4695. marble: 0,
  4696. glass: 0,
  4697. sulfur: 0,
  4698. lastSpyMessageId: 0,
  4699. lastSpyTime: 0,
  4700. };
  4701. this.combats = {};
  4702. }
  4703. }
  4704. function combatComparer(combat1, combat2) {
  4705. return combat2.time - combat1.time;
  4706. };
  4707. $.extend(Target.prototype, {
  4708. getId: function getId() {
  4709. return this.id;
  4710. },
  4711. getName: function getName() {
  4712. return this.name;
  4713. },
  4714. getTownLevel: function getTownLevel() {
  4715. return this.townLevel;
  4716. },
  4717. getWallLevel: function getWallLevel() {
  4718. return this.wallLevel;
  4719. },
  4720. getIslandCoordinates: function getIslandCoordinates() {
  4721. return this.coords;
  4722. },
  4723. getIslandId: function getIslandId() {
  4724. return this.islandId;
  4725. },
  4726. getPlayer: function _getPlayer() {
  4727. return getPlayer(this.playerId);
  4728. },
  4729. getOccupier: function getOccupier() {
  4730. if (this.occupierId) {
  4731. return getPlayer(this.occupierId);
  4732. }
  4733. return null;
  4734. },
  4735. getBlockader: function getBlockader() {
  4736. if (this.blockaderId) {
  4737. return getPlayer(this.blockaderId);
  4738. }
  4739. },
  4740. getTradeGoodType: function getTradeGoodType() {
  4741. return this.tradeGoodType;
  4742. },
  4743. getMilitary: function getMilitary() {
  4744. return this.military;
  4745. },
  4746. getOtherMilitary: function getOtherMilitary() {
  4747. return this.otherMilitary;
  4748. },
  4749. hasResourceInfo: function hasResourceInfo() {
  4750. return this.resources.lastSpyMessageId > 0;
  4751. },
  4752. hasMilitaryInfo: function hasMilitaryInfo() {
  4753. return this.militaryLastSpyMessageId > 0;
  4754. },
  4755. getLootableResources: function getLootableResources(type) {
  4756. var available = this.resources[type];
  4757. $.each(this.getCombats(View.gameTimeNow() - this.resources.lastSpyTime),
  4758. function subtractCombatResources(index, combat) {
  4759. available -= combat.getLooted(type);
  4760. });
  4761. return Math.max(0, available -
  4762. (this.getPlayer().isInactive() ?
  4763. Constants.GamePlay.RESOURCE_PROTECTION_WAREHOUSE_INACTIVE :
  4764. Constants.GamePlay.RESOURCE_PROTECTION_WAREHOUSE) * this.warehouseLevel -
  4765. Constants.GamePlay.BASE_RESOURCE_PROTECTION);
  4766. },
  4767. getResourcesSpyTime: function getResourcesSpyTime() {
  4768. return this.resources.lastSpyTime;
  4769. },
  4770. getMilitarySpyTime: function getMilitarySpyTime() {
  4771. return this.militaryLastSpyTime;
  4772. },
  4773. getCombats: function getCombats(maxAge) {
  4774. var combats = [];
  4775. $.each(this.combats, function(index, combat) {
  4776. if (View.gameTimeNow() - combat.time <= maxAge) {
  4777. combats.push(combat);
  4778. }
  4779. });
  4780. combats.sort(combatComparer);
  4781. return combats;
  4782. },
  4783. remove: function remove() {
  4784. delete espionageData.get().targets[this.id];
  4785. espionageData.saveAsync();
  4786. raiseEspionageChanged({
  4787. type: 'targetRemoved',
  4788. targets: [this]
  4789. });
  4790. },
  4791. _refresh: function refresh(hasSpiesPresent, callback) {
  4792. if (View.gameTimeNow() - this.lastRefreshTime < Constants.Time.MILLIS_PER_HOUR) {
  4793. console.log('Skipping refresh');
  4794. callback();
  4795. return;
  4796. }
  4797. this.lastRefreshTime = new Date().getTime();
  4798. var datasLoaded = hasSpiesPresent ? 2 : 1;
  4799. function doneLoading() {
  4800. if (--datasLoaded == 0) {
  4801. callback();
  4802. }
  4803. }
  4804. Utils.backgroundFetchIkariamFullPage(
  4805. 'http://' + document.domain + '/index.php?view=island&cityId=' + this.id,
  4806. this._refreshIslandCallback.bind(this, doneLoading));
  4807. if (hasSpiesPresent) {
  4808. Utils.backgroundFetchIkariamFullPage(
  4809. 'http://' + document.domain + '/index.php?view=city&cityId=' + this.id,
  4810. this._refreshCityCallback.bind(this, doneLoading));
  4811. }
  4812. },
  4813. _refreshIslandCallback: function refreshIslandCallback(callback, response, ajaxResponse) {
  4814. var target = this;
  4815. Utils.iterateIkariamAjaxResponse(ajaxResponse,
  4816. function refreshTargetData(index, name, data) {
  4817. if (name == IkaTools.Constants.IkariamAjaxResponseType.UPDATE_BACKGROUND_DATA) {
  4818. $.each(data.cities, function findTarget(index, city) {
  4819. if (parseInt(city.id) == target.id) {
  4820. var playerId = parseInt(city.ownerId);
  4821. target.name = city.name;
  4822. target.playerId = parseInt(city.ownerId);
  4823. target.townLevel = parseInt(city.level);
  4824. target.islandId = parseInt(data.id);
  4825. target.coords = [parseInt(data.xCoord), parseInt(data.yCoord)];
  4826. target.tradeGoodType = TRADE_GOOD_LOOKUP[data.tradegood];
  4827. updateOrAddPlayer(playerId, city.ownerName, city.state, parseInt(city.ownerAllyId),
  4828. city.ownerAllyTag,
  4829. parseInt(data.avatarScores[playerId].army_score_main.split(',').join('').split(',').join('')) / 100);
  4830. if (city.infos && city.infos['occupation']) {
  4831. target.occupierId = city.infos['occupation'].id;
  4832. updateOrAddPlayer(city.infos['occupation'].id, city.infos['occupation'].name);
  4833. } else {
  4834. target.occupierId = 0;
  4835. }
  4836. if (city.infos && city.infos['fleetAction']) {
  4837. target.blockaderId = city.infos['fleetAction'].id;
  4838. updateOrAddPlayer(city.infos['fleetAction'].id, city.infos['fleetAction'].name);
  4839. } else {
  4840. target.blockaderId = 0;
  4841. }
  4842. }
  4843. });
  4844. }
  4845. });
  4846. callback(this);
  4847. },
  4848. _refreshCityCallback: function refreshCityCallback(callback, response, ajaxResponse) {
  4849. var target = this;
  4850. Utils.iterateIkariamAjaxResponse(ajaxResponse,
  4851. function refreshTargetData(index, name, data) {
  4852. if (name == IkaTools.Constants.IkariamAjaxResponseType.UPDATE_BACKGROUND_DATA) {
  4853. target.wallLevel = parseInt(data.position[14].level) || 0;
  4854. target.warehouseLevel = 0;
  4855. $.each(data.position, function(index, item) {
  4856. if (item.building == Constants.Buildings.WAREHOUSE) {
  4857. target.warehouseLevel += parseInt(item.level);
  4858. }
  4859. });
  4860. }
  4861. });
  4862. callback(this);
  4863. },
  4864. _getOrAddCombat: function getOrAddCombat(id) {
  4865. var combat = this.combats[id];
  4866. if (!combat) {
  4867. combat = new Target.Combat(id);
  4868. this.combats[id] = combat;
  4869. }
  4870. return combat;
  4871. },
  4872. });
  4873. Target.Combat = function Target_Combat(id) {
  4874. this._ikaToolsType = 'targetCombat';
  4875. if (id) {
  4876. this.id = id;
  4877. this.type = undefined;
  4878. this.time = undefined;
  4879. this.resources = {
  4880. wood: 0,
  4881. wine: 0,
  4882. marble: 0,
  4883. glass: 0,
  4884. sulfur: 0,
  4885. };
  4886. }
  4887. }
  4888. $.extend(Target.Combat.prototype, {
  4889. getType: function getType() {
  4890. return this.type;
  4891. },
  4892. getTime: function getTime() {
  4893. return this.time;
  4894. },
  4895. getLooted: function getLooted(resourceType) {
  4896. return this.resources[resourceType];
  4897. },
  4898. });
  4899. function Player(id) {
  4900. this._ikaToolsType = 'player';
  4901. if (id) {
  4902. this.id = id;
  4903. this.name = null;
  4904. this.allianceId = null;
  4905. this.militaryScore = null;
  4906. }
  4907. }
  4908. $.extend(Player.prototype, {
  4909. _update: function update(name, state, allianceId, militaryScore) {
  4910. this.name = name;
  4911. if (state !== undefined) {
  4912. this.allianceId = allianceId;
  4913. this.militaryScore = militaryScore;
  4914. this.state = state;
  4915. }
  4916. },
  4917. getAlliance: function getAlliance() {
  4918. if (this.allianceId) {
  4919. return espionageData.get().alliances[this.allianceId];
  4920. } else {
  4921. return;
  4922. }
  4923. },
  4924. getName: function getName() {
  4925. return this.name;
  4926. },
  4927. getState: function getState() {
  4928. return this.state;
  4929. },
  4930. getMilitaryScore: function getMilitaryScore() {
  4931. return this.militaryScore;
  4932. },
  4933. isInactive: function isInactive() {
  4934. return this.state == Constants.PlayerState.INACTIVE;
  4935. },
  4936. });
  4937. function updateOrAddPlayer(id, name, state, allianceId, allianceName, militaryScore) {
  4938. var players = espionageData.get().players;
  4939. var player = players[id];
  4940. if (!player) {
  4941. player = new Player(id);
  4942. players[id] = player;
  4943. }
  4944. player._update(name, state, allianceId, militaryScore);
  4945. updateOrAddAlliance(allianceId, allianceName);
  4946. }
  4947. function Alliance(id) {
  4948. this._ikaToolsType = 'alliance';
  4949. if (id) {
  4950. this.id = id;
  4951. this.name = null;
  4952. }
  4953. }
  4954. $.extend(Alliance.prototype, {
  4955. _update: function update(name) {
  4956. this.name = name;
  4957. },
  4958. getName: function getName() {
  4959. return this.name;
  4960. },
  4961. getId: function getId() {
  4962. return this.id;
  4963. }
  4964. });
  4965. function updateOrAddAlliance(id, name) {
  4966. if (id) {
  4967. var alliances = espionageData.get().alliances;
  4968. var alliance = alliances[id];
  4969. if (!alliance) {
  4970. alliance = new Alliance(id);
  4971. alliances[id] = alliance;
  4972. }
  4973. alliance._update(name);
  4974. }
  4975. }
  4976. function addTargetById(id, hasSpiesPresent, callback) {
  4977. var targets = espionageData.get().targets;
  4978. var target = targets[id];
  4979. if (!target) {
  4980. var target = new Target(id);
  4981. target._refresh(hasSpiesPresent, function() {
  4982. targets[id] = target;
  4983. callback(target);
  4984. });
  4985. } else {
  4986. target._refresh(hasSpiesPresent, function() {
  4987. callback(target);
  4988. });
  4989. }
  4990. }
  4991. function getTargets() {
  4992. var targets = [];
  4993. $.each(espionageData.get().targets, function(index, target) {
  4994. targets.push(target);
  4995. });
  4996. return targets;
  4997. }
  4998. function getTarget(id) {
  4999. return espionageData.get().targets[id];
  5000. }
  5001. function getPlayer(id) {
  5002. return espionageData.get().players[id];
  5003. }
  5004. function getPlayers() {
  5005. return espionageData.get().players;
  5006. }
  5007. var espionageData = new Data.Value(
  5008. 'espionageData',
  5009. {
  5010. targets: {},
  5011. alliances: {},
  5012. players: {},
  5013. },
  5014. {
  5015. reviver: function espionageDataReviver(key, value) {
  5016. if (value && value._ikaToolsType) {
  5017. var obj;
  5018. switch(value._ikaToolsType) {
  5019. case 'target': obj = new Target(); break;
  5020. case 'targetCombat': obj = new Target.Combat(); break;
  5021. case 'player': obj = new Player(); break;
  5022. case 'alliance': obj = new Alliance(); break;
  5023. case 'militaryUnits': obj = new MilitaryUnits(); break;
  5024. }
  5025. $.extend(obj, value);
  5026. if (obj._postLoad) {
  5027. obj._postLoad();
  5028. }
  5029. return obj;
  5030. }
  5031. return value;
  5032. },
  5033. version: 6,
  5034. loadCallback: function espionageDataLoaded() {
  5035. },
  5036. });
  5037. var espionageChangedEvent = Utils.thunk(function() {
  5038. return new Utils.EventDispatcher();
  5039. });
  5040. function registerEspionageChangedHandler(callback) {
  5041. return espionageChangedEvent().addListener(callback);
  5042. }
  5043. function raiseEspionageChanged(changes) {
  5044. espionageChangedEvent().send(changes);
  5045. }
  5046. function startTracking() {
  5047. var messageIdRegex = /message(\d+)/
  5048. espionageData.load();
  5049. View.registerIkariamAjaxResponseCallback(
  5050. function processHideoutView(response) {
  5051. IkaTools.Utils.iterateIkariamAjaxResponse(response, function(index, name, data) {
  5052. if (name == IkaTools.Constants.IkariamAjaxResponseType.CHANGE_VIEW) {
  5053. if (data[0] == Constants.View.HIDEOUT) {
  5054. var targetCount = 0;
  5055. var targets = [];
  5056. $('#tabSafehouse li.city a').each(function(index, item) {
  5057. targetCount++;
  5058. addTargetById(parseInt(Utils.parseUrlParams($(item).attr('href'))['cityId']), true,
  5059. function targetAdded(target) {
  5060. targets.push(target);
  5061. targetCount--;
  5062. if (targetCount == 0) {
  5063. espionageData.saveAsync();
  5064. raiseEspionageChanged({
  5065. type: 'targetsChanged',
  5066. targets: targets
  5067. });
  5068. }
  5069. });
  5070. });
  5071. var reportHeaders = $(
  5072. '#espionageReports tr.espionageReports, #espionageReports tr.espionageReportsalt');
  5073. if (reportHeaders.length) {
  5074. var changedTargets = [];
  5075. reportHeaders.each(function(index, reportHeader) {
  5076. var success = $('td.resultImage img', reportHeader).attr('src') == '/cdn/all/both/buttons/yes.png';
  5077. var target = getTarget(parseInt(
  5078. Utils.parseUrlParams($('td.targetCity a', reportHeader).attr('href'))['selectCity']));
  5079. var messageId = parseInt(reportHeader.id.match(messageIdRegex)[1]);
  5080. var tableMailMessage = $('#tbl_mail' + messageId);
  5081. if (success && target) {
  5082. if ($('td.money', reportHeader).length &&
  5083. messageId > target.resources.lastSpyMessageId) {
  5084. // Warehouse resources mission
  5085. var resourceTds = $('#tbl_mail' + messageId + ' td.count');
  5086. target.resources.wood = parseInt(resourceTds.get(0).textContent.replace(',', ''));
  5087. target.resources.wine = parseInt(resourceTds.get(1).textContent.replace(',', ''));
  5088. target.resources.marble = parseInt(resourceTds.get(2).textContent.replace(',', ''));
  5089. target.resources.glass = parseInt(resourceTds.get(3).textContent.replace(',', ''));
  5090. target.resources.sulfur = parseInt(resourceTds.get(4).textContent.replace(',', ''));
  5091. target.resources.lastSpyMessageId = messageId;
  5092. target.resources.lastSpyTime =
  5093. Utils.parseIkariamTimestamp($('td.date', reportHeader).text()).getTime();
  5094. target._refresh(false, function() {
  5095. espionageData.saveAsync(),
  5096. raiseEspionageChanged({
  5097. type: 'targetsRefreshed',
  5098. targets: [target],
  5099. });
  5100. });
  5101. changedTargets.push(target);
  5102. } else if ($('td.garrison', reportHeader).length &&
  5103. messageId > target.militaryLastSpyMessageId &&
  5104. tableMailMessage.find(' table.reportTable').length) {
  5105. readSpiedMilitary(target.getMilitary(),
  5106. tableMailMessage.find('table.reportTable tr:nth-child(2) td.count'));
  5107. readSpiedMilitary(target.getOtherMilitary(),
  5108. tableMailMessage.find('table.reportTable tr:nth-child(3) td.count'));
  5109. target.militaryLastSpyMessageId = messageId;
  5110. target.militaryLastSpyTime = Utils.parseIkariamTimestamp($('td.date', reportHeader).text()).getTime();
  5111. target._refresh(false, function() {
  5112. espionageData.saveAsync(),
  5113. raiseEspionageChanged({
  5114. type: 'targetsRefreshed',
  5115. targets: [target],
  5116. });
  5117. });
  5118. changedTargets.push(target);
  5119. }
  5120. }
  5121. });
  5122. if (changedTargets.length) {
  5123. espionageData.saveAsync();
  5124. raiseEspionageChanged({
  5125. type: 'targetsChanged',
  5126. targets: changedTargets
  5127. });
  5128. }
  5129. }
  5130. } else if (data[0] == Constants.View.MILITARY_ADVISOR_REPORT) {
  5131. var report = $('#troopsReport');
  5132. var defender = report.find('.defender b:first a:first');
  5133. if (defender.length) {
  5134. var target = getTarget(parseInt(
  5135. Utils.parseUrlParams(defender.attr('href'))['cityId']));
  5136. if (target) {
  5137. var header = report.find('.header');
  5138. var result = report.find('div.result');
  5139. var combatId = parseInt(Utils.parseUrlParams(
  5140. report.find('div p.link:first a.button:first',report).attr('href'))['detailedCombatId']);
  5141. var combatTime = Utils.parseIkariamTimestamp(report.find('.header .date').text()).getTime();
  5142. var type = report.find('.overview .fleet').length ?
  5143. IkaTools.Constants.CombatType.BLOCKADE : IkaTools.Constants.CombatType.PILLAGE;
  5144. var combat = target._getOrAddCombat(combatId);
  5145. combat.type = type;
  5146. combat.time = combatTime;
  5147. result.find('.resources li.value').each(function(index, item) {
  5148. var resourceInfo = $(item);
  5149. var type = resourceInfo.find('img').attr('src').match(/icon_([a-z]*)_small.png/)[1];
  5150. if (type == 'crystal') {
  5151. type = Constants.Resources.GLASS;
  5152. }
  5153. var amount = parseInt(resourceInfo.text());
  5154. combat.resources[type] = amount;
  5155. });
  5156. target._refresh(false, function() {
  5157. espionageData.saveAsync(),
  5158. raiseEspionageChanged({
  5159. type: 'targetsRefreshed',
  5160. targets: [target],
  5161. });
  5162. });
  5163. }
  5164. espionageData.saveAsync();
  5165. raiseEspionageChanged({
  5166. type: 'combatUpdated',
  5167. targets: [target],
  5168. });
  5169. }
  5170. }
  5171. } else if (name == Constants.IkariamAjaxResponseType.BACKGROUND_DATA) {
  5172. }
  5173. });
  5174. }, true);
  5175. }
  5176. function readSpiedMilitary(military, unitTds) {
  5177. var baseArmyCount = true;
  5178. var baseNavyCount = true;
  5179. var navyOffset = 0;
  5180. if (unitTds.length == 14) { // army only
  5181. baseNavyCount = 0;
  5182. } else if (unitTds.length == 11) { // navy only
  5183. baseArmyCount = 0;
  5184. navyOffset = -14;
  5185. } else if (unitTds.length == 0) { // nothing
  5186. baseNavyCount = 0;
  5187. baseArmyCount = 0;
  5188. }
  5189. military._setCount(Constants.Military.HOPLITE, baseArmyCount && parseInt($(unitTds[0]).text()) || 0);
  5190. military._setCount(Constants.Military.STEAM_GIANT, baseArmyCount && parseInt($(unitTds[1]).text()) || 0);
  5191. military._setCount(Constants.Military.SPEARMAN, baseArmyCount && parseInt($(unitTds[2]).text()) || 0);
  5192. military._setCount(Constants.Military.SWORDSMAN, baseArmyCount && parseInt($(unitTds[3]).text()) || 0);
  5193. military._setCount(Constants.Military.SLINGER, baseArmyCount && parseInt($(unitTds[4]).text()) || 0);
  5194. military._setCount(Constants.Military.ARCHER, baseArmyCount && parseInt($(unitTds[5]).text()) || 0);
  5195. military._setCount(Constants.Military.GUNNER, baseArmyCount && parseInt($(unitTds[6]).text()) || 0);
  5196. military._setCount(Constants.Military.BATTERING_RAM, baseArmyCount && parseInt($(unitTds[7]).text()) || 0);
  5197. military._setCount(Constants.Military.CATAPULT, baseArmyCount && parseInt($(unitTds[8]).text()) || 0);
  5198. military._setCount(Constants.Military.MORTAR, baseArmyCount && parseInt($(unitTds[9]).text()) || 0);
  5199. military._setCount(Constants.Military.GYROCOPTER, baseArmyCount && parseInt($(unitTds[10]).text()) || 0);
  5200. military._setCount(Constants.Military.BALLOON_BOMBADIER, baseArmyCount && parseInt($(unitTds[11]).text()) || 0);
  5201. military._setCount(Constants.Military.COOK, baseArmyCount && parseInt($(unitTds[12]).text()) || 0);
  5202. military._setCount(Constants.Military.DOCTOR, baseArmyCount && parseInt($(unitTds[13]).text()) || 0);
  5203. military._setCount(Constants.Military.RAM_SHIP, parseInt(baseNavyCount && $(unitTds[16 + navyOffset]).text()) || 0);
  5204. military._setCount(Constants.Military.FLAME_THROWER, parseInt(baseNavyCount && $(unitTds[14 + navyOffset]).text()) || 0);
  5205. military._setCount(Constants.Military.STEAM_RAM, parseInt(baseNavyCount && $(unitTds[15 + navyOffset]).text()) || 0);
  5206. military._setCount(Constants.Military.BALLISTA_SHIP, parseInt(baseNavyCount && $(unitTds[18 + navyOffset]).text()) || 0);
  5207. military._setCount(Constants.Military.CATAPULT_SHIP, parseInt(baseNavyCount && $(unitTds[17 + navyOffset]).text()) || 0);
  5208. military._setCount(Constants.Military.MORTAR_SHIP, parseInt(baseNavyCount && $(unitTds[19 + navyOffset]).text()) || 0);
  5209. military._setCount(Constants.Military.SUBMARINE, parseInt(baseNavyCount && $(unitTds[21 + navyOffset]).text()) || 0);
  5210. military._setCount(Constants.Military.PADDLE_SPEED_SHIP, parseInt(baseNavyCount && $(unitTds[22 + navyOffset]).text()) || 0);
  5211. military._setCount(Constants.Military.BALLOON_CARRIER, parseInt(baseNavyCount && $(unitTds[23 + navyOffset]).text()) || 0);
  5212. military._setCount(Constants.Military.TENDER, parseInt(baseNavyCount && $(unitTds[24 + navyOffset]).text()) || 0);
  5213. military._setCount(Constants.Military.ROCKET_SHIP, parseInt(baseNavyCount && $(unitTds[20 + navyOffset]).text()) || 0);
  5214. }
  5215. function getDebugString(includePrivateData) {
  5216. return JSON.stringify(espionageData.get(), function debugStringify(name, value) {
  5217. if (name === 'name' || name === 'coordinates') {
  5218. return undefined;
  5219. }
  5220. return value;
  5221. });
  5222. }
  5223. function resetData() {
  5224. espionageData.reset();
  5225. }
  5226. return {
  5227. startTracking: startTracking,
  5228. registerEspionageChangedHandler: registerEspionageChangedHandler,
  5229. getTargets: getTargets,
  5230. getPlayers: getPlayers,
  5231. getDebugString: getDebugString,
  5232. resetData: resetData,
  5233. };
  5234. }();
  5235. return {
  5236. updateAndStartTracking: Logging.debuggable(
  5237. { label: 'IkaTools.EmpireData.updateAndStartTracking', alwaysTime: true },
  5238. updateAndStartTracking),
  5239. updateMovements: updateMovements,
  5240. calculateTravelTime: calculateTravelTime,
  5241. getCities: Utils.thunk(getCities),
  5242. getOwnCities: Utils.thunk(getOwnCities),
  5243. getCivilizationData: getCivilizationData,
  5244. getCity: getCity,
  5245. getDebugString: getDebugString,
  5246. resetData: resetData,
  5247. registerCivilizationDataChangedHandler: registerCivilizationDataChangedHandler,
  5248. registerResourcesChangedHandler: registerResourcesChangedHandler,
  5249. registerBuildingsChangedHandler: registerBuildingsChangedHandler,
  5250. registerMilitaryChangedHandler: registerMilitaryChangedHandler,
  5251. registerMovementsChangedHandler: registerMovementsChangedHandler,
  5252. Espionage:Espionage,
  5253. };
  5254. }();
  5255. function processAnchor() {
  5256. var anchor = window.location.hash;
  5257. if (anchor == '#ikaScriptToolsSuppressCityPreselect') {
  5258. document.location.hash = '';
  5259. //unsafeWindow.ikariam.backgroundView.screen.preselectCity =
  5260. // function suppressPreselectCity() { };
  5261. View.suppressFirstChangeViewOfType('cityDetails');
  5262. }
  5263. if (anchor.substring(0, 35) == '#ikaScriptToolsLoadLocalIkariamUrl=') {
  5264. var url = decodeURIComponent(anchor.substring(35));
  5265. document.location.hash = '';
  5266. IkaTools.View.loadLocalIkariamUrl(url);
  5267. if (IkaTools.View.viewIsIsland()) {
  5268. View.suppressFirstChangeViewOfType('cityDetails');
  5269. }
  5270. } else if (anchor.substring(0, 62) ==
  5271. '#ikaScriptToolsLoadLocalIkariamUrl_DoNotSuppressFirstCityInfo=') {
  5272. var url = decodeURIComponent(anchor.substring(62));
  5273. document.location.hash = '';
  5274. IkaTools.View.loadLocalIkariamUrl(url);
  5275. }
  5276. }
  5277. function initialize(options) {
  5278. processAnchor();
  5279. $(window).bind('hashchange', processAnchor);
  5280. options = $.extend({ trackData: true }, options);
  5281. View.setGameTimeDifference(new Date().getTime() -
  5282. unsafeWindow.ikariam.model.requestTime * Constants.Time.MILLIS_PER_SECOND -
  5283. Constants.Time.INITIAL_PAGE_LOAD_DELTA);
  5284. if (options.trackData) {
  5285. IkaTools.EmpireData.updateAndStartTracking();
  5286. }
  5287. }
  5288. return {
  5289. Logging: Logging,
  5290. Utils: Utils,
  5291. View: View,
  5292. Data: Data,
  5293. Intl: Intl,
  5294. EmpireData: EmpireData,
  5295. Constants: Constants,
  5296. UI: UI,
  5297. Settings: Settings,
  5298. initialize: initialize,
  5299. processAnchor: processAnchor,
  5300. };
  5301. })();
  5302. }