Greasy Fork is available in English.

Ikariam Developer Tools V0.5.0+

Base scripting tools and data tracking utilities to simplify

Skrip ini tidak untuk dipasang secara langsung. Ini adalah pustaka skrip lain untuk disertakan dengan direktif meta // @require https://update.greatest.deepsurf.us/scripts/8494/40629/Ikariam%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.  
  134. return function debuggableWrapper() {
  135. var time = ((options.timings && debugOptions.time) || debugOptions.alwaysTime) &&
  136. console && console.time;
  137. var group = debugOptions.group && (options.group) && console && console.group;
  138. var profile = debugOptions.profile && options.profile && console && console.profile;
  139. try {
  140. if (profile) {
  141. console.profile(debugOptions.label);
  142. }
  143. if (group) {
  144. console.group(debugOptions.label);
  145. }
  146. if (time) {
  147. console.time(debugOptions.label);
  148. }
  149. return func.apply(this, arguments);
  150. } catch (ex) {
  151. Logging.debugException(debugOptions.label, ex);
  152. if (!debugOptions.swallowException) {
  153. throw ex;
  154. }
  155. } finally {
  156. if (time) {
  157. console.timeEnd(debugOptions.label);
  158. }
  159. if (group) {
  160. console.groupEnd(debugOptions.label);
  161. }
  162. if (profile) {
  163. console.profileEnd(debugOptions.label);
  164. }
  165. }
  166. };
  167. }
  168.  
  169. /**
  170. * Wrap a console.time/timeEnd pair around a function. May be disabled
  171. * through options.
  172. */
  173. function time(timeOptions, func) {
  174. return function time() {
  175. var time = (options.timings || timeOptions.alwaysTime) && console && console.time;
  176. try {
  177. if (time) {
  178. console.time(timeOptions.label);
  179. }
  180. return func.apply(this, arguments);
  181. } finally {
  182. if (time) {
  183. console.timeEnd(timeOptions.label);
  184. }
  185. }
  186. }
  187. }
  188. /**
  189. * Wrap a console.group/groupEnd pair around a function. May be disabled
  190. * through options.
  191. */
  192. function group(groupOptions, func) {
  193. var label;
  194.  
  195. var collapsed = false;
  196.  
  197. return function group() {
  198. var group = (options.timings || options.debug) && console && console.group;
  199. try {
  200. if (group) {
  201. if (groupOptions.collapsed && console.groupCollapsed) {
  202. console.groupCollapsed(groupOptions.label);
  203. } else {
  204. console.group(groupOptions.label);
  205. }
  206. }
  207. return func.apply(this, arguments);
  208. } finally {
  209. if (group) {
  210. console.groupEnd(groupOptions.label);
  211. }
  212. }
  213. }
  214. }
  215. /**
  216. * Sets logging options. Properties are debug, timings, and profile.
  217. */
  218. function setOptions(newOptions) {
  219. $.extend(options, newOptions);
  220. if (console && console.log) {
  221. //console.log("Set logging options to: ", options);
  222. }
  223. }
  224. /**
  225. * Allows logging options to be configured by browsing to various anchors
  226. * (and persisted for future page views).
  227. */
  228. function setAndSaveOptionsFromPageAnchor() {
  229. var savedOptions = new Data.Value(
  230. 'debugOptions',
  231. { debug: false, timings: false, profile: false , group: false},
  232. { useDomain: false, version: 0 });
  233. savedOptions.get();
  234. var anchor = window.location.hash;
  235.  
  236. if (anchor.substring(0, 15) == '#ikaScriptTools') {
  237. if (anchor == '#ikaScriptToolsDebugAll') {
  238. savedOptions.get().debug = true;
  239. savedOptions.get().timings = true;
  240. savedOptions.get().profile = true;
  241. } else if (anchor == '#ikaScriptToolsDebugNone') {
  242. savedOptions.get().debug = false;
  243. savedOptions.get().timings = false;
  244. savedOptions.get().profile = false;
  245. } else if (anchor == '#ikaScriptToolsDebugOn') {
  246. savedOptions.get().debug = true;
  247. } else if (anchor == '#ikaScriptToolsDebugOff') {
  248.  
  249. savedOptions.get().debug = false;
  250. } else if (anchor == '#ikaScriptToolsGroupOn') {
  251. savedOptions.get().group = true;
  252. } else if (anchor == '#ikaScriptToolsGroupOff') {
  253. savedOptions.get().group = false;
  254. } else if (anchor == '#ikaScriptToolsTimingsOn') {
  255. savedOptions.get().timings = true;
  256. } else if (anchor == '#ikaScriptToolsTimingsOff') {
  257. savedOptions.get().timings = false;
  258. } else if (anchor == '#ikaScriptToolsProfilesOn') {
  259. savedOptions.get().profile = true;
  260. } else if (anchor == '#ikaScriptToolsProfilesOff') {
  261. savedOptions.get().profile = false;
  262. }
  263. savedOptions.saveAsync();
  264. }
  265. setOptions(savedOptions.get());
  266. }
  267.  
  268. return {
  269. debug: debug,
  270. debugException: debugException,
  271. debuggable: debuggable,
  272. time: time,
  273. group: group,
  274. setAndSaveOptionsFromPageAnchor: setAndSaveOptionsFromPageAnchor,
  275. getExceptionLog: getExceptionLog,
  276. };
  277. })();
  278. /**
  279. * Random utils that don't belong anywhere else.
  280. */
  281. var Utils = function() {
  282. function thunk(func) {
  283. var computed = false;
  284. var value;
  285. function thunker() {
  286. if (!computed) {
  287. value = func();
  288. computed = true;
  289. }
  290. return value;
  291. }
  292. return thunker;
  293. }
  294. function resettable(func) {
  295. var value = func();
  296. function getValue() {
  297. return value;
  298. }
  299. getValue.reset = function() {
  300. var ret = value;
  301. value = func();
  302. return ret;
  303. }
  304. getValue.set = function(v) {
  305. value = v;
  306. }
  307. return getValue;
  308. }
  309. function fixedFunction(value) {
  310. return function() {
  311. return value;
  312. }
  313. }
  314.  
  315. var nextId = function() {
  316. var id = 10000;
  317. function nextId(prefix) {
  318. id++;
  319. if (prefix) {
  320. return prefix + id.toString(16);
  321. } else {
  322. return id;
  323. }
  324. };
  325. return nextId;
  326. }();
  327. var nextIntegerId = function() {
  328. var id = 100000;
  329. return function nextIntegerId() {
  330. return id++;
  331. };
  332. }();
  333.  
  334. function EventDispatcher(name) {
  335. this.name = name;
  336. this.listeners = [];
  337. }
  338. $.extend(EventDispatcher.prototype, {
  339. addListener: function addListener(l) {
  340. var listener = Logging.debuggable(
  341. {
  342. label: 'EventDispatcher[' + this.name + '].ListenerWrapper',
  343. swallowException: true,
  344. },
  345. function() {
  346. l.apply(null, arguments)
  347. });
  348. this.listeners.push(listener);
  349. return {
  350. cancel: this._cancelListener.bind(this, listener),
  351. };
  352. },
  353. _cancelListener: function(listener) {
  354. for (var i = 0, len = this.listeners.length; i < len; i++) {
  355. if (this.listeners[i] === listener) {
  356. this.listeners.splice(i, 1);
  357. return;
  358. }
  359. }
  360. },
  361. /*bindEventArgs: function bindEventArgs() {
  362. var that = this;
  363. var args = Array.prototype.slice.call(arguments);
  364. return {
  365. bindEventArgs: function boundBindEventArgs() {
  366.  
  367. return that.bindEventArgs.apply(that,
  368. args.concat(Array.prototype.slice.call(arguments)));
  369. },
  370. send: function boundSend() {
  371. that.send.apply(that, args.concat(Array.prototype.slice.call(arguments)));
  372. },
  373.  
  374. scheduleSend: function boundScheduleSend(name, delay, callback) {
  375. return that.scheduleSend.apply(that,
  376. [name, delay, callback].concat(args).concat(Array.prototype.slice.call(arguments, 3)));
  377. },
  378. toJSON: function toJSON() {
  379. return undefined;
  380. },
  381. };
  382. },*/
  383. send: function send() {
  384. var listeners = this.listeners.slice();
  385. for (var i = 0, len = listeners.length; i < len; i++) {
  386. listeners[i].apply(null, arguments);
  387. }
  388. },
  389. scheduleSend: function(name, delay, callback) {
  390. var that = this;
  391. var sendArgs = [];
  392. for (var i = 3; i < arguments.length; i++) {
  393. sendArgs.push(arguments[i]);
  394. }
  395. return clearTimeout.bind(null, setTimeout(
  396. function scheduledSend() {
  397. callback();
  398. that.send.apply(that, sendArgs);
  399. },
  400. Math.max(delay, 10)));
  401. },
  402. /*startIntervalSend: function startIntervalSend(name, initialDelay, interval) {
  403. this.cancelIntervalSend();
  404. var sendCall = this.send.bind(this);
  405. var sendArgs = [];
  406. for (var i = 2; i < arguments.length; i++) {
  407. sendArgs.push(arguments[i]);
  408. }
  409. this.cancelInterval = clearInterval.bind(null, setInterval(
  410. IkaTools.Logging.debuggable(
  411. 'IkaTools.Utils.EventDispatcher.intervalSend[' + name + ']',
  412. function() {
  413. sendCall.apply(null, sendArgs);
  414. }),
  415. interval));
  416. },
  417. cancelIntervalSend: function cancelIntevalSend() {
  418. if (this.cancelInterval) {
  419. this.cancelInterval();
  420. }
  421. },*/
  422. toJSON: function toJSON() {
  423. return undefined;
  424. },
  425. });
  426. function getVersion() {
  427. var parts = $.map(
  428. $('#GF_toolbar li.version span').text().match(/[0-9]+/g),
  429. function(x) {
  430. return parseInt(x);
  431. }).concat([0,0,0,0,0,0,0]);
  432. return {
  433. greaterThanOrEqual: function() {
  434. for (var i = 0; i < arguments.length; i++) {
  435. if (parts[i] != arguments[i]) {
  436. return parts[i] >= arguments[i];
  437. }
  438. }
  439. return true;
  440. },
  441. lessThan: function() {
  442. for (var i = 0; i < arguments.length; i++) {
  443. if (parts[i] != arguments[i]) {
  444. return parts[i] < arguments[i];
  445. }
  446. }
  447. return false;
  448. },
  449. };
  450. }
  451. function isChrome() {
  452. return navigator.vendor.match(/Google/) || navigator.userAgent.match(/Chrome/);
  453. }
  454. function iterateIkariamAjaxResponse(response, f) {
  455. $.each(response, function iterateIkariamAjaxResponseItem(index, item) {
  456. f(index, item[0], item[1]);
  457. });
  458. }
  459. function forEachIkariamAjaxResponseFunction(f) {
  460. return function forEachIkariamResponse(response) {
  461. iterateIkariamAjaxResponse(response, f);
  462. }
  463. }
  464.  
  465. function backgroundFetchPage(url, callback, options) {
  466. options = $.extend({method: 'GET', data: ''}, options);
  467. var headers = {
  468. 'User-agent': navigator.userAgent,
  469. 'Cookie': document.cookie,
  470. 'Referer': 'http://' + document.domain + '/index.php',
  471. };
  472. if(options.method == 'POST') {
  473. headers['Content-type'] = 'application/x-www-form-urlencoded';
  474. }
  475. setTimeout(function() {
  476. GM_xmlhttpRequest ({
  477. method: options.method,
  478. url: url,
  479. data: options.data,
  480. headers: headers,
  481. onload: Logging.debuggable('IkaTools.Utils.backgroundGetIkariamPage[' + url + ']', callback)
  482. });
  483. }, 0);
  484. }
  485.  
  486. function backgroundFetchIkariamAjaxPage(url, callback, options) {
  487. backgroundFetchPage(url, function(response) {
  488. callback(JSON.parse(response.responseText));
  489. }, options);
  490. }
  491.  
  492. var jsonResponseRegex = /ikariam.getClass\(ajax.Responder, (.*?)\);$/m;
  493. function backgroundFetchIkariamFullPage(url, callback, options) {
  494. backgroundFetchPage(url, function(response) {
  495. var match = jsonResponseRegex.exec(response.responseText);
  496. jsonResponse = [];
  497. if (match) {
  498. jsonResponse = JSON.parse(match[1]);
  499. }
  500. callback(response, jsonResponse);
  501. }, options);
  502. }
  503.  
  504. var urlParamsRegex = /\?([^\#]*)(#|$)/;
  505. function parseUrlParams(url) {
  506. var paramsList = url.match(urlParamsRegex)[1].split('&');
  507. var params = {};
  508. $.each(paramsList, function(index, item) {
  509. var paramParts = item.split('=');
  510. if (paramParts.length == 2) {
  511. params[paramParts[0]] = decodeURIComponent(paramParts[1]);
  512. }
  513. });
  514. return params;
  515. }
  516.  
  517. var timestampRegex = /(\d+)\.(\d+)\.(\d+)\s+(\d+):(\d+):(\d+)/i;
  518. function parseIkariamTimestamp(timestamp){
  519. var d = new Date();
  520. //Get the local GMT offset in hours
  521. //Attempt to match the constituent parts of the timestamp
  522. var match = timestamp.match(timestampRegex);
  523. if (match){
  524. d.setTime(0);
  525. d.setDate(parseInt(match[1], 10));
  526. d.setMonth(parseInt(match[2], 10)-1);
  527. d.setFullYear(parseInt(match[3], 10));
  528. d.setHours(parseInt(match[4], 10));
  529. d.setMinutes(parseInt(match[5], 10));
  530. d.setSeconds(parseInt(match[6], 10));
  531. //Adjust the time to get its TZ correct
  532. d.setTime(d.getTime() - d.getTimezoneOffset() * Constants.Time.MILLIS_PER_MINUTE -
  533. Constants.Time.MILLIS_PER_HOUR); // Server time is german = GMT+1
  534. }
  535. return d;
  536. }
  537.  
  538. return {
  539. thunk: thunk,
  540. resettable: resettable,
  541. fixedFunction: fixedFunction,
  542. EventDispatcher: EventDispatcher,
  543. nextId: nextId,
  544. nextIntegerId: nextIntegerId,
  545. getVersion: thunk(getVersion),
  546. isChrome: thunk(isChrome),
  547. iterateIkariamAjaxResponse: iterateIkariamAjaxResponse,
  548. forEachIkariamAjaxResponseFunction: forEachIkariamAjaxResponseFunction,
  549.  
  550. backgroundFetchIkariamAjaxPage: backgroundFetchIkariamAjaxPage,
  551. backgroundFetchIkariamFullPage: backgroundFetchIkariamFullPage,
  552.  
  553. parseUrlParams: parseUrlParams,
  554. parseIkariamTimestamp: parseIkariamTimestamp,
  555. };
  556. }();
  557. /**
  558. * Internationalization and localization routines.
  559. */
  560. var Intl = function() {
  561. function Localizer(allLanguages) {
  562. this.allLanguages = allLanguages;
  563. this.languages = ['en'];
  564. }
  565. $.extend(Localizer.prototype, {
  566. localize: function localize(identifier) {
  567. var identifierParts = identifier.split('.');
  568. if (arguments.length > 1) {
  569. identifierParts = $.makeArray(arguments);
  570. }
  571. for (var i = 0; i < this.languages.length; i++) {
  572. var languageConfig = this.allLanguages[this.languages[i]];
  573. if (languageConfig !== undefined) {
  574. var translation = this.find(languageConfig, identifierParts);
  575. if (translation !== undefined) {
  576. return translation;
  577. }
  578. }
  579. }
  580. return '?#!' + identifierParts.join(',') + '!#?';
  581. },
  582. delayedLocalize: function delayedLocalize() {
  583. var args = arguments;
  584. var t = this;
  585. return function doLocalize() {
  586. return t.localize.apply(t, args);
  587. };
  588. },
  589. find: function find(config, parts) {
  590.  
  591. for (var i = 0; i < parts.length; i++) {
  592. config = config[parts[i]];
  593. if (config === undefined) {
  594. return undefined;
  595. }
  596. }
  597. return config;
  598. },
  599. setPreferredLanguage: function setPreferredLanguage(language) {
  600. this.languages.unshift(language);
  601. },
  602. });
  603. var baseLocalizer = new Localizer({
  604. en: {
  605. formatting: {
  606. "thousandsSeparator": ",",
  607. "unknown": "?",
  608. hourFormatAMPM: true,
  609. },
  610. timeunits: {
  611. long: {
  612. day: 'Day',
  613. hour: 'Hour',
  614. week: 'Week',
  615. },
  616. short: {
  617. day: 'D',
  618. hour: 'h',
  619. minute: 'm',
  620. second: 's',
  621. },
  622. complete: '-',
  623. yesterday: 'yesterday',
  624. tomorrow: 'tomorrow',
  625. today: 'today',
  626. am: 'AM',
  627. pm: 'PM',
  628. },
  629. settings: {
  630. script_settings: 'Script Options',
  631. settings: 'Settings',
  632. save: 'Save',
  633. },
  634. },
  635. de: {
  636. formatting: {
  637. "thousandsSeparator": ".",
  638. "unknown": "?",
  639. hourFormatAMPM: false,
  640. },
  641. timeunits: {
  642. long: {
  643. day: 'Tag',
  644. hour: 'Stunde.',
  645. week: 'Woche',
  646. },
  647. short: {
  648. day: 'T',
  649. hour: 'h',
  650. minute: 'm',
  651. second: 's',
  652. },
  653. complete: '-',
  654. yesterday: 'gestern',
  655. tomorrow: 'morgen',
  656. today: 'heute',
  657. am: 'AM',
  658. pm: 'PM',
  659. },
  660. settings: {
  661. script_settings: 'Script Optionen',
  662. settings: 'Einstellungen',
  663. save: 'speichern',
  664. },
  665. },
  666. hu: {
  667. formatting: {
  668. "thousandsSeparator": ",",
  669. "unknown": "?",
  670. hourFormatAMPM: true,
  671. },
  672. timeunits: {
  673. long: {
  674. day: 'Nap',
  675. hour: 'Óra',
  676. week: 'Hét',
  677. },
  678. short: {
  679. day: 'n',
  680. hour: 'ó',
  681. minute: 'p',
  682. second: 'mp',
  683. },
  684. complete: '-',
  685. yesterday: 'tegnap',
  686. tomorrow: 'holnap',
  687. today: 'ma',
  688. am: 'Délelőtt',
  689. pm: 'Délután',
  690. },
  691. settings: {
  692. script_settings: 'Script Beállítások',
  693. settings: 'Beállítások',
  694. save: 'Mentés',
  695. },
  696. },
  697. pl: {
  698. formatting: {
  699. "thousandsSeparator": ",",
  700. "unknown": "?",
  701. hourFormatAMPM: true,
  702. },
  703. timeunits: {
  704. long: {
  705. day: 'Dzien',
  706. hour: 'Godzina',
  707. week: 'Tydzien',
  708. },
  709. short: {
  710. day: 'D',
  711. hour: 'h',
  712. minute: 'm',
  713. second: 's',
  714. },
  715. complete: '-',
  716. yesterday: 'wczoraj',
  717. tomorrow: 'jutro',
  718. today: 'dzis',
  719. am: 'AM',
  720. pm: 'PM',
  721. },
  722. settings: {
  723. script_settings: 'Opcje Skryptu',
  724. settings: 'Ustawienia',
  725. save: 'Zapisz',
  726. },
  727. },
  728. ro: {
  729. formatting: {
  730. "thousandsSeparator": ",",
  731. "unknown": "?",
  732. hourFormatAMPM: false,
  733. },
  734. timeunits: {
  735. long: {
  736. day: 'Zi',
  737. hour: 'Ora',
  738. week: 'Saptamana',
  739. },
  740. short: {
  741. day: 'D',
  742. hour: 'h',
  743. minute: 'm',
  744. second: 's',
  745. },
  746. complete: '-',
  747. yesterday: 'ieri',
  748. tomorrow: 'maine',
  749. today: 'azi',
  750. am: 'AM',
  751. pm: 'PM',
  752. },
  753. settings: {
  754. script_settings: 'Optiuni Script',
  755. settings: 'Setari',
  756. save: 'Salveaza',
  757. },
  758. },
  759. tr: {
  760. formatting: {
  761. "thousandsSeparator": ",",
  762. "unknown": "?",
  763. hourFormatAMPM: false,
  764. },
  765. timeunits: {
  766. long: {
  767. day: 'Gün',
  768. hour: 'Saat',
  769. week: 'Hafta',
  770. },
  771. short: {
  772. day: 'Gn',
  773. hour: 'Sa',
  774. minute: 'Dk',
  775. second: 'Sn',
  776. },
  777. complete: '-',
  778. yesterday: 'dün',
  779. tomorrow: 'yarın',
  780. today: 'bugün',
  781. am: 'AM',
  782. pm: 'PM',
  783. },
  784. settings: {
  785. script_settings: 'Script Seçenekleri',
  786. settings: 'Ayarlar',
  787. save: 'Kaydet',
  788. },
  789. },
  790. });
  791. function formatInteger(number, forceShowSign) {
  792. if (number === undefined || isNaN(number)) {
  793. return baseLocalizer.localize('formatting.unknown');
  794. }
  795.  
  796. number = Math.floor(number);
  797.  
  798. var separator = baseLocalizer.localize('formatting.thousandsSeparator');
  799. var s = number.toString();
  800.  
  801. var sign = forceShowSign ? '+' : '';
  802. if (number === 0) {
  803. sign = '';
  804. } else if (number < 0) {
  805. sign = '-';
  806. s = s.substring(1);
  807. }
  808.  
  809. var i = s.length - 3;
  810. while (i > 0) {
  811. s = s.substring(0, i) + separator + s.substring(i);
  812. i -= 3;
  813. }
  814. return sign + s;
  815. };
  816. function formatDecimal(number, digits, forceShowSign) {
  817. if (number === undefined || isNaN(number)) {
  818. return baseLocalizer.localize('formatting.unknown');
  819. }
  820. var value = number.toFixed(digits);
  821. if (forceShowSign && number >= 0) { // .5 * Math.pow(10, -digits)) {
  822. value = '+' + value;
  823. }
  824. return value;
  825. }
  826. function formatRemainingTime(millis, complete, maxResolution) {
  827. maxResolution = maxResolution || 4;
  828. if (millis == Number.POSITIVE_INFINITY) {
  829. return '&infin;';
  830. }
  831. var seconds = Math.ceil(millis / Constants.Time.MILLIS_PER_SECOND);
  832. if (seconds <= 0) {
  833. return complete || baseLocalizer.localize('timeunits','complete');
  834. }
  835. var parts = [];
  836. if (maxResolution >= 1) {
  837. parts.push({value: Math.floor((seconds / 60 / 60 / 24)), label: 'day'});
  838. }
  839. if (maxResolution >= 2) {
  840. parts.push({value: Math.floor((seconds / 60/ 60) % 24), label: 'hour' });
  841. }
  842. if (maxResolution >= 3) {
  843. parts.push({value: Math.floor((seconds / 60) % 60), label: 'minute' });
  844. }
  845. if (maxResolution >= 4) {
  846. parts.push({value: Math.floor((seconds) % 60), label: 'second' });
  847. }
  848. while (parts[0] && !parts[0].value) {
  849. parts.shift();
  850. }
  851. parts.splice(2);
  852. return $.map(parts, function(part) {
  853. return '%s%s'.format(part.value,
  854. baseLocalizer.localize('timeunits','short',part.label));
  855. }).join(' ');
  856. }
  857. function padLeft(s, length, char) {
  858. while (s.length < length) {
  859. s = char + s;
  860. }
  861. return s;
  862. }
  863. function formatAbsoluteTime(date) {
  864. var now = new Date();
  865. var dateDay = Math.floor(date.valueOf() / Constants.Time.MILLIS_PER_DAY -
  866. date.getTimezoneOffset() / Constants.Time.MINUTES_PER_DAY);
  867. var nowDay = Math.floor(now.valueOf() / Constants.Time.MILLIS_PER_DAY -
  868. now.getTimezoneOffset() / Constants.Time.MINUTES_PER_DAY);
  869. var dayString = '';
  870. if (dateDay == nowDay + 1) {
  871. dayString = baseLocalizer.localize('timeunits','tomorrow');
  872. } else if (dateDay == nowDay - 1) {
  873. dayString = baseLocalizer.localize('timeunits','yesterday');
  874. } else if (dateDay == nowDay) {
  875. dayString = baseLocalizer.localize('timeunits','today');
  876. } else {
  877. dayString = date.toLocaleDateString();
  878. }
  879. var timeString = '';
  880. if (baseLocalizer.localize('formatting', 'hourFormatAMPM')) {
  881. var m = '';
  882. if (date.getHours() == 0) {
  883. timeString = '12';
  884. m = baseLocalizer.localize('timeunits','am');
  885. } else if (date.getHours() == 12) {
  886. timeString = '12';
  887. m = baseLocalizer.localize('timeunits','pm');
  888. } else if (date.getHours() > 12) {
  889. timeString = (date.getHours() - 12).toString();
  890. m = baseLocalizer.localize('timeunits','pm');
  891. } else {
  892. timeString = date.getHours().toString();
  893. m = baseLocalizer.localize('timeunits','am');
  894. }
  895. timeString = timeString + ':' +
  896. padLeft(date.getMinutes().toString(), 2, 0) + ' ' + m;
  897. } else {
  898. timeString = date.getHours().toString() + ':' +
  899.  
  900. padLeft(date.getMinutes().toString(), 2, '0');
  901. }
  902. return dayString + ' ' + timeString;
  903. }
  904. return {
  905. Localizer: Localizer,
  906. localizer: baseLocalizer,
  907. formatInteger: formatInteger,
  908. formatDecimal: formatDecimal,
  909. formatRemainingTime: formatRemainingTime,
  910. formatAbsoluteTime: formatAbsoluteTime,
  911. };
  912. }();
  913. var View = function() {
  914. function getDomain() {
  915. return document.domain;
  916. }
  917. function getCurrentCityId() {
  918. var relatedCityData = unsafeWindow.ikariam.model.relatedCityData;
  919. return relatedCityData[relatedCityData.selectedCity].id;
  920. }
  921. function getCurrentCity() {
  922. return EmpireData.getCity(getCurrentCityId());
  923. }
  924. function isActiveCity(city) {
  925. return city.getId() == getCurrentCityId();
  926. }
  927. function getCurrentIslandId() {
  928. // This is available in javascript data in island and city views (unfortunately at
  929. // different locations). It does not appear to be anywhere in world view data.
  930. return parseInt($("#js_islandBread").attr("href").match(/islandId=(\d+)/)[1]);
  931. };
  932. function getViewType() {
  933. return unsafeWindow.ikariam.backgroundView.id;
  934. }
  935. function viewIsIsland() {
  936. return getViewType() == 'island';
  937. }
  938. function viewIsCity() {
  939. return getViewType() == 'city';
  940. }
  941. function getIkariamBaseViewParams() {
  942. var mainboxX = unsafeWindow.ikariam.mainbox_x;
  943. var mainboxY = unsafeWindow.ikariam.mainbox_y;
  944. var mainboxZ = unsafeWindow.ikariam.mainbox_z;
  945. if (mainboxX || mainboxY || mainboxZ) {
  946. return {
  947. mainbox_x: mainboxX,
  948. mainbox_y: mainboxY,
  949. mainbox_z: mainboxZ,
  950. };
  951. }
  952. return {};
  953. }
  954. function makeFullIkariamUrl(params, anchor) {
  955. return 'http://' + getDomain() + '/index.php?' +
  956. $.map(params, function(value, key) {
  957. return encodeURIComponent(key) + '=' +
  958. encodeURIComponent(value);
  959. }).join('&') +
  960. (anchor ? '#' + anchor : '');
  961. }
  962. function makeLocalIkariamUrl(params) {
  963. return '?' + $.map(params, function(value, key) { return key + '=' + value; }).join('&');
  964. }
  965. function loadLocalIkariamUrl(url) {
  966. Logging.debug("loadLocalIkariamUrl: ", url);
  967. // This is an odd way to make the ajaxHandlerCall rather than just calling it directly.
  968. // It is done this way so that in Chrome the actions run when the response is recieved
  969. // are run in the page's javascript context instead of the javascript context of the
  970. // TamperMonkey extension. This is necessary to properly evaluate script actions in
  971. // returned ikariam pages or stuff like transport sliders simply will not work.
  972. document.location = 'javascript:ajaxHandlerCall(' + JSON.stringify(url) + '); void(0);';
  973. }
  974. function goToIkariamPage(city, mainView, mainParams, view, viewParams, anchor) {
  975. var changeParams = {
  976. // Whacked up logic I don't really understand that makes transitioning to
  977. // island mine/mill pages work. Yes, it's completely incomprehensible how Ikariam
  978. // developers could screw this up, but somehow they can make the mill go to the mine
  979. // if you say the old view is island when you want to go to an island page.
  980. // Truly incredible!
  981. oldView: mainView == 'island' ? getViewType() : mainView,
  982. action: 'header',
  983. function: 'changeCurrentCity',
  984. cityId: city.getId(),
  985. actionRequest: unsafeWindow.ikariam.model.actionRequest,
  986. };
  987. $.extend(changeParams, getIkariamBaseViewParams(), mainParams);
  988. if (view) {
  989. $.extend(changeParams, viewParams);
  990. changeParams.templateView = view;
  991. }
  992. $.extend(changeParams, { backgroundView: mainView } );
  993. if (mainView == 'island' && view && !anchor) {
  994. // Stupid ikariam developers still include the city preselect when we ask for a
  995. // specific view. Which will overwrite whatever view (mine/mill/wonder) we
  996. // actually want to see. Set this hack to suppress that transition when the page
  997. // loads.
  998. anchor = 'ikaScriptToolsSuppressCityPreselect';
  999. }
  1000. if (getViewType() == mainView) {
  1001. loadLocalIkariamUrl(makeLocalIkariamUrl(changeParams));
  1002. return;
  1003. }
  1004. var url = makeFullIkariamUrl(changeParams, anchor);
  1005. Logging.debug('goToIkariamPage: ', url);
  1006. window.location.assign(url);
  1007. }
  1008. function goToLocalView(view, params) {
  1009. loadLocalIkariamUrl(makeLocalIkariamUrl($.extend({view: view}, params)));
  1010. }
  1011. function goToCitysIslandView(city, view, params) {
  1012. if (isActiveCity(city) && viewIsIsland() &&
  1013. getCurrentIslandId() == city.getIslandId()) {
  1014. if (view) {
  1015. loadLocalIkariamUrl(makeLocalIkariamUrl($.extend({view: view}, params)));
  1016. }
  1017. } else if (viewIsIsland()) {
  1018. goToIkariamPage(city, 'island', null, view, params);
  1019. } else {
  1020. goToIkariamPage(city, 'island', null, null, null,
  1021. makeIkariamLoadLocalPageAnchor($.extend({view: view}, params)));
  1022. }
  1023. }
  1024. function goToIslandView(city, islandId, view, params) {
  1025. if (isActiveCity(city) && viewIsIsland() &&
  1026. getCurrentIslandId() == islandId) {
  1027. if (view) {
  1028. loadLocalIkariamUrl(makeLocalIkariamUrl($.extend({view: view}, params)));
  1029. }
  1030. } else {
  1031. goToIkariamPage(city, 'island', { currentIslandId: islandId }, view, params);
  1032. }
  1033. }
  1034.  
  1035. function goToIkariamFullPage(params, anchor) {
  1036. url = makeFullIkariamUrl(params, anchor);
  1037. Logging.debug('goToIkariamFullPage: ', url);
  1038. window.location.replace(url);
  1039. }
  1040.  
  1041. function makeIkariamLoadLocalPageAnchor(params, doNotSuppressFirstCityInfo) {
  1042. if (doNotSuppressFirstCityInfo) {
  1043. return 'ikaScriptToolsLoadLocalIkariamUrl_DoNotSuppressFirstCityInfo=' +
  1044. encodeURIComponent(makeLocalIkariamUrl(params));
  1045. } else {
  1046. return 'ikaScriptToolsLoadLocalIkariamUrl=' +
  1047. encodeURIComponent(makeLocalIkariamUrl(params));
  1048. }
  1049. }
  1050. function goToCityView(city, view, params) {
  1051. if (isActiveCity(city) && viewIsCity()) {
  1052. if (view) {
  1053. loadLocalIkariamUrl(makeLocalIkariamUrl($.extend({view: view}, params)));
  1054. }
  1055. } else {
  1056. goToIkariamPage(city, 'city', null, view, params);
  1057. }
  1058. }
  1059.  
  1060. function activateCity(city) {
  1061. if (!isActiveCity(city)) {
  1062. goToIkariamPage(city, getViewType());
  1063. }
  1064. }
  1065. var suppressingChangeView = false;
  1066. var superSuppressChangeView = Utils.resettable(Utils.fixedFunction(false));
  1067. var initialPageAjaxResponse = Utils.thunk(function findInitialPageAjaxResponse() {
  1068. var regex = /ikariam.getClass\(ajax.Responder, (.*)\);/;
  1069. var response = [];
  1070. $('script').each(function findInitialPageAjaxResponse(index, script) {
  1071. var match = regex.exec(script.innerHTML);
  1072. if (match) {
  1073. response = JSON.parse(match[1]);
  1074. }
  1075. });
  1076. return response;
  1077. });
  1078. unsafeWindow.ajax.Responder.changeView = function(changeView) {
  1079. return Logging.debuggable(
  1080. 'IkaTools.View.changeViewReplacement',
  1081. function customChangeView(params) {
  1082. if (suppressingChangeView && suppressingChangeView == params[0]) {
  1083. Logging.debug("Suppressing change to view: ", params[0]);
  1084. } else if (superSuppressChangeView() == params[0]) {
  1085. superSuppressChangeView.reset();
  1086. Logging.debug("Super suppressing change to view", params[0]);
  1087. } else {
  1088. changeView.apply(this, arguments);
  1089. }
  1090. });
  1091. }(unsafeWindow.ajax.Responder.changeView);
  1092. var ajaxResponseEvent = new Utils.EventDispatcher();
  1093. function registerIkariamAjaxResponseCallback(f, fireInitialPageView) {
  1094. var canceller = ajaxResponseEvent.addListener(f);
  1095. if (fireInitialPageView) {
  1096. f(initialPageAjaxResponse());
  1097. }
  1098. return canceller;
  1099. }
  1100. var suppressNextAjaxChangeView = Utils.resettable(Utils.fixedFunction(null));
  1101. function suppressChangeViewOfNextAjaxResponse(type) {
  1102. suppressNextAjaxChangeView.set(type);
  1103. }
  1104. function suppressFirstChangeViewOfType(type) {
  1105. superSuppressChangeView.set(type);
  1106. }
  1107. var nextAjaxResponseEvent =
  1108. Utils.resettable(function() { return new Utils.EventDispatcher(); });
  1109. function registerNextIkariamAjaxRequestCallback(f) {
  1110.  
  1111. return nextAjaxResponseEvent().addListener(f);
  1112. }
  1113. function replaceExecuteAjaxRequest(executeAjaxRequest) {
  1114. return function customExecuteAjaxRequest() {
  1115. var ajaxEvent = nextAjaxResponseEvent.reset();
  1116. var suppressChangeView = suppressNextAjaxChangeView.reset();
  1117. var args = $.makeArray(arguments);
  1118. args.push(undefined);
  1119. if (!args[1]) {
  1120. args[1] = function customAjaxCallback(responseText) {
  1121. suppressingChangeView = suppressChangeView;
  1122. var responder = unsafeWindow.ikariam.getClass(
  1123. unsafeWindow.ajax.Responder, responseText);
  1124. unsafeWindow.ikariam.controller.ajaxResponder = responder;
  1125. suppressingChangeView = null;
  1126. ajaxResponseEvent.send(responder.responseArray);
  1127. ajaxEvent.send(responder.responseArray);
  1128. }
  1129. args[1].isScriptInterceptor = true;
  1130. } else if (args[1].isScriptInterceptor) {
  1131. // Allows multiple instances of this script to work
  1132. var func = args[1];
  1133. args[1] = function customAjaxCallbackWrapper() {
  1134. suppressingChangeView = suppressChangeView;
  1135. func.apply(this, arguments);
  1136. suppressingChangeView = null;
  1137. var responseArray = unsafeWindow.ikariam.controller.ajaxResponder.responseArray;
  1138. ajaxResponseEvent.send(responseArray);
  1139. ajaxEvent.send(responseArray);
  1140. }
  1141. }
  1142. var ret = executeAjaxRequest.apply(this, args);
  1143. };
  1144. }
  1145. if (unsafeWindow.ikariam.controller) {
  1146. unsafeWindow.ikariam.controller.executeAjaxRequest =
  1147. replaceExecuteAjaxRequest(unsafeWindow.ikariam.controller.executeAjaxRequest);
  1148. } else {
  1149. unsafeWindow.ikariam.Controller.executeAjaxRequest =
  1150. replaceExecuteAjaxRequest(unsafeWindow.ikariam.Controller.executeAjaxRequest);
  1151. }
  1152. var ajaxFormEvent = new Utils.EventDispatcher();
  1153. unsafeWindow.ajaxHandlerCallFromForm = function(ajaxHandlerCallFromForm) {
  1154. return function customerAjaxHandlerCallFromForm(form) {
  1155. ajaxFormEvent.send(form);
  1156. return ajaxHandlerCallFromForm.apply(this, arguments);
  1157. };
  1158. }(unsafeWindow.ajaxHandlerCallFromForm);
  1159. function registerAjaxFormSubmitCallback(f) {
  1160. return ajaxFormEvent.addListener(f);
  1161. }
  1162. var gameTimeDifference = 0;
  1163. function setGameTimeDifference(value) {
  1164. Logging.debug("Game time difference: ", value);
  1165. gameTimeDifference = value;
  1166. }
  1167. function getGameTimeDifference() {
  1168. return gameTimeDifference;
  1169. }
  1170. function gameTimeNow() {
  1171. return new Date().getTime() - gameTimeDifference;
  1172. }
  1173.  
  1174. return {
  1175. getDomain: getDomain,
  1176. getCurrentCityId: getCurrentCityId,
  1177. getCurrentCity: getCurrentCity,
  1178. isActiveCity: isActiveCity,
  1179. getCurrentIslandId: getCurrentIslandId,
  1180. getViewType: getViewType,
  1181. viewIsIsland: viewIsIsland,
  1182. viewIsCity: viewIsCity,
  1183.  
  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. /**
  1205. * Data value class for encapsulating GM_getValue/GM_setValue access and
  1206. * serialization/deserialization.
  1207.  
  1208. */
  1209. var Data = function() {
  1210. function Value(key, defaultValue, options) {
  1211. this.options = $.extend({ useDomain: true, loadCallback: function() {} }, options);
  1212. if (this.options.useDomain) {
  1213. this.key = View.getDomain() + "/" + key + "/" +
  1214. (options.version || ikaToolsVersion) + '-' + ikaToolsVersion;
  1215. } else {
  1216. this.key = key + "/" + ikaToolsVersion;
  1217. }
  1218. this.defaultValue = defaultValue;
  1219. this.data = defaultValue;
  1220. this.needsSave = false;
  1221. }
  1222. $.extend(Value.prototype, {
  1223. load: function load() {
  1224. var rawValue = GM_getValue(this.key, "null");
  1225. if (rawValue !== undefined) {
  1226. var data = JSON.parse(rawValue, this.options.reviver);
  1227. Logging.debug('Loaded data "%s": %s -> %o', this.key, rawValue, data);
  1228. if (data !== null) {
  1229. this.data = data;
  1230. }
  1231. } else {
  1232. }
  1233. this.loaded = true;
  1234. this.options.loadCallback(this.data);
  1235. return this.data;
  1236. },
  1237. save: function save() {
  1238. return this.doSave(true);
  1239. },
  1240. doSave: function doSave(force) {
  1241. if (this.needsSave || force) {
  1242. var value = JSON.stringify(this.data, this.options.stringifier);
  1243. Logging.debug('Saved data "%s": %o -> %s', this.key, this.data, value);
  1244. GM_setValue(this.key, value);
  1245. this.needsSave = false;
  1246. }
  1247. return this.data;
  1248. },
  1249. saveAsync: function saveAsync() {
  1250. this.needsSave = true;
  1251. setTimeout(Logging.debuggable('IkaTools.Data.Value[' + this.key + ']',
  1252. this.doSave.bind(this, false)), 0);
  1253. },
  1254. get: function get() {
  1255. if (!this.loaded) {
  1256. var value = this.load();
  1257. return value;
  1258. }
  1259. return this.data;
  1260. },
  1261. set: function set(data) {
  1262. this.data = data;
  1263. return data;
  1264. },
  1265. reset: function reset() {
  1266. this.set(this.defaultValue);
  1267. this.save();
  1268. },
  1269. });
  1270. return {
  1271. Value: Value,
  1272. };
  1273. }();
  1274. var UI = function() {
  1275. function ToolTipHandler(toolTipClass, toolTipContainer, options) {
  1276. this.toolTips = {};
  1277. this.options = $.extend({
  1278. delay: 200,
  1279. activeClass: 'active',
  1280. offsetX: 0,
  1281. offsetY: 20,
  1282. toolTipClass: toolTipClass,
  1283. toolTipContainer: toolTipContainer || $('<div/>'),
  1284. }, options);
  1285. this.toolTipContainer =
  1286. $('<div style="position: absolute; z-index: 100000; display:none;"/>');
  1287. this.toolTipContainer.append(this.options.toolTipContainer);
  1288. this.activeToolTipElement = null;
  1289. this.pendingShowEvent = null;
  1290. this.activeToolTip = null;
  1291. var body = $('body');
  1292. body.append(this.toolTipContainer);
  1293. }
  1294. $.extend(ToolTipHandler.prototype, {
  1295. _getCurrentToolTip: function _getCurrentToolTip() {
  1296. if (this.activeToolTipElement) {
  1297. var id = this.activeToolTipElement.id;
  1298. if (id) {
  1299. return this.toolTips[id];
  1300. }
  1301. }
  1302. },
  1303. _reset: function _reset() {
  1304. clearTimeout(this.pendingShowEvent);
  1305. this.toolTipContainer.hide();
  1306. var toolTipInfo = this._getCurrentToolTip();
  1307. if (this.activeToolTip && this.activeToolTip.deactivated) {
  1308. this.activeToolTip.deactivated($(this.activeToolTipElement));
  1309. }
  1310. this.activeToolTip = null;
  1311. this.options.toolTipContainer.empty();
  1312. this.activeToolTipElement = null;
  1313. },
  1314. _showToolTip: function _showToolTip() {
  1315. var toolTipInfo = this._getCurrentToolTip();
  1316. if (toolTipInfo) {
  1317. this.activeToolTip = toolTipInfo.contentCreator($(this.activeToolTipElement));
  1318. this.options.toolTipContainer.append(this.activeToolTip);
  1319. this.toolTipContainer.show();
  1320. }
  1321. },
  1322. _mouseOver: function _mouseOver(e) {
  1323. var toolTipElement = $(e.target).closest('.' + this.options.toolTipClass);
  1324. if (toolTipElement.get(0) == this.activeToolTipElement) {
  1325. return;
  1326. }
  1327. this._reset();
  1328.  
  1329. if (toolTipElement.length > 0) {
  1330. this.activeToolTipElement = toolTipElement[0];
  1331. this.toolTipContainer.css({
  1332. left: (e.pageX + this.options.offsetX) + 'px',
  1333. top: (e.pageY + this.options.offsetY) + 'px',
  1334. });
  1335.  
  1336. this.pendingShowEvent = setTimeout(IkaTools.Logging.debuggable(
  1337. 'IkaTools.UI.ToolTipHandler.showToolTip[' + this.options.toolTipClass + ']',
  1338. this._showToolTip.bind(this)), this.options.delay);
  1339. }
  1340. },
  1341. _mouseOut: function _mouseOut(e) {
  1342. if (this.activeToolTipElement) {
  1343. var target = $(e.relatedTarget).closest('.' + this.options.toolTipClass);
  1344. if (target.get(0) != this.activeToolTipElement) {
  1345. this._reset();
  1346. return;
  1347. }
  1348. }
  1349. },
  1350. _mouseMove: function _mouseMove(e) {
  1351. if (this.activeToolTipElement && !this.activeToolTip) {
  1352. this.toolTipContainer.css({
  1353. left: (e.pageX + this.options.offsetX)+ 'px',
  1354. top: (e.pageY + this.options.offsetY) + 'px',
  1355. });
  1356. }
  1357. },
  1358. register: function registerToolTip(id, contentCreator) {
  1359. this.toolTips[id] = {
  1360. contentCreator: contentCreator,
  1361. };
  1362. },
  1363. registerSimple: function registerSimpleToolTip(id, content) {
  1364. this.register(id, function() {
  1365. return $(content);
  1366. });
  1367. },
  1368. registerRefreshable: function registerRefreshableToolTip(id, contentGenerator) {
  1369. var toolTip = this;
  1370. this.register(id, function() {
  1371. var id = Utils.nextId();
  1372. var interval = setInterval(Logging.debuggable('IkaTools.ToolTip.refresh[' + id + ']',
  1373. function refreshToolTip() {
  1374. toolTip.options.toolTipContainer.html(contentGenerator());
  1375. }), Constants.Time.MILLIS_PER_SECOND);
  1376. var tip = $(contentGenerator());
  1377. tip.deactivated = function() {
  1378. clearInterval(interval);
  1379. };
  1380. return tip;
  1381. });
  1382. },
  1383. deregister: function deregister(id) {
  1384. delete this.toolTips[id];
  1385. },
  1386. startHandling: function startHandling(element) {
  1387. element.on('mouseover'/*, '.' + this.options.toolTipClass*/, Logging.debuggable(
  1388. 'IkaTools.UI.ToolTipHandler.mouseOver[' + this.options.toolTipClass + ']',
  1389. this._mouseOver.bind(this)));
  1390. element.on('mouseout'/*, '.' + this.options.toolTipClass*/, Logging.debuggable(
  1391. 'IkaTools.UI.ToolTipHandler.mouseOut[' + this.options.toolTipClass + ']',
  1392. this._mouseOut.bind(this)))
  1393. element.on('mousemove', '.' + this.options.toolTipClass, Logging.debuggable(
  1394. 'IkaTools.UI.ToolTipHandler.mouseMove[' + this.options.toolTipClass + ']',
  1395. this._mouseMove.bind(this)))
  1396. }
  1397. });
  1398. function LeftMenu(items, options) {
  1399. this.items = items;
  1400. this.active = false;
  1401. this.options = $.extend({atTop: false }, options);
  1402. }
  1403. $.extend(LeftMenu.prototype, {
  1404. ITEM_CONTRACTED_WIDTH: 53,
  1405. ITEM_EXPANDED_WIDTH: 199,
  1406. ITEM_Z_INDEX_EXPANDED: 120000,
  1407. ITEM_Z_INDEX_CONTRACTED: 65,
  1408. ANIMATION_DURATION: 300,
  1409. display: function display() {
  1410. // Add leftMenu div and "standard" contents if we are in a
  1411. // view where it is not already present
  1412. var leftMenuDiv = $('#leftMenu');
  1413. if (!leftMenuDiv.length) {
  1414. leftMenuDiv = $('<div id="leftMenu" >' +
  1415. '<div class="slot_menu city_menu" style="z-index: 65; ">' +
  1416. '<ul class="menu_slots"/>' +
  1417. '</div>' +
  1418. '</div>');
  1419. $('#container').append(leftMenuDiv);
  1420. }
  1421. // Setup event handlers
  1422. for (var i = 0; i < this.items.length; i++) {
  1423. var item = this.items[i];
  1424. item.element.width(this.ITEM_COLLAPSED_WIDTH + 'px');
  1425. item.element.mouseenter(this.expand.bind(this, item));
  1426. item.element.mouseleave(this.contract.bind(this, item));
  1427. }
  1428. this.holderDiv = $('.slot_menu', leftMenuDiv);
  1429. this.holderDiv.hover(this.menuActivated.bind(this),
  1430. this.menuPassivated.bind(this));
  1431. // Add elements to ui
  1432. var menuSlots = $('ul.menu_slots', leftMenuDiv);
  1433. if (this.options.atTop) {
  1434. for (var i = this.items.length - 1; i >= 0; i--) {
  1435. menuSlots.prepend(this.items[i].element);
  1436. }
  1437. } else {
  1438. for (var i = 0; i < this.items.length; i++) {
  1439. menuSlots.append(this.items[i].element);
  1440. }
  1441. }
  1442. },
  1443. menuActivated: function menuActivated() {
  1444. this.active = true;
  1445. this.holderDiv.css('z-index', this.ITEM_Z_INDEX_EXPANDED);
  1446. },
  1447. menuPassivated: function menuPassivated() {
  1448. this.active = false;
  1449. },
  1450. contract: function contract(item) {
  1451. var holder = item.element.parent().parent();
  1452. item.element.animate(
  1453. { width: this.ITEM_CONTRACTED_WIDTH },
  1454. 300,
  1455. 'swing',
  1456. this.contractComplete.bind(this)
  1457. );
  1458. },
  1459. contractComplete: function contractComplete() {
  1460. if (!this.active) {
  1461. this.holderDiv.css('z-index', this.ITEM_Z_INDEX_CONTRACTED);
  1462. }
  1463. },
  1464. expand: function expand(item) {
  1465. item.element.animate(
  1466. { width: this.ITEM_EXPANDED_WIDTH },
  1467. 300,
  1468. 'swing');
  1469. this.holderDiv.css('z-index', this.ITEM_Z_INDEX_EXPANDED);
  1470.  
  1471. },
  1472. });
  1473. LeftMenu.Item = function LeftMenu_Item(element) {
  1474. this.element = element;
  1475. }
  1476. function resizePopup() {
  1477. unsafeWindow.ikariam.controller.adjustSizes();
  1478. }
  1479. var destroyedTemplateViewEvent = Utils.thunk(function() {
  1480. var dispatcher = new Utils.EventDispatcher();
  1481. var oldDestroyTemplateView = unsafeWindow.ikariam.TemplateView.destroyTemplateView;
  1482. unsafeWindow.ikariam.TemplateView.destroyTemplateView =
  1483. function customDestroyTemplateView() {
  1484. oldDestroyTemplateView.apply(this, arguments);
  1485. dispatcher.send();
  1486. };
  1487. return dispatcher;
  1488. });
  1489. function PopupWindow(id, header, content, options) {
  1490. this.id = id;
  1491. this.headerElement = $(header);
  1492. this.contentElement = $(content);
  1493. this.options = $.extend({
  1494. sidebars: [],
  1495. oversized: false,
  1496. activatedCallback: function() {},
  1497. deactivatedCallback: function() {},
  1498. }, options);
  1499. this.isActive = false;
  1500. destroyedTemplateViewEvent().addListener(this._popupDestroyed.bind(this));
  1501. }
  1502.  
  1503. $.extend(PopupWindow.prototype, {
  1504. _popupDestroyed: function _popupDestroyed() {
  1505. if (this.isActive) {
  1506. this.options.deactivatedCallback(this);
  1507. }
  1508. this.isActive = false;
  1509. },
  1510. display: function display(skipResize) {
  1511. // Always display it. There's no good way to track if it is
  1512. // already displayed because there is no callback when it is destroyed.
  1513. // (One can replace unsafeWindow.ikariam.destroyTemplateView, but there
  1514. // are still some issues with quickly switching between views that can
  1515. // mess things up and will still be considered "active" when its not visible.
  1516. templateViewArg = {
  1517. boxId: this.id,
  1518. headerElem: this.headerElement.html(),
  1519. contentElem: '<div><div id="ikaPopupTempHolder"></div></div>',
  1520. sidebarEls: this.options.sidebars,
  1521. oversized: this.options.oversized,
  1522. replaceBox: true,
  1523. keepSidebars: false
  1524. };
  1525. this.isActive = true;
  1526. this.activePopup = unsafeWindow.ikariam.createTemplateView(templateViewArg);
  1527. // Null out the id or submitting the change city form will send it as the
  1528. // templateView
  1529. unsafeWindow.ikariam.templateView.id = null;
  1530. unsafeWindow.ikariam.model.viewParams = null;
  1531. $('#ikaPopupTempHolder').replaceWith(this.contentElement);
  1532.  
  1533. this.options.activatedCallback(this);
  1534. if (skipResize) {
  1535. unsafeWindow.ikariam.controller.adjustSizes();
  1536. }
  1537. },
  1538. close: function close() {
  1539. if (this.isActive) {
  1540. unsafeWindow.ikariam.TemplateView.destroyTemplateView();
  1541. }
  1542. }
  1543. });
  1544. function TabPane(tabs, options) {
  1545. this.tabs = tabs;
  1546. this.options = $.extend({ tabActivatedCallback: function() {} }, options);
  1547. this.currentTab = null;
  1548.  
  1549. this.container = $("<div/>");
  1550.  
  1551. var tabsContainer = $('<ul class="tabmenu"/>');
  1552. this.container.append(tabsContainer);
  1553.  
  1554. for (var i = 0; i < tabs.length; i++) {
  1555. var tab = tabs[i];
  1556. tab.tabPane = this;
  1557.  
  1558. tabsContainer.append(tab.tabHolder)
  1559. this.container.append(tab.contentHolder);
  1560. tab.contentHolder.hide();
  1561. tab.tabHolder.click(
  1562. IkaTools.Logging.debuggable('IkaTools.TabPane.Tab.click',
  1563. this.activate.bind(this, tab)));
  1564. }
  1565. }
  1566. $.extend(TabPane.prototype, {
  1567. getContainer: function getContainer() {
  1568. return this.container;
  1569. },
  1570. activate: function activate(tab) {
  1571. if (this.currentTab !== tab) {
  1572.  
  1573. if (this.currentTab) {
  1574. this.currentTab.contentHolder.hide();
  1575. this.currentTab.tabHolder.removeClass('selected');
  1576. this.currentTab.deactivated();
  1577. }
  1578.  
  1579. tab.contentHolder.show();
  1580. tab.tabHolder.addClass('selected');
  1581. }
  1582. tab.activated();
  1583. this.options.tabActivatedCallback(this.currentTab, tab);
  1584. this.currentTab = tab;
  1585. }
  1586. });
  1587. TabPane.Tab = function Tab(tab, content, options) {
  1588. this.tab = $(tab);
  1589. this.content = $(content);
  1590. this.options = $.extend({ activatedCallback: function() {},
  1591. deactivatedCallback: function() {} },
  1592. options);
  1593.  
  1594. this.contentHolder = $('<div/>');
  1595. this.contentHolder.append(this.content);
  1596.  
  1597. this.tabHolder = $('<li class="tab"/>');
  1598. this.tabHolder.append(this.tab);
  1599. }
  1600. $.extend(TabPane.Tab.prototype, {
  1601. activate: function activate() {
  1602. this.tabPane.activate(this);
  1603. },
  1604. activated: function activated() {
  1605. this.options.activatedCallback(this);
  1606. },
  1607. deactivated: function deactivated() {
  1608. this.options.deactivatedCallback(this);
  1609. },
  1610. });
  1611. function SettingsWindow(id, scriptName, settings, settingGroups) {
  1612. this.id = id;
  1613. this.settings = settings;
  1614. this.scriptName = scriptName;
  1615. this.settingGroups = settingGroups;
  1616. this.saveEvent = new Utils.EventDispatcher();
  1617. }
  1618.  
  1619. $.extend(SettingsWindow.prototype, {
  1620. show: function show() {
  1621. var tabs = $.map(this.settingGroups, function makeTab(group, index) {
  1622. var content = $.map(group.getSettings(), this.renderSetting.bind(this)).join('');
  1623. content = '<div class="contentBox01h">' +
  1624. '<h3 class="header">' +
  1625. Intl.localizer.localize('settings','settings') +
  1626. '</h3>' +
  1627. '<div class="content">' +
  1628. '<table class="table01"><tbody>' + content + '</tbody></table>' +
  1629. '</div>' +
  1630. '<div class="footer"/>' +
  1631. '</div>' +
  1632. '<div class="centerButton">' +
  1633. '<a class="ikaScriptToolsSaveOptions button">' +
  1634. Intl.localizer.localize('settings','save') +
  1635. '</a>' +
  1636. '</div>';
  1637. return new TabPane.Tab('<b>' + group.getName() + '</b>', content, {});
  1638. }.bind(this));
  1639. var tabPane = new TabPane(tabs, {
  1640. tabActivatedCallback: function() {
  1641. IkaTools.UI.resizePopup();
  1642. },
  1643. });
  1644. var popup = new PopupWindow(
  1645. 'options',
  1646. $('<div>' + Intl.localizer.localize('settings','script_settings') + ': ' +
  1647. this.scriptName + '</div>'),
  1648. tabPane.getContainer());
  1649. tabs[0].activate();
  1650. popup.display();
  1651. $.each(this.settingGroups, function postRenderSettingsGroup(index, group) {
  1652. $.each(group.getSettings(), this.postRenderSetting.bind(this));
  1653. }.bind(this));
  1654. $('.ikaScriptToolsSaveOptions').click(Logging.debuggable(
  1655. 'IkaTools.UI.SettingsWindow.saveSettings',
  1656. this._save.bind(this)));
  1657. },
  1658. renderSetting: function renderSetting(setting, index) {
  1659. var type = setting.getType();
  1660. var html = '<tr><td>' + setting.getLabel()() + '</td><td class="left">';
  1661. if (type == Settings.Type.BOOLEAN) {
  1662. html += '<input id="ikaScriptToolsSettingInput_' + setting.getName() +
  1663. '" type="checkbox" ' + (setting.isEnabled() ? 'checked' : '' ) + '/>';
  1664. } else if (type == Settings.Type.CHOICE) {
  1665. html += '<select id="ikaScriptToolsSettingInput_' + setting.getName() + '">';
  1666. $.each(setting.getChoices(), function renderOption(key, value) {
  1667. html += '<option value="' + value + '"' +
  1668. (setting.getValue() == value ? 'selected="selected"' : '') + '>' + key + '</option>';
  1669. })
  1670. html += '</select>';
  1671. } else if (type == Settings.Type.HTML) {
  1672. html += setting.getHtml()();
  1673. } else if (type == Settings.Type.TEXT) {
  1674. html += '<input id="ikaScriptToolsSettingInput_' + setting.getName()
  1675. + '" value="' + setting.getValue() + '"/>';
  1676. }
  1677. html += '</td>';
  1678. return html;
  1679. },
  1680. postRenderSetting: function postRenderSetting(index, setting) {
  1681. var type = setting.getType();
  1682. if (type == Settings.Type.HTML) {
  1683.  
  1684. setting.getPostRender()();
  1685. }
  1686. },
  1687. _save: function save() {
  1688. $.each(this.settingGroups, function saveGroup(index, group) {
  1689. $.each(group.getSettings(), this._saveSetting.bind(this));
  1690. }.bind(this));
  1691. this.settings.save();
  1692. this.saveEvent.send(this);
  1693. },
  1694. _saveSetting: function saveSetting(index, setting) {
  1695. var type = setting.getType();
  1696. if (type == Settings.Type.BOOLEAN) {
  1697. setting.setEnabled(
  1698. $('#ikaScriptToolsSettingInput_' + setting.getName()).is(':checked'));
  1699. } else if (type == Settings.Type.CHOICE) {
  1700. setting.setValue(
  1701. $('#ikaScriptToolsSettingInput_' + setting.getName()).val());
  1702. } else if (type == Settings.Type.TEXT) {
  1703. setting.setValue(
  1704. $('#ikaScriptToolsSettingInput_' + setting.getName()).val());
  1705. }
  1706. },
  1707. registerSavedSettingsHandler: function registerSavedSettingsHandler(f) {
  1708. return this.saveEvent.addListener(f);
  1709. },
  1710. addAsScriptOptionsLink: function addAsScriptOptionsLink() {
  1711. if($('#IkaScriptToolSettingsDropdown').size() == 0) {
  1712. GM_addStyle(
  1713. '#IkaScriptToolSettingsDropdown { ' +
  1714. ' position:absolute; ' +
  1715. '}' +
  1716. '#IkaScriptToolSettingsDropdown:hover {' +
  1717. ' padding-bottom:20px;' +
  1718. '}' +
  1719. '#IkaScriptToolSettingsDropdown #IkaScriptToolsSettingsDropdownLinks { ' +
  1720. ' display:none;' +
  1721. '}' +
  1722. '#IkaScriptToolSettingsDropdown:hover #IkaScriptToolsSettingsDropdownLinks {' +
  1723. ' display:block;' +
  1724. '}' +
  1725. '#IkaScriptToolsSettingsDropdownLinks { ' +
  1726. ' background-color:#FFF5E1; ' +
  1727. ' padding:.5em; ' +
  1728. ' padding-bottom:0; ' +
  1729. ' border:1px solid #666; ' +
  1730. ' position:absolute; ' +
  1731. ' right:-80px; ' +
  1732. ' margin-top:2px; ' +
  1733. ' width:170px;' +
  1734. '}' +
  1735. '#IkaScriptToolsSettingsDropdownLinks a { ' +
  1736. ' color:#666; ' +
  1737. ' cursor:pointer; ' +
  1738. ' margin-left:0; ' +
  1739. ' padding-left:.2em; ' +
  1740. ' display:block; ' +
  1741. ' margin-bottom:.5em;' +
  1742. '}'
  1743. );
  1744. var li = document.createElement('li');
  1745. li.id = 'IkaOptionsDropdown';
  1746. $('#GF_toolbar ul').append($(
  1747. '<li id="IkaScriptToolSettingsDropdown">' +
  1748. '<a href="javascript:void(0);">' +
  1749. Intl.localizer.localize('settings','script_settings') + '</a>' +
  1750. '<div id="IkaScriptToolsSettingsDropdownLinks">' +
  1751. '</li>'));
  1752. }
  1753. var link = $('<a>' + this.scriptName + '</a>');
  1754. link.click(Logging.debuggable('IkaTools.UI.SettingsWindow.showSettings',
  1755. this.show.bind(this)));
  1756. $('#IkaScriptToolsSettingsDropdownLinks').append(link);
  1757. },
  1758. });
  1759. SettingsWindow.Group = function(name, settings) {
  1760. this.name = name;
  1761. this.settings = settings;
  1762. }
  1763. $.extend(SettingsWindow.Group.prototype, {
  1764. getName: function getName() {
  1765. return this.name;
  1766. },
  1767. getSettings: function getSettings() {
  1768. return this.settings;
  1769. },
  1770. });
  1771. return {
  1772. ToolTipHandler: ToolTipHandler,
  1773. LeftMenu: LeftMenu,
  1774. resizePopup: resizePopup,
  1775. PopupWindow: PopupWindow,
  1776. TabPane: TabPane,
  1777. SettingsWindow: SettingsWindow,
  1778. };
  1779. }();
  1780. var Settings = function() {
  1781. var Type = {
  1782. BOOLEAN: 1,
  1783. CHOICE: 2,
  1784. HTML: 3,
  1785. TEXT: 4,
  1786. }
  1787. function Settings(name) {
  1788. this.name = name;
  1789. this.data = new Data.Value('scriptOptions_' + name, { }, { version: 1 });
  1790. }
  1791. $.extend(Settings.prototype, {
  1792. _getValue: function getValue(name, defaultValue) {
  1793. var value = this.data.get()[name];
  1794. return value === undefined ? defaultValue : value;
  1795. },
  1796. _setValue: function setValue(name, value) {
  1797. this.data.get()[name] = value;
  1798. },
  1799. save: function save() {
  1800. this.data.save();
  1801. },
  1802. boolean: function boolean(name, enabled, labelFunc) {
  1803. return new Boolean(this, name, this._getValue(name, enabled), labelFunc);
  1804. },
  1805. choice: function choice(name, value, choices, labelFunc) {
  1806. return new Choice(this, name, this._getValue(name, value), choices, labelFunc);
  1807. },
  1808. html: function html(htmlFunc, postRender, labelFunc) {
  1809. return new Html(htmlFunc, postRender, labelFunc);
  1810. },
  1811.  
  1812. text: function text(name, value, labelFunc) {
  1813. return new Text(this, name, this._getValue(name, value), labelFunc);
  1814. }
  1815. });
  1816. function Boolean(settings, name, enabled, labelFunc) {
  1817. this.settings = settings;
  1818. this.name = name;
  1819. this.enabled = enabled;
  1820. this.labelFunc= labelFunc;
  1821. }
  1822. $.extend(Boolean.prototype, {
  1823. isEnabled: function isEnabled() {
  1824. return this.enabled;
  1825. },
  1826. setEnabled: function(enabled) {
  1827. this.enabled = enabled;
  1828. this.settings._setValue(this.name, enabled);
  1829. },
  1830. getName: function getName() {
  1831. return this.name;
  1832. },
  1833. getType: function getType() {
  1834. return Type.BOOLEAN;
  1835. },
  1836. getLabel: function getLabel() {
  1837. return this.labelFunc;
  1838. },
  1839. });
  1840. function Choice(settings, name, value, choices, labelFunc) {
  1841. this.settings = settings;
  1842. this.name = name;
  1843. this.value = value;
  1844. this.choices = choices;
  1845. this.labelFunc = labelFunc;
  1846. }
  1847. $.extend(Choice.prototype, {
  1848. getValue: function getValue() {
  1849. return this.value;
  1850. },
  1851. setValue: function setValue(value) {
  1852. this.value = value;
  1853. this.settings._setValue(this.name, value);
  1854. },
  1855. getChoices: function getChoices() {
  1856. return this.choices;
  1857. },
  1858. getName: function getName() {
  1859. return this.name;
  1860. },
  1861. getType: function getType() {
  1862. return Type.CHOICE;
  1863. },
  1864. getLabel: function getLabel() {
  1865. return this.labelFunc;
  1866. },
  1867. });
  1868. function Html(htmlFunc, postRender, labelFunc) {
  1869. this.labelFunc = labelFunc;
  1870. this.htmlFunc = htmlFunc;
  1871. this.postRender = postRender;
  1872. }
  1873. $.extend(Html.prototype, {
  1874. getHtml: function getHtml() {
  1875. return this.htmlFunc;
  1876. },
  1877. getPostRender: function getPostRender() {
  1878. return this.postRender;
  1879. },
  1880. getType: function getType() {
  1881. return Type.HTML;
  1882. },
  1883. getLabel: function getLabel() {
  1884. return this.labelFunc;
  1885. },
  1886. });
  1887.  
  1888. function Text(settings, name, value, labelFunc) {
  1889. this.settings = settings;
  1890. this.name = name;
  1891. this.value = value;
  1892. this.labelFunc = labelFunc;
  1893. }
  1894.  
  1895. $.extend(Text.prototype, {
  1896. getValue: function getValue() {
  1897. return this.value;
  1898. },
  1899. setValue: function setValue(value) {
  1900. this.value = value;
  1901. this.settings._setValue(this.name, value);
  1902. },
  1903. getName: function getName() {
  1904. return this.name;
  1905. },
  1906. getType: function getType() {
  1907. return Type.TEXT;
  1908. },
  1909. getLabel: function getLabel() {
  1910. return this.labelFunc;
  1911. },
  1912. });
  1913. return {
  1914. Settings: Settings,
  1915. Type: Type,
  1916. };
  1917. }();
  1918. var Constants = {
  1919. Resources: {
  1920. WOOD: 'wood',
  1921. WINE: 'wine',
  1922. MARBLE: 'marble',
  1923. GLASS: 'glass',
  1924. SULFUR: 'sulfur',
  1925. POPULATION: 'population',
  1926. CITIZENS: 'citizens',
  1927.  
  1928. SCIENTISTS: 'scientists',
  1929. ACTION_POINTS: 'actionPoints',
  1930. CULTURAL_GOODS: 'culturalGoods',
  1931. TAVERN_WINE_LEVEL: 'tavernWineLevel',
  1932. PRIESTS: 'priests',
  1933. },
  1934. CivilizationData: {
  1935. GOVERNMENT: 'government',
  1936. RESEARCH: 'research',
  1937. MOVEMENT: 'movement',
  1938. PREMIUM_FEATURE: 'premiumFeature',
  1939. },
  1940. PremiumFeatures: {
  1941. DOUBLED_STORAGE_CAPACITY: 'doubledStorageCapacity',
  1942. DOUBLED_SAFE_CAPACITY: 'doubledSafeCapacity',
  1943. },
  1944. Movements: {
  1945. Mission: {
  1946. TRANSPORT: 'transport',
  1947. DEPLOY_ARMY: 'deployarmy',
  1948. DEPLOY_NAVY: 'deployfleet',
  1949. PLUNDER: 'plunder',
  1950. },
  1951.  
  1952. Stage: {
  1953. LOADING: 'loading',
  1954. EN_ROUTE: 'en_route',
  1955. RETURNING: 'returning',
  1956. },
  1957.  
  1958. EventType: {
  1959. DATA_UPDATED: 'dataUpdated',
  1960. STAGE_CHANGED: 'stageChanged',
  1961. CANCELLED: 'cancelled',
  1962. COMPLETED: 'completed',
  1963. },
  1964. MissionData: {
  1965. transport: {
  1966. icon: 'skin/interface/mission_transport.png',
  1967. },
  1968. deployarmy: {
  1969. icon: 'skin/interface/mission_deployarmy.png',
  1970. },
  1971. deployfleet: {
  1972. icon: 'skin/interface/mission_deployfleet.png',
  1973. },
  1974. plunder: {
  1975. icon: 'skin/interface/mission_plunder.png',
  1976. },
  1977. piracyRaid: {
  1978. icon: 'skin/interface/mission_piracyRaid.png',
  1979. },
  1980. }
  1981. },
  1982. BuildingEventType: {
  1983. DATA_REFRESH: 'dataRefresh',
  1984. UPGRADE_COMPLETE: 'upgradeComplete',
  1985. },
  1986. Buildings: {
  1987. TOWN_HALL: 'townHall',
  1988. PALACE: 'palace',
  1989. GOVERNORS_RESIDENCE: 'palaceColony',
  1990. TAVERN: 'tavern',
  1991. MUSEUM: 'museum',
  1992. ACADEMY: 'academy',
  1993. WORKSHOP: 'workshop',
  1994. TEMPLE: 'temple',
  1995. EMBASSY: 'embassy',
  1996. WAREHOUSE: 'warehouse',
  1997. DUMP: 'dump',
  1998. TRADING_PORT: 'port',
  1999. TRADING_POST: 'branchOffice',
  2000. BLACK_MARKET: 'blackMarket',
  2001. MARINE_CHART_ARCHIVE: 'marineChartArchive',
  2002. WALL: 'wall',
  2003. HIDEOUT: 'safehouse',
  2004. BARRACKS: 'barracks',
  2005. SHIPYARD: 'shipyard',
  2006. PIRATE_FORTRESS: 'pirateFortress',
  2007. FORESTER: 'forester',
  2008. CARPENTER: 'carpentering',
  2009. WINERY: 'winegrower',
  2010. WINE_PRESS: 'vineyard',
  2011. STONEMASON: 'stonemason',
  2012. ARCHITECT: 'architect',
  2013. GLASSBLOWER: 'glassblowing',
  2014. OPTICIAN: 'optician',
  2015. ALCHEMISTS_TOWER: 'alchemist',
  2016. FIREWORK_TEST_AREA: 'fireworker',
  2017. },
  2018. // Time data from http://ikariam.wikia.com/wiki/User_blog:Warrior_fr/Actual_building_Time_formula
  2019. // Rest of data from http://ikariam.wikia.com/ building pages.
  2020. BuildingData: {
  2021. academy: {
  2022. maxLevel: 32,
  2023. wood :[64, 68, 115, 263, 382, 626, 982, 1330, 2004, 2665, 3916, 5156, 7446, 9753, 12751, 18163, 23691, 33451, 43571, 56729, 73832, 103459, 144203, 175058, 243930, 317208, 439967, 536310, 743789, 1027469, 1257244, 1736681],
  2024. glass :[0, 0, 0, 0, 225, 428, 744, 1089, 1748, 2454, 3786, 5216, 7862, 10729, 14599, 21627, 29321, 43020, 58213, 78724, 106414, 154857, 224146, 282571, 408877, 552141, 795252, 1006647, 1449741, 2079650, 2642546, 3790581],
  2025. marble:0,
  2026. sulfur:0,
  2027. wine :0,
  2028. time :{a:1440, b:1, c:1.2, d:720},
  2029. icon :'skin/img/city/academy_l.png',
  2030. 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 ],
  2031. },
  2032. alchemist: {
  2033. maxLevel: 32,
  2034. wood :[274, 467, 718, 1045, 1469, 2021, 2738, 3671, 4883, 6459, 8508, 11172, 14634, 19135, 24987, 32594, 42483, 55339, 72050, 93778, 122021, 158740, 206471, 268524, 349194, 454063, 590393, 767620, 998018, 1297535, 1686906, 2193088],
  2035. glass :0,
  2036. marble:[0, 116, 255, 436, 671, 977, 1375, 1892, 2564, 3437, 4572, 6049, 7968, 10462, 13705, 17921, 23402, 30527, 39790, 51830, 67485, 87835, 114289, 148680, 193389, 251512, 327069, 425294, 552986, 718987, 934789, 1215329],
  2037. sulfur:0,
  2038. wine :0,
  2039. time :{a:72000, b:11, c:1.1, d:6120},
  2040. icon :'skin/img/city/alchemist_l.png',
  2041. },
  2042. architect: {
  2043. maxLevel: 32,
  2044. wood :[185, 291, 413, 555, 720, 911, 1133, 1390, 1689, 2035, 2437, 2902, 3443, 4070, 4797, 5640, 6618, 7754, 9070, 10598, 12369, 14424, 16807, 19573, 22780, 26501, 30817, 35826, 41631, 48371, 56185, 65251],
  2045. glass :0,
  2046. marble:[106, 160, 222, 295, 379, 475, 587, 716, 865, 1036, 1233, 1460, 1722, 2023, 2369, 2767, 3226, 3752, 4358, 5056, 5857, 6778, 7836, 9052, 10448, 12054, 13899, 16289, 18450, 21246, 24455, 28141],
  2047. sulfur:0,
  2048. wine :0,
  2049. time :{a:125660, b:37, c:1.06, d:2628},
  2050. icon :'skin/img/city/architect_l.png',
  2051. },
  2052. barracks: {
  2053. maxLevel: 49,
  2054. wood :[48, 114, 195, 296, 420, 574, 766, 1003, 1297, 1662, 2115, 2676, 3371, 4234, 5304, 6630, 8275, 10314, 12843, 15979, 19868, 24690, 30669, 38083, 47277, 58676, 72812, 90341, 112076, 139028, 172448, 213889, 265276, 328996, 408008, 505984],
  2055. glass :0,
  2056. marble:[0, 0, 0, 0, 0, 0, 0, 0, 178, 431, 745, 1134, 1616, 2214, 2956, 3875, 5015, 6429, 8183, 10357, 13052, 16395, 20540, 25680, 32054, 39957, 49757, 61909, 76977, 95661, 118830, 147560, 183185, 227359, 282136, 350059],
  2057. sulfur:0,
  2058. wine :0,
  2059. time :{a:25200, b:11, c:1.1, d:1728},
  2060. icon :'skin/img/city/barracks_l.png',
  2061. },
  2062. branchOffice: {
  2063. maxLevel: 32,
  2064. wood :[48, 173, 346, 581, 896, 1314, 1863, 2580, 3509, 4706, 6241, 8203, 10699, 13866, 17872, 22926, 29286, 37272, 47283, 59806, 75447, 94954, 119245, 149453, 186977, 233530, 291225, 362658, 451015, 560208, 695038, 861391],
  2065. glass :0,
  2066. marble:[0, 0, 0, 0, 540, 792, 1123, 1555, 2115, 2837, 3762, 4945, 6450, 8359, 10774, 13820, 17654, 22469, 28503, 36051, 45482, 57240, 71883, 90092, 112712, 121067, 175556, 218617, 271878, 337705, 418983, 446564],
  2067. sulfur:0,
  2068. wine :0,
  2069. time :{a:108000, b:11, c:1.1, d:9360},
  2070. icon :'skin/img/city/branchoffice_l.png',
  2071. },
  2072. blackMarket: {
  2073. maxLevel: 25,
  2074. 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],
  2075. glass :0,
  2076. 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],
  2077. sulfur:0,
  2078. wine :0,
  2079. time :{a:0, b:0, c:0, d:0},
  2080. icon :'skin/img/city/blackmarket_l.png',
  2081. },
  2082. marineChartArchive: {
  2083. maxLevel: 40,
  2084. 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, 243539, 283039, 328865, 382024, 443687, 515217, 598191, 694442, 806092, 935607, 1085844, 1260119],
  2085. 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],
  2086. 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],
  2087. sulfur:0,
  2088. wine :0,
  2089. time :{a:0, b:0, c:0, d:0},
  2090. icon :'skin/img/city/marinechartarchive_l.png',
  2091. },
  2092. carpentering: {
  2093. maxLevel: 32,
  2094. wood :[63, 122, 191, 274, 372, 486, 620, 777, 962, 1178, 1432, 1730, 2078, 2486, 2964, 3524, 4178, 4945, 5841, 6890, 8117, 9550, 11229, 13190, 15484, 18166, 21299, 24963, 29245, 34247, 40096, 46930],
  2095. glass :0,
  2096. marble:[0, 0, 0, 0, 0, 0, 0, 359, 444, 546, 669, 816, 993, 1205, 1459, 1765, 2131, 2571, 3097, 3731, 4490, 5402, 6496, 7809, 9383, 11274, 13543, 16265, 19531, 23450, 28154, 33798],
  2097. sulfur:0,
  2098. wine :0,
  2099. time :{a:125660, b:37, c:1.06, d:2808},
  2100. icon :'skin/img/city/carpentering_l.png',
  2101. },
  2102. dump: {
  2103. maxLevel: 40,
  2104. wood :[640, 1152, 1766, 2504, 3388, 4450, 5724, 7253, 9088, 11289, 13931, 17101, 20905, 25470, 30948, 37522, 45410, 54876, 66236, 79867, 96223, 115852, 139407, 167672, 201592, 242293, 291136, 349749, 420081, 504483, 605763, 727300, 873143, 1048157, 1258171, 1510191, 1812613, 2175519, 2611007, 3133592],
  2105. glass :[701, 1146, 1668, 2278, 2991, 3526, 4803, 5946, 7283, 8847, 10678, 12819, 15324, 18257, 21687, 25700, 30395, 35889, 42316, 49837, 58635, 68929, 80973, 95065, 111553, 130843, 153414, 179821, 201716, 246864, 289157, 338642, 396536, 464274, 543528, 636253, 744742, 871676, 1020187, 1193945],
  2106. marble:[497, 932, 1445, 2051, 2762, 3609, 4604, 5778, 7164, 8799, 10728, 13005, 15691, 18862, 22602, 27016, 32225, 38371, 45623, 54181, 64278, 76194, 90256, 106847, 126424, 149528, 176787, 208956, 246913, 291702, 344555, 406921, 480512, 567350, 669817, 790730, 933408, 1101767, 1300431, 1534855],
  2107. sulfur:[384, 845, 1398, 2061, 2858, 3813, 4960, 6336, 7987, 9968, 12346, 15199, 18623, 22731, 27661, 33578, 40677, 49197, 59420, 71688, 86409, 104076, 125274, 150714, 181241, 217872, 261830, 314581, 377881, 453842, 544994, 654378, 785637, 943149, 1132163, 1358979, 1631159, 1957774, 2349714, 2820041],
  2108.  
  2109. wine :0,
  2110. time :{a:32000, b:13, c:1.17, d:2160},
  2111. icon :'skin/img/city/dump_l.png',
  2112. },
  2113. embassy: {
  2114. maxLevel: 32,
  2115. wood :[242, 415, 623, 873, 1173, 1532, 1964, 2482, 3103, 3849, 4743, 5817, 7105, 8651, 10507, 12733, 15610, 18498, 22457, 27074, 32290, 33764, 47240, 56812, 70157, 84318, 101310, 121979, 146503, 175932, 222202, 266778],
  2116. glass :0,
  2117. marble:[155, 342, 571, 850, 1190, 1606, 2112, 2730, 3484, 4404, 5527, 6896, 8566, 10604, 13090, 16123, 19824, 24339, 29846, 36564, 45216, 47097, 66967, 81859, 104537, 129580, 158759, 193849, 236659, 288888, 358869, 437985 ],
  2118. sulfur:0,
  2119. wine :0,
  2120. time :{a:96000, b:7, c:1.05, d:10080},
  2121. icon :'skin/img/city/embassy_l.png',
  2122. },
  2123. fireworker: {
  2124. maxLevel: 32,
  2125. wood :[272, 353, 445, 551, 673, 813, 974, 1159, 1373, 1618, 1899, 2223, 2596, 3025, 3517, 4084, 4736, 5485, 6346, 7338, 8478, 9790, 11297, 13030, 14990, 17317, 19954, 22986, 26472, 30484, 35096, 40398],
  2126. glass :0,
  2127. marble:[135, 212, 302, 405, 526, 665, 827, 1015, 1233, 1486, 1779, 2120, 2514, 2972, 3503, 4119, 4834, 5662, 6623, 7738, 9032, 10534, 12275, 13355, 16636, 19354, 22507, 26163, 30404, 35325, 41033, 47652],
  2128. sulfur:0,
  2129. wine :0,
  2130. time :{a:125660, b:37, c:1.06, d:2628},
  2131. icon :'skin/img/city/fireworker_l.png',
  2132. },
  2133. forester: {
  2134. maxLevel: 32,
  2135. wood :[250, 430, 664, 968, 1364, 1878, 2546, 3415, 4544, 6013, 7922, 10403, 13629, 17823, 23274, 30362, 39574, 51552, 67123, 87363, 113680, 147889, 192360, 250173, 325258, 423034, 550049, 715169, 929826, 1208878, 1571646, 2043246],
  2136. glass :0,
  2137. marble:[0, 104, 237, 410, 635, 928, 1309, 1803, 2446, 3282, 4368, 5781, 7617, 10422, 13108, 17142, 22386, 29204, 38068, 49589, 64569, 84041, 109356, 142266, 185046, 240663, 312965, 406956, 529144, 687989, 894489, 1162937],
  2138. sulfur:0,
  2139. wine :0,
  2140. time :{a:72000, b:11, c:1.1, d:6120},
  2141. icon :'skin/img/city/forester_l.png',
  2142. },
  2143. glassblowing: {
  2144. maxLevel: 32,
  2145. wood :[274, 467, 718, 1045, 1469, 2021, 2738, 3671, 4883, 6459, 8508, 11172, 14634, 19135, 24987, 32594, 42483, 55339, 72050, 93778, 122021, 158740, 206471, 268524, 349194, 454063, 590393, 767620, 998018, 1297535, 1686906, 2193088],
  2146. glass :0,
  2147. marble:[0, 116, 255, 436, 671, 977, 1375, 1892, 2564, 3437, 4572, 6049, 7968, 10462, 13705, 17921, 23402, 30527, 39790, 51830, 67485, 87835, 114289, 148680, 193389, 251512, 327069, 425294, 552986, 718987, 934789, 1215329],
  2148. sulfur:0,
  2149. wine :0,
  2150. time :{a:72000, b:11, c:1.1, d:6120},
  2151. icon :'skin/img/city/glassblowing_l.png',
  2152. },
  2153. museum: {
  2154. maxLevel: 21,
  2155. wood :[560, 1435, 2748, 4716, 7669, 12099, 18744, 28710, 43661, 66086, 99724, 150181, 225866, 339394, 509686, 765124, 1148280, 1723016, 2585120, 3878276],
  2156. glass :0,
  2157. marble:[280, 1190, 2573, 4676, 7871, 12729, 20112, 31335, 48394, 74323, 113736, 173643, 264701, 403110, 613492, 933272, 1419338, 2158157, 3281164, 4988135],
  2158. sulfur:0,
  2159. wine :0,
  2160. time :{a:18000, b:1, c:1.1, d:14040},
  2161. icon :'skin/img/city/museum_r.png',
  2162. },
  2163. optician: {
  2164. maxLevel: 32,
  2165. wood :[119, 188, 269, 362, 471, 597, 742, 912, 1108, 1335, 1600, 1906, 2261, 2673, 3152, 3706, 4348, 5096, 5962, 6966, 8131, 9482, 11050, 12868, 14978, 17424, 20262, 23553, 27373, 31804, 36943, 42904],
  2166. glass :0,
  2167. marble:[0, 35, 96, 167, 249, 345, 455, 584, 733, 905, 1106, 1338, 1608, 1921, 2283, 2704, 3191, 3759, 4416, 5178, 6062, 7087, 8276, 9656, 11257, 13113, 15267, 17762, 20662, 24024, 27922, 32447],
  2168. sulfur:0,
  2169. wine :0,
  2170. time :{a:125660, b:37, c:1.06, d:2772},
  2171. icon :'skin/img/city/optician_l.png',
  2172. },
  2173. palace: {
  2174. maxLevel: 11,
  2175. wood :[712, 5823, 16048, 36496, 77392, 159184, 322768, 649936, 1304272, 2612944, 4743517],
  2176. glass :[0, 0, 0, 0, 21188, 42400, 84824, 169672, 339368, 678760, 1357543],
  2177. marble:[0, 1433, 4546, 10770, 23218, 48114, 97906, 197490, 396658, 794994, 1591666],
  2178. sulfur:[0, 0, 3088, 10300, 24725, 53573, 111269, 226661, 457445, 919013, 1842149],
  2179. wine :[0, 0, 0, 10898, 22110, 44534, 89382, 179078, 358470, 717254, 1434821],
  2180. time :{a:11520, b:1, c:1.4, d:0},
  2181. icon :'skin/img/city/palace_l.png',
  2182. },
  2183. palaceColony:{
  2184. maxLevel: 11,
  2185. wood :[712, 5823, 16048, 36496, 77392, 159184, 322768, 649936, 1304272, 2612944, 4743517],
  2186. glass :[0, 0, 0, 0, 21188, 42400, 84824, 169672, 339368, 678760, 1357543],
  2187. marble:[0, 1433, 4546, 10770, 23218, 48114, 97906, 197490, 396658, 794994, 1591666],
  2188. sulfur:[0, 0, 3088, 10300, 24725, 53573, 111269, 226661, 457445, 919013, 1842149],
  2189. wine :[0, 0, 0, 10898, 22110, 44534, 89382, 179078, 358470, 717254, 1434821],
  2190. time :{a:11520, b:1, c:1.4, d:0},
  2191. icon :'skin/img/city/palaceColony_l.png',
  2192. },
  2193. pirateFortress:{
  2194. maxLevel: 30,
  2195. wood :[450, 906, 1389, 1935, 2593, 3427, 4516, 5950, 7834, 10284, 13430, 17415, 22394, 28534, 36015, 45029, 55779, 68482, 83366, 100671, 120648, 143562, 169686, 199309, 232729, 270255, 312210, 358926, 410748, 468032],
  2196. glass :0,
  2197. marble :[250, 505, 783, 1112, 1534, 2103, 2883, 3949, 5388, 7296, 9782, 12964, 16970, 21938, 28019, 35370, 44162, 54573, 66793, 81020, 97463, 116341, 137883, 162325, 189915, 220912, 255580, 294197, 337048, 384429],
  2198. sulfur :0,
  2199. wine :0,
  2200. time :{a:1550, b:1, c:1.2, d:1800},
  2201. icon :'skin/img/city/pirateFortress_l.png',
  2202. },
  2203. port: {
  2204. maxLevel: 47,
  2205. wood :[60, 150, 274, 429, 637, 894, 1207, 1645, 2106, 2735, 3537, 4492, 5689, 7103, 8850, 11094, 13731, 17062, 21097, 25965, 31810, 39190, 47998, 58713, 71955, 87627, 107102, 130776, 159019, 193938, 235849, 286514, 348718, 423990, 513947, 625160, 758178, 919693, 1116013, 1353517, 1642274, 1990223, 2411061],
  2206. glass :0,
  2207. marble:[0, 0, 0, 0, 0, 176, 326, 540, 791, 1138, 1598, 2176, 2928, 3859, 5051, 6628, 8566, 11089, 14265, 18241, 23197, 29642, 37636, 47703, 60556, 76367, 96639, 122156, 153753, 194089, 244300, 307174, 386955, 486969, 610992, 769302, 965792, 1212790, 1523570, 1913072, 2403313, 3015688, 3782992],
  2208. sulfur:0,
  2209. wine :0,
  2210. time :{a:50400, b:23, c:1.15, d:1512},
  2211. loadingSpeed: [10, 30, 60, 93, 129, 169, 213, 261, 315, 373, 437, 508, 586, 672, 766, 869, 983, 1108, 1246, 1398, 1565, 1748, 1950, 2172, 2416, 2685, 2980, 3305, 3663, 4056, 4489, 4965, 5488, 6064, 6698, 7394, 8161, 9004, 9931, 10951, 12073, 13308, 14666, 16159, 17803, 19616, 21613, 23813, 26237],
  2212.  
  2213. icon :'skin/img/city/port_l.png',
  2214. },
  2215. safehouse: {
  2216. maxLevel: 32,
  2217. wood :[113, 248, 402, 578, 779, 1007, 1267, 1564, 1903, 2288, 2728, 3230, 3801, 4453, 5195, 6042, 7008, 8108, 9363, 10793, 12423, 14282, 16401, 18816, 21570, 24709, 28288, 32368, 37019, 42321, 48365, 55255],
  2218. glass :0,
  2219. marble:[0, 0, 0, 129, 197, 275, 366, 471, 593, 735, 900, 1090, 1312, 1569, 1866, 2212, 2613, 3078, 3617, 4243, 4968, 5810, 6787, 7919, 9233, 10758, 12526, 14577, 16956, 19716, 22917, 26631],
  2220. sulfur:0,
  2221. wine :0,
  2222. time :{a:96000, b:7, c:1.05, d:12960},
  2223. icon :'skin/img/city/safehouse_l.png',
  2224. },
  2225. shipyard: {
  2226. maxLevel: 32,
  2227. wood :[98, 202, 324, 477, 671, 914, 1222, 1609, 2096, 2711, 3485, 4459, 5688, 7238, 9190, 11648, 14746, 18650, 23568, 29765, 37573, 47412, 59808, 75428, 95108, 119906, 151151, 190520, 240124, 302626, 381378, 480605],
  2228. glass :0,
  2229. marble:[0, 0, 0, 0, 0, 778, 1052, 1397, 1832, 2381, 3070, 3941, 5037, 6420, 8161, 10354, 13118, 16601, 20989, 26517, 33484, 42261, 53321, 67256, 84814, 106938, 134814, 169937, 214192, 269954, 340214, 428741],
  2230. sulfur:0,
  2231. wine :0,
  2232. time :{a:64800, b:7, c:1.05, d:7128},
  2233. icon :'skin/img/city/shipyard_l.png',
  2234. },
  2235. stonemason: {
  2236. maxLevel: 32,
  2237. wood :[274, 467, 718, 1045, 1469, 2021, 2738, 3671, 4883, 6459, 8508, 11172, 14634, 19135, 24987, 32594, 42483, 55339, 72050, 93778, 122021, 158740, 206471, 268524, 349194, 454063, 590393, 767620, 998018, 1297535, 1686906, 2193088],
  2238. glass :0,
  2239. marble:[0, 116, 255, 436, 671, 977, 1375, 1892, 2564, 3437, 4572, 6049, 7968, 10462, 13705, 17921, 23402, 30527, 39790, 51830, 67485, 87835, 114289, 148680, 193389, 251512, 327069, 425294, 552986, 718987, 934789, 1215329],
  2240. sulfur:0,
  2241. wine :0,
  2242. time :{a:72000, b:11, c:1.1, d:6120},
  2243. icon :'skin/img/city/stonemason_l.png',
  2244. },
  2245. temple: {
  2246. maxLevel: 32,
  2247. wood :[216, 228, 333, 465, 598, 760, 958, 1197, 1432, 1773, 2112, 2512, 3082, 3655, 4458, 5126, 6232, 7167, 8687, 10247, 11784, 14228, 16752, 19265, 23156, 26663, 32026, 36830, 43256, 50782, 59591, 68528],
  2248. glass :[173, 190, 290, 423, 567, 752, 989, 1290, 1610, 2080, 2586, 3210, 4109, 5084, 6471, 7765, 9851, 11821, 14952, 18402, 22082, 27824, 34184, 41020, 51514, 61817, 77477, 92972, 113941, 139577, 170910, 205093],
  2249. marble:0,
  2250. sulfur:0,
  2251. wine :0,
  2252. time :{a:2160, b:1, c:1.1, d:0},
  2253. icon :'skin/img/city/temple_l.png',
  2254. },
  2255. tavern: {
  2256. maxLevel: 47,
  2257. wood :[101, 222, 367, 541, 750, 1001, 1302, 1663, 2097, 2617, 3241, 3990, 4888, 5967, 7261, 8814, 10678, 12914, 15598, 18818, 22683, 27320, 32885, 39562, 47576, 57192, 68731, 82578, 99194, 119134, 143061, 171774, 206230, 247577, 297193, 356732, 428179, 513916, 616800, 740261, 888413, 1066196, 1279537, 1535545, 1842756, 2211407, 2653789 ],
  2258. glass :0,
  2259. marble:[0, 0, 0, 94, 122, 158, 206, 267, 348, 452, 587, 764, 993, 1290, 1677, 2181, 2835, 3685, 4791, 6228, 8097, 10526, 13684, 17789, 23125, 30063, 39082, 50806, 66048, 85862, 111621, 145107, 188640, 245232, 318801, 414441, 538774, 700406, 910528, 1183686, 1538791, 2000427, 2600557, 3380725, 4394943, 5713425, 7427454],
  2260. sulfur:0,
  2261. wine :0,
  2262. time :{a:10800, b:1, c:1.06, d:10440},
  2263. icon :'skin/img/city/taverne_r.png',
  2264. 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],
  2265. },
  2266. townHall: {
  2267. maxLevel: 40,
  2268. wood :[0, 158, 335, 623, 923, 1390, 2015, 2706, 3661, 4776, 6173, 8074, 10281, 13023, 16424, 20986, 25423, 32285, 40232, 49286, 61207, 74804, 93956, 113035, 141594, 170213, 210011, 258875, 314902, 387656, 471194, 572580, 695615, 854728, 1037814, 1274043, 1714396, 1876185, 2276285, 2761291],
  2269. glass :0,
  2270. marble:[0, 0, 0, 0, 285, 551, 936, 1411, 2091, 2945, 4072, 5664, 7637, 10214, 13575, 18254, 23250, 31022, 40599, 52216, 68069, 87316, 115101, 145326, 191053, 241039, 312128, 403825, 515593, 666228, 850031, 1084292, 1382826, 1783721, 2273685, 2930330, 3692589, 4756439, 6058680, 7716365],
  2271. sulfur:0,
  2272. wine :0,
  2273. time :{a:1800, b:1, c:1.17, d:-1080},
  2274. icon :'skin/img/city/townhall_l.png',
  2275. },
  2276. vineyard: {
  2277. maxLevel: 32,
  2278. wood :[339, 423, 520, 631, 758, 905, 1074, 1269, 1492, 1749, 2045, 2384, 2775, 3225, 3741, 4336, 5019, 5813, 6875, 7941, 8944, 10319, 11900, 13718, 15809, 18215, 20978, 24159, 27816, 32021, 36857, 42419],
  2279. glass :0,
  2280. marble:[123, 198, 285, 387, 504, 640, 798, 981, 1194, 1440, 1726, 2058, 2443, 2889, 3407, 4008, 4705, 5513, 6450, 7537, 8800, 10263, 11961, 13930, 16214, 18864, 21938, 25503, 29639, 34437, 40002, 46457],
  2281. sulfur:0,
  2282. wine :0,
  2283. time :{a:125660, b:37, c:1.06, d:2232},
  2284. icon :'skin/img/city/vineyard_l.png',
  2285. },
  2286. wall: {
  2287. maxLevel: 48,
  2288. wood :[114, 361, 657, 1012, 1439, 1951, 2565, 3302, 4186, 5247, 6521, 8049, 9882, 12083, 14724, 17892, 21695, 26258, 31733, 38304, 46189, 55650, 67004, 80629, 96979, 116599, 140143, 168395, 202298, 242982, 291802, 350387, 420689, 505049, 606284, 727765, 873541, 1048473, 1258393, 1510294, 1812577, 2175317, 2610603, 3132948, 3759764],
  2289. glass :0,
  2290. marble:[0, 203, 516, 892, 1344, 1885, 2535, 3315, 4251, 5374, 6721, 8338, 10279, 12608, 15402, 18755, 22779, 27607, 33402, 40355, 48699, 58711, 70726, 85144, 102446, 123208, 148122, 178019, 213896, 256948, 308610, 370605, 444998, 534270, 641397, 769949, 924213, 1109328, 1331467, 1598031, 1917913, 2301767, 2762392, 3315144, 3978446],
  2291. sulfur:0,
  2292. wine :0,
  2293. time :{a:57600, b:11, c:1.1, d:3240},
  2294. icon :'skin/img/city/wall.png',
  2295. },
  2296. warehouse: {
  2297. maxLevel: 40,
  2298. wood :[160, 288, 442, 626, 847, 1113, 1431, 1813, 2272, 2822, 3483, 4275, 5226, 6368, 7737, 9380, 11353, 13719, 16559, 19967, 24056, 28963, 34852, 41918, 50398, 60574, 72784, 87437, 105021, 126121, 151441, 181825, 218286, 262039, 314543, 377548, 453153, 543880, 652752, 783398],
  2299.  
  2300. glass :0,
  2301. marble:[0, 0, 0, 96, 211, 349, 515, 714, 953, 1240, 1584, 1997, 2492, 3086, 3800, 4656, 5683, 6915, 8394, 10169, 12299, 14855, 17922, 21602, 26019, 31319, 37678, 45310, 54468, 65458, 78645, 94471, 113461, 136249, 163595, 196409, 235787, 283041, 339745, 407790 ],
  2302. sulfur:0,
  2303. wine :0,
  2304. time :{a:2880, b:1, c:1.14, d:2160},
  2305. icon :'skin/img/city/warehouse_l.png',
  2306. },
  2307. winegrower: {
  2308. maxLevel: 32,
  2309. wood :[274, 467, 718, 1045, 1469, 2021, 2738, 3671, 4883, 6459, 8508, 11172, 14634, 19135, 24987, 32594, 42483, 55339, 72050, 93778, 122021, 158740, 206471, 268524, 349194, 454063, 590393, 767620, 998018, 1297535, 1686906, 2193088],
  2310. glass :0,
  2311. marble:[0, 116, 255, 436, 671, 977, 1375, 1892, 2564, 3437, 4572, 6049, 7968, 10462, 13705, 17921, 23402, 30527, 39790, 51830, 67485, 87835, 114289, 148680, 193389, 251512, 327069, 425294, 552986, 718987, 934789, 1215329],
  2312. sulfur:0,
  2313. wine :0,
  2314. time :{a:72000, b:11, c:1.1, d:6120},
  2315. icon :'skin/img/city/winegrower_l.png',
  2316. },
  2317. workshop: {
  2318. maxLevel: 32,
  2319. wood :[206, 383, 569, 781, 1023, 1299, 1613, 1972, 2380, 2846, 3377, 3982, 4672, 5458, 6355, 7377, 8542, 9870, 11385, 13111, 15078, 17714, 19481, 22796, 26119, 29909, 34228, 39153, 44766, 51166, 58462, 66778],
  2320. glass :0,
  2321. marble:[89, 167, 251, 349, 461, 592, 744, 920, 1125, 1362, 1637, 1956, 2326, 2755, 3253, 3831, 4500, 5279, 6180, 7226, 8439, 9776, 11477, 13373, 15570, 18118, 21074, 24503, 28481, 33095, 38447, 44656],
  2322. sulfur:0,
  2323. wine :0,
  2324. time :{a:96000, b:7, c:1.05, d:11880},
  2325. icon :'skin/img/city/workshop_l.png',
  2326. },
  2327. },
  2328. Research: {
  2329. Seafaring: {
  2330. CARPENTRY: 2150,
  2331. DECK_WEAPONS: 1010,
  2332. PIRACY: 1170,
  2333. SHIP_MAINTENANCE: 1020,
  2334. DRAFT: 1130,
  2335. EXPANSION: 1030,
  2336. FOREIGN_CULTURES: 1040,
  2337. PITCH: 1050,
  2338. MARKET: 2070,
  2339. GREEK_FIRE: 1060,
  2340. COUNTERWEIGHT: 1070,
  2341. DIPLOMACY: 1080,
  2342. SEA_MAPS: 1090,
  2343. PADDLE_WHEEL_ENGINE: 1100,
  2344. CAULKING: 1140,
  2345. MORTAR_ATTACHMENT: 1110,
  2346. MASSIVE_RAM: 1150,
  2347. OFFSHORE_BASE: 1160,
  2348. SEAFARING_FUTURE: 1999,
  2349. },
  2350. Economy: {
  2351. CONSERVATION: 2010,
  2352. PULLEY: 2020,
  2353. WEALTH: 2030,
  2354. WINE_CULTURE: 2040,
  2355. IMPROVED_RESOURCE_GATHERING: 2130,
  2356. GEOMETRY: 2060,
  2357. ARCHITECTURE: 1120,
  2358. HOLIDAY: 2080,
  2359. LEGISLATION: 2170,
  2360. CULINARY_SPECIALITIES: 2050,
  2361. HELPING_HANDS: 2090,
  2362. SPIRIT_LEVEL: 2100,
  2363. WINE_PRESS: 2140,
  2364. DEPOT: 2160,
  2365. BUREACRACY: 2110,
  2366. UTOPIA: 2120,
  2367. ECONOMIC_FUTURE: 2999,
  2368. },
  2369. Science: {
  2370. WELL_CONSTRUCTION: 3010,
  2371. PAPER: 3020,
  2372. ESPIONAGE: 3030,
  2373. POLYTHEISM: 3040,
  2374. INK: 3050,
  2375. GOVERNMENT_FORMATION: 3150,
  2376. INVENTION: 3140,
  2377. CULTURAL_EXCHANGE: 3060,
  2378. ANATOMY: 3070,
  2379. OPTICS: 3080,
  2380. EXPERIMENTS: 3081,
  2381. MECHANICAL_PEN: 3090,
  2382. BIRDS_FLIGHT: 3100,
  2383. LETTER_CHUTE: 3110,
  2384. STATE_RELIGION: 3160,
  2385. PRESSURE_CHAMBER: 3120,
  2386. ARCHIMEDEAN_PRINCIPLE: 3130,
  2387. SCIENTIFIC_FUTURE: 3999,
  2388. },
  2389. Military: {
  2390. DRY_DOCKS: 4010,
  2391. MAPS: 4020,
  2392. PROFESSIONAL_ARMY: 4030,
  2393. SEIGE: 4040,
  2394. CODE_OF_HONOR: 4050,
  2395. BALLISTICS: 4060,
  2396. LAW_OF_THE_LEVEL: 4070,
  2397. GOVERNOR: 4080,
  2398. PYROTECHNICS: 4130,
  2399. LOGISTICS: 4090,
  2400. GUNPOWDER: 4100,
  2401. ROBOTICS: 4110,
  2402. CANNON_CASTING: 4120,
  2403. MILITARISTIC_FUTURE: 4999,
  2404. },
  2405. },
  2406. Government: {
  2407. ANARCHY: 'anarchie',
  2408. IKACRACY: 'ikakratie',
  2409. ARISTOCRACY: 'aristokratie',
  2410. DICTATORSHIP: 'diktatur',
  2411. DEMOCRACY: 'demokratie',
  2412. NOMOCRACY: 'nomokratie',
  2413. OLIGARCHY: 'oligarchie',
  2414. TECHNOCRACY: 'technokratie',
  2415. THEOCRACY: 'theokratie',
  2416. },
  2417. TradeGoodOrdinals: {
  2418. WINE: 1,
  2419. MARBLE: 2,
  2420. GLASS: 3,
  2421. SULFUR: 4,
  2422. },
  2423. Time: {
  2424. SECONDS_PER_HOUR: 3600,
  2425. SECONDS_PER_MINUTE: 60,
  2426. MILLIS_PER_DAY: 1000 * 60 * 60 * 24,
  2427. MILLIS_PER_HOUR: 1000 * 60 * 60,
  2428. MILLIS_PER_SECOND: 1000,
  2429. MILLIS_PER_MINUTE: 60000,
  2430. MINUTES_PER_DAY: 24 * 60,
  2431. MINUTES_PER_HOUR: 60,
  2432. HOURS_PER_DAY: 24,
  2433. HOURS_PER_WEEK: 24 * 7,
  2434. SAFE_TIME_DELTA: 1000,
  2435. INITIAL_PAGE_LOAD_DELTA: 2000,
  2436. },
  2437. GamePlay: {
  2438. BUILDING_SPOTS: 19,
  2439. HAPPINESS_PER_CULTURAL_GOOD: 50,
  2440. HAPPINESS_PER_WINE_SERVING_LEVEL: 60,
  2441. BASE_RESOURCE_PROTECTION: 100,
  2442. RESOURCES_PER_TRANSPORT: 500,
  2443. RESOURCE_PROTECTION_WAREHOUSE: 480,
  2444. RESOURCE_PROTECTION_WAREHOUSE_INACTIVE: 80,
  2445. },
  2446. BaseView: {
  2447. ISLAND: 'island',
  2448. CITY: 'city',
  2449. WORLD: 'worldview',
  2450. },
  2451. Military: {
  2452. // Army
  2453. HOPLITE: 'phalanx',
  2454. STEAM_GIANT: 'steamgiant',
  2455. SPEARMAN: 'spearman',
  2456. SWORDSMAN: 'swordsman',
  2457. SLINGER: 'slinger',
  2458. ARCHER: 'archer',
  2459. GUNNER: 'marksman',
  2460. BATTERING_RAM: 'ram',
  2461. CATAPULT: 'catapult',
  2462. MORTAR: 'mortar',
  2463. GYROCOPTER: 'gyrocopter',
  2464. BALLOON_BOMBADIER: 'bombardier',
  2465. COOK: 'cook',
  2466. DOCTOR: 'medic',
  2467. ARMY: 'army',
  2468. // Navy
  2469. RAM_SHIP: 'ship_ram',
  2470. FLAME_THROWER: 'ship_flamethrower',
  2471. STEAM_RAM: 'ship_steamboat',
  2472. BALLISTA_SHIP: 'ship_ballista',
  2473. CATAPULT_SHIP: 'ship_catapult',
  2474. MORTAR_SHIP: 'ship_mortar',
  2475. SUBMARINE: 'ship_submarine',
  2476. PADDLE_SPEED_SHIP: 'ship_paddlespeedship',
  2477. BALLOON_CARRIER: 'ship_ballooncarrier',
  2478. TENDER: 'ship_tender',
  2479. ROCKET_SHIP: 'ship_rocketship',
  2480. NAVY: 'navy',
  2481. },
  2482. UnitData: {
  2483. spearman: {
  2484. minimumBuildingLevelToBuild: 1,
  2485. baseBuildTime: 60,
  2486. isArmy: true,
  2487. speed: 60,
  2488. cargoSize: 3,
  2489. },
  2490. slinger: {
  2491. minimumBuildingLevelToBuild: 2,
  2492. baseBuildTime: 90,
  2493. isArmy: true,
  2494. speed: 60,
  2495. cargoSize: 3,
  2496. },
  2497. ram: {
  2498. minimumBuildingLevelToBuild: 3,
  2499. baseBuildTime: 600,
  2500. isArmy: true,
  2501. speed: 40,
  2502. cargoSize: 30,
  2503. },
  2504. phalanx: {
  2505. minimumBuildingLevelToBuild: 4,
  2506. baseBuildTime: 300,
  2507. isArmy: true,
  2508. speed: 60,
  2509. cargoSize: 5,
  2510. },
  2511. cook: {
  2512. minimumBuildingLevelToBuild: 5,
  2513. baseBuildTime: 1200,
  2514. isArmy: true,
  2515. speed: 40,
  2516. cargoSize: 20,
  2517. },
  2518. swordsman: {
  2519. minimumBuildingLevelToBuild: 6,
  2520. baseBuildTime: 180,
  2521. isArmy: true,
  2522. speed: 60,
  2523. cargoSize: 3,
  2524. },
  2525. archer: {
  2526. minimumBuildingLevelToBuild: 7,
  2527. baseBuildTime: 240,
  2528. isArmy: true,
  2529. speed: 60,
  2530. cargoSize: 5,
  2531. },
  2532. catapult: {
  2533. minimumBuildingLevelToBuild: 8,
  2534. baseBuildTime: 1800,
  2535. isArmy: true,
  2536. speed: 40,
  2537. cargoSize: 30,
  2538. },
  2539. medic: {
  2540. minimumBuildingLevelToBuild: 9,
  2541. baseBuildTime: 1200,
  2542.  
  2543. isArmy: true,
  2544. speed: 60,
  2545. cargoSize: 10,
  2546. },
  2547. gyrocopter: {
  2548. minimumBuildingLevelToBuild: 10,
  2549. baseBuildTime: 900,
  2550. isArmy: true,
  2551. speed: 80,
  2552. cargoSize: 15,
  2553. },
  2554. bombardier: {
  2555. minimumBuildingLevelToBuild: 11,
  2556. baseBuildTime: 1800,
  2557. isArmy: true,
  2558. speed: 20,
  2559. cargoSize: 30,
  2560. },
  2561. steamgiant: {
  2562. minimumBuildingLevelToBuild: 12,
  2563. baseBuildTime: 900,
  2564. isArmy: true,
  2565. speed: 40,
  2566. cargoSize: 15,
  2567. },
  2568. marksman: {
  2569. minimumBuildingLevelToBuild: 13,
  2570. baseBuildTime: 600,
  2571. isArmy: true,
  2572. speed: 60,
  2573. cargoSize: 5,
  2574. },
  2575. mortar: {
  2576. minimumBuildingLevelToBuild: 14,
  2577. baseBuildTime: 2400,
  2578. isArmy: true,
  2579. speed: 40,
  2580. cargoSize: 30,
  2581. },
  2582. barbarian: {
  2583. minimumBuildingLevelToBuild: 1,
  2584. baseBuildTime: 1,
  2585. isArmy: true,
  2586. speed: 40,
  2587. cargoSize: 5,
  2588. },
  2589. ship_ram: {
  2590. minimumBuildingLevelToBuild: 1,
  2591. baseBuildTime: 2400,
  2592. isArmy: false,
  2593. speed: 40,
  2594. cargoSize: 0,
  2595. },
  2596. ship_flamethrower: {
  2597. minimumBuildingLevelToBuild: 4,
  2598. baseBuildTime: 1800,
  2599. isArmy: false,
  2600. speed: 40,
  2601. cargoSize: 0,
  2602. },
  2603. ship_submarine: {
  2604. minimumBuildingLevelToBuild: 19,
  2605. baseBuildTime: 3600,
  2606. isArmy: false,
  2607. speed: 40,
  2608. cargoSize: 0,
  2609. },
  2610. ship_ballista: {
  2611. minimumBuildingLevelToBuild: 3,
  2612. baseBuildTime: 3000,
  2613. isArmy: false,
  2614. speed: 40,
  2615. cargoSize: 0,
  2616. },
  2617. ship_catapult: {
  2618. minimumBuildingLevelToBuild: 3,
  2619. baseBuildTime: 3000,
  2620. isArmy: false,
  2621. speed: 40,
  2622. cargoSize: 0,
  2623. },
  2624. ship_mortar: {
  2625. minimumBuildingLevelToBuild: 17,
  2626. baseBuildTime: 3000,
  2627. isArmy: false,
  2628. speed: 30,
  2629. cargoSize: 0,
  2630. },
  2631. ship_steamboat: {
  2632. minimumBuildingLevelToBuild: 15,
  2633. baseBuildTime: 2400,
  2634. isArmy: false,
  2635. speed: 40,
  2636. cargoSize: 0,
  2637. },
  2638. ship_rocketship: {
  2639. minimumBuildingLevelToBuild: 11,
  2640. baseBuildTime: 3600,
  2641. isArmy: false,
  2642. speed: 30,
  2643. cargoSize: 0,
  2644. },
  2645. ship_paddlespeedship: {
  2646. minimumBuildingLevelToBuild: 13,
  2647. baseBuildTime: 1800,
  2648. isArmy: false,
  2649. speed: 60,
  2650. cargoSize: 0,
  2651. },
  2652. ship_ballooncarrier: {
  2653. minimumBuildingLevelToBuild: 7,
  2654. baseBuildTime: 3900,
  2655. isArmy: false,
  2656. speed: 20,
  2657. cargoSize: 0,
  2658. },
  2659. ship_tender: {
  2660. minimumBuildingLevelToBuild: 9,
  2661. baseBuildTime: 2400,
  2662. isArmy: false,
  2663. speed: 30,
  2664. cargoSize: 0,
  2665. },
  2666. },
  2667.  
  2668. UnitIds: {
  2669. 301: 'slinger',
  2670. 302: 'swordsman',
  2671. 303: 'phalanx',
  2672. 304: 'marksman',
  2673. 305: 'mortar',
  2674. 306: 'catapult',
  2675. 307: 'ram',
  2676. 308: 'steamgiant',
  2677. 309: 'bombardier',
  2678. 310: 'cook',
  2679. 311: 'medic',
  2680. 312: 'gyrocopter',
  2681. 313: 'archer',
  2682. 315: 'spearman',
  2683. 316: 'barbarian',
  2684. 210: 'ship_ram',
  2685. 211: 'ship_flamethrower',
  2686. 212: 'ship_submarine',
  2687. 213: 'ship_ballista',
  2688. 214: 'ship_catapult',
  2689. 215: 'ship_mortar',
  2690. 216: 'ship_steamboat',
  2691. 217: 'ship_rocketship',
  2692. 218: 'ship_paddlespeedship',
  2693. 219: 'ship_ballooncarrier',
  2694. 220: 'ship_tender',
  2695. },
  2696. IkariamAjaxResponseType: {
  2697. RELOAD: 'reload',
  2698. PROVIDE_FEEDBACK: 'provideFeedback',
  2699. QUEST_DATA: 'questData',
  2700. UPDATE_GLOBAL_DATA: 'updateGlobalData',
  2701. UPDATE_TEMPLATE_DATA: 'updateTemplateData',
  2702. UPDATE_BACKGROUND_DATA: 'updateBackgroundData',
  2703. CLOSE_VIEW: 'closeView',
  2704. CHANGE_VIEW: 'changeView',
  2705. ADD_WINDOW: 'addWindow',
  2706. CREATE_VIEW: 'createView',
  2707. EVAL_SCRIPT: 'evalScript',
  2708. },
  2709. CityType: {
  2710. OWN: 'ownCity',
  2711. DEPLOYMENT: 'deployedCities',
  2712. OCCUPATION: 'occupiedCities',
  2713. },
  2714. View: {
  2715. CITY_DETAIL: 'cityDetails',
  2716. CITY_MILITARY: 'cityMilitary',
  2717. RELATED_CITIES: 'relatedCities',
  2718. ACADEMY: 'academy',
  2719. PALACE: 'palace',
  2720. MUSEUM: 'museum',
  2721. ASSIGN_CULTURAL_POSSESSIONS: 'culturalPossessions_assign',
  2722. TOWN_HALL: 'townHall',
  2723. TEMPLE: 'temple',
  2724. RESEARCH_ADVISOR: 'researchAdvisor',
  2725. FINANCES: 'finances',
  2726. BARRACKS: 'barracks',
  2727. SHIPYARD: 'shipyard',
  2728. PIRATE_FORTRESS: 'pirateFortress',
  2729. MILITARY_ADVISOR: 'militaryAdvisor',
  2730. MILITARY_ADVISOR_REPORT: 'militaryAdvisorReportView',
  2731. PREMIUM: 'premium',
  2732. TRANSPORT: 'transport',
  2733. DEPLOY: 'deployment',
  2734. BRANCH_OFFICE: 'branchOffice',
  2735. BLACK_MARKET: 'blackMarket',
  2736. TAKE_OFFER: 'takeOffer',
  2737. RESOURCE: 'resource',
  2738. TRADE_GOOD: 'tradegood',
  2739. ABOLISH_CITY: 'abolishCity',
  2740. HIDEOUT: 'safehouse',
  2741. PILLAGE: 'plunder',
  2742. BLOCKADE: 'blockade',
  2743. SEND_SPY: 'sendSpy',
  2744. SPY_MISSION: 'spyMissions',
  2745. HIGH_SCORE: 'highscore',
  2746. ALLIANCE_PAGE: 'allyPage',
  2747. OCCUPY: 'occupy',
  2748. COLONIZE: 'colonize',
  2749. },
  2750.  
  2751. PlayerState: {
  2752. INACTIVE: 'inactive',
  2753. NORMAL: '',
  2754. VACATION: 'vacation',
  2755. NEW: 'noob',
  2756. },
  2757.  
  2758. CombatType: {
  2759. BLOCKADE: 'blockade',
  2760. PILLAGE: 'plunder',
  2761. },
  2762. };
  2763. var EmpireData = function() {
  2764. function Military(city) {
  2765. this._ikaToolsType = 'military';
  2766. this.lastArmyUpdate = 0;
  2767. this.lastNavyUpdate = 0;
  2768. this.present = new MilitaryUnits();
  2769. this.armyTrainingBatches = [];
  2770. this.navyTrainingBatches = [];
  2771. this._setCity(city);
  2772. }
  2773. $.extend(Military.prototype, {
  2774. _setCity: function setCity(city) {
  2775. if (city) {
  2776. this.city = Utils.fixedFunction(city);
  2777. this._startArmyTrainingTimers();
  2778. this._startNavyTrainingTimers();
  2779. }
  2780. },
  2781. _startTrainingTimers: function startTrainingTimers(batches) {
  2782. var military = this;
  2783. while (batches.length > 0 && batches[0].completionTime <= View.gameTimeNow()) {
  2784. var batch = batches.shift();
  2785. if ((batch.type == Constants.Military.ARMY &&
  2786. batch.getCompletionTime() > this.lastArmyUpdate) ||
  2787. (batch.type == Constants.Military.NAVY &&
  2788. batch.getCompletionTime() > this.lastNavyUpdate)) {
  2789. military.present._increment(batch.getUnits());
  2790. }
  2791. }
  2792. $.each(batches, function startTrainingBatchTimers(index, batch) {
  2793. if (batch.completionEvent) {
  2794. batch.completionEvent();
  2795. }
  2796. batch.completionEvent = militaryChangedEvent().scheduleSend(
  2797. 'MilitaryTrainingComplete[' + military.city().getId() + ']',
  2798. batch.getCompletionTime() - View.gameTimeNow() +
  2799. Constants.Time.SAFE_TIME_DELTA,
  2800. function militaryTrainingComplete() {
  2801. military.present._increment(batches.shift().getUnits());
  2802. empireData.saveAsync();
  2803. },
  2804. [{
  2805. city: military.city(),
  2806. military: military,
  2807. type: 'training_complete',
  2808. }]);
  2809. });
  2810. },
  2811. _startArmyTrainingTimers: function startArmyTrainingTimers() {
  2812. this._startTrainingTimers(this.armyTrainingBatches);
  2813. },
  2814. _startNavyTrainingTimers: function startNavyTrainingTimers() {
  2815. this._startTrainingTimers(this.navyTrainingBatches);
  2816. },
  2817. _updatePresent: function updatePresent(unit, count) {
  2818. return this.present._setCount(unit, count);
  2819. },
  2820. _markPresentUpdated: function markPresentUpdated(army, navy) {
  2821. if (army || army === undefined) {
  2822. this.lastArmyUpdate = View.gameTimeNow();
  2823. }
  2824. if (navy || navy === undefined) {
  2825. this.lastNavyUpdate = View.gameTimeNow();
  2826. }
  2827. },
  2828. _setArmyTrainingBatches: function setArmyTrainingBatches(batches) {
  2829.  
  2830. $.each(this.armyTrainingBatches, function cancelTrainingBatchTimer(index, batch) {
  2831. if (batch.completionEvent) {
  2832. batch.completionEvent();
  2833. }
  2834. });
  2835. this.armyTrainingBatches = batches;
  2836. this._startArmyTrainingTimers();
  2837. },
  2838. _setNavyTrainingBatches: function setNavyTrainingBatches(batches) {
  2839. $.each(this.navyTrainingBatches, function cancelTrainingBatchTimer(index, batch) {
  2840. if (batch.completionEvent) {
  2841. batch.completionEvent();
  2842. }
  2843. });
  2844.  
  2845. this.navyTrainingBatches = batches;
  2846.  
  2847. this._startNavyTrainingTimers();
  2848. },
  2849. _increment: function increment(units) {
  2850. this.present._increment(units);
  2851. },
  2852. _decrement: function decrement(units) {
  2853. this.present._decrement(units);
  2854. },
  2855. _clear: function clear() {
  2856. this.present._clear();
  2857. this.armyTrainingBatches = [];
  2858. this.navyTrainingBatches = [];
  2859. },
  2860. getTrainingBatches: function getTrainingBatches(batches) {
  2861. return $.merge($.merge([], this.armyTrainingBatches), this.navyTrainingBatches);
  2862. },
  2863. getPresent: function getPresent() {
  2864. return this.present;
  2865. },
  2866. });
  2867. function MilitaryUnits() {
  2868. this._ikaToolsType = 'militaryUnits';
  2869. this.units = {};
  2870. }
  2871. $.extend(MilitaryUnits.prototype, {
  2872. _setCount: function setCount(unit, count) {
  2873. var oldCount = this.units[unit];
  2874. this.units[unit] = count;
  2875. return oldCount != count;
  2876. },
  2877. _increment: function increment(units) {
  2878. $.each(units.units, function(unit, count) {
  2879. this._setCount(unit, (this.getCount(unit) || 0) + count);
  2880. }.bind(this));
  2881. },
  2882. _decrement: function decrement(units) {
  2883. $.each(units.units, function(unit, count) {
  2884. this._setCount(unit, Math.max(0, (this.getCount(unit) || 0) - count));
  2885. }.bind(this));
  2886. },
  2887. _clear: function clear() {
  2888. this.units = {};
  2889. },
  2890. getCount: function getCount(unit) {
  2891. return this.units[unit];
  2892. },
  2893. getCounts: function getCounts() {
  2894. return this.units;
  2895. },
  2896. getCargoSize: function getCargoSize() {
  2897. var cargoSize = 0;
  2898. $.each(this.units, function addCargoSize(unit, count) {
  2899. cargoSize += Constants.UnitData[unit].cargoSize * count;
  2900. });
  2901. return cargoSize;
  2902. },
  2903. });
  2904. function TrainingBatch(type, completionTime, units) {
  2905. this._ikaToolsType = 'trainingBatch';
  2906. this.type = type;
  2907. this.units = units;
  2908. this.completionTime = completionTime;
  2909. }
  2910. $.extend(TrainingBatch.prototype, {
  2911. getUnits: function getUnits() {
  2912. return this.units;
  2913. },
  2914. getCompletionTime: function getCompletionTime() {
  2915. return this.completionTime;
  2916. },
  2917. _getType: function getType() {
  2918. return this.type;
  2919. },
  2920. });
  2921. function City(id, type) {
  2922. this._ikaToolsType = 'city';
  2923. if (id) {
  2924. this.id = id;
  2925. this.type = type;
  2926. this.level = 0;
  2927.  
  2928. this.resources = {
  2929. wood: new City.Resource(this),
  2930. wine: new City.Resource(this),
  2931. marble: new City.Resource(this),
  2932. glass: new City.Resource(this),
  2933. sulfur: new City.Resource(this),
  2934. };
  2935.  
  2936. this.buildings = new Array(Constants.GamePlay.BUILDING_SPOTS);
  2937. for (var i = 0; i < Constants.GamePlay.BUILDING_SPOTS; i++) {
  2938. this.buildings[i] = new City.Building(this);
  2939. }
  2940. this.military = new Military();
  2941. this.actionPoints = 0;
  2942. this.scientists = 0;
  2943. this.culturalGoods = 0;
  2944. this.priests = 0;
  2945. this.tavernWineLevel = 0;
  2946. this.population = undefined;
  2947. this.resourceUpdateTime = 0;
  2948. this.islandCoordinates = undefined;
  2949. }
  2950. }
  2951. $.extend(City.prototype, {
  2952. _postLoad: function postLoad() {
  2953. while (this.buildings.length < Constants.GamePlay.BUILDING_SPOTS) {
  2954. this.buildings.push(new City.Building(this));
  2955. }
  2956. for (var i = 0; i < Constants.GamePlay.BUILDING_SPOTS; i++) {
  2957. this.buildings[i]._setCity(this);
  2958. }
  2959. if (this.isOwn()) {
  2960. this.resources[Constants.Resources.WOOD]._setCity(this);
  2961. this.resources[Constants.Resources.WINE]._setCity(this);
  2962. this.resources[Constants.Resources.MARBLE]._setCity(this);
  2963. this.resources[Constants.Resources.GLASS]._setCity(this);
  2964. this.resources[Constants.Resources.SULFUR]._setCity(this);
  2965. }
  2966. this.military._setCity(this);
  2967. },
  2968. _updateFromGlobalData: function _updateFromGlobalData(data, correctWineConsumption) {
  2969. var changes = [];
  2970. this._updateActionPoints(data.maxActionPoints, changes);
  2971. if (this.isOwn()) {
  2972. var wineConsumption = data.wineSpendings;
  2973. var baseWineConsumption = data.wineSpendings;
  2974. var winePress = this.getBuildingByType(Constants.Buildings.WINE_PRESS);
  2975. if (correctWineConsumption) {
  2976. if (winePress) {
  2977. wineConsumption = wineConsumption * (100 - winePress.getLevel()) / 100;
  2978. }
  2979. } else {
  2980. if (winePress) {
  2981. baseWineConsumption = Math.floor(
  2982. wineConsumption / (100 - winePress.getLevel()) * 100);
  2983. }
  2984. }
  2985. var use = Constants.BuildingData[Constants.Buildings.TAVERN].wineUse;
  2986. for (var i = 0; i < 48; i++) {
  2987. if (use[i] >= baseWineConsumption) {
  2988. this._updateTavernWineLevel(i, changes);
  2989. break;
  2990. }
  2991. }
  2992. this._updateResources(data.currentResources,
  2993. data.maxResources,
  2994. data.resourceProduction,
  2995. data.tradegoodProduction,
  2996. wineConsumption / Constants.Time.SECONDS_PER_HOUR,
  2997. changes);
  2998. }
  2999. raiseResourcesChanged(changes);
  3000. },
  3001. _updateFromBackgroundData: function _updateFromBackgroundData(data) {
  3002. this.islandId = parseInt(data.islandId);
  3003. if (this.isOwn()) {
  3004. this._updateBuildings(data.position);
  3005. }
  3006. },
  3007. _updateBuildings: function _updateBuildings(buildingsData) {
  3008. var changes = [];
  3009. for (var i = 0; i < Constants.GamePlay.BUILDING_SPOTS; i++) {
  3010. var building = this.buildings[i];
  3011. if (buildingsData[i]) {
  3012. if (building._update(i, buildingsData[i])) {
  3013. changes.push({
  3014. city: this,
  3015. building: building,
  3016. type: Constants.BuildingEventType.DATA_REFRESH,
  3017. });
  3018. }
  3019. }
  3020. }
  3021. this.level = this.buildings[0].getLevel();
  3022. raiseBuildingsChanged(changes);
  3023. },
  3024. _updateResources: function updateResources(
  3025. currentInfo, maxInfo, woodProduction, resourceProduction, wineConsumption,
  3026. changedAccumulator) {
  3027. this._updateResource(Constants.Resources.WOOD,
  3028. changedAccumulator,
  3029. currentInfo["resource"],
  3030.  
  3031. woodProduction);
  3032. this._updateResource(Constants.Resources.WINE,
  3033. changedAccumulator,
  3034. currentInfo["1"],
  3035. this.getTradeGoodType() == Constants.Resources.WINE ?
  3036. resourceProduction : undefined,
  3037. wineConsumption);
  3038. this._updateResource(Constants.Resources.MARBLE,
  3039. changedAccumulator,
  3040. currentInfo["2"],
  3041. this.getTradeGoodType() == Constants.Resources.MARBLE ?
  3042. resourceProduction : undefined);
  3043. this._updateResource(Constants.Resources.GLASS,
  3044. changedAccumulator,
  3045. currentInfo["3"],
  3046. this.getTradeGoodType() == Constants.Resources.GLASS ?
  3047. resourceProduction : undefined);
  3048. this._updateResource(Constants.Resources.SULFUR,
  3049. changedAccumulator,
  3050. currentInfo["4"],
  3051. this.getTradeGoodType() == Constants.Resources.SULFUR ?
  3052. resourceProduction : undefined);
  3053. this._updatePopulation(currentInfo.population, changedAccumulator);
  3054. this.resourceUpdateTime = View.gameTimeNow();
  3055. },
  3056. _updateResource: function updateResource(
  3057. name, changedAccumulator, current, max, production, consumption) {
  3058. var resource = this.resources[name];
  3059. if (resource._update(current, max, production, consumption)) {
  3060. changedAccumulator.push({
  3061. city: this,
  3062. type: name,
  3063. value: resource,
  3064. });
  3065. }
  3066. },
  3067. _incrementResource: function incrementResource(name, changedAccumulator, delta) {
  3068. var resource = this.resources[name];
  3069. if (delta) {
  3070. resource._increment(delta);
  3071. changedAccumulator.push({
  3072. city: this,
  3073. type: name,
  3074. value: resource,
  3075. });
  3076. }
  3077. },
  3078. _updateActionPoints: function updateActionPoints(actionPoints, changedAccumulator) {
  3079. if (this.actionPoints != actionPoints) {
  3080. changedAccumulator.push({
  3081. city: this,
  3082. type: Constants.Resources.ACTION_POINTS,
  3083. value: actionPoints,
  3084. });
  3085. }
  3086. this.actionPoints = actionPoints;
  3087. },
  3088. _updateActionPointsBy: function updateActionPointsBy(delta, changedAccumulator) {
  3089. this._updateActionPoints(
  3090. Math.max(0, Math.min(this.actionPoints + delta, this.getMaxActionPoints())),
  3091. changedAccumulator);
  3092. },
  3093. _updateScientists: function updateScientists(scientists, changedAccumulator) {
  3094. if (this.scientists != scientists) {
  3095. changedAccumulator.push({
  3096. city: this,
  3097. type: Constants.Resources.SCIENTISTS,
  3098. value: scientists,
  3099. });
  3100. }
  3101. this.scientists = scientists;
  3102. },
  3103. _updateTavernWineLevel: function updateTavernWineLevel(level, changedAccumulator) {
  3104. if (this.tavernWineLevel != level) {
  3105. changedAccumulator.push({
  3106. city: this,
  3107. type: Constants.Resources.TAVERN_WINE_LEVEL,
  3108. value: level,
  3109. });
  3110. }
  3111. this.tavernWineLevel = level;
  3112. },
  3113. _updateCulturalGoods: function updateCulturalGoods(culturalGoods, changedAccumulator) {
  3114. if (this.culturalGoods != culturalGoods) {
  3115. changedAccumulator.push({
  3116. city: this,
  3117. type: Constants.Resources.CULTURAL_GOODS,
  3118. value: culturalGoods,
  3119. });
  3120. }
  3121. this.culturalGoods = culturalGoods;
  3122. },
  3123. _updatePopulation: function updatePopulation(population, changedAccumulator) {
  3124. if (Math.abs(this.getPopulationData().population - population) >= 1) {
  3125. changedAccumulator.push({
  3126. city: this,
  3127. type: Constants.Resources.POPULATION,
  3128. value: population,
  3129. });
  3130. }
  3131. this.population = population;
  3132. },
  3133. _updatePriests: function updatePriests(priests, changedAccumulator) {
  3134. if (this.priests != priests) {
  3135. changedAccumulator.push({
  3136. city: this,
  3137. type: Constants.Resources.PRIESTS,
  3138. value: priests,
  3139. });
  3140. }
  3141. this.priests = priests;
  3142. },
  3143. getLastResourceUpdate: function getLastResourceUpdate() {
  3144. return this.resourceUpdateTime;
  3145. },
  3146. getTimeSinceResourceUpdate: function getTimeSinceLastResourceUpdate() {
  3147. return View.gameTimeNow() - this.resourceUpdateTime;
  3148. },
  3149. isOwn: function isOwn() {
  3150. return this.type == Constants.CityType.OWN;
  3151. },
  3152. isDeployment: function isDeployment() {
  3153. return this.type == Constants.CityType.DEPLOYMENT;
  3154. },
  3155. isOccupation: function isOccupation() {
  3156. return this.type == Constants.CityType.OCCUPATION;
  3157. },
  3158. getType: function getType() {
  3159. return this.type;
  3160. },
  3161. getBuildingByPosition: function getBuildingByPosition(position) {
  3162. return this.buildings[position];
  3163. },
  3164. getBuildingsByType: function getBuildingsByType(type) {
  3165. return this.buildings.filter(function buildingsFilter(building) {
  3166. return building.getType() == type;
  3167. });
  3168. },
  3169. getBuildingByType: function getBuildingByType(type) {
  3170. for (var i = 0; i < Constants.GamePlay.BUILDING_SPOTS; i++) {
  3171. var building = this.buildings[i];
  3172. if (building && building.getType() == type) {
  3173. return building;
  3174. }
  3175. }
  3176. return null;
  3177. },
  3178. getBuildings: function getBuildings() {
  3179. return this.buildings;
  3180. },
  3181. getMilitary: function getMilitary() {
  3182. return this.military;
  3183. },
  3184. getId: function getId() {
  3185. return this.id;
  3186. },
  3187. getName: function getName() {
  3188. return this.name;
  3189. },
  3190. getIslandId: function getIslandId() {
  3191. return this.islandId;
  3192. },
  3193. getTradeGoodType: function getTradeGoodType() {
  3194. return this.tradeGoodType;
  3195. },
  3196. getActionPoints: function getActionPoints() {
  3197. return this.actionPoints;
  3198. },
  3199. getMaxActionPoints: function getMaxActionPoints() {
  3200. return 3 + Math.floor(this.level / 4) - (this.isOwn() ? 0 : 2);
  3201. },
  3202. getCulturalGoods: function getCulturalGoods() {
  3203. return this.culturalGoods;
  3204. },
  3205. getTavernWineLevel: function getTavernWineLevel() {
  3206. return this.tavernWineLevel;
  3207. },
  3208. getPopulationData: function getPopulationData() {
  3209. var max = 0;
  3210. var happiness = 196;
  3211. var townHall = this.getBuildingByType(Constants.Buildings.TOWN_HALL);
  3212. var temple = this.getBuildingByType(Constants.Buildings.TEMPLE);
  3213. var palace = this.getBuildingByType(Constants.Buildings.PALACE);
  3214. var tavern = this.getBuildingByType(Constants.Buildings.TAVERN);
  3215. var museum = this.getBuildingByType(Constants.Buildings.MUSEUM);
  3216. var civData = getCivilizationData();
  3217. if (townHall) {
  3218. // Formula from http://ikariam.wikia.com/wiki/Citizen
  3219. max += Math.floor(10 * Math.pow(townHall.getLevel(), 1.5)) * 2 + 40;
  3220.  
  3221. }
  3222. if (civData.hasResearched(Constants.Research.Economy.HOLIDAY)) {
  3223. max += 50;
  3224. happiness += 25;
  3225. }
  3226. if (civData.hasResearched(Constants.Research.Economy.ECONOMIC_FUTURE)) {
  3227. var level = civData.getResearchLevel(Constants.Research.Economy.ECONOMIC_FUTURE);
  3228. max += 20 * level;
  3229. happiness += 10 * level;
  3230. }
  3231. if (palace) {
  3232. if (civData.hasResearched(Constants.Research.Science.WELL_CONSTRUCTION)) {
  3233. max += 50;
  3234. happiness += 50;
  3235. }
  3236. if (civData.hasResearched(Constants.Research.Economy.UTOPIA)) {
  3237. max += 200;
  3238. happiness += 200;
  3239. }
  3240. }
  3241. if (tavern) {
  3242. happiness += 12 * tavern.getLevel();
  3243. }
  3244. happiness += Constants.GamePlay.HAPPINESS_PER_WINE_SERVING_LEVEL *
  3245. this.getTavernWineLevel();
  3246. if (museum) {
  3247. happiness += 20 * museum.getLevel();
  3248. }
  3249. happiness += Constants.GamePlay.HAPPINESS_PER_CULTURAL_GOOD * this.getCulturalGoods();
  3250. var government = civData.getGovernment();
  3251. if (government == Constants.Government.DEMOCRACY) {
  3252. happiness += 75;
  3253. } else if (government == Constants.Government.DICTATORSHIP) {
  3254. happiness -= 75;
  3255. } else if (government == Constants.Government.THEOCRACY) {
  3256. if (temple) {
  3257. happiness += Math.min(150, this.getPriests() * 5 / max * 100 * 2);
  3258. } else {
  3259. happiness -= 20;
  3260. }
  3261. }
  3262. happiness = happiness * (1 - this.getCorruption());
  3263. var happinessDelta = happiness - this.population;
  3264. var currentPopulation = this.population +
  3265. happinessDelta * (1 - Math.pow(Math.E,
  3266. -(this.getTimeSinceResourceUpdate() / 50 / Constants.Time.MILLIS_PER_HOUR)));
  3267. var population = Math.min(currentPopulation, max);
  3268. return {
  3269. population: population,
  3270. max: max,
  3271. happiness: happiness - population,
  3272. growth: max == population && happiness > (population - 1)
  3273. ? 0 : (happiness - population) / 50,
  3274. };
  3275. },
  3276. getCorruption: function getCorruption() {
  3277. var palace = this.getBuildingByType(Constants.Buildings.GOVERNORS_RESIDENCE) ||
  3278. this.getBuildingByType(Constants.Buildings.PALACE);
  3279. var level = palace ? palace.getLevel() : 0;
  3280. var corruption = 1 - (level + 1) / getOwnCities().length;
  3281. var government = getCivilizationData().getGovernment();
  3282. if (government == Constants.Government.ARISTOCRACY ||
  3283. government == Constants.Government.OLIGARCHY) {
  3284. corruption += .03;
  3285. } else if (government == Constants.Government.NOMOCRACY) {
  3286. corruption -= .05;
  3287. } else if (government == Constants.Government.ANARCHY) {
  3288. corruption += .25;
  3289. }
  3290. return Math.min(Math.max(corruption, 0), 1);
  3291. },
  3292. getScientists: function getScientists() {
  3293. return this.scientists;
  3294. },
  3295. getResearch: function getResearch() {
  3296. var civData = getCivilizationData();
  3297. var multiplier = 1.0;
  3298. multiplier += civData.hasResearched(
  3299. IkaTools.Constants.Research.Science.PAPER) ? .02 : 0;
  3300. multiplier += civData.hasResearched(
  3301. IkaTools.Constants.Research.Science.INK) ? .04 : 0;
  3302. multiplier += civData.hasResearched(
  3303. IkaTools.Constants.Research.Science.MECHANICAL_PEN) ? .08 : 0;
  3304. multiplier += (civData.getResearchLevel(
  3305. IkaTools.Constants.Research.Science.SCIENTIFIC_FUTURE) || 0) * .02;
  3306. multiplier -= this.getCorruption();
  3307. return this.scientists * multiplier;
  3308. },
  3309.  
  3310. getResource: function getResource(resourceName) {
  3311. return this.resources[resourceName];
  3312. },
  3313. getResourceCapacity: function getResourceMaximumCapacity() {
  3314. var total = 1500;
  3315. var safe = 100;
  3316. $.each(this.getBuildingsByType(Constants.Buildings.WAREHOUSE), function(i, building) {
  3317. total += 8000 * building.getLevel();
  3318. safe += 480 * building.getLevel();
  3319. });
  3320. var dump = this.getBuildingByType(Constants.Buildings.DUMP);
  3321. if (dump) {
  3322. total += 32000 * dump.getLevel();
  3323. }
  3324. var civilizationData = getCivilizationData();
  3325. if (civilizationData.isPremiumFeatureEnabled(
  3326. Constants.PremiumFeatures.DOUBLED_STORAGE_CAPACITY)) {
  3327. total *= 2;
  3328. }
  3329. if (civilizationData.isPremiumFeatureEnabled(
  3330. Constants.PremiumFeatures.DOUBLED_SAFE_CAPACITY)) {
  3331. safe *= 2;
  3332. }
  3333. return {
  3334. maximum: total,
  3335. safe: safe,
  3336. };
  3337. },
  3338. getIslandCoordinates: function getIslandCoordinates() {
  3339. return this.islandCoordinates;
  3340. },
  3341. getLoadingSpeed: function getLoadingSpeed() {
  3342. var speed = 10;
  3343. var ports = this.getBuildingsByType(Constants.Buildings.TRADING_PORT);
  3344. if (ports[0]) {
  3345. speed = Constants.BuildingData[Constants.Buildings.TRADING_PORT]
  3346. .loadingSpeed[ports[0].getLevel()];
  3347. }
  3348. if (ports[1]) {
  3349. speed += Constants.BuildingData[Constants.Buildings.TRADING_PORT]
  3350. .loadingSpeed[ports[1].getLevel()];
  3351. }
  3352. return speed / Constants.Time.SECONDS_PER_MINUTE;
  3353. },
  3354. getPriests: function getPriests() {
  3355. return this.priests;
  3356. },
  3357. });
  3358. City.Resource = function City_Resource(city) {
  3359. this._ikaToolsType = 'cityResource';
  3360. this._setCity(city);
  3361. }
  3362. $.extend(City.Resource.prototype, {
  3363. _setCity: function setCity(city) {
  3364. if (city) {
  3365. this.city = Utils.fixedFunction(city);
  3366. }
  3367. },
  3368. _update: function _update(current, production, consumption) {
  3369. var changed =
  3370. Math.abs(current - this.getCurrent()) > 3 ||
  3371. this.production != production ||
  3372. this.consumption != consumption;
  3373. this.current = current;
  3374. this.production = production;
  3375. this.consumption = consumption;
  3376. return changed;
  3377. },
  3378. _increment: function _increment(delta) {
  3379. this.current = Math.max(this.current + delta, 0);
  3380. },
  3381. getCurrent: function getCurrent() {
  3382. if (this.current === undefined) {
  3383. return undefined;
  3384. }
  3385. var current = this.current;
  3386. var now = View.gameTimeNow();
  3387. var max = this.city().getResourceCapacity().maximum;
  3388. var lastUpdate = this.city().getLastResourceUpdate();
  3389. if (this.production) {
  3390. current += this.production * (now - lastUpdate) /
  3391. Constants.Time.MILLIS_PER_SECOND;
  3392. }
  3393. if (this.consumption) {
  3394.  
  3395. // Wine use takes place on the hour.
  3396. var startHour = Math.floor(lastUpdate / Constants.Time.MILLIS_PER_HOUR);
  3397. var nowHour = Math.floor(now / Constants.Time.MILLIS_PER_HOUR);
  3398. current -=
  3399.  
  3400. this.consumption * Constants.Time.SECONDS_PER_HOUR * (nowHour - startHour);
  3401. }
  3402. return Math.max(0, Math.min(max, current));
  3403. },
  3404. /**
  3405. * In milliseconds.
  3406. */
  3407. getTimeUntilFull: function getTimeUntilFull() {
  3408. var overallProduction = (this.production || 0) - (this.consumption || 0);
  3409. if (this.current === undefined) {
  3410. return Number.POSITIVE_INFINITY;
  3411. } else {
  3412. var current = this.getCurrent();
  3413. var max = this.city().getResourceCapacity().maximum;
  3414. var production = this.production;
  3415. if (overallProduction > 0) {
  3416. var secondsToNextHour = (Constants.Time.MILLIS_PER_HOUR -
  3417. (View.gameTimeNow() % Constants.Time.MILLIS_PER_HOUR)) /
  3418. Constants.Time.MILLIS_PER_SECOND;
  3419. var atNextHour = current + secondsToNextHour * production;
  3420.  
  3421. if (atNextHour >= max) {
  3422. return (max - current) / production * Constants.Time.MILLIS_PER_SECOND;
  3423. } else {
  3424. var hours = Math.floor(
  3425. (max - atNextHour) / overallProduction / Constants.Time.SECONDS_PER_HOUR);
  3426. var atHours = atNextHour + overallProduction * hours * Constants.Time.SECONDS_PER_HOUR;
  3427. return (secondsToNextHour +
  3428. hours * Constants.Time.SECONDS_PER_HOUR +
  3429. (max - atHours) / production) * Constants.Time.MILLIS_PER_SECOND;
  3430. }
  3431. } else if (this.current && this.getCurrent() == max) {
  3432. // No production, but filled exactly to capacity
  3433. return 0;
  3434. } else {
  3435. return Number.POSITIVE_INFINITY;
  3436. }
  3437. }
  3438. },
  3439. /**
  3440. * In milliseconds.
  3441. */
  3442. getTimeUntilEmpty: function getTimeUntilEmpty() {
  3443. if (this.current === undefined) {
  3444. return Number.POSITIVE_INFINITY;
  3445. } else if (this.consumption) {
  3446. if (this.production > this.consumption) {
  3447. // Could run out in first hour, but nobody is going to run their empire that
  3448. // way so it's not worth the effort of calculating.
  3449. return Number.POSITIVE_INFINITY;
  3450. } else {
  3451. // Wine use takes place on the hour.
  3452. var current = this.getCurrent();
  3453. var production = this.production || 0;
  3454. var secondsToNextHour = (Constants.Time.MILLIS_PER_HOUR -
  3455. (View.gameTimeNow() % Constants.Time.MILLIS_PER_HOUR)) /
  3456. Constants.Time.MILLIS_PER_SECOND;
  3457. // Compute to end of next hour
  3458. var atNextHour = current - this.consumption * Constants.Time.SECONDS_PER_HOUR +
  3459. production * secondsToNextHour;
  3460. if (atNextHour <= 0) {
  3461. return secondsToNextHour * Constants.Time.MILLIS_PER_SECOND;
  3462. } else {
  3463. var hourlyDiff =
  3464. (this.consumption - production) * Constants.Time.SECONDS_PER_HOUR;
  3465. return Constants.Time.MILLIS_PER_SECOND * (secondsToNextHour +
  3466. Math.ceil(atNextHour / hourlyDiff) * Constants.Time.SECONDS_PER_HOUR);
  3467. }
  3468. }
  3469. }
  3470. return Number.POSITIVE_INFINITY;
  3471. },
  3472. getCapacity: function getCapacity() {
  3473. return this.city().getResourceCapacity();
  3474. },
  3475.  
  3476. getProduction: function getProduction() {
  3477. return this.production;
  3478. },
  3479. getConsumption: function getConsumption() {
  3480. return this.consumption;
  3481. },
  3482. });
  3483.  
  3484. City.Building = function City_Building(city) {
  3485. this._ikaToolsType = 'building';
  3486. this._setCity(city);
  3487.  
  3488. this.position = null;
  3489. this.type = null;
  3490. this.level = 0;
  3491. }
  3492. $.extend(City.Building.prototype, {
  3493. _setCity: function setCity(city) {
  3494. if (city) {
  3495. this.city = Utils.fixedFunction(city);
  3496. this._scheduleUpgradeComplete();
  3497. }
  3498. },
  3499. _update: function _update(position, data) {
  3500. this.position = position;
  3501. var changed = false;
  3502. if (data.building.indexOf('buildingGround') >= 0) {
  3503. changed = !this.isEmpty();
  3504. this.type = '';
  3505. delete this.level;
  3506. delete this.completionTime;
  3507. } else {
  3508. var type = data.building.split(' ')[0];
  3509. var level = parseInt(data.level);
  3510. var isUpgrading = 'completed' in data;
  3511. changed = (type != this.getType() ||
  3512. level != this.getLevel() ||
  3513. isUpgrading != this.isUpgrading());
  3514. this.type = type;
  3515. this.level = level;
  3516. if (isUpgrading) {
  3517. var completionTime = parseInt(data.completed) * 1000;
  3518. if (this.completionTime != completionTime) {
  3519. this.completionTime = completionTime;
  3520. this._scheduleUpgradeComplete();
  3521. }
  3522. } else {
  3523. delete this.completionTime;
  3524. }
  3525. }
  3526.  
  3527. return changed;
  3528. },
  3529. _scheduleUpgradeComplete: function _scheduleUpgradeComplete() {
  3530. if (this.upgradeEvent) {
  3531. this.upgradeEvent();
  3532. }
  3533. if (this.completionTime) {
  3534. if (this.completionTime <= View.gameTimeNow()) {
  3535. this.level = this.level + 1;
  3536. delete this.completionTime;
  3537. raiseBuildingsChanged([{
  3538. city: this.city(),
  3539. building: this,
  3540. type: Constants.BuildingEventType.UPGRADE_COMPLETE,
  3541. }]);
  3542. } else {
  3543. this.upgradeEvent = buildingsChangedEvent().scheduleSend(
  3544. this.type + "->" + (this.level + 1),
  3545. // 0.5.0 still does a full page refresh if you're viewing the city when the
  3546. // building completes. So we cheat a bit and send this event a few seconds
  3547. // before it actually takes place so it doesn't get lost as part of a page
  3548. // refresh that just sees it as a normal "info_refresh" event.
  3549. this.completionTime - View.gameTimeNow() + Constants.Time.SAFE_TIME_DELTA,
  3550. function() {
  3551. this.level = this.level + 1;
  3552. delete this.completionTime;
  3553. empireData.saveAsync();
  3554. }.bind(this),
  3555. [{
  3556. city: this.city(),
  3557. building: this,
  3558. type: Constants.BuildingEventType.UPGRADE_COMPLETE,
  3559. }]);
  3560. }
  3561. }
  3562. },
  3563. getPosition: function getPosition() {
  3564. return this.position;
  3565. },
  3566. isEmpty: function isEmpty() {
  3567. return !this.type;
  3568. },
  3569. getType: function getType() {
  3570. return this.type;
  3571. },
  3572. getLevel: function getLevel() {
  3573. return this.level;
  3574. },
  3575. isUpgrading: function isUpgrading() {
  3576. return (this.completionTime > View.gameTimeNow());
  3577. },
  3578. getRemainingUpgradeTime: function getRemainingUpgradeTime() {
  3579. var diff = this.completionTime - View.gameTimeNow();
  3580. return Math.max(diff, 0);
  3581. },
  3582. getCompletionTime: function getCompletionTime() {
  3583. return new Date(this.completionTime);
  3584. },
  3585. getUpgradeCosts: function getUpgradeCost() {
  3586.  
  3587. var city = this.city();
  3588. var civData = getCivilizationData();
  3589. var buildingCostData = Constants.BuildingData[this.getType()];
  3590. var level = this.getLevel() + (this.isUpgrading() ? 1 : 0);
  3591. var timeParams = buildingCostData.time;
  3592. var costs = {
  3593. wood: buildingCostData.wood[level] || 0,
  3594. wine: buildingCostData.wine[level] || 0,
  3595. marble: buildingCostData.marble[level] || 0,
  3596. glass: buildingCostData.glass[level] || 0,
  3597. sulfur: buildingCostData.sulfur[level] || 0,
  3598. time: Math.round(timeParams.a / timeParams.b *
  3599. Math.pow(timeParams.c, level+1) - timeParams.d) * 1000,
  3600. };
  3601. var multiplier = 1.0;
  3602. multiplier -= civData.hasResearched(Constants.Research.Economy.PULLEY) ? .02 : 0;
  3603. multiplier -= civData.hasResearched(Constants.Research.Economy.GEOMETRY) ? .04 : 0;
  3604. multiplier -= civData.hasResearched(Constants.Research.Economy.SPIRIT_LEVEL) ? .08 : 0;
  3605. var carpenter = city.getBuildingByType(Constants.Buildings.CARPENTER);
  3606. var winePress = city.getBuildingByType(Constants.Buildings.WINE_PRESS);
  3607. var architect = city.getBuildingByType(Constants.Buildings.ARCHITECT);
  3608. var optician = city.getBuildingByType(Constants.Buildings.OPTICIAN);
  3609. var fireworker = city.getBuildingByType(Constants.Buildings.FIREWORK_TEST_AREA);
  3610. return {
  3611. wood: costs.wood * (multiplier - (carpenter ? carpenter.getLevel() / 100 : 0)),
  3612. wine: costs.wine * (multiplier - (winePress ? winePress.getLevel() / 100 : 0)),
  3613. marble: costs.marble * (multiplier - (architect ? architect.getLevel() / 100 : 0)),
  3614. glass: costs.glass * (multiplier - (optician ? optician.getLevel() / 100 : 0)),
  3615. sulfur: costs.sulfur * (multiplier - (fireworker ? fireworker.getLevel() / 100 : 0)),
  3616. time: costs.time,
  3617. };
  3618. },
  3619. isMaxLevel: function isMaxLevel() {
  3620. return (this.getLevel() + (this.isUpgrading() ? 1 : 0)) >=
  3621. Constants.BuildingData[this.getType()].maxLevel;
  3622. },
  3623. });
  3624. CivilizationData = function CivilizationData() {
  3625. this._ikaToolsType = 'civilizationData';
  3626.  
  3627. this.research = {};
  3628. this.government = 'ikakratie';
  3629. this.movements = {};
  3630. this.premiumFeatures = {};
  3631. }
  3632. $.extend(CivilizationData.prototype, {
  3633. _startMovementTimers: function _startMovementTimers() {
  3634. $.each(this.movements, function updateMovementsOnLoad(id, movement) {
  3635. if (movement._updateAndStartTimer()) {
  3636. delete this.movements[id];
  3637. }
  3638. }.bind(this));
  3639. },
  3640. _updateGovernment: function updateGovernment(government, changedAccumulator) {
  3641. if (this.government != government) {
  3642. changedAccumulator.push({
  3643. type: Constants.CivilizationData.GOVERNMENT,
  3644. government: government,
  3645. });
  3646.  
  3647. }
  3648. this.government = government;
  3649. },
  3650. _updateResearch: function updateResearch(researchId, level, changedAccumulator) {
  3651. var oldResearch = this.research[researchId];
  3652. if (!oldResearch || oldResearch.level != level) {
  3653. changedAccumulator.push({
  3654. type: Constants.CivilizationData.RESEARCH,
  3655. id: researchId,
  3656. level: level,
  3657. });
  3658. }
  3659. this.research[researchId] = { level: level };
  3660. },
  3661. _updateMovement: function updateMovement(movement, changedAccumulator) {
  3662. var existing = this.movements[movement.getId()];
  3663. if (existing) {
  3664. existing._cancelTimer();
  3665. }
  3666. this.movements[movement.getId()] = movement;
  3667. movement._updateAndStartTimer();
  3668. if (!existing || (movement.getCompletionTime() != existing.getCompletionTime())) {
  3669. changedAccumulator.push({
  3670. movement: movement,
  3671. type: Constants.Movements.EventType.DATA_UPDATED,
  3672. });
  3673. }
  3674. },
  3675. _removeMovement: function removeMovement(movementId, changedAccumulator) {
  3676. var movement = this.movements[movementId];
  3677. if (movement) {
  3678. if (movement.stage == Constants.Movements.Stage.LOADING && movementId >= 0) {
  3679. var originCity = movement.getOriginCity();
  3680. movement._updateCity(originCity, originCity);
  3681. }
  3682. movement._cancelTimer();
  3683. delete this.movements[movementId];
  3684. changedAccumulator.push({
  3685. movement: movement,
  3686. type: Constants.Movements.EventType.CANCELLED,
  3687. });
  3688. }
  3689. },
  3690. _updatePremiumFeature: function updatePremiumFeature(
  3691. changedAccumulator, feature, enabled) {
  3692. var currentlyEnabled = this.premiumFeatures[feature] || false;
  3693. if (currentlyEnabled != enabled) {
  3694. changedAccumulator.push({
  3695. type: Constants.CivilizationData.PREMIUM_FEATURE,
  3696. feature: feature,
  3697. enabled: enabled,
  3698. });
  3699. }
  3700. this.premiumFeatures[feature] = enabled;
  3701. },
  3702. hasResearched: function hasResearched(researchId) {
  3703. var research = this.research[researchId];
  3704. return research ? research.level > 0 : undefined;
  3705. },
  3706. getResearchLevel: function getResearchLevel(researchId) {
  3707. var research = this.research[researchId];
  3708. return research ? research.level : undefined;
  3709. },
  3710. getGovernment: function getGovernment() {
  3711. return this.government;
  3712. },
  3713. getMovements: function getMovements() {
  3714. var movements = [];
  3715. $.each(this.movements, function(id, movement) {
  3716. movements.push(movement);
  3717. });
  3718. movements.sort(function compareMovements(m1, m2) {
  3719. return m1.getArrivalTime() - m2.getArrivalTime();
  3720. });
  3721. return movements;
  3722. },
  3723. getMovement: function getMovement(movementId) {
  3724. return this.movements[movementId];
  3725. },
  3726. isPremiumFeatureEnabled: function isPremiumFeatureEnabled(feature) {
  3727. return this.premiumFeatures[feature];
  3728. },
  3729. });
  3730. function calculateTravelTime(island1Coords, island2Coords, units, transporters) {
  3731. // same island
  3732. if (island1Coords[0] == island2Coords[0] &&
  3733. island1Coords[1] == island2Coords[1]) {
  3734. var baseTime = 10 * Constants.Time.MILLIS_PER_MINUTE;
  3735. var multiplier = transporters ? 60 : 80; // fastest unit
  3736. if (units) {
  3737. $.each(units.getCounts(), function applyUnitSpeed(type, count) {
  3738. if (count) {
  3739. var data = Constants.UnitData[type];
  3740. multiplier = Math.min(multiplier, data.speed);
  3741. }
  3742. });
  3743. }
  3744. return baseTime * 60 / multiplier;
  3745. } else {
  3746. var baseTime = 20 * Math.sqrt(Math.pow(island1Coords[0] - island2Coords[0], 2) +
  3747. Math.pow(island1Coords[1] - island2Coords[1], 2)) *
  3748. Constants.Time.MILLIS_PER_MINUTE;
  3749. var multiplier = 60; // fastest ship
  3750. if (units) {
  3751. $.each(units.getCounts(), function applyUnitSpeed(type, count) {
  3752. if (count) {
  3753. var data = Constants.UnitData[type];
  3754. if (!data.isArmy) {
  3755. multiplier = Math.min(multiplier, data.speed);
  3756. }
  3757. }
  3758. });
  3759. }
  3760. return baseTime * 60 / multiplier;
  3761. }
  3762. }
  3763. function Movement(
  3764. id, type, completionTime, mission, stage, originCityId, targetCityId,
  3765. transports, units, resources, transportTime) {
  3766. this._ikaToolsType = 'movement';
  3767. if (id) {
  3768. this.id = id;
  3769. this.completionTime = completionTime;
  3770. this.mission = mission;
  3771. this.stage = stage;
  3772. this.originCityId = originCityId;
  3773. this.targetCityId = targetCityId;
  3774. this.units = units;
  3775. this.transports = transports;
  3776. this.resources = resources;
  3777.  
  3778. this.transportTime = transportTime;
  3779.  
  3780. this.type = type;
  3781. var originCity = this.getOriginCity();
  3782. var targetCity = this.getTargetCity();
  3783.  
  3784. if (originCity && targetCity) {
  3785. var originCoords = originCity.getIslandCoordinates();
  3786. var targetCoords = targetCity.getIslandCoordinates();
  3787.  
  3788. if (originCoords && targetCoords) {
  3789. this.transportTime = calculateTravelTime(
  3790. originCoords, targetCoords, this.units, this.transports);
  3791. }
  3792. }
  3793. if (!this.transportTime) {
  3794. this.transportTime = Number.POSITIVE_INFINITY;
  3795. }
  3796. if (this.completionTime <= new Date().getTime()) {
  3797. this._toNextStage();
  3798. }
  3799. }
  3800. }
  3801. $.extend(Movement.prototype, {
  3802. _cancelTimer: function cancelTimer() {
  3803. if (this.completionEvent) {
  3804. this.completionEvent();
  3805. }
  3806. },
  3807. _startTimer: function startTimer() {
  3808. var remainingTime = this.getTimeRemaining();
  3809. if (isFinite(remainingTime)) {
  3810. this.completionEvent = movementsChangedEvent().scheduleSend(
  3811. 'Movement[' + this.id + ']',
  3812. remainingTime + Constants.Time.SAFE_TIME_DELTA,
  3813. function moveToNextStage() {
  3814. if (this._toNextStage()) {
  3815. getCivilizationData()._removeMovement(this.id, []);
  3816. }
  3817. empireData.saveAsync();
  3818. }.bind(this),
  3819. [{
  3820. previousStage: this.getStage(),
  3821. movement: this,
  3822. type: this._isFinalStage() ?
  3823. Constants.Movements.EventType.COMPLETED :
  3824. Constants.Movements.EventType.STAGE_CHANGED,
  3825. }]);
  3826. }
  3827. },
  3828. _updateCity: function updateCity(city, originCity) {
  3829. var resourceChanges = [];
  3830. if (city) {
  3831. if (originCity) {
  3832. originCity._updateActionPointsBy(1, resourceChanges);
  3833. }
  3834. if (city.isOwn() &&
  3835. (this.mission == Constants.Movements.Mission.TRANSPORT ||
  3836. this.mission == Constants.Movements.Mission.PLUNDER)) {
  3837. $.each(this.resources, function updateCityResource(name, value) {
  3838. city._incrementResource(name, resourceChanges, value);
  3839. });
  3840. }
  3841. raiseResourcesChanged(resourceChanges);
  3842. if (this.mission == Constants.Movements.Mission.DEPLOY_ARMY ||
  3843. this.mission == Constants.Movements.Mission.DEPLOY_NAVY) {
  3844. var military = city.getMilitary();
  3845. military._increment(this.units);
  3846. raiseMilitaryChanged([{
  3847. military: military,
  3848. city: city,
  3849. type: 'deployment_arrived',
  3850. }]);
  3851. }
  3852. }
  3853. },
  3854. _updateAndStartTimer: function updateAndStartTimer() {
  3855. this._cancelTimer();
  3856. if (this.completionTime <= View.gameTimeNow()) {
  3857. return this._toNextStage();
  3858. } else {
  3859. this._startTimer();
  3860. }
  3861. },
  3862. _toNextStage: function toNextStage() {
  3863. var isFinalStage = this._isFinalStage();
  3864. if (this.stage == Constants.Movements.Stage.LOADING) {
  3865. this.stage = Constants.Movements.Stage.EN_ROUTE;
  3866. this.completionTime += this.transportTime;
  3867. this._startTimer();
  3868. } else if (this.stage == Constants.Movements.Stage.EN_ROUTE) {
  3869. if (isFinalStage) {
  3870. this._updateCity(this.getTargetCity(), this.getOriginCity());
  3871. }
  3872. } else if (this.stage == Constants.Movements.Stage.RETURNING) {
  3873. if (isFinalStage) {
  3874. var originCity = this.getOriginCity();
  3875. this._updateCity(originCity, originCity);
  3876. }
  3877. }
  3878. return isFinalStage;
  3879. },
  3880. _isFinalStage: function isFinalStage() {
  3881. if (this.stage == Constants.Movements.Stage.LOADING) {
  3882. return false;
  3883. } else if (this.stage == Constants.Movements.Stage.EN_ROUTE) {
  3884. if (this.mission == Constants.Movements.Mission.TRANSPORT) {
  3885. var city = getCity(this.targetCityId);
  3886. return city && city.isOwn();
  3887. } else if (this.mission == Constants.Movements.Mission.DEPLOY_ARMY ||
  3888. this.mission == Constants.Movements.Mission.DEPLOY_NAVY) {
  3889. return true;
  3890. }
  3891. } else {
  3892. return true;
  3893. }
  3894. },
  3895. getId: function getId() {
  3896. return this.id;
  3897. },
  3898. getMission: function getMission() {
  3899. return this.mission;
  3900. },
  3901. getStage: function getStage() {
  3902. return this.stage;
  3903. },
  3904. getOriginCityId: function getOriginCityId() {
  3905. return this.originCityId;
  3906. },
  3907. getTargetCityId: function getTargetCityId() {
  3908. return this.targetCityId;
  3909. },
  3910. getOriginCity: function getOriginCity() {
  3911. return this.originCityId && getCity(this.originCityId);
  3912. },
  3913. getTargetCity: function getTargetCity() {
  3914. return this.targetCityId && getCity(this.targetCityId);
  3915. },
  3916. getCompletionTime: function getCompletionTime() {
  3917. return this.completionTime;
  3918. },
  3919. getTimeRemaining: function getTimeRemaining() {
  3920. return this.completionTime - View.gameTimeNow();
  3921. },
  3922. getArrivalTime: function() {
  3923. var time = this.getCompletionTime();
  3924. if (this.stage == Constants.Movements.Stage.LOADING) {
  3925. time += this.transportTime;
  3926. }
  3927. return time;
  3928. },
  3929. getUnits: function getUnits() {
  3930. return this.units;
  3931. },
  3932. getResource: function getResource(resourceName) {
  3933. return this.resources[resourceName];
  3934. },
  3935. isHostile: function isHostile() {
  3936. return this.type.indexOf('hostile') >= 0;
  3937. },
  3938. isOwn: function isOwn() {
  3939. return this.type.indexOf('own') >= 0;
  3940. }
  3941. });
  3942. var empireData = new Data.Value(
  3943. 'empireData',
  3944.  
  3945. {
  3946. cities: {},
  3947. cityOrder: [],
  3948. civilizationData: new CivilizationData(),
  3949. },
  3950. {
  3951. reviver: function empireDataReviver(key, value) {
  3952. if (value && value._ikaToolsType) {
  3953. var obj;
  3954. switch(value._ikaToolsType) {
  3955. case 'city': obj = new City(); break;
  3956. case 'building': obj = new City.Building; break;
  3957. case 'cityResource': obj = new City.Resource(); break;
  3958. case 'military': obj = new Military(); break;
  3959. case 'militaryUnits': obj = new MilitaryUnits(); break;
  3960. case 'trainingBatch': obj = new TrainingBatch(); break;
  3961. case 'civilizationData': obj = new CivilizationData(); break;
  3962. case 'movement': obj = new Movement(); break;
  3963. }
  3964. $.extend(obj, value);
  3965. if (obj._postLoad) {
  3966. obj._postLoad();
  3967. }
  3968. return obj;
  3969. }
  3970. return value;
  3971. },
  3972. version: 28,
  3973. loadCallback: function empireDataLoaded() {
  3974. getCivilizationData()._startMovementTimers();
  3975. },
  3976. });
  3977. function raiseCivilizationDataChanged(changes) {
  3978. if (changes.length) {
  3979. civilizationDataChangedEvent().send(changes);
  3980. }
  3981. }
  3982.  
  3983. var civilizationDataChangedEvent = Utils.thunk(function() {
  3984. return new Utils.EventDispatcher();
  3985. });
  3986. function registerCivilizationDataChangedHandler(callback) {
  3987. return civilizationDataChangedEvent().addListener(callback);
  3988. }
  3989. function raiseMovementsChanged(changes) {
  3990. if (changes.length) {
  3991. movementsChangedEvent().send(changes);
  3992. }
  3993. }
  3994. var movementsChangedEvent = Utils.thunk(function() {
  3995. return new Utils.EventDispatcher();
  3996. });
  3997. function registerMovementsChangedHandler(callback) {
  3998. return movementsChangedEvent().addListener(callback);
  3999. }
  4000.  
  4001. function raiseResourcesChanged(changes) {
  4002. if (changes.length) {
  4003. resourcesChangedEvent().send(changes);
  4004. }
  4005. }
  4006. var resourcesChangedEvent = Utils.thunk(function() {
  4007. return new Utils.EventDispatcher();
  4008. });
  4009. function registerResourcesChangedHandler(callback) {
  4010. return resourcesChangedEvent().addListener(callback);
  4011. }
  4012. var buildingsChangedEvent = Utils.thunk(function() {
  4013. var dispatcher = new Utils.EventDispatcher();
  4014. return dispatcher;
  4015. });
  4016. function raiseBuildingsChanged(changes) {
  4017. if (changes.length) {
  4018. buildingsChangedEvent().send(changes);
  4019. }
  4020. }
  4021. function registerBuildingsChangedHandler(callback) {
  4022. return buildingsChangedEvent().addListener(callback);
  4023. }
  4024. var militaryChangedEvent = Utils.thunk(function() {
  4025. var dispatcher = new Utils.EventDispatcher();
  4026. return dispatcher;
  4027. });
  4028. function raiseMilitaryChanged(changes) {
  4029. if (changes.length) {
  4030. militaryChangedEvent().send(changes);
  4031. }
  4032. }
  4033. function registerMilitaryChangedHandler(callback) {
  4034. return militaryChangedEvent().addListener(callback);
  4035. };
  4036.  
  4037. var TRADE_GOOD_LOOKUP = {
  4038. "1": Constants.Resources.WINE,
  4039. "2": Constants.Resources.MARBLE,
  4040. "3": Constants.Resources.GLASS,
  4041. "4": Constants.Resources.SULFUR,
  4042. };
  4043. function getCity(id) {
  4044. return empireData.get().cities[id];
  4045. };
  4046. var coordsRegex = /\[(\d+):(\d+)\]/;
  4047. function parseCoordinates(coords) {
  4048. var match = coords.match(coordsRegex);
  4049. return [parseInt(match[1]), parseInt(match[2])];
  4050. }
  4051. function processTransportForm(form) {
  4052. var city = View.getCurrentCity();
  4053. var transports = parseInt($('#transporterCount').val()) || 0;
  4054. var resources = {};
  4055. resources[Constants.Resources.WOOD] =
  4056. parseInt($('#textfield_wood').val()) || 0;
  4057. resources[Constants.Resources.WINE] =
  4058. parseInt($('#textfield_wine').val()) || 0;
  4059. resources[Constants.Resources.MARBLE] =
  4060. parseInt($('#textfield_marble').val()) || 0;
  4061. resources[Constants.Resources.GLASS] =
  4062. parseInt($('#textfield_glass').val()) || 0;
  4063. resources[Constants.Resources.SULFUR] =
  4064. parseInt($('#textfield_sulfur').val()) || 0;
  4065. if ($('#createColony').length) {
  4066. resources[Constants.Resources.WOOD] += 1250;
  4067. }
  4068. var destinationCityId = parseInt($(form.elements['destinationCityId']).val());
  4069. var totalResources =
  4070. resources[Constants.Resources.WOOD] +
  4071. resources[Constants.Resources.WINE] +
  4072. resources[Constants.Resources.MARBLE] +
  4073. resources[Constants.Resources.GLASS] +
  4074. resources[Constants.Resources.SULFUR];
  4075. var loadingCompletion = View.gameTimeNow() +
  4076. (totalResources / city.getLoadingSpeed() * Constants.Time.MILLIS_PER_SECOND);
  4077.  
  4078. var movement = new Movement(
  4079. -(new Date().getTime()),
  4080. 'own',
  4081. loadingCompletion, //TODO: multiple loads from same town (if own) stack
  4082. Constants.Movements.Mission.TRANSPORT,
  4083. Constants.Movements.Stage.LOADING,
  4084. city.getId(),
  4085. destinationCityId,
  4086. transports,
  4087. new MilitaryUnits(),
  4088. resources
  4089. // TODO: transport time for towns other than one's that are tracked
  4090. );
  4091. View.registerNextIkariamAjaxRequestCallback(function saveTransportData(response) {
  4092. Utils.iterateIkariamAjaxResponse(response,
  4093. function lookForSuccessFeedback(index, name, data) {
  4094. if (name == Constants.IkariamAjaxResponseType.PROVIDE_FEEDBACK &&
  4095. data[0].type == 10) {
  4096. var changes = [];
  4097. getCivilizationData()._updateMovement(movement, changes);
  4098. raiseMovementsChanged(changes);
  4099. }
  4100. });
  4101. });
  4102. }
  4103. function coordinatesEqual(coordinates1, coordinates2) {
  4104. if (!coordinates1 || !coordinates2) {
  4105. return false;
  4106. }
  4107. return coordinates1[0] == coordinates2[0] &&
  4108. coordinates1[1] == coordinates2[1];
  4109. }
  4110. function processDeploymentForm(form) {
  4111. var city = View.getCurrentCity();
  4112. var transports = parseInt($('#transporterCount').html()) || 0;
  4113. var mission = $(form.elements['function']).val() == 'deployArmy' ?
  4114. Constants.Movements.Mission.DEPLOY_ARMY : Constants.Movements.Mission.DEPLOY_NAVY;
  4115. var destinationCityId = parseInt($(form.elements['destinationCityId']).val());
  4116. var destinationCity = getCity(destinationCityId);
  4117. var units = new MilitaryUnits();
  4118. $.each(Constants.UnitIds, function countDeployingUnits(id, type) {
  4119. var elementId;
  4120. if (Constants.UnitData[type].isArmy) {
  4121. elementId = '#cargo_army_' + id;
  4122. } else {
  4123. elementId = '#cargo_fleet_' + id;
  4124. }
  4125. units._setCount(type, parseInt($(elementId).val()) || 0);
  4126. });
  4127. var cargoSize = units.getCargoSize();
  4128. var loadingCompletion = new Date().getTime() +
  4129. (units.getCargoSize() / city.getLoadingSpeed() * Constants.Time.MILLIS_PER_SECOND);
  4130.  
  4131. if (destinationCity &&
  4132. coordinatesEqual(destinationCity.getIslandCoordinates(),
  4133. city.getIslandCoordinates())) {
  4134. loadingCompletion = new Date().getTime();
  4135. }
  4136. var movement = new Movement(
  4137. -(new Date().getTime()),
  4138. 'own',
  4139. loadingCompletion, //TODO: multiple loads from same town (if own) stack
  4140. mission,
  4141. Constants.Movements.Stage.LOADING,
  4142. city.getId(),
  4143. destinationCityId,
  4144. transports,
  4145. units,
  4146. {}
  4147. // TODO: transport time for towns other than one's that are tracked
  4148. );
  4149. View.registerNextIkariamAjaxRequestCallback(function saveDeploymentData(response) {
  4150. Utils.iterateIkariamAjaxResponse(response,
  4151. function lookForSuccessFeedback(index, name, data) {
  4152. if (name == Constants.IkariamAjaxResponseType.PROVIDE_FEEDBACK &&
  4153. data[0].type == 10) {
  4154. var military = city.getMilitary();
  4155. military._decrement(units);
  4156. raiseMilitaryChanged([{
  4157. military: military,
  4158. city: city,
  4159. type: 'movement_started',
  4160. }]);
  4161. var changes = [];
  4162. getCivilizationData()._updateMovement(movement, changes);
  4163. raiseMovementsChanged(changes);
  4164. }
  4165. });
  4166. });
  4167. }
  4168. function processPlunderForm(form) {
  4169. var city = View.getCurrentCity();
  4170. var transports = parseInt($('#transporterCount').html()) || 0;
  4171. var destinationCityId = parseInt($(form.elements['destinationCityId']).val());
  4172. var destinationCity = getCity(destinationCityId);
  4173. var units = new MilitaryUnits();
  4174. $.each(Constants.UnitIds, function countDeployingUnits(id, type) {
  4175. units._setCount(type, parseInt($('#cargo_army_' + id).val()) || 0);
  4176. });
  4177. var cargoSize = units.getCargoSize();
  4178. var loadingCompletion = new Date().getTime() +
  4179. (units.getCargoSize() / city.getLoadingSpeed() * Constants.Time.MILLIS_PER_SECOND);
  4180. if (destinationCity &&
  4181. coordinatesEqual(destinationCity.getIslandCoordinates(),
  4182. city.getIslandCoordinates())) {
  4183. loadingCompletion = new Date().getTime();
  4184. }
  4185. var movement = new Movement(
  4186. -(new Date().getTime()),
  4187. 'own',
  4188. loadingCompletion, //TODO: multiple loads from same town (if own) stack
  4189. Constants.Movements.Mission.PLUNDER,
  4190. Constants.Movements.Stage.LOADING,
  4191. city.getId(),
  4192. destinationCityId,
  4193. transports,
  4194. units,
  4195. {}
  4196. // TODO: transport time for towns other than one's that are tracked
  4197. );
  4198. View.registerNextIkariamAjaxRequestCallback(function savePlunderData(response) {
  4199. Utils.iterateIkariamAjaxResponse(response,
  4200. function lookForSuccessFeedback(index, name, data) {
  4201. if (name == Constants.IkariamAjaxResponseType.PROVIDE_FEEDBACK &&
  4202. data[0].type == 10) {
  4203. var military = city.getMilitary();
  4204. military._decrement(units);
  4205. raiseMilitaryChanged([{
  4206. military: military,
  4207. city: city,
  4208. type: 'movement_started',
  4209. }]);
  4210. var changes = [];
  4211. getCivilizationData()._updateMovement(movement, changes);
  4212. raiseMovementsChanged(changes);
  4213. }
  4214. });
  4215. });
  4216. }
  4217. function processCityMilitaryView(data) {
  4218. var city = View.getCurrentCity();
  4219. var military = city.getMilitary();
  4220. var armyTds = $('#tabUnits').find('tr.count td');
  4221. var e = false;
  4222. e |= military._updatePresent(Constants.Military.HOPLITE, parseInt(armyTds[0].innerHTML));
  4223. e |= military._updatePresent(Constants.Military.STEAM_GIANT, parseInt(armyTds[1].innerHTML));
  4224. e |= military._updatePresent(Constants.Military.SPEARMAN, parseInt(armyTds[2].innerHTML));
  4225. e |= military._updatePresent(Constants.Military.SWORDSMAN, parseInt(armyTds[3].innerHTML));
  4226. e |= military._updatePresent(Constants.Military.SLINGER, parseInt(armyTds[4].innerHTML));
  4227. e |= military._updatePresent(Constants.Military.ARCHER, parseInt(armyTds[5].innerHTML));
  4228. e |= military._updatePresent(Constants.Military.GUNNER, parseInt(armyTds[6].innerHTML));
  4229. e |= military._updatePresent(Constants.Military.BATTERING_RAM, parseInt(armyTds[7].innerHTML));
  4230. e |= military._updatePresent(Constants.Military.CATAPULT, parseInt(armyTds[8].innerHTML));
  4231. e |= military._updatePresent(Constants.Military.MORTAR, parseInt(armyTds[9].innerHTML));
  4232. e |= military._updatePresent(Constants.Military.GYROCOPTER, parseInt(armyTds[10].innerHTML));
  4233. e |= military._updatePresent(Constants.Military.BALLOON_BOMBADIER, parseInt(armyTds[11].innerHTML));
  4234. e |= military._updatePresent(Constants.Military.COOK, parseInt(armyTds[12].innerHTML));
  4235. e |= military._updatePresent(Constants.Military.DOCTOR, parseInt(armyTds[13].innerHTML));
  4236.  
  4237. var navyTds = $('#tabShips').find('tr.count td');
  4238. e |= military._updatePresent(Constants.Military.RAM_SHIP, parseInt(navyTds[2].innerHTML));
  4239. e |= military._updatePresent(Constants.Military.FLAME_THROWER, parseInt(navyTds[0].innerHTML));
  4240. e |= military._updatePresent(Constants.Military.STEAM_RAM, parseInt(navyTds[1].innerHTML));
  4241. e |= military._updatePresent(Constants.Military.BALLISTA_SHIP, parseInt(navyTds[4].innerHTML));
  4242. e |= military._updatePresent(Constants.Military.CATAPULT_SHIP, parseInt(navyTds[3].innerHTML));
  4243. e |= military._updatePresent(Constants.Military.MORTAR_SHIP, parseInt(navyTds[5].innerHTML));
  4244. e |= military._updatePresent(Constants.Military.SUBMARINE, parseInt(navyTds[7].innerHTML));
  4245. e |= military._updatePresent(Constants.Military.PADDLE_SPEED_SHIP, parseInt(navyTds[8].innerHTML));
  4246. e |= military._updatePresent(Constants.Military.BALLOON_CARRIER, parseInt(navyTds[9].innerHTML));
  4247. e |= military._updatePresent(Constants.Military.TENDER, parseInt(navyTds[10].innerHTML));
  4248. e |= military._updatePresent(Constants.Military.ROCKET_SHIP, parseInt(navyTds[6].innerHTML));
  4249. military._markPresentUpdated();
  4250. if (e) {
  4251. raiseMilitaryChanged([{
  4252. city: city,
  4253. military: military,
  4254. type: 'data_updated',
  4255. }]);
  4256. }
  4257. }
  4258. function processRelatedCitiesView(data) {
  4259. var city = View.getCurrentCity();
  4260. var military = city.getMilitary();
  4261. military._clear();
  4262. var changed = false;
  4263. var info = $('#relatedCities .contentBox01h:eq(0)');
  4264. var whitespace = /\s+/;
  4265. info.find('.troops .armybutton').each(function(i, element) {
  4266. var type = element.className.split(whitespace)[1];
  4267. changed |= military._updatePresent(type, parseInt(element.innerHTML));
  4268. });
  4269. info.find('.troops .fleetbutton').each(function(i, element) {
  4270. var type = element.className.split(whitespace)[1];
  4271. changed |= military._updatePresent(type, parseInt(element.innerHTML));
  4272. });
  4273.  
  4274. military._markPresentUpdated();
  4275. if (changed) {
  4276. raiseMilitaryChanged([{
  4277. city: city,
  4278. military: military,
  4279. type: 'data_updated',
  4280. }]);
  4281. }
  4282. }
  4283. function parseMilitaryUnitsFromPending(container) {
  4284. var idRegex = /\d+/;
  4285. var units = new MilitaryUnits();
  4286. container.children('.army_wrapper').each(function(index, unitNode) {
  4287. var unitNode = $(unitNode);
  4288. var type = Constants.UnitIds[
  4289. parseInt(unitNode.find('.army').attr('class').match(idRegex)[0])];
  4290. var count = parseInt(unitNode.find('.unitcounttextlabel').html());
  4291. units._setCount(type, count);
  4292. });
  4293. return units;
  4294. }
  4295. function parseTrainingBatches(viewHtmlText, type, buildingLevel) {
  4296. var trainingBatches = [];
  4297. var constructionList = $('#unitConstructionList');
  4298. if (constructionList.length) {
  4299. var completionTime = parseInt(viewHtmlText.match(
  4300. /showUnitCountdown.'buildCountDown', 'buildProgress', (\d+)/)[1]) * 1000;
  4301. trainingBatches.push(new TrainingBatch(type,
  4302. completionTime, parseMilitaryUnitsFromPending(constructionList)));
  4303.  
  4304. constructionList.children('.constructionBlock').each(function() {
  4305. var units = parseMilitaryUnitsFromPending($(this));
  4306. completionTime += computeTrainingTime(buildingLevel, units);
  4307. trainingBatches.push(new TrainingBatch(type, completionTime, units));
  4308. });
  4309. }
  4310. return trainingBatches;
  4311. }
  4312. function computeTrainingTime(barracksLevel, units) {
  4313. var time = 0;
  4314. $.each(units.getCounts(), function(type, count) {
  4315. var data = Constants.UnitData[type];
  4316. time += count * Math.pow(0.95, barracksLevel - data.minimumBuildingLevelToBuild) *
  4317. data.baseBuildTime;
  4318. });
  4319. return time * Constants.Time.MILLIS_PER_SECOND;
  4320. }
  4321. function processBarracksView(viewHtmlText, data) {
  4322. var city = View.getCurrentCity();
  4323. var military = city.getMilitary();
  4324. var barracks = city.getBuildingByType(Constants.Buildings.BARRACKS);
  4325. var changed = false;
  4326. function update(type, dataName) {
  4327. if (data[dataName]) {
  4328. changed = military._updatePresent(type, parseInt(data[dataName].text));
  4329. }
  4330. }
  4331. update(Constants.Military.HOPLITE, 'js_barracksUnitUnitsAvailable1');
  4332. update(Constants.Military.STEAM_GIANT, 'js_barracksUnitUnitsAvailable2');
  4333. update(Constants.Military.SPEARMAN, 'js_barracksUnitUnitsAvailable3');
  4334. update(Constants.Military.SWORDSMAN, 'js_barracksUnitUnitsAvailable4');
  4335. update(Constants.Military.SLINGER, 'js_barracksUnitUnitsAvailable5');
  4336. update(Constants.Military.ARCHER, 'js_barracksUnitUnitsAvailable6');
  4337. update(Constants.Military.GUNNER, 'js_barracksUnitUnitsAvailable7');
  4338. update(Constants.Military.BATTERING_RAM, 'js_barracksUnitUnitsAvailable8');
  4339. update(Constants.Military.CATAPULT, 'js_barracksUnitUnitsAvailable9');
  4340. update(Constants.Military.MORTAR, 'js_barracksUnitUnitsAvailable10');
  4341. update(Constants.Military.GYROCOPTER, 'js_barracksUnitUnitsAvailable11');
  4342. update(Constants.Military.BALLOON_BOMBADIER, 'js_barracksUnitUnitsAvailable12');
  4343. update(Constants.Military.COOK, 'js_barracksUnitUnitsAvailable13');
  4344. update(Constants.Military.DOCTOR, 'js_barracksUnitUnitsAvailable14');
  4345. military._markPresentUpdated(true, false);
  4346.  
  4347. military._setArmyTrainingBatches(
  4348. parseTrainingBatches(viewHtmlText, Constants.Military.ARMY, barracks.getLevel()));
  4349.  
  4350. raiseMilitaryChanged([{
  4351. city: city,
  4352. military: military,
  4353. type: 'data_updated',
  4354. }]);
  4355. }
  4356. function processShipyardView(viewHtmlText, data) {
  4357. var city = View.getCurrentCity();
  4358. var military = city.getMilitary();
  4359. var shipyard = city.getBuildingByType(Constants.Buildings.SHIPYARD);
  4360. var change = false;
  4361. function update(type, dataName) {
  4362. if (data[dataName]) {
  4363. changed = military._updatePresent(type, parseInt(data[dataName].text));
  4364. }
  4365. }
  4366. update(Constants.Military.FLAME_THROWER, 'js_barracksUnitUnitsAvailable1');
  4367. update(Constants.Military.STEAM_RAM, 'js_barracksUnitUnitsAvailable2');
  4368. update(Constants.Military.RAM_SHIP, 'js_barracksUnitUnitsAvailable3');
  4369. update(Constants.Military.CATAPULT_SHIP, 'js_barracksUnitUnitsAvailable4');
  4370. update(Constants.Military.BALLISTA_SHIP, 'js_barracksUnitUnitsAvailable5');
  4371. update(Constants.Military.MORTAR_SHIP, 'js_barracksUnitUnitsAvailable6');
  4372. update(Constants.Military.ROCKET_SHIP, 'js_barracksUnitUnitsAvailable7');
  4373. update(Constants.Military.SUBMARINE, 'js_barracksUnitUnitsAvailable8');
  4374. update(Constants.Military.PADDLE_SPEED_SHIP, 'js_barracksUnitUnitsAvailable9');
  4375. update(Constants.Military.BALLOON_CARRIER, 'js_barracksUnitUnitsAvailable10');
  4376. update(Constants.Military.TENDER, 'js_barracksUnitUnitsAvailable11');
  4377. military._markPresentUpdated(false, true);
  4378. military._setNavyTrainingBatches(
  4379. parseTrainingBatches(viewHtmlText, Constants.Military.NAVY, shipyard.getLevel()));
  4380. raiseMilitaryChanged([{
  4381. city: city,
  4382. military: military,
  4383. type: 'data_updated',
  4384. }]);
  4385. }
  4386. function processAcademyView(data) {
  4387. var changes = [];
  4388. View.getCurrentCity()._updateScientists(
  4389. parseInt(data['js_academy_research_tooltip_basic_production'].text),
  4390. changes);
  4391. raiseResourcesChanged(changes);
  4392. }
  4393. function processSetScientistsForm(form) {
  4394. var scientists = parseInt($('#inputScientists').val());
  4395. View.registerNextIkariamAjaxRequestCallback(function saveScientstsData(response) {
  4396. Utils.iterateIkariamAjaxResponse(response,
  4397. function lookForSuccessFeedback(index, name, data) {
  4398. if (name == Constants.IkariamAjaxResponseType.PROVIDE_FEEDBACK &&
  4399. data[0].type == 10) {
  4400. var changes = [];
  4401. View.getCurrentCity()._updateScientists(scientists, changes);
  4402. raiseResourcesChanged(changes);
  4403. }
  4404. });
  4405. });
  4406. }
  4407. function processPalaceView(data) {
  4408. var changes = [];
  4409. getCivilizationData()._updateGovernment(
  4410. $('.government_pic img').attr('src').slice(16, -8), changes);
  4411. raiseCivilizationDataChanged(changes);
  4412. }
  4413. function processMuseumView(data) {
  4414. var changes = [];
  4415. View.getCurrentCity()._updateCulturalGoods(
  4416. parseInt(/\d+/.exec($('#val_culturalGoodsDeposit').parent().text())[0]),
  4417. changes);
  4418. raiseResourcesChanged(changes);
  4419. }
  4420. function processCulturalPossessionsAssignView(data) {
  4421. // Have to delay this because the script elements in the changed view
  4422. // need to run before we can access the cultural good information.
  4423. // There is no feasible way to extract the data at this point.
  4424. setTimeout(function() {
  4425. var cityIdRegex = /textfield_city_(\d+)/
  4426. var changes = [];
  4427. $('#moveCulturalGoods ul li input').each(function (index, item) {
  4428. item = $(item);
  4429.  
  4430. var city = getCity(
  4431. parseInt(cityIdRegex.exec(item.attr('id'))[1]));
  4432. city._updateCulturalGoods(parseInt(item.val()), changes);
  4433. });
  4434. raiseResourcesChanged(changes);
  4435. empireData.saveAsync();
  4436. }, 0);
  4437. }
  4438. function processTownHallView(data) {
  4439. var changes = [];
  4440. var city = View.getCurrentCity();
  4441. city._updatePriests(
  4442. parseInt(data['js_TownHallPopulationGraphPriestCount'].text), changes);
  4443. city._updateCulturalGoods(
  4444. parseInt(
  4445. data['js_TownHallSatisfactionOverviewCultureBoniTreatyBonusValue'].text) / 50,
  4446. changes);
  4447. city._updateTavernWineLevel(
  4448. parseInt(data['js_TownHallSatisfactionOverviewWineBoniServeBonusValue'].text) / 60,
  4449. changes);
  4450. raiseResourcesChanged(changes);
  4451. }
  4452. function processTempleView(data) {
  4453. var changes = [];
  4454. View.getCurrentCity()._updatePriests(
  4455. parseInt(data['js_TempleSlider'].slider.ini_value), changes);
  4456. raiseResourcesChanged(changes);
  4457. }
  4458. function processResearchAdvisorView(data) {
  4459. var civData = getCivilizationData();
  4460. var idRegex = /researchId=([0-9]+)/i
  4461. var levelRegex = /\((\d+)\)/;
  4462.  
  4463. var researches =
  4464. JSON.parse(data['new_js_params'] || data['load_js'].params).currResearchType;
  4465.  
  4466. var changes = [];
  4467. $.each(researches, function (name, researchData) {
  4468. var id = parseInt(idRegex.exec(researchData.aHref)[1]);
  4469. var levelMatch = levelRegex.exec(name);
  4470. var level = levelMatch
  4471. ? parseInt(levelMatch[1]) - 1
  4472. : (researchData.liClass == 'explored' ? 1 : 0);
  4473.  
  4474. civData._updateResearch(id, level, changes);
  4475. });
  4476. raiseCivilizationDataChanged(changes);
  4477. }
  4478. function processFinancesView(data) {
  4479. var cities = getOwnCities();
  4480. var scientistCost = 6;
  4481. if (getCivilizationData().hasResearched(Constants.Research.Science.LETTER_CHUTE)) {
  4482. scientistCost = 3;
  4483. }
  4484.  
  4485. var changes = []
  4486. $('#finances .table01:eq(1) tr').slice(1, -1).each(function(index, row) {
  4487. var tds = $(row).children('td');
  4488. var city = cities[index];
  4489. if ($(tds[0]).text() == city.getName()) {
  4490. city._updateScientists(
  4491. Math.round(-parseInt($(tds[2]).text().replace(',', '')) / scientistCost), changes);
  4492. }
  4493. });
  4494. raiseResourcesChanged(changes);
  4495. }
  4496. function processMilitaryAdvisorView(data) {
  4497. var civilizationData = getCivilizationData();
  4498. var movementIds = {};
  4499. var changes = [];
  4500. $.each(civilizationData.getMovements(), function(index, movement) {
  4501. movementIds[movement.getId()] = movement;
  4502. });
  4503. var movementMainValueRegex = /^js_MilitaryMovementsEventRow(\d+)$/;
  4504. var cityIdRegex = /cityId=(\d+)/;
  4505. $.each(data, function(key, value) {
  4506. var match = movementMainValueRegex.exec(key);
  4507. if (match /*&& value.class.indexOf('own') > 0*/) {
  4508. var movementId = parseInt(match[1]);
  4509. delete movementIds[movementId];
  4510.  
  4511. var type = value.class;
  4512. var completionTime =
  4513. data['js_MilitaryMovementsEventRow' + movementId + 'ArrivalTime']
  4514. .countdown.enddate * Constants.Time.MILLIS_PER_SECOND;
  4515. var originCityId = parseInt(
  4516. data['js_MilitaryMovementsEventRow' + movementId + 'OriginLink'].href
  4517. .match(cityIdRegex)[1]);
  4518. var targetCityId = data['js_MilitaryMovementsEventRow' + movementId + 'TargetLink'].href
  4519. ? parseInt(data['js_MilitaryMovementsEventRow' + movementId + 'TargetLink'].href
  4520. .match(cityIdRegex)[1]) : 0;
  4521. var mission = data['js_MilitaryMovementsEventRow' + movementId + 'MissionIcon']
  4522. .class.split(' ')[1];
  4523. var stage = Constants.Movements.Stage.LOADING;
  4524. var statusClass =
  4525. data['js_MilitaryMovementsEventRow' + movementId + 'Mission'].class;
  4526. if (statusClass && statusClass.indexOf('arrow_right_green') >= 0) {
  4527. stage = Constants.Movements.Stage.EN_ROUTE;
  4528. } else if (statusClass && statusClass.indexOf('arrow_left_green') >= 0) {
  4529. stage = Constants.Movements.Stage.RETURNING;
  4530. }
  4531. var transports = 0;
  4532. var resources = {};
  4533. var units = new MilitaryUnits();
  4534.  
  4535. $.each(
  4536. data['js_MilitaryMovementsEventRow' + movementId + 'UnitDetails']
  4537. .appendElement || [],
  4538. function processUnit(index, item) {
  4539. var count = parseInt(item.text);
  4540. if (item.class.indexOf('ship_transport') >= 0) {
  4541. transports = count;
  4542. }
  4543. if (item.class.indexOf(Constants.Resources.WOOD) >= 0) {
  4544. resources[Constants.Resources.WOOD] = count;
  4545. } else if (item.class.indexOf(Constants.Resources.WINE) >= 0) {
  4546. resources[Constants.Resources.WINE] = count;
  4547. } else if (item.class.indexOf(Constants.Resources.MARBLE) >= 0) {
  4548. resources[Constants.Resources.MARBLE] = count;
  4549. } else if (item.class.indexOf(Constants.Resources.GLASS) >= 0) {
  4550. resources[Constants.Resources.GLASS] = count;
  4551. } else if (item.class.indexOf(Constants.Resources.SULFUR) >= 0) {
  4552. resources[Constants.Resources.SULFUR] = count;
  4553. }
  4554. $.each(Constants.Military, function findIsUnit(key, type) {
  4555. if (item.class.indexOf(' ' + type) >= 0) {
  4556. units._setCount(type, count);
  4557. return false;
  4558. }
  4559. });
  4560. });
  4561. var movement = new Movement(
  4562. movementId, type, completionTime, mission, stage, originCityId, targetCityId,
  4563. transports, units, resources);
  4564. civilizationData._updateMovement(movement, changes);
  4565. }
  4566. });
  4567. $.each(movementIds, function removeMissingMovements(id, value) {
  4568. civilizationData._removeMovement(id, changes);
  4569. });
  4570. raiseMovementsChanged(changes);
  4571. }
  4572. function processPremiumView(data) {
  4573. var civilizationData = getCivilizationData();
  4574. var changes = [];
  4575.  
  4576. civilizationData._updatePremiumFeature(changes,
  4577. Constants.PremiumFeatures.DOUBLED_SAFE_CAPACITY,
  4578. $('#js_buySafecapacityBonusActiveTime').hasClass('green'));
  4579. civilizationData._updatePremiumFeature(changes,
  4580. Constants.PremiumFeatures.DOUBLED_STORAGE_CAPACITY,
  4581. $('#js_buyStoragecapacityBonusActiveTime').hasClass('green'));
  4582. raiseCivilizationDataChanged(changes);
  4583. }
  4584. function updateAndStartTracking() {
  4585. // Process all known cities that show up in the dropdown.
  4586. // Drop any cities that are no longer there.
  4587. var cities = { };
  4588. var cityOrder = [];
  4589. function updateCurrentCity(globalData, backgroundData, correctWineConsumption) {
  4590. var currentCity = View.getCurrentCity();
  4591. if (View.viewIsCity() && currentCity.getId() == parseInt(backgroundData.id)) {
  4592. currentCity._updateFromBackgroundData(backgroundData);
  4593. }
  4594. currentCity._updateFromGlobalData(globalData, correctWineConsumption);
  4595. Logging.debug("Current city %s[%s]: ",
  4596. currentCity.name, currentCity.id, currentCity);
  4597. }
  4598.  
  4599. $.each(unsafeWindow.dataSetForView.relatedCityData,
  4600. function updateFromPage_Each(key, value) {
  4601. if (key.substring(0, 5) == "city_") {
  4602. var city = empireData.get().cities[value.id] ||
  4603. new City(value.id, value.relationship);
  4604. city.type = value.relationship;
  4605.  
  4606. city.name = value.name;
  4607. city.islandCoordinates = parseCoordinates(value.coords);
  4608. if (value.tradegood) {
  4609. city.tradeGoodType = TRADE_GOOD_LOOKUP[value.tradegood];
  4610. }
  4611.  
  4612. empireData.get().cities[city.id] = city;
  4613. cityOrder.push(city.id);
  4614.  
  4615. Logging.debug("City %s[%s]: %o", city.name, city.id, city);
  4616. }
  4617. });
  4618.  
  4619. empireData.get().cityOrder = cityOrder;
  4620.  
  4621. var globalData = {
  4622. maxActionPoints: parseInt($('#js_GlobalMenu_maxActionPoints').text()),
  4623. };
  4624. updateCurrentCity($.extend(globalData, unsafeWindow.dataSetForView),
  4625. unsafeWindow.ikariam.backgroundView.screen.data,
  4626. true);
  4627. empireData.saveAsync();
  4628. function updateEmpireDataFromGlobalData(data) {
  4629.  
  4630. View.setGameTimeDifference(new Date().getTime() - (data['time'] || data[1]) *
  4631. Constants.Time.MILLIS_PER_SECOND);
  4632. updateCurrentCity(data['headerData'] || data[10], data['backgroundData'] || data[11]);
  4633. }
  4634. View.registerIkariamAjaxResponseCallback(
  4635. function updateEmpireDataFromAjaxResponse(response) {
  4636. var globalData;
  4637. var view;
  4638. var viewHtml;
  4639. var templateData;
  4640.  
  4641. Utils.iterateIkariamAjaxResponse(response, function(index, name, data) {
  4642. if (name == Constants.IkariamAjaxResponseType.UPDATE_GLOBAL_DATA) {
  4643. globalData = data;
  4644. } else if (name == Constants.IkariamAjaxResponseType.CHANGE_VIEW) {
  4645. view = data[0];
  4646. viewHtml = data[1];
  4647. } else if (name == Constants.IkariamAjaxResponseType.UPDATE_TEMPLATE_DATA) {
  4648. templateData = data;
  4649. }
  4650. });
  4651.  
  4652. if (globalData) {
  4653. updateEmpireDataFromGlobalData(globalData);
  4654. }
  4655.  
  4656. if (view == Constants.View.CITY_MILITARY) {
  4657. processCityMilitaryView(templateData);
  4658. } else if (view == Constants.View.RELATED_CITIES) {
  4659. processRelatedCitiesView(templateData);
  4660. } else if (view == Constants.View.ACADEMY) {
  4661. processAcademyView(templateData);
  4662. } else if (view == Constants.View.PALACE) {
  4663. processPalaceView(templateData);
  4664. } else if (view == Constants.View.MUSEUM) {
  4665. processMuseumView(templateData);
  4666. } else if (view == Constants.View.ASSIGN_CULTURAL_POSSESSIONS) {
  4667. processCulturalPossessionsAssignView(templateData);
  4668. } else if (view == Constants.View.TOWN_HALL) {
  4669. processTownHallView(templateData);
  4670. } else if (view == Constants.View.TEMPLE) {
  4671. processTempleView(templateData);
  4672. } else if (view == Constants.View.RESEARCH_ADVISOR) {
  4673. processResearchAdvisorView(templateData);
  4674. } else if (view == Constants.View.FINANCES) {
  4675. processFinancesView(templateData);
  4676. } else if (view == Constants.View.BARRACKS) {
  4677. processBarracksView(viewHtml, templateData);
  4678. } else if (view == Constants.View.SHIPYARD) {
  4679. processShipyardView(viewHtml, templateData);
  4680. } else if (view == Constants.View.MILITARY_ADVISOR) {
  4681. processMilitaryAdvisorView(templateData);
  4682. } else if (view == Constants.View.PREMIUM) {
  4683. processPremiumView(templateData);
  4684. }
  4685.  
  4686. if (unsafeWindow.ikariam.templateView) {
  4687. if (unsafeWindow.ikariam.templateView.id == Constants.View.RESEARCH_ADVISOR) {
  4688. processResearchAdvisorView(templateData);
  4689. }
  4690. }
  4691.  
  4692. empireData.saveAsync();
  4693. }, true);
  4694. View.registerAjaxFormSubmitCallback(
  4695. function ajaxHandlerCallFromFormEmpireDataUpdate(form) {
  4696. if (form.id == 'transport' || form.id == 'transportForm') {
  4697. processTransportForm(form);
  4698. } else if (form.id == 'deploymentForm') {
  4699. processDeploymentForm(form);
  4700. } else if (form.id == 'plunderForm') {
  4701. processPlunderForm(form);
  4702. } else if (form.id == 'setScientists') {
  4703. processSetScientistsForm(form);
  4704. }
  4705. });
  4706. }
  4707. function updateMovements(callback) {
  4708. View.backgroundGetIkariamPage(
  4709. 'http://' + document.domain + '/index.php?view=militaryAdvisor&activeTab=militaryMovements&ajax=1',
  4710. function updateMovementsCallback(response) {
  4711. var dataResponse = JSON.parse(response.responseText);
  4712. Utils.iterateIkariamAjaxResponse(dataResponse, function(index, name, data) {
  4713. if (name == Constants.IkariamAjaxResponseType.UPDATE_TEMPLATE_DATA) {
  4714. processMilitaryAdvisorView(data);
  4715. }
  4716. });
  4717. empireData.saveAsync();
  4718. callback(dataResponse);
  4719. },
  4720. 'POST');
  4721. }
  4722. function getCities() {
  4723. var data = empireData.get();
  4724. var cities = [];
  4725. for (var i = 0; i < data.cityOrder.length; i++) {
  4726. cities.push(data.cities[data.cityOrder[i]]);
  4727. }
  4728. return cities;
  4729. }
  4730. function getOwnCities() {
  4731. return getCities().filter(function(city) {
  4732. return city.isOwn();
  4733. });
  4734. }
  4735. function getCivilizationData() {
  4736. return empireData.get().civilizationData;
  4737. }
  4738. function getDebugString(includePrivateData) {
  4739. return JSON.stringify(empireData.get(), function debugStringify(name, value) {
  4740. if (name === 'name' || name === 'islandCoordinates') {
  4741. return undefined;
  4742. }
  4743. return value;
  4744. });
  4745. }
  4746. function resetData() {
  4747. empireData.reset();
  4748. }
  4749.  
  4750. var Espionage = function() {
  4751. function Target(id) {
  4752. this._ikaToolsType = 'target';
  4753. if (id) {
  4754. this.id = id;
  4755.  
  4756. this.playerId = undefined;
  4757. this.allianceId = undefined;
  4758. this.townLevel = undefined;
  4759. this.wallLevel = undefined;
  4760. this.warehouseLevel = undefined;
  4761.  
  4762. this.islandId = undefined;
  4763. this.coords = undefined;
  4764.  
  4765. this.occupierId = undefined;
  4766. this.blockaderId = undefined;
  4767. this.tradeGoodType = undefined;
  4768.  
  4769. this.lastUpdateTime = 0;
  4770. this.military = new MilitaryUnits();
  4771. this.otherMilitary = new MilitaryUnits();
  4772. this.militaryLastSpyMessageId = 0;
  4773. this.militaryLastSpyTime = 0;
  4774.  
  4775. this.resources = {
  4776. wood: 0,
  4777. wine: 0,
  4778. marble: 0,
  4779. glass: 0,
  4780. sulfur: 0,
  4781.  
  4782. lastSpyMessageId: 0,
  4783. lastSpyTime: 0,
  4784. };
  4785.  
  4786. this.combats = {};
  4787. }
  4788. }
  4789.  
  4790. function combatComparer(combat1, combat2) {
  4791. return combat2.time - combat1.time;
  4792. };
  4793.  
  4794. $.extend(Target.prototype, {
  4795. getId: function getId() {
  4796. return this.id;
  4797. },
  4798. getName: function getName() {
  4799. return this.name;
  4800. },
  4801. getTownLevel: function getTownLevel() {
  4802. return this.townLevel;
  4803. },
  4804. getWallLevel: function getWallLevel() {
  4805. return this.wallLevel;
  4806. },
  4807. getIslandCoordinates: function getIslandCoordinates() {
  4808. return this.coords;
  4809. },
  4810. getIslandId: function getIslandId() {
  4811. return this.islandId;
  4812. },
  4813. getPlayer: function _getPlayer() {
  4814. return getPlayer(this.playerId);
  4815. },
  4816. getOccupier: function getOccupier() {
  4817. if (this.occupierId) {
  4818. return getPlayer(this.occupierId);
  4819. }
  4820. return null;
  4821. },
  4822. getBlockader: function getBlockader() {
  4823. if (this.blockaderId) {
  4824. return getPlayer(this.blockaderId);
  4825. }
  4826. },
  4827. getTradeGoodType: function getTradeGoodType() {
  4828. return this.tradeGoodType;
  4829. },
  4830. getMilitary: function getMilitary() {
  4831. return this.military;
  4832. },
  4833. getOtherMilitary: function getOtherMilitary() {
  4834. return this.otherMilitary;
  4835. },
  4836. hasResourceInfo: function hasResourceInfo() {
  4837. return this.resources.lastSpyMessageId > 0;
  4838. },
  4839. hasMilitaryInfo: function hasMilitaryInfo() {
  4840. return this.militaryLastSpyMessageId > 0;
  4841. },
  4842. getLootableResources: function getLootableResources(type) {
  4843. var available = this.resources[type];
  4844. $.each(this.getCombats(View.gameTimeNow() - this.resources.lastSpyTime),
  4845. function subtractCombatResources(index, combat) {
  4846. available -= combat.getLooted(type);
  4847. });
  4848. return Math.max(0, available -
  4849. (this.getPlayer().isInactive() ?
  4850. Constants.GamePlay.RESOURCE_PROTECTION_WAREHOUSE_INACTIVE :
  4851. Constants.GamePlay.RESOURCE_PROTECTION_WAREHOUSE) * this.warehouseLevel -
  4852. Constants.GamePlay.BASE_RESOURCE_PROTECTION);
  4853. },
  4854. getResourcesSpyTime: function getResourcesSpyTime() {
  4855. return this.resources.lastSpyTime;
  4856. },
  4857. getMilitarySpyTime: function getMilitarySpyTime() {
  4858. return this.militaryLastSpyTime;
  4859. },
  4860. getCombats: function getCombats(maxAge) {
  4861. var combats = [];
  4862. $.each(this.combats, function(index, combat) {
  4863. if (View.gameTimeNow() - combat.time <= maxAge) {
  4864. combats.push(combat);
  4865. }
  4866. });
  4867. combats.sort(combatComparer);
  4868. return combats;
  4869. },
  4870. remove: function remove() {
  4871. delete espionageData.get().targets[this.id];
  4872. espionageData.saveAsync();
  4873. raiseEspionageChanged({
  4874. type: 'targetRemoved',
  4875. targets: [this]
  4876. });
  4877. },
  4878. _refresh: function refresh(hasSpiesPresent, callback) {
  4879. if (View.gameTimeNow() - this.lastRefreshTime < Constants.Time.MILLIS_PER_HOUR) {
  4880. console.log('Skipping refresh');
  4881. callback();
  4882. return;
  4883. }
  4884.  
  4885. this.lastRefreshTime = new Date().getTime();
  4886. var datasLoaded = hasSpiesPresent ? 2 : 1;
  4887. function doneLoading() {
  4888. if (--datasLoaded == 0) {
  4889. callback();
  4890. }
  4891. }
  4892. Utils.backgroundFetchIkariamFullPage(
  4893. 'http://' + document.domain + '/index.php?view=island&cityId=' + this.id,
  4894. this._refreshIslandCallback.bind(this, doneLoading));
  4895. if (hasSpiesPresent) {
  4896. Utils.backgroundFetchIkariamFullPage(
  4897. 'http://' + document.domain + '/index.php?view=city&cityId=' + this.id,
  4898. this._refreshCityCallback.bind(this, doneLoading));
  4899. }
  4900. },
  4901. _refreshIslandCallback: function refreshIslandCallback(callback, response, ajaxResponse) {
  4902. var target = this;
  4903. Utils.iterateIkariamAjaxResponse(ajaxResponse,
  4904. function refreshTargetData(index, name, data) {
  4905. if (name == IkaTools.Constants.IkariamAjaxResponseType.UPDATE_BACKGROUND_DATA) {
  4906. $.each(data.cities, function findTarget(index, city) {
  4907. if (parseInt(city.id) == target.id) {
  4908. var playerId = parseInt(city.ownerId);
  4909. target.name = city.name;
  4910. target.playerId = parseInt(city.ownerId);
  4911. target.townLevel = parseInt(city.level);
  4912. target.islandId = parseInt(data.id);
  4913. target.coords = [parseInt(data.xCoord), parseInt(data.yCoord)];
  4914. target.tradeGoodType = TRADE_GOOD_LOOKUP[data.tradegood];
  4915. updateOrAddPlayer(playerId, city.ownerName, city.state, parseInt(city.ownerAllyId),
  4916. city.ownerAllyTag,
  4917. parseInt(data.avatarScores[playerId].army_score_main.split(',').join('').split(',').join('')) / 100);
  4918. if (city.infos && city.infos['occupation']) {
  4919. target.occupierId = city.infos['occupation'].id;
  4920. updateOrAddPlayer(city.infos['occupation'].id, city.infos['occupation'].name);
  4921. } else {
  4922. target.occupierId = 0;
  4923. }
  4924. if (city.infos && city.infos['fleetAction']) {
  4925. target.blockaderId = city.infos['fleetAction'].id;
  4926. updateOrAddPlayer(city.infos['fleetAction'].id, city.infos['fleetAction'].name);
  4927. } else {
  4928. target.blockaderId = 0;
  4929. }
  4930. }
  4931. });
  4932. }
  4933. });
  4934. callback(this);
  4935. },
  4936. _refreshCityCallback: function refreshCityCallback(callback, response, ajaxResponse) {
  4937. var target = this;
  4938. Utils.iterateIkariamAjaxResponse(ajaxResponse,
  4939. function refreshTargetData(index, name, data) {
  4940. if (name == IkaTools.Constants.IkariamAjaxResponseType.UPDATE_BACKGROUND_DATA) {
  4941. target.wallLevel = parseInt(data.position[14].level) || 0;
  4942. target.warehouseLevel = 0;
  4943. $.each(data.position, function(index, item) {
  4944. if (item.building == Constants.Buildings.WAREHOUSE) {
  4945. target.warehouseLevel += parseInt(item.level);
  4946. }
  4947. });
  4948. }
  4949. });
  4950. callback(this);
  4951. },
  4952. _getOrAddCombat: function getOrAddCombat(id) {
  4953. var combat = this.combats[id];
  4954. if (!combat) {
  4955. combat = new Target.Combat(id);
  4956. this.combats[id] = combat;
  4957. }
  4958. return combat;
  4959. },
  4960. });
  4961.  
  4962. Target.Combat = function Target_Combat(id) {
  4963. this._ikaToolsType = 'targetCombat';
  4964.  
  4965. if (id) {
  4966. this.id = id;
  4967. this.type = undefined;
  4968. this.time = undefined;
  4969.  
  4970. this.resources = {
  4971. wood: 0,
  4972. wine: 0,
  4973. marble: 0,
  4974. glass: 0,
  4975. sulfur: 0,
  4976. };
  4977. }
  4978. }
  4979.  
  4980. $.extend(Target.Combat.prototype, {
  4981. getType: function getType() {
  4982. return this.type;
  4983. },
  4984. getTime: function getTime() {
  4985. return this.time;
  4986. },
  4987. getLooted: function getLooted(resourceType) {
  4988. return this.resources[resourceType];
  4989. },
  4990. });
  4991.  
  4992. function Player(id) {
  4993. this._ikaToolsType = 'player';
  4994. if (id) {
  4995. this.id = id;
  4996. this.name = null;
  4997. this.allianceId = null;
  4998. this.militaryScore = null;
  4999. }
  5000. }
  5001.  
  5002. $.extend(Player.prototype, {
  5003. _update: function update(name, state, allianceId, militaryScore) {
  5004. this.name = name;
  5005. if (state !== undefined) {
  5006. this.allianceId = allianceId;
  5007. this.militaryScore = militaryScore;
  5008. this.state = state;
  5009. }
  5010. },
  5011. getAlliance: function getAlliance() {
  5012. if (this.allianceId) {
  5013. return espionageData.get().alliances[this.allianceId];
  5014. } else {
  5015. return;
  5016. }
  5017. },
  5018. getName: function getName() {
  5019. return this.name;
  5020. },
  5021. getState: function getState() {
  5022. return this.state;
  5023. },
  5024. getMilitaryScore: function getMilitaryScore() {
  5025. return this.militaryScore;
  5026. },
  5027. isInactive: function isInactive() {
  5028. return this.state == Constants.PlayerState.INACTIVE;
  5029. },
  5030. });
  5031.  
  5032. function updateOrAddPlayer(id, name, state, allianceId, allianceName, militaryScore) {
  5033. var players = espionageData.get().players;
  5034. var player = players[id];
  5035. if (!player) {
  5036. player = new Player(id);
  5037. players[id] = player;
  5038. }
  5039. player._update(name, state, allianceId, militaryScore);
  5040. updateOrAddAlliance(allianceId, allianceName);
  5041. }
  5042.  
  5043. function Alliance(id) {
  5044. this._ikaToolsType = 'alliance';
  5045. if (id) {
  5046. this.id = id;
  5047. this.name = null;
  5048. }
  5049. }
  5050.  
  5051. $.extend(Alliance.prototype, {
  5052. _update: function update(name) {
  5053. this.name = name;
  5054. },
  5055. getName: function getName() {
  5056. return this.name;
  5057. },
  5058. getId: function getId() {
  5059. return this.id;
  5060. }
  5061. });
  5062.  
  5063. function updateOrAddAlliance(id, name) {
  5064. if (id) {
  5065. var alliances = espionageData.get().alliances;
  5066. var alliance = alliances[id];
  5067. if (!alliance) {
  5068. alliance = new Alliance(id);
  5069. alliances[id] = alliance;
  5070. }
  5071. alliance._update(name);
  5072. }
  5073. }
  5074.  
  5075. function addTargetById(id, hasSpiesPresent, callback) {
  5076. var targets = espionageData.get().targets;
  5077. var target = targets[id];
  5078. if (!target) {
  5079. var target = new Target(id);
  5080. target._refresh(hasSpiesPresent, function() {
  5081. targets[id] = target;
  5082. callback(target);
  5083. });
  5084. } else {
  5085. target._refresh(hasSpiesPresent, function() {
  5086. callback(target);
  5087. });
  5088. }
  5089. }
  5090.  
  5091. function getTargets() {
  5092. var targets = [];
  5093. $.each(espionageData.get().targets, function(index, target) {
  5094. targets.push(target);
  5095. });
  5096. return targets;
  5097. }
  5098.  
  5099. function getTarget(id) {
  5100. return espionageData.get().targets[id];
  5101. }
  5102.  
  5103. function getPlayer(id) {
  5104. return espionageData.get().players[id];
  5105. }
  5106.  
  5107. function getPlayers() {
  5108. return espionageData.get().players;
  5109. }
  5110.  
  5111. var espionageData = new Data.Value(
  5112. 'espionageData',
  5113. {
  5114. targets: {},
  5115. alliances: {},
  5116. players: {},
  5117. },
  5118. {
  5119. reviver: function espionageDataReviver(key, value) {
  5120. if (value && value._ikaToolsType) {
  5121. var obj;
  5122.  
  5123. switch(value._ikaToolsType) {
  5124. case 'target': obj = new Target(); break;
  5125. case 'targetCombat': obj = new Target.Combat(); break;
  5126. case 'player': obj = new Player(); break;
  5127. case 'alliance': obj = new Alliance(); break;
  5128. case 'militaryUnits': obj = new MilitaryUnits(); break;
  5129. }
  5130. $.extend(obj, value);
  5131. if (obj._postLoad) {
  5132. obj._postLoad();
  5133. }
  5134. return obj;
  5135. }
  5136. return value;
  5137. },
  5138. version: 6,
  5139.  
  5140. loadCallback: function espionageDataLoaded() {
  5141. },
  5142. });
  5143.  
  5144. var espionageChangedEvent = Utils.thunk(function() {
  5145. return new Utils.EventDispatcher();
  5146. });
  5147. function registerEspionageChangedHandler(callback) {
  5148. return espionageChangedEvent().addListener(callback);
  5149. }
  5150.  
  5151. function raiseEspionageChanged(changes) {
  5152. espionageChangedEvent().send(changes);
  5153. }
  5154.  
  5155. function startTracking() {
  5156. var messageIdRegex = /message(\d+)/
  5157. espionageData.load();
  5158. View.registerIkariamAjaxResponseCallback(
  5159. function processHideoutView(response) {
  5160. IkaTools.Utils.iterateIkariamAjaxResponse(response, function(index, name, data) {
  5161. if (name == IkaTools.Constants.IkariamAjaxResponseType.CHANGE_VIEW) {
  5162. if (data[0] == Constants.View.HIDEOUT) {
  5163. var targetCount = 0;
  5164. var targets = [];
  5165. $('#tabSafehouse li.city a').each(function(index, item) {
  5166. targetCount++;
  5167. addTargetById(parseInt(Utils.parseUrlParams($(item).attr('href'))['cityId']), true,
  5168. function targetAdded(target) {
  5169. targets.push(target);
  5170. targetCount--;
  5171. if (targetCount == 0) {
  5172. espionageData.saveAsync();
  5173. raiseEspionageChanged({
  5174. type: 'targetsChanged',
  5175. targets: targets
  5176. });
  5177. }
  5178. });
  5179. });
  5180.  
  5181. var reportHeaders = $(
  5182. '#espionageReports tr.espionageReports, #espionageReports tr.espionageReportsalt');
  5183. if (reportHeaders.length) {
  5184. var changedTargets = [];
  5185. reportHeaders.each(function(index, reportHeader) {
  5186. var success = $('td.resultImage img', reportHeader).attr('src') == 'skin/buttons/yes.png';
  5187. var target = getTarget(parseInt(
  5188. Utils.parseUrlParams($('td.targetCity a', reportHeader).attr('href'))['selectCity']));
  5189. var messageId = parseInt(reportHeader.id.match(messageIdRegex)[1]);
  5190. var tableMailMessage = $('#tbl_mail' + messageId);
  5191. if (success && target) {
  5192. if ($('td.money', reportHeader).length &&
  5193. messageId > target.resources.lastSpyMessageId) {
  5194. // Warehouse resources mission
  5195. var resourceTds = $('#tbl_mail' + messageId + ' td.count');
  5196. target.resources.wood = parseInt(resourceTds.get(0).textContent.replace(',', ''));
  5197. target.resources.wine = parseInt(resourceTds.get(1).textContent.replace(',', ''));
  5198. target.resources.marble = parseInt(resourceTds.get(2).textContent.replace(',', ''));
  5199. target.resources.glass = parseInt(resourceTds.get(3).textContent.replace(',', ''));
  5200. target.resources.sulfur = parseInt(resourceTds.get(4).textContent.replace(',', ''));
  5201. target.resources.lastSpyMessageId = messageId;
  5202. target.resources.lastSpyTime =
  5203. Utils.parseIkariamTimestamp($('td.date', reportHeader).text()).getTime();
  5204. target._refresh(false, function() {
  5205. espionageData.saveAsync(),
  5206. raiseEspionageChanged({
  5207. type: 'targetsRefreshed',
  5208. targets: [target],
  5209. });
  5210. });
  5211. changedTargets.push(target);
  5212. } else if ($('td.garrison', reportHeader).length &&
  5213. messageId > target.militaryLastSpyMessageId &&
  5214. tableMailMessage.find(' table.reportTable').length) {
  5215.  
  5216. readSpiedMilitary(target.getMilitary(),
  5217. tableMailMessage.find('table.reportTable tr:nth-child(2) td.count'));
  5218. readSpiedMilitary(target.getOtherMilitary(),
  5219. tableMailMessage.find('table.reportTable tr:nth-child(3) td.count'));
  5220. target.militaryLastSpyMessageId = messageId;
  5221. target.militaryLastSpyTime = Utils.parseIkariamTimestamp($('td.date', reportHeader).text()).getTime();
  5222.  
  5223. target._refresh(false, function() {
  5224. espionageData.saveAsync(),
  5225. raiseEspionageChanged({
  5226. type: 'targetsRefreshed',
  5227. targets: [target],
  5228. });
  5229. });
  5230. changedTargets.push(target);
  5231. }
  5232. }
  5233. });
  5234. if (changedTargets.length) {
  5235. espionageData.saveAsync();
  5236. raiseEspionageChanged({
  5237. type: 'targetsChanged',
  5238. targets: changedTargets
  5239. });
  5240. }
  5241. }
  5242. } else if (data[0] == Constants.View.MILITARY_ADVISOR_REPORT) {
  5243. var report = $('#troopsReport');
  5244. var defender = report.find('.defender b:first a:first');
  5245. if (defender.length) {
  5246. var target = getTarget(parseInt(
  5247. Utils.parseUrlParams(defender.attr('href'))['cityId']));
  5248. if (target) {
  5249. var header = report.find('.header');
  5250. var result = report.find('div.result');
  5251. var combatId = parseInt(Utils.parseUrlParams(
  5252. report.find('div p.link:first a.button:first',report).attr('href'))['detailedCombatId']);
  5253. var combatTime = Utils.parseIkariamTimestamp(report.find('.header .date').text()).getTime();
  5254. var type = report.find('.overview .fleet').length ?
  5255. IkaTools.Constants.CombatType.BLOCKADE : IkaTools.Constants.CombatType.PILLAGE;
  5256.  
  5257. var combat = target._getOrAddCombat(combatId);
  5258. combat.type = type;
  5259. combat.time = combatTime;
  5260. result.find('.resources li.value').each(function(index, item) {
  5261. var resourceInfo = $(item);
  5262. var type = resourceInfo.find('img').attr('src').match(/icon_([a-z]*)_small.png/)[1];
  5263. if (type == 'crystal') {
  5264. type = Constants.Resources.GLASS;
  5265. }
  5266. var amount = parseInt(resourceInfo.text());
  5267. combat.resources[type] = amount;
  5268. });
  5269.  
  5270. target._refresh(false, function() {
  5271. espionageData.saveAsync(),
  5272. raiseEspionageChanged({
  5273. type: 'targetsRefreshed',
  5274. targets: [target],
  5275. });
  5276. });
  5277. }
  5278. espionageData.saveAsync();
  5279. raiseEspionageChanged({
  5280. type: 'combatUpdated',
  5281. targets: [target],
  5282. });
  5283. }
  5284. }
  5285. } else if (name == Constants.IkariamAjaxResponseType.BACKGROUND_DATA) {
  5286. }
  5287. });
  5288. }, true);
  5289. }
  5290.  
  5291. function readSpiedMilitary(military, unitTds) {
  5292. var baseArmyCount = true;
  5293. var baseNavyCount = true;
  5294. var navyOffset = 0;
  5295. if (unitTds.length == 14) { // army only
  5296. baseNavyCount = 0;
  5297. } else if (unitTds.length == 11) { // navy only
  5298. baseArmyCount = 0;
  5299. navyOffset = -14;
  5300. } else if (unitTds.length == 0) { // nothing
  5301. baseNavyCount = 0;
  5302. baseArmyCount = 0;
  5303. }
  5304. military._setCount(Constants.Military.HOPLITE, baseArmyCount && parseInt($(unitTds[0]).text()) || 0);
  5305. military._setCount(Constants.Military.STEAM_GIANT, baseArmyCount && parseInt($(unitTds[1]).text()) || 0);
  5306. military._setCount(Constants.Military.SPEARMAN, baseArmyCount && parseInt($(unitTds[2]).text()) || 0);
  5307. military._setCount(Constants.Military.SWORDSMAN, baseArmyCount && parseInt($(unitTds[3]).text()) || 0);
  5308. military._setCount(Constants.Military.SLINGER, baseArmyCount && parseInt($(unitTds[4]).text()) || 0);
  5309. military._setCount(Constants.Military.ARCHER, baseArmyCount && parseInt($(unitTds[5]).text()) || 0);
  5310. military._setCount(Constants.Military.GUNNER, baseArmyCount && parseInt($(unitTds[6]).text()) || 0);
  5311. military._setCount(Constants.Military.BATTERING_RAM, baseArmyCount && parseInt($(unitTds[7]).text()) || 0);
  5312. military._setCount(Constants.Military.CATAPULT, baseArmyCount && parseInt($(unitTds[8]).text()) || 0);
  5313. military._setCount(Constants.Military.MORTAR, baseArmyCount && parseInt($(unitTds[9]).text()) || 0);
  5314. military._setCount(Constants.Military.GYROCOPTER, baseArmyCount && parseInt($(unitTds[10]).text()) || 0);
  5315. military._setCount(Constants.Military.BALLOON_BOMBADIER, baseArmyCount && parseInt($(unitTds[11]).text()) || 0);
  5316. military._setCount(Constants.Military.COOK, baseArmyCount && parseInt($(unitTds[12]).text()) || 0);
  5317. military._setCount(Constants.Military.DOCTOR, baseArmyCount && parseInt($(unitTds[13]).text()) || 0);
  5318.  
  5319. military._setCount(Constants.Military.RAM_SHIP, parseInt(baseNavyCount && $(unitTds[16 + navyOffset]).text()) || 0);
  5320. military._setCount(Constants.Military.FLAME_THROWER, parseInt(baseNavyCount && $(unitTds[14 + navyOffset]).text()) || 0);
  5321. military._setCount(Constants.Military.STEAM_RAM, parseInt(baseNavyCount && $(unitTds[15 + navyOffset]).text()) || 0);
  5322. military._setCount(Constants.Military.BALLISTA_SHIP, parseInt(baseNavyCount && $(unitTds[18 + navyOffset]).text()) || 0);
  5323. military._setCount(Constants.Military.CATAPULT_SHIP, parseInt(baseNavyCount && $(unitTds[17 + navyOffset]).text()) || 0);
  5324. military._setCount(Constants.Military.MORTAR_SHIP, parseInt(baseNavyCount && $(unitTds[19 + navyOffset]).text()) || 0);
  5325. military._setCount(Constants.Military.SUBMARINE, parseInt(baseNavyCount && $(unitTds[21 + navyOffset]).text()) || 0);
  5326. military._setCount(Constants.Military.PADDLE_SPEED_SHIP, parseInt(baseNavyCount && $(unitTds[22 + navyOffset]).text()) || 0);
  5327. military._setCount(Constants.Military.BALLOON_CARRIER, parseInt(baseNavyCount && $(unitTds[23 + navyOffset]).text()) || 0);
  5328. military._setCount(Constants.Military.TENDER, parseInt(baseNavyCount && $(unitTds[24 + navyOffset]).text()) || 0);
  5329. military._setCount(Constants.Military.ROCKET_SHIP, parseInt(baseNavyCount && $(unitTds[20 + navyOffset]).text()) || 0);
  5330. }
  5331. function getDebugString(includePrivateData) {
  5332. return JSON.stringify(espionageData.get(), function debugStringify(name, value) {
  5333. if (name === 'name' || name === 'coordinates') {
  5334. return undefined;
  5335. }
  5336. return value;
  5337. });
  5338. }
  5339.  
  5340. function resetData() {
  5341. espionageData.reset();
  5342. }
  5343. return {
  5344. startTracking: startTracking,
  5345. registerEspionageChangedHandler: registerEspionageChangedHandler,
  5346. getTargets: getTargets,
  5347. getPlayers: getPlayers,
  5348. getDebugString: getDebugString,
  5349. resetData: resetData,
  5350. };
  5351. }();
  5352. return {
  5353. updateAndStartTracking: Logging.debuggable(
  5354. { label: 'IkaTools.EmpireData.updateAndStartTracking', alwaysTime: true },
  5355. updateAndStartTracking),
  5356. updateMovements: updateMovements,
  5357. calculateTravelTime: calculateTravelTime,
  5358. getCities: Utils.thunk(getCities),
  5359. getOwnCities: Utils.thunk(getOwnCities),
  5360. getCivilizationData: getCivilizationData,
  5361. getCity: getCity,
  5362. getDebugString: getDebugString,
  5363. resetData: resetData,
  5364. registerCivilizationDataChangedHandler: registerCivilizationDataChangedHandler,
  5365. registerResourcesChangedHandler: registerResourcesChangedHandler,
  5366. registerBuildingsChangedHandler: registerBuildingsChangedHandler,
  5367. registerMilitaryChangedHandler: registerMilitaryChangedHandler,
  5368. registerMovementsChangedHandler: registerMovementsChangedHandler,
  5369.  
  5370. Espionage:Espionage,
  5371. };
  5372. }();
  5373. function processAnchor() {
  5374. var anchor = window.location.hash;
  5375.  
  5376. if (anchor == '#ikaScriptToolsSuppressCityPreselect') {
  5377. document.location.hash = '';
  5378. //unsafeWindow.ikariam.backgroundView.screen.preselectCity =
  5379. // function suppressPreselectCity() { };
  5380. View.suppressFirstChangeViewOfType('cityDetails');
  5381. }
  5382. if (anchor.substring(0, 35) == '#ikaScriptToolsLoadLocalIkariamUrl=') {
  5383. var url = decodeURIComponent(anchor.substring(35));
  5384. document.location.hash = '';
  5385. IkaTools.View.loadLocalIkariamUrl(url);
  5386. if (IkaTools.View.viewIsIsland()) {
  5387. View.suppressFirstChangeViewOfType('cityDetails');
  5388. }
  5389. } else if (anchor.substring(0, 62) ==
  5390. '#ikaScriptToolsLoadLocalIkariamUrl_DoNotSuppressFirstCityInfo=') {
  5391. var url = decodeURIComponent(anchor.substring(62));
  5392. document.location.hash = '';
  5393. IkaTools.View.loadLocalIkariamUrl(url);
  5394. }
  5395. }
  5396. function initialize(options) {
  5397. processAnchor();
  5398. $(window).bind('hashchange', processAnchor);
  5399. options = $.extend({ trackData: true }, options);
  5400. View.setGameTimeDifference(new Date().getTime() -
  5401. unsafeWindow.ikariam.model.requestTime * Constants.Time.MILLIS_PER_SECOND -
  5402. Constants.Time.INITIAL_PAGE_LOAD_DELTA);
  5403. if (options.trackData) {
  5404. IkaTools.EmpireData.updateAndStartTracking();
  5405. }
  5406. }
  5407. return {
  5408. Logging: Logging,
  5409. Utils: Utils,
  5410. View: View,
  5411. Data: Data,
  5412. Intl: Intl,
  5413. EmpireData: EmpireData,
  5414. Constants: Constants,
  5415. UI: UI,
  5416. Settings: Settings,
  5417. initialize: initialize,
  5418. processAnchor: processAnchor,
  5419. };
  5420. })();
  5421. }