franklinglzhou

Untitled

Apr 20th, 2023
16
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 81.15 KB | None | 0 0
  1. /*
  2. Cookie Clicker Agronomicon by Acharvak, 2018–2020
  3. With contributions by Rebecca Turner
  4.  
  5. Permission is hereby granted, free of charge, to any person obtaining a copy
  6. of this software and associated documentation files (the "Software"), to deal
  7. in the Software without restriction, including without limitation the rights
  8. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. copies of the Software, and to permit persons to whom the Software is
  10. furnished to do so, subject to the following conditions:
  11.  
  12. The above copyright notice and this permission notice shall be included in all
  13. copies or substantial portions of the Software.
  14.  
  15. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. SOFTWARE.
  22. */
  23.  
  24. 'use strict';
  25.  
  26. (function() {
  27.  
  28. // ======= DATA =======
  29.  
  30. var VERSION = 2.022; // Should match the game version
  31. var REVISION = 0; // If not 0, the version of the script will be e.g. "2.022
  32. var IS_DEV = false; // If true, the version will be e.g. "2.022-next"
  33. var IS_BETA = false; // If true, the version display will show "(beta)" after the version
  34. // No other effects for now.
  35.  
  36. var RECIPE_AUTOUNLOCKED = 1;
  37. var RECIPE_WEED = 2;
  38. var RECIPE_CREATED_ON_KILL = 3;
  39. var RECIPE_MUTATION = 4;
  40. var RECIPE_SPREAD = 5;
  41.  
  42. var PLANT_LOCKED = 0;
  43. var PLANT_UNLOCKABLE = 1;
  44. var PLANT_MAYGROWEVENTUALLY = 2;
  45. var PLANT_MAYGROW = 3;
  46. var PLANT_WEED = 4;
  47. var PLANT_PREMATURE_DANGER = 5;
  48. var PLANT_PREMATURE = 6;
  49. var PLANT_MATURE_DANGER = 7;
  50. var PLANT_MATURE = 8;
  51. var PLANT_UNLOCKED = 9;
  52.  
  53.  
  54. var Agronomicon = undefined; // Will be set during initialization
  55. var Game = window.Game;
  56.  
  57. /**
  58. * RECIPES is a list of recipes that Agronomicon must check and consider paths to unlock plants.
  59. * Syntax:
  60. * [[ plant key, number mature, number any age, max number plus one (0 if none)], [next plant key, ...], ...]
  61. * Agronomicon will call garden.getMuts to find the outcome of each recipe
  62. */
  63. var RECIPES = [
  64. //if (neighsM['bakerWheat']>=2) muts.push(['bakerWheat',0.2],['thumbcorn',0.05],['bakeberry',0.001]);
  65. [['bakerWheat', 2, 0, 0]],
  66. //if (neighsM['bakerWheat']>=1 && neighsM['thumbcorn']>=1) muts.push(['cronerice',0.01]);
  67. [['bakerWheat', 1, 0, 0], ['thumbcorn', 1, 0, 0]],
  68. //if (neighsM['thumbcorn']>=2) muts.push(['thumbcorn',0.1],['bakerWheat',0.05]);
  69. [['thumbcorn', 2, 0, 0]],
  70. //if (neighsM['cronerice']>=1 && neighsM['thumbcorn']>=1) muts.push(['gildmillet',0.03]);
  71. [['cronerice', 1, 0, 0], ['thumbcorn', 1, 0, 0]],
  72. //if (neighsM['cronerice']>=2) muts.push(['thumbcorn',0.02]);
  73. [['cronerice', 2, 0, 0]],
  74. //if (neighsM['bakerWheat']>=1 && neighsM['gildmillet']>=1) muts.push(['clover',0.03],['goldenClover',0.0007]);
  75. [['bakerWheat', 1, 0, 0], ['gildmillet', 1, 0, 0]],
  76. //if (neighsM['clover']>=1 && neighsM['gildmillet']>=1) muts.push(['shimmerlily',0.02]);
  77. [['clover', 1, 0, 0], ['gildmillet', 1, 0, 0]],
  78. //if (neighsM['clover']>=2 && neighs['clover']<5) muts.push(['clover',0.007],['goldenClover',0.0001]);
  79. [['clover', 2, 0, 5]],
  80. //if (neighsM['clover']>=4) muts.push(['goldenClover',0.0007]);
  81. [['clover', 4, 0, 5]],
  82. [['clover', 4, 1, 0]],
  83. //if (neighsM['shimmerlily']>=1 && neighsM['cronerice']>=1) muts.push(['elderwort',0.01]);
  84. [['shimmerlily', 1, 0, 0], ['cronerice', 1, 0, 0]],
  85. //if (neighsM['wrinklegill']>=1 && neighsM['cronerice']>=1) muts.push(['elderwort',0.002]);
  86. [['wrinklegill', 1, 0, 0], ['cronerice', 1, 0, 0]],
  87.  
  88. //if (neighsM['bakerWheat']>=1 && neighs['brownMold']>=1) muts.push(['chocoroot',0.1]);
  89. [['bakerWheat', 1, 0, 0], ['brownMold', 0, 1, 0]],
  90. //if (neighsM['chocoroot']>=1 && neighs['whiteMildew']>=1) muts.push(['whiteChocoroot',0.1]);
  91. [['chocoroot', 1, 0, 0], ['whiteMildew', 0, 1, 0]],
  92. //if (neighsM['whiteMildew']>=1 && neighs['brownMold']<=1) muts.push(['brownMold',0.5]);
  93. [['whiteMildew', 1, 0, 0], ['brownMold', 0, 0, 2]],
  94. //if (neighsM['brownMold']>=1 && neighs['whiteMildew']<=1) muts.push(['whiteMildew',0.5]);
  95. [['brownMold', 1, 0, 0], ['whiteMildew', 0, 0, 2]],
  96. //if (neighsM['meddleweed']>=1 && neighs['meddleweed']<=3) muts.push(['meddleweed',0.15]);
  97. [['meddleweed', 1, 0, 4]],
  98.  
  99. //if (neighsM['shimmerlily']>=1 && neighsM['whiteChocoroot']>=1) muts.push(['whiskerbloom',0.01]);
  100. [['shimmerlily', 1, 0, 0], ['whiteChocoroot', 1, 0, 0]],
  101. //if (neighsM['shimmerlily']>=1 && neighsM['whiskerbloom']>=1) muts.push(['chimerose',0.05]);
  102. [['shimmerlily', 1, 0, 0], ['whiskerbloom', 1, 0, 0]],
  103. //if (neighsM['chimerose']>=2) muts.push(['chimerose',0.005]);
  104. [['chimerose', 2, 0, 0]],
  105. //if (neighsM['whiskerbloom']>=2) muts.push(['nursetulip',0.05]);
  106. [['whiskerbloom', 2, 0, 0]],
  107. //if (neighsM['chocoroot']>=1 && neighsM['keenmoss']>=1) muts.push(['drowsyfern',0.005]);
  108. [['chocoroot', 1, 0, 0], ['keenmoss', 1, 0, 0]],
  109. //if ((neighsM['cronerice']>=1 && neighsM['keenmoss']>=1) || (neighsM['cronerice']>=1 && neighsM['whiteMildew']>=1)) muts.push(['wardlichen',0.005]);
  110. [['cronerice', 1, 0, 0], ['keenmoss', 1, 0, 2]],
  111. [['cronerice', 1, 0, 0], ['keenmoss', 1, 1, 0]],
  112. [['cronerice', 1, 0, 0], ['whiteMildew', 1, 0, 0]],
  113. //if (neighsM['wardlichen']>=1 && neighs['wardlichen']<2) muts.push(['wardlichen',0.05]);
  114. [['wardlichen', 1, 0, 2]],
  115. //if (neighsM['greenRot']>=1 && neighsM['brownMold']>=1) muts.push(['keenmoss',0.1]);
  116. [['greenRot', 1, 0, 0], ['brownMold', 1, 0, 0]],
  117. //if (neighsM['keenmoss']>=1 && neighs['keenmoss']<2) muts.push(['keenmoss',0.05]);
  118. [['keenmoss', 1, 0, 0], ['keenmoss', 0, 0, 2]],
  119. //if (neighsM['chocoroot']>=1 && neighsM['bakeberry']>=1) muts.push(['queenbeet',0.01]);
  120. [['chocoroot', 1, 0, 0], ['bakeberry', 1, 0, 0]],
  121. //if (neighsM['queenbeet']>=8) muts.push(['queenbeetLump',0.001]);
  122. [['queenbeet', 8, 0, 0]],
  123. //if (neighsM['queenbeet']>=2) muts.push(['duketater',0.001]);
  124. [['queenbeet', 2, 0, 8]],
  125.  
  126. //if (neighsM['crumbspore']>=1 && neighs['crumbspore']<=1) muts.push(['crumbspore',0.07]);
  127. [['crumbspore', 1, 0, 2]],
  128. //if (neighsM['crumbspore']>=1 && neighsM['thumbcorn']>=1) muts.push(['glovemorel',0.02]);
  129. [['crumbspore', 1, 0, 0], ['thumbcorn', 1, 0, 0]],
  130. //if (neighsM['crumbspore']>=1 && neighsM['shimmerlily']>=1) muts.push(['cheapcap',0.04]);
  131. [['crumbspore', 1, 0, 0], ['shimmerlily', 1, 0, 0]],
  132. //if (neighsM['doughshroom']>=1 && neighsM['greenRot']>=1) muts.push(['foolBolete',0.04]);
  133. [['doughshroom', 1, 0, 0], ['greenRot', 1, 0, 0]],
  134. //if (neighsM['crumbspore']>=2) muts.push(['doughshroom',0.005]);
  135. [['crumbspore', 2, 0, 0]],
  136. //if (neighsM['doughshroom']>=1 && neighs['doughshroom']<=1) muts.push(['doughshroom',0.07]);
  137. [['doughshroom', 1, 0, 2]],
  138. //if (neighsM['doughshroom']>=2) muts.push(['crumbspore',0.005]);
  139. [['doughshroom', 2, 0, 0]],
  140. //if (neighsM['crumbspore']>=1 && neighsM['brownMold']>=1) muts.push(['wrinklegill',0.06]);
  141. [['crumbspore', 1, 0, 0], ['brownMold', 1, 0, 0]],
  142. //if (neighsM['whiteMildew']>=1 && neighsM['clover']>=1) muts.push(['greenRot',0.05]);
  143. [['whiteMildew', 1, 0, 0], ['clover', 1, 0, 0]],
  144.  
  145. //if (neighsM['wrinklegill']>=1 && neighsM['elderwort']>=1) muts.push(['shriekbulb',0.001]);
  146. [['wrinklegill', 1, 0, 0], ['elderwort', 1, 0, 0]],
  147. //if (neighsM['elderwort']>=5) muts.push(['shriekbulb',0.001]);
  148. [['elderwort', 5, 0, 0]],
  149. //if (neighs['duketater']>=3) muts.push(['shriekbulb',0.005]);
  150. [['duketater', 0, 3, 0]],
  151. //if (neighs['doughshroom']>=4) muts.push(['shriekbulb',0.002]);
  152. [['doughshroom', 0, 4, 0]],
  153. //if (neighsM['queenbeet']>=5) muts.push(['shriekbulb',0.001]);
  154. [['queenbeet', 5, 0, 8]],
  155. //if (neighs['shriekbulb']>=1 && neighs['shriekbulb']<2) muts.push(['shriekbulb',0.005]);
  156. [['shriekbulb', 0, 1, 2]],
  157. //if (neighsM['bakerWheat']>=1 && neighsM['whiteChocoroot']>=1) muts.push(['tidygrass',0.002]);
  158. [['bakerWheat', 1, 0, 0], ['whiteChocoroot', 1, 0, 0]],
  159. //if (neighsM['tidygrass']>=3 && neighsM['elderwort']>=3) muts.push(['everdaisy',0.002]);
  160. [['tidygrass', 3, 0, 0], ['elderwort', 3, 0, 0]],
  161. //if (neighsM['elderwort']>=1 && neighsM['crumbspore']>=1) muts.push(['ichorpuff',0.002]);
  162. [['elderwort', 1, 0, 0], ['crumbspore', 1, 0, 0]],
  163. ]
  164.  
  165.  
  166. // ======= GENERAL LOGIC =======
  167.  
  168.  
  169. /**
  170. * Standard compare function (for Array.prototype.sort)
  171. */
  172. var compare = function(a, b) {
  173. return (a < b ? -1 : (a > b ? 1 : 0));
  174. }
  175.  
  176. /**
  177. * Calculate the probability that a plant with the given ageTick, ageTickR,
  178. * on a plot with given ageBoost will age by N percent or more in the next
  179. * tick.
  180. */
  181. var calcProbAgingGT = function(N, ageTick, ageTickR, ageBoost) {
  182. if(N % 1 != 0) {
  183. throw "In calcProbAgingGT, N must be an integer, got " + N;
  184. }
  185. if(N <= 0) {
  186. return 1;
  187. }
  188. ageTick *= ageBoost;
  189. ageTickR *= ageBoost;
  190. var p1 = 0;
  191. var p2 = 0;
  192. var p3 = 0;
  193. if(ageTickR > 0) {
  194. // Probability that ageTick + ageTickR * Math.random() will be less than N - 1
  195. p1 = (N - 1 - ageTick) / ageTickR;
  196. p1 = Math.min(Math.max(p1, 0), 1);
  197.  
  198. // Probability that ageTick + ageTickR * Math.random() will be greater than N
  199. p2 = (ageTickR - N + ageTick) / ageTickR;
  200. p2 = Math.min(Math.max(p2, 0), 1);
  201. } else {
  202. p1 = (ageTick < N - 1 ? 1 : 0);
  203. p2 = (ageTick >= N ? 1 : 0);
  204. }
  205.  
  206. if(p1 + p2 < 1) {
  207. /*
  208. * Probability that if ageTick + ageTickR * Math.random() is between N - 1 and N,
  209. * it will be rounded to N. I hope my analysis of the probability disribution was
  210. * correct.
  211. */
  212. var a = (ageTick < N - 1 ? 0 : ageTick % 1);
  213. var b = (ageTick + ageTickR < N ? ageTickR % 1 : 1 - a);
  214. p3 = (1 - p1 - p2) * (a + b / 2);
  215. }
  216.  
  217. return p2 + p3;
  218. }
  219.  
  220.  
  221. /**
  222. * Given a list of mutations (like from garden.getMuts), calculate the total
  223. * probability of each outcome, multiply by prior_prob and add to plantsNextTick (per plant).
  224. *
  225. * Note: the complexity is 2(N ** 2)log(2N), where N = muts.length; may be reducible,
  226. * but with our list sizes probably not worth it.
  227. */
  228. var getMutProbs = function(muts, prior_prob, plantsNextTick) {
  229. var selections = [];
  230. selections.length = muts.length;
  231. var getPartMutProbs = function(i, prior_prob) {
  232. if(i === muts.length) {
  233. var num_entries = 0;
  234. for(var k = 0; k < selections.length; ++k) {
  235. if(selections[k]) {
  236. ++num_entries;
  237. }
  238. }
  239. for(var k = 0; k < selections.length; ++k) {
  240. if(selections[k]) {
  241. plantsNextTick[muts[k][0]].probImmature += prior_prob / num_entries;
  242. }
  243. }
  244. } else {
  245. // Assume mutation i selected
  246. selections[i] = true;
  247. getPartMutProbs(i + 1, prior_prob * muts[i][1]);
  248. // Assume not
  249. selections[i] = false;
  250. getPartMutProbs(i + 1, prior_prob * (1 - muts[i][1]));
  251. }
  252. }
  253. getPartMutProbs(0, prior_prob);
  254. }
  255.  
  256.  
  257. function GardenWrapper(garden, autoUnlocked, weedKey, weedProb, deathDrops, recipes, plantUpgrades, percentagePrecision) {
  258. this.garden = garden;
  259. this.autoUnlocked = autoUnlocked;
  260. this.weedKey = weedKey;
  261. this.weedProb = weedProb;
  262. this.setPercentagePrecision(percentagePrecision);
  263. this.plantsUnlockable = 0;
  264.  
  265. // Plant status
  266. this.plantStatus = {};
  267. this.plantKeys = [];
  268. for(var plant in garden.plants) {
  269. // Initialize seed status
  270. var plantdata = garden.plants[plant];
  271. var SS = {
  272. key: plant,
  273. name: plantdata.name,
  274. plantable: plantdata.plantable, // Is it plantable at all?
  275. status: (plantdata.unlocked ? PLANT_UNLOCKED : PLANT_LOCKED), // Is the seed unlocked?
  276. recipes: [], // All recipes for this plant
  277. recipesUnlocked: [], // Which recipes are unlocked (bools)
  278. growing: [], // Tiles where it's currently growing ([x1, y1, x2, y2 ...])
  279. mature: [], // Tiles where it's mature
  280. growingDanger: [], // Tiles where it's growing, but may be overtaken (contaminated) or die next tick
  281. canGrowNextTick: [], // Tiles where it may start growing next tick (including contaminations)
  282. canGrowPotentially: [], // Tiles where it may potentially grow (always empty if the option to check is disabled)
  283. // canGrowPotentially may be inaccurate
  284. wasGrowingLastTick: false,
  285. totalProbGrowthNextTick: 0,
  286. }
  287. this.plantStatus[plant] = SS;
  288. this.plantKeys.push(plant);
  289. // Add auto-unlocked and weeds
  290. if(this.autoUnlocked[plant]) {
  291. SS.recipes.push({type: RECIPE_AUTOUNLOCKED, prob: 1.0, html: "<b>Always unlocked</b>"});
  292. }
  293. if(plant === weedKey) {
  294. SS.recipes.push();
  295. }
  296. }
  297.  
  298. // The weed
  299. if(weedKey) {
  300. this.plantStatus[weedKey].recipes.push({
  301. type: RECIPE_WEED,
  302. prob: Agronomicon.weedProb,
  303. html: "<b>Weed: grows spontaneously on empty tiles with no neighbors (" +
  304. this.formatPercentage(weedProb) + " each tick)</b>",
  305. });
  306. }
  307.  
  308. // Sort the list of keys
  309. this.plantKeys.sort();
  310.  
  311. // Add death drops
  312. for(var parent in deathDrops) {
  313. var dd = deathDrops[parent];
  314. if(dd[2] === 0) {
  315. for(var i = 0; i < dd[0].length; ++i) {
  316. this.plantStatus[dd[0][i]].recipes.push({
  317. type: RECIPE_CREATED_ON_KILL,
  318. baseProb: deathDrops[parent][1] / dd[0].length,
  319. ageProb: 0,
  320. reqs: parent,
  321. html: "May sprout from <b>" + this.plantStatus[parent].name + "</b> (AA) when it's harvested (<b>" +
  322. this.formatPercentage(dd[1] / dd[0].length) + "</b>",
  323. });
  324. }
  325. } else {
  326. for(var i = 0; i < deathDrops[parent][0].length; ++i) {
  327. this.plantStatus[deathDrops[parent][0][i]].recipes.push({
  328. type: RECIPE_CREATED_ON_KILL,
  329. baseProb: dd[1] / dd[0].length,
  330. ageProb: dd[2] / dd[0].length,
  331. reqs: parent,
  332. html: "May sprout from <b>" + this.plantStatus[parent].name + "</b> (AA) when it's harvested (up to <b>" +
  333. this.formatPercentage((dd[1] + dd[2]) / dd[0].length) + "</b>, the older the better)",
  334. });
  335. }
  336. }
  337. }
  338.  
  339. // Add other recipes
  340. for(var i = 0; i < recipes.length; ++i) {
  341. var pnt = {};
  342. for(var k = 0; k < this.plantKeys.length; ++k) {
  343. pnt[this.plantKeys[k]] = { probMature: 0, probImmature: 0 };
  344. }
  345. var r = recipes[i];
  346. r.sort(function(a, b) { return compare(garden.plants[a[0]].name, garden.plants[b[0]].name); });
  347. var neighs = {};
  348. var neighsM = {};
  349. var html_prereqs = '';
  350. var html_conds = '';
  351. for(var k = 0; k < r.length; ++k) {
  352. var pname = garden.plants[r[k][0]].name;
  353. neighs[r[k][0]] = r[k][1] + r[k][2];
  354. neighsM[r[k][0]] = r[k][1];
  355. if(r[k][1]) {
  356. if(html_prereqs) {
  357. html_prereqs += ' + ';
  358. }
  359. html_prereqs += '<b>' + r[k][1] + '</b> × <b>' + pname + '</b> (M)';
  360. }
  361. if(r[k][2]) {
  362. if(html_prereqs) {
  363. html_prereqs += ' + ';
  364. }
  365. html_prereqs += '<b>' + r[k][2] + '</b> × <b>' + pname + '</b> (AA)';
  366. }
  367. if(r[k][3]) {
  368. if(html_conds) {
  369. html_conds += ', ';
  370. } else {
  371. html_conds = ' if ';
  372. }
  373. if(r[k][3] === '1') {
  374. html_conds += 'no <b>' + pname + '</b> (AA)';
  375. } else {
  376. html_conds += '<b>' + pname + '</b> (AA) &lt; <b>' + r[k][3] + '</b>';
  377. }
  378. }
  379. }
  380. var muts = garden.getMuts(neighs, neighsM);
  381. getMutProbs(muts, 1, pnt);
  382. var num_outcomes = 0;
  383. var prob_all_outcomes = 0;
  384. for(var key in pnt) {
  385. if(pnt[key].probImmature > 0) {
  386. ++num_outcomes;
  387. prob_all_outcomes += pnt[key].probImmature;
  388. }
  389. }
  390. for(var key in pnt) {
  391. if(pnt[key].probImmature > 0) {
  392. var prob_oo = prob_all_outcomes - pnt[key].probImmature;
  393. this.plantStatus[key].recipes.push({
  394. type: (neighs[key] ? RECIPE_SPREAD : RECIPE_MUTATION),
  395. baseProb: pnt[key].probImmature,
  396. reqs: r,
  397. oo: num_outcomes - 1,
  398. ooProb: prob_oo,
  399. html: (neighs[key] ? 'Spread: ' : 'Mut.: ') + html_prereqs + html_conds +
  400. ' = <b>' + this.formatPercentage(pnt[key].probImmature) + '</b>' +
  401. (num_outcomes > 1 ? ' (+' + (num_outcomes - 1) + ' OO at ' + this.formatPercentage(prob_oo) + ')' : ''),
  402. });
  403. }
  404. }
  405. }
  406.  
  407. // Sort all recipes for clearer overview and add plant upgrades
  408. for(var key in this.plantStatus) {
  409. this.plantStatus[key].recipes.sort(function(a, b) {
  410. if(a.type < b.type) {
  411. return -1;
  412. } else if(a.type > b.type) {
  413. return 1;
  414. } else {
  415. return (
  416. a.baseProb < b.baseProb ? 1 : //Inverse sort
  417. a.baseProb > b.baseProb ? -1 :
  418. compare(a.html, b.html)
  419. );
  420. }
  421. });
  422. this.plantStatus[key].recipesUnlocked.length = this.plantStatus[key].recipes.length;
  423. if(plantUpgrades[key]) {
  424. this.plantStatus[key].upgradeName = plantUpgrades[key][0];
  425. this.plantStatus[key].upgradeProb = plantUpgrades[key][1];
  426. } else {
  427. this.plantStatus[key].upgradeName = null;
  428. this.plantStatus[key].upgradeProb = null;
  429. }
  430. }
  431.  
  432. // Initialize zero plant maps
  433. this.zeroPlantMaps = [{}, {}];
  434. for(var i = 0; i < this.zeroPlantMaps.length; ++i) {
  435. var zpm = this.zeroPlantMaps[i];
  436. for(var k = 0; k < this.plantKeys.length; ++k) {
  437. zpm[this.plantKeys[k]] = 0;
  438. }
  439. }
  440.  
  441. // Base contamination probabilities for each soil
  442. var bcp = [];
  443. var contam_map = {};
  444. for(var key in garden.plantContam) {
  445. bcp.push([key, garden.plantContam[key]]);
  446. contam_map[key] = { probMature: 0, probImmature: 0 };
  447. }
  448. var bcpbs = [];
  449. bcpbs.length = garden.soilsById.length;
  450. for(var i = 0; i < bcpbs.length; ++i) {
  451. if(i > 0) {
  452. for(var key in contam_map) {
  453. contam_map[key].probImmature = 0;
  454. }
  455. }
  456. var soil = garden.soilsById[i];
  457. var bcp2 = [];
  458. bcp2.length = bcp.length;
  459. for(var k = 0; k < bcp.length; ++k) {
  460. bcp2[k] = [bcp[k][0], garden.plants[bcp[k][0]].weed ? bcp[k][1] * garden.soilsById[i].weedMult : bcp[k][1]];
  461. }
  462. // Contaminations are selected basically by the same process as mutations
  463. getMutProbs(bcp2, 1, contam_map);
  464. bcpbs[i] = {};
  465. for(var key in contam_map) {
  466. if(contam_map[key].probImmature > 0) {
  467. bcpbs[i][key] = contam_map[key].probImmature;
  468. }
  469. }
  470. }
  471. this.baseContamProbsBySoilId = bcpbs;
  472.  
  473. // Tile status
  474. var gpl = garden.plotLimits[garden.plotLimits.length - 1];
  475. this.maxPlotWidth = gpl[2] - gpl[0];
  476. this.maxPlotHeight = gpl[3] - gpl[1];
  477. this.tileStatus = [];
  478. this.tileStatus.length = this.maxPlotHeight;
  479. for(var y = 0; y < this.maxPlotHeight; ++y) {
  480. var tmp = [];
  481. tmp.length = this.maxPlotWidth;
  482. for(var x = 0; x < tmp.length; ++x) {
  483. tmp[x] = {
  484. unlocked: false,
  485. plant: null,
  486. underEffects: false, // Tile is under effects from neighboring plants
  487. probGrowthNextTick: 0, // Probability that the plant will do any growth (at least 1%) next tick
  488. probDeathNextTick: 0, // Probability that current plant will die next tick
  489. plantsNextTick: {}, // The probability of every plant being in this tile next tick
  490. // (name -> {probMature, probImmature})
  491. probEmptyNextTick: 0, // Probability that no plant will be growing here next tick
  492. };
  493. for(var k = 0; k < this.plantKeys.length; ++k) {
  494. tmp[x].plantsNextTick[this.plantKeys[k]] = {
  495. probMature: 0,
  496. probImmature: 0,
  497. }
  498. }
  499. }
  500. this.tileStatus[y] = tmp;
  501. }
  502. }
  503.  
  504. /**
  505. * Format a value as percentage (up to the precision set in `this`)
  506. */
  507. GardenWrapper.prototype.formatPercentage = function(value) {
  508. if(!this.percentagePrecision) {
  509. return value * 100 + '%';
  510. } else {
  511. var sign = (value < 0 ? '-' : '');
  512. value = Math.abs(value);
  513. if(value < this.minPercentage) {
  514. if(sign) {
  515. return '-' + this.minPercentageStr + 'to 0%';
  516. } else {
  517. return '<' + this.minPercentageStr;
  518. }
  519. } else {
  520. // Decided on floor instead of round, it is more similar to the expectations
  521. value = Math.floor(value * 100 * this.percentagePow10);
  522. var low = value % this.percentagePow10;
  523. var low_digits = this.percentagePrecision;
  524. while(low > 0 && low % 10 === 0) {
  525. low /= 10;
  526. low_digits -= 1;
  527. }
  528. if(low) {
  529. low = '' + low;
  530. while(low.length < low_digits) {
  531. low = '0' + low;
  532. }
  533. low = '.' + low;
  534. } else {
  535. low = '';
  536. }
  537. var high = Math.floor(value / this.percentagePow10);
  538. return sign + high + low + '%';
  539. }
  540. }
  541. }
  542.  
  543. /**
  544. * Set precision for percentage formatting
  545. */
  546. GardenWrapper.prototype.setPercentagePrecision = function(precision) {
  547. if(precision < 0) {
  548. throw "In setPercentagePrecision: precision must be >= 0, got " + precision;
  549. } else {
  550. this.percentagePrecision = precision;
  551. if(!precision) {
  552. this.minPercentage = null;
  553. this.minPercentageStr = null;
  554. this.percentagePow10 = null;
  555. } else {
  556. this.minPercentage = Math.pow(10, -precision - 2);
  557. this.minPercentageStr = '' + Math.pow(10, -precision) + '%';
  558. this.percentagePow10 = Math.pow(10, precision);
  559. }
  560. }
  561. }
  562.  
  563. /**
  564. * Call calcPartMutationProbability on every neighbor, then calcMutationProbabilities
  565. */
  566. GardenWrapper.prototype.callNextOffset = function(x, y, x_offset, y_offset, prior_prob, neighs, neighsM, plantsNextTick) {
  567. // Goes counter-clockwise
  568. var done = false;
  569. do {
  570. if(y_offset === 1) {
  571. ++x_offset;
  572. if(x_offset === 2) {
  573. y_offset = 0;
  574. x_offset = 1;
  575. }
  576. } else if(y_offset === -1) {
  577. if(x_offset === -1) {
  578. y_offset = 0;
  579. x_offset = -1;
  580. } else {
  581. --x_offset;
  582. }
  583. } else { // y_offset === 0
  584. if(x_offset === 0) {
  585. // Initial call to this function
  586. x_offset = -1;
  587. y_offset = 1;
  588. } else if(x_offset === -1) {
  589. done = true;
  590. } else { //x_offset === 1
  591. y_offset = -1;
  592. }
  593. }
  594. } while(!done && !this.garden.isTileUnlocked(x + x_offset, y + y_offset));
  595. if(done) {
  596. return this.calcMutationProbabilities(x, y, prior_prob, neighs, neighsM, plantsNextTick);
  597. } else {
  598. var cant_change = (y_offset > 0 || y_offset === 0 && x_offset > 0);
  599. return this.calcPartMutationProbability(x, y, x_offset, y_offset, prior_prob, neighs, neighsM, plantsNextTick, cant_change);
  600. }
  601. }
  602.  
  603. /**
  604. * Compute mutation probabilities (recursively called on every neighbor)
  605. */
  606. GardenWrapper.prototype.calcPartMutationProbability = function(x, y, x_offset, y_offset, prior_prob, neighs, neighsM,
  607. plantsNextTick, cant_change) {
  608. var plant_id = this.garden.plot[y + y_offset][x + x_offset][0] - 1;
  609. var plant = (plant_id >= 0 ? this.garden.plantsById[plant_id] : null);
  610. if(cant_change) {
  611. if(plant !== null) {
  612. var is_mature = this.garden.plot[y + y_offset][x + x_offset][1] >= plant.mature;
  613. ++neighs[plant.key];
  614. if(is_mature) {
  615. ++neighsM[plant.key];
  616. }
  617. this.callNextOffset(x, y, x_offset, y_offset, prior_prob, neighs, neighsM, plantsNextTick);
  618. --neighs[plant.key];
  619. if(is_mature) {
  620. --neighsM[plant.key];
  621. }
  622. } else {
  623. return this.callNextOffset(x, y, x_offset, y_offset, prior_prob, neighs, neighsM, plantsNextTick);
  624. }
  625. } else {
  626. var ts = this.tileStatus[y + y_offset][x + x_offset];
  627. for(var i = 0; i < this.plantKeys.length; ++i) {
  628. var key = this.plantKeys[i];
  629. var pnt = ts.plantsNextTick[key];
  630. if(!pnt.prevented) {
  631. if(pnt.probImmature > 0) {
  632. ++neighs[key];
  633. this.callNextOffset(x, y, x_offset, y_offset, prior_prob * pnt.probImmature, neighs, neighsM, plantsNextTick);
  634. --neighs[key];
  635. }
  636. if(pnt.probMature > 0) {
  637. ++neighs[key];
  638. ++neighsM[key];
  639. this.callNextOffset(x, y, x_offset, y_offset, prior_prob * pnt.probMature, neighs, neighsM, plantsNextTick);
  640. --neighsM[key];
  641. --neighs[key];
  642. }
  643. }
  644. }
  645. if(ts.probEmptyNextTick > 0) {
  646. this.callNextOffset(x, y, x_offset, y_offset, prior_prob * ts.probEmptyNextTick, neighs, neighsM, plantsNextTick);
  647. }
  648. }
  649. }
  650.  
  651. /**
  652. * Sum mutation probabilities for a given combination of plants
  653. */
  654. GardenWrapper.prototype.calcMutationProbabilities = function(x, y, prior_prob, neighs, neighsM, plantsNextTick) {
  655. var muts = this.garden.getMuts(neighs, neighsM);
  656. var new_muts = [];
  657. for(var i = 0; i < muts.length; ++i) {
  658. if(this.garden.plants[muts[i][0]].weed) {
  659. muts[i][1] *= this.garden.soilsById[this.garden.soil].weedMult * this.garden.plotBoost[y][x][2];
  660. } else if(this.garden.plants[muts[i][0]].fungus) {
  661. muts[i][1] *= this.garden.plotBoost[y][x][2];
  662. }
  663. if(muts[i][1] > 0) {
  664. new_muts.push([muts[i][0], Math.min(muts[i][1], 1)]);
  665. }
  666. }
  667. if(new_muts) {
  668. getMutProbs(new_muts, prior_prob, plantsNextTick);
  669. }
  670. }
  671.  
  672. /**
  673. * Get the probability of mature neighbors in cardinal directions
  674. */
  675. GardenWrapper.prototype.getMatureCardinalNeighbors = function(x, y) {
  676. var result = {};
  677. // South and east
  678. var coords = [[0, +1], [+1, 0]];
  679. for(var i = 0; i < coords.length; ++i) {
  680. var dxy = coords[i];
  681. var tile = this.garden.getTile(x + dxy[0], y + dxy[1]);
  682. if(tile[0] !== 0) {
  683. var plant = this.garden.plantsById[tile[0] - 1];
  684. if(tile[1] >= plant.mature) {
  685. result[plant.key] = 1;
  686. }
  687. }
  688. }
  689. // NORTH and same plants in the NORTH and the west
  690. var ts_north = (this.garden.isTileUnlocked(x, y - 1) ? this.tileStatus[y - 1][x] : null);
  691. var ts_west = (this.garden.isTileUnlocked(x - 1, y) ? this.tileStatus[y][x - 1] : null);
  692. if(ts_north) {
  693. for(key in ts_north.plantsNextTurn) {
  694. if(!result[key] && !ts_north.plantsNextTurn[key].prevented &&
  695. ts_north.plantsNextTurn[key].probMature > 0) {
  696. if(ts_west && !ts_west.plantsNextTurn[key].prevented) {
  697. result[key] = 1 - (1 - ts_north.plantsNextTurn[key].probMature)
  698. * (1 - ts_west.plantsNextTurn[key].probMature);
  699. } else {
  700. result[key] = ts_north.plantsNextTurn[key].probMature;
  701. }
  702. }
  703. }
  704. }
  705. // Rest of the west
  706. if(ts_west) {
  707. for(key in ts_west.plantsNextTurn) {
  708. if(!result[key] && !ts_west.plantsNextTurn[key].prevented) {
  709. result[key] = ts_west.plantsNextTurn[key].probMature;
  710. }
  711. }
  712. }
  713. return result;
  714. }
  715.  
  716. /**
  717. * Calculate probability that the tile (x, y) will have no neighbors next tick
  718. */
  719. GardenWrapper.prototype.calcProbNoNeighbors = function(x, y) {
  720. var coords = [[-1, +1], [0, +1], [+1, +1], [+1, 0]];
  721. var dxy;
  722. for(var i = 0; i < coords.length; ++i) {
  723. dxy = coords[i];
  724. if(this.garden.isTileUnlocked(x + dxy[0], y + dxy[1]) && this.garden.plot[y + dxy[1]][x + dxy[0]][0] !== 0) {
  725. return 0;
  726. }
  727. }
  728. var result = 1;
  729. coords = [[+1, -1], [0, -1], [-1, -1], [-1, 0]];
  730. for(var i = 0; i < coords.length; ++i) {
  731. dxy = coords[i];
  732. if(this.garden.isTileUnlocked(x + dxy[0], y + dxy[1])) {
  733. result *= this.tileStatus[y + dxy[1]][x + dxy[0]].probEmptyNextTick;
  734. if(result === 0) {
  735. break;
  736. }
  737. }
  738. }
  739. return result;
  740. }
  741.  
  742. /**
  743. * Recalculate odds for a particular tile and save in tileStatus
  744. */
  745. GardenWrapper.prototype.recalculateTile = function(x, y, loops) {
  746. var s = this.tileStatus[y][x];
  747. if(!this.garden.isTileUnlocked(x, y)) {
  748. if(s.unlocked) {
  749. // The tile somehow became locked
  750. s.plant = null;
  751. s.probGrowthNextTick = 0;
  752. s.probDeathNextTick = 0;
  753. s.probEmptyNextTick = 0;
  754. s.underEffects = false;
  755. }
  756. } else {
  757. s.unlocked = true;
  758. for(var key in s.plantsNextTick) {
  759. var ss = s.plantsNextTick[key];
  760. ss.probMature = 0;
  761. ss.probImmature = 0;
  762. }
  763. var tile = this.garden.plot[y][x];
  764. var tile_boost = this.garden.plotBoost[y][x];
  765. s.underEffects = (tile_boost[0] !== 1 || tile_boost[2] !== 1);
  766. if(tile[0] === 0) {
  767. s.plant = null;
  768. s.probGrowthNextTick = 0;
  769. s.probDeathNextTick = 0;
  770. } else {
  771. s.plant = this.garden.plantsById[tile[0]-1];
  772. s.probGrowthNextTick = calcProbAgingGT(1, s.plant.ageTick, s.plant.ageTickR, tile_boost[0]);
  773. s.probDeathNextTick = (s.plant.immortal ? 0 : calcProbAgingGT(100 - tile[1], s.plant.ageTick, s.plant.ageTickR, tile_boost[0]));
  774. }
  775. if(s.plant) {
  776. // Contamination
  777. var prob_contam = 0;
  778. var baseContamProbs = this.baseContamProbsBySoilId[this.garden.soil];
  779. if(s.probDeathNextTick < 1 && !s.plant.noContam) {
  780. var mcn = this.getMatureCardinalNeighbors(x, y);
  781. for(key in baseContamProbs) {
  782. if(baseContamProbs[key] && s.plant.key !== key && mcn[key]) {
  783. var newplant = this.garden.plants[key];
  784. var this_prob_contam = (1 - s.probDeathNextTick) * baseContamProbs[key]
  785. * Math.min(((newplant.weed || newplant.fungus) ? tile_boost[2] : 1), 1);
  786. s.plantsNextTick[key].probImmature = this_prob_contam;
  787. prob_contam += this_prob_contam;
  788. }
  789. }
  790. }
  791. // Total probability of the tile being empty
  792. s.probEmptyNextTick = s.probDeathNextTick * (1 - prob_contam);
  793. // Maturity
  794. if(tile[1] >= s.plant.mature) {
  795. s.plantsNextTick[s.plant.key].probMature = (1 - s.probDeathNextTick) * (1 - prob_contam);
  796. } else {
  797. var prob_mature = calcProbAgingGT(Math.ceil(s.plant.mature - tile[1]), s.plant.ageTick,
  798. s.plant.ageTickR, tile_boost[0]);
  799. s.plantsNextTick[s.plant.key].probImmature = (1 - s.probDeathNextTick)
  800. * (1 - prob_contam) * (1 - prob_mature);
  801. s.plantsNextTick[s.plant.key].probMature = (1 - s.probDeathNextTick) * (1 - prob_contam) * prob_mature;
  802. }
  803. } else {
  804. // If there is no plant, calculate mutations and weeds
  805. // A plant cannot die and be replaced on the same tick
  806. var prob_empty = 1;
  807. if(loops > 0) { // Maybe there will be some need to call this function with loops = 0
  808. this.callNextOffset(x, y, 0, 0, 1, this.zeroPlantMaps[0], this.zeroPlantMaps[1], s.plantsNextTick);
  809. for(var key in s.plantsNextTick) {
  810. var p = s.plantsNextTick[key];
  811. prob_empty -= p.probImmature;
  812. if(loops > 1) {
  813. // Since .probMature must be 0, we use it to temorarily store this probability
  814. p.probMature = p.probImmature;
  815. }
  816. }
  817. if(loops > 1) {
  818. for(var loop = 2; loop <= loops; ++loop) {
  819. var new_prob_empty = 1;
  820. for(key in s.plantsNextTick) {
  821. var p = s.plantsNextTick[key];
  822. p.probImmature += prob_empty * p.probMature;
  823. new_prob_empty -= p.probImmature;
  824. }
  825. prob_empty = new_prob_empty;
  826. }
  827. for(key in s.plantsNextTick) {
  828. s.plantsNextTick[key].probMature = 0;
  829. }
  830. }
  831. }
  832. // Weeds
  833. if(prob_empty > 0 && this.weedKey) {
  834. var prob_nn = this.calcProbNoNeighbors(x, y);
  835. if(prob_nn > 0) {
  836. var wp = this.weedProb * prob_empty * prob_nn * this.garden.soilsById[this.garden.soil].weedMult * tile_boost[2];
  837. s.plantsNextTick[this.weedKey].probImmature += wp;
  838. prob_empty *= (1 - wp);
  839. }
  840. }
  841. // Final probability that it'll remain empty
  842. s.probEmptyNextTick = prob_empty;
  843. }
  844. }
  845. }
  846.  
  847. /**
  848. * Recalculate odds for all tiles
  849. */
  850. GardenWrapper.prototype.recalculateAllTiles = function(loops) {
  851. // Doesn't account for the possibility that x or y can ALWAYS be greater than 0,
  852. // but in unmodded game it's impossible
  853. for(var y = 0; y < this.maxPlotHeight; ++y) {
  854. for(var x = 0; x < this.maxPlotWidth; ++x) {
  855. this.recalculateTile(x, y, loops);
  856. }
  857. }
  858. }
  859.  
  860. /**
  861. * Set plant status, but only to a better status
  862. */
  863. GardenWrapper.prototype.improvePlantStatus = function(key, new_status) {
  864. var old_status = this.plantStatus[key].status;
  865. if(old_status < new_status) {
  866. if(old_status === PLANT_LOCKED && new_status !== PLANT_UNLOCKED) {
  867. ++this.plantsUnlockable;
  868. } else if(new_status === PLANT_UNLOCKED && old_status !== PLANT_LOCKED) {
  869. --this.plantsUnlockable;
  870. }
  871. this.plantStatus[key].status = new_status;
  872. }
  873. }
  874.  
  875. /**
  876. * Check if plant can already be used in recipes
  877. */
  878. GardenWrapper.prototype.isPlantAccessible = function(key) {
  879. return (
  880. this.plantStatus[key].status === PLANT_UNLOCKED ||
  881. this.plantStatus[key].status === PLANT_PREMATURE ||
  882. this.plantStatus[key].status === PLANT_PREMATURE_DANGER ||
  883. this.plantStatus[key].status === PLANT_MATURE ||
  884. this.plantStatus[key].status === PLANT_MATURE_DANGER
  885. );
  886. }
  887.  
  888. /**
  889. * Recalculate plant status. Call after recalculateAllTiles
  890. */
  891. GardenWrapper.prototype.recalculatePlantStatus = function() {
  892. // Check locked and unlocked plants; set totalProbGrowthNextTick to 1
  893. this.plantsUnlockable = 0;
  894. for(var key in this.plantStatus) {
  895. var ps = this.plantStatus[key];
  896. if(this.garden.plants[key].unlocked) {
  897. ps.status = PLANT_UNLOCKED;
  898. } else if(key === this.weedKey) {
  899. ps.status = PLANT_WEED;
  900. } else {
  901. ps.status = PLANT_LOCKED;
  902. }
  903. ps.totalProbGrowthNextTick = 1;
  904. }
  905.  
  906. // Find what is growing/may grow
  907. for(var y = 0; y < this.maxPlotHeight; ++y) {
  908. for(var x = 0; x < this.maxPlotWidth; ++x) {
  909. if(this.garden.isTileUnlocked(x, y)) {
  910. var pnt = this.tileStatus[y][x].plantsNextTick;
  911. var plant_id = this.garden.plot[y][x][0] - 1;
  912. if(plant_id >= 0) {
  913. var plant = this.garden.plantsById[plant_id];
  914. var key = plant.key;
  915. var danger = (pnt[key].probImmature + pnt[key].probMature < 1);
  916. var new_status = (
  917. this.garden.plot[y][x][1] < plant.mature ?
  918. (danger ? PLANT_PREMATURE_DANGER : PLANT_PREMATURE) :
  919. (danger ? PLANT_MATURE_DANGER : PLANT_MATURE)
  920. );
  921. this.improvePlantStatus(key, new_status);
  922. } else {
  923. for(var key in pnt) {
  924. if(pnt[key].probMature + pnt[key].probImmature > 0) {
  925. this.improvePlantStatus(key, PLANT_MAYGROW);
  926. this.plantStatus[key].totalProbGrowthNextTick *=
  927. 1 - pnt[key].probMature - pnt[key].probImmature;
  928. }
  929. }
  930. }
  931. }
  932. }
  933. }
  934.  
  935. // Check recipes and find unlockable plants; finalize totalProbGrowthNextTick
  936. for(var key in this.plantStatus) {
  937. var ps = this.plantStatus[key];
  938. if(ps.status === PLANT_WEED) {
  939. ++this.plantsUnlockable;
  940. }
  941. ps.totalProbGrowthNextTick = 1 - ps.totalProbGrowthNextTick;
  942. for(var i = 0; i < ps.recipes.length; ++i) {
  943. var r = ps.recipes[i];
  944. if(r.type === RECIPE_AUTOUNLOCKED || r.type === RECIPE_WEED) {
  945. ps.recipesUnlocked[i] = true;
  946. } else if(r.type === RECIPE_CREATED_ON_KILL) {
  947. ps.recipesUnlocked[i] = this.isPlantAccessible(r.reqs);
  948. if(ps.recipesUnlocked[i] && ps.status < PLANT_UNLOCKABLE) {
  949. ps.status = PLANT_UNLOCKABLE;
  950. ++this.plantsUnlockable;
  951. }
  952. } else { // RECIPE_SPREAD or RECIPE_MUTATION
  953. ps.recipesUnlocked[i] = true;
  954. for(var k = 0; k < r.reqs.length; ++k) {
  955. if((r.reqs[k][1] > 0 || r.reqs[k][2] > 0) && !this.isPlantAccessible(r.reqs[k][0])) {
  956. ps.recipesUnlocked[i] = false;
  957. break;
  958. }
  959. }
  960. if(ps.recipesUnlocked[i] && ps.status < PLANT_UNLOCKABLE) {
  961. ps.status = PLANT_UNLOCKABLE;
  962. ++this.plantsUnlockable;
  963. }
  964. }
  965. }
  966. }
  967.  
  968.  
  969. }
  970.  
  971.  
  972. // ======= USER INTERFACE =======
  973.  
  974.  
  975. // Hooks will be called with this === garden
  976.  
  977. var FROM_BUILD_PLOT = 1;
  978. var FROM_COMPUTE_EFFS = 2;
  979.  
  980. var tileStatusHook = function(GWrapperAgronomicon, from) {
  981. var gwrapper = GWrapperAgronomicon.wrapper;
  982. if(from === FROM_BUILD_PLOT && gwrapper.garden.toCompute) {
  983. // Wait for computeEffs
  984. return;
  985. }
  986. gwrapper.recalculateAllTiles(gwrapper.garden.soilsById[gwrapper.garden.soil].key === 'woodchips' ? 3 : 1);
  987. gwrapper.recalculatePlantStatus();
  988. gwrapper.garden.buildPanel(); // TODO: maybe optimize it
  989.  
  990. // Do the alerts. TODO: rework the whole alert system, this one is just to get the mod out there quickly
  991. if(GWrapperAgronomicon.newTick) {
  992. GWrapperAgronomicon.newTick = false;
  993. var ipid = false;
  994. var nps = false;
  995. var md = false;
  996. for(var key in gwrapper.plantStatus) {
  997. var ps = gwrapper.plantStatus[key];
  998. if(ps.status === PLANT_PREMATURE || ps.status === PLANT_PREMATURE_DANGER ||
  999. ps.status === PLANT_MATURE || ps.status === PLANT_MATURE_DANGER) {
  1000. if(!ps.wasGrowingLastTick) {
  1001. nps = true;
  1002. }
  1003. ps.wasGrowingLastTick = true;
  1004. } else {
  1005. ps.wasGrowingLastTick = false;
  1006. }
  1007. }
  1008. for(var y = 0; (!ipid || !md) && y < gwrapper.maxPlotHeight; ++y) {
  1009. for(var x = 0; (!ipid || !md) && x < gwrapper.maxPlotWidth; ++x) {
  1010. var ts = gwrapper.tileStatus[y][x];
  1011. var plant = ts.plant;
  1012. if(plant && ts.plantsNextTick[plant.key].probImmature + ts.plantsNextTick[plant.key].probMature < 1) {
  1013. if(gwrapper.garden.plot[y][x][1] >= plant.mature) {
  1014. md = true;
  1015. if(gwrapper.plantStatus[plant.key].upgradeName && !Game.HasUnlocked(gwrapper.plantStatus[plant.key].upgradeName)) {
  1016. ipid = true;
  1017. }
  1018. }
  1019. if(plant.key === 'queenbeetLump' || !plant.unlocked) {
  1020. ipid = true;
  1021. }
  1022. }
  1023. }
  1024. }
  1025. // Play the sounds
  1026. if(ipid && Game.prefs.AcharvaksAgronomicon_IPIDAlert || md && Game.prefs.AcharvaksAgronomicon_MDAlert) {
  1027. window.PlaySound(Agronomicon.SOUND2_URL);
  1028. } else if(nps && Game.prefs.AcharvaksAgronomicon_NPSAlert) {
  1029. window.PlaySound(Agronomicon.SOUND1_URL);
  1030. }
  1031. }
  1032. }
  1033.  
  1034. var buildPlotHook = function() {
  1035. this.AcharvaksAgronomicon.oldBuildPlot.call(this);
  1036. tileStatusHook(this.AcharvaksAgronomicon, FROM_BUILD_PLOT);
  1037. }
  1038.  
  1039. var computeEffsHook = function() {
  1040. this.AcharvaksAgronomicon.oldComputeEffs.call(this);
  1041. tileStatusHook(this.AcharvaksAgronomicon, FROM_COMPUTE_EFFS);
  1042. }
  1043.  
  1044. var tileTooltipHook = function(x, y) {
  1045. var old_fn = this.AcharvaksAgronomicon.oldTileTooltip.call(this, x, y);
  1046. var garden = this;
  1047. return function() {
  1048. var str = old_fn();
  1049. if(!garden.isTileUnlocked(x, y)) {
  1050. return str;
  1051. } else {
  1052. var tile = garden.plot[y][x];
  1053. var wrapper = garden.AcharvaksAgronomicon.wrapper;
  1054. var ts = wrapper.tileStatus[y][x];
  1055. var msg;
  1056. if(tile[0] === 0) {
  1057. if(ts.probEmptyNextTick === 1) {
  1058. msg = "<b>Will remain empty next tick</b>";
  1059. } else {
  1060. msg = "<div class='line'></div><b>These plants can grow here next tick:</b><br><br><div style='text-align: left;'>";
  1061. var nextmuts = [];
  1062. for(var key in ts.plantsNextTick) {
  1063. if(ts.plantsNextTick[key].probImmature > 0) {
  1064. nextmuts.push([garden.plants[key].name, ts.plantsNextTick[key].probImmature]);
  1065. }
  1066. }
  1067. nextmuts.sort(function(a, b) { return -compare(a[1], b[1]); });
  1068. for(var i = 0; i < nextmuts.length; ++i) {
  1069. msg += "<b>" + nextmuts[i][0] + ":</b> " + wrapper.formatPercentage(nextmuts[i][1]) + "<br>";
  1070. }
  1071. msg += "<b>[Nothing]:</b> " + wrapper.formatPercentage(ts.probEmptyNextTick);
  1072. msg += "</div>";
  1073. }
  1074. } else {
  1075. msg = '';
  1076. var plant = garden.plantsById[tile[0] - 1];
  1077. var tmp;
  1078. var dh = '<div style="margin-top: 0.5em"';
  1079. if(tile[1] < plant.mature) {
  1080. tmp = ts.plantsNextTick[plant.key].probMature;
  1081. if(tmp === 1) {
  1082. msg = dh + ' class="green">Will mature next tick (<b>100%</b>)</div>'
  1083. } else if(tmp > 0) {
  1084. msg = dh + ' class="green">May mature next tick (<b>' +
  1085. wrapper.formatPercentage(tmp) + '</b>)</div>';
  1086. }
  1087. }
  1088. if(ts.probEmptyNextTick === 1) {
  1089. msg += dh + ' class="red">Will die next tick (<b>100%</b>)</div>'
  1090. } else if(ts.probEmptyNextTick) {
  1091. msg += dh + ' class="red">May die next tick (<b>' +
  1092. wrapper.formatPercentage(ts.probEmptyNextTick) + '</b>)</span>';
  1093. }
  1094. var contam = [];
  1095. for(var key in ts.plantsNextTick) {
  1096. var p = ts.plantsNextTick[key].probImmature;
  1097. if(key !== plant.key && p > 0) {
  1098. contam.push([garden.plants[key].name, p]);
  1099. }
  1100. contam.sort(function(a, b) { return -compare(a[1], b[1]); });
  1101. }
  1102. if(contam) {
  1103. for(var i = 0; i < contam.length; ++i) {
  1104. msg += dh + ' class="red">May be overtaken by <b>' + contam[i][0] +
  1105. '</b> (<b>' + wrapper.formatPercentage(contam[i][1]) + '</b>)</div>';
  1106. }
  1107. }
  1108. if(msg) {
  1109. msg = '<div class="line"></div>' + msg;
  1110. }
  1111. }
  1112. if(msg) {
  1113. return str.replace(/<q>.*<\/q>/, '').replace(/<\/div>$/, msg + '</div>');
  1114. } else {
  1115. return str;
  1116. }
  1117. }
  1118. }
  1119. }
  1120.  
  1121. var getPlantDescHook = function(me) {
  1122. var desc = this.AcharvaksAgronomicon.oldGetPlantDesc.call(this, me);
  1123. var ps = this.AcharvaksAgronomicon.wrapper.plantStatus[me.key];
  1124. var seedlessToNay = Game.HasAchiev('Seedless to nay');
  1125. var upgradeProbFactor = seedlessToNay ? 1.05 : 1;
  1126. var formattedUpgradeProb = this.AcharvaksAgronomicon.wrapper.formatPercentage(
  1127. upgradeProbFactor * ps.upgradeProb);
  1128. if(ps && ps.upgradeName && !Game.HasUnlocked(ps.upgradeName)) {
  1129. return desc.replace(/<\/div>$/,
  1130. '<div class="line"></div>' +
  1131. '<div style="text-align: center; white-space: nowrap;">' +
  1132. 'When harvested mature, may drop <span class="green">' + ps.upgradeName +
  1133. '</span> (<b>' + formattedUpgradeProb + '</b>)</div></div>');
  1134. } else {
  1135. return desc;
  1136. }
  1137. }
  1138.  
  1139. var seedTooltipHook = function(id) {
  1140. var garden = this;
  1141. var wrapper = this.AcharvaksAgronomicon.wrapper;
  1142. var old_fn = this.AcharvaksAgronomicon.oldSeedTooltip.call(this, id);
  1143. return function() {
  1144. var tt = old_fn();
  1145. if(id < 0 || id >= garden.plantsById.length) {
  1146. return tt;
  1147. } else {
  1148. var plant = garden.plantsById[id];
  1149. var rhtml;
  1150. var ps = wrapper.plantStatus[plant.key];
  1151. if(ps.status === PLANT_PREMATURE_DANGER) {
  1152. rhtml = '<span class="red">This plant is growing in your garden, and is in danger</span>';
  1153. } else if(ps.status === PLANT_MATURE_DANGER) {
  1154. rhtml = '<span class="red"><b>This plant is mature in your garden, and is in danger</b></span>';
  1155. } else if(ps.status === PLANT_PREMATURE) {
  1156. rhtml = '<span class="green">This plant is growing in your garden</span>';
  1157. } else if(ps.status === PLANT_MATURE) {
  1158. rhtml = '<span class="green"><b>This plant is mature in your garden</b></span>';
  1159. } else if(ps.status === PLANT_MAYGROW) {
  1160. rhtml = '<b>This plant may grow in your garden next tick (' +
  1161. wrapper.formatPercentage(ps.totalProbGrowthNextTick) +')</b>';
  1162. } else if(ps.status !== PLANT_UNLOCKED) {
  1163. rhtml = "You haven't unlocked this seed yet";
  1164. } else {
  1165. rhtml = '';
  1166. }
  1167. if(rhtml) {
  1168. rhtml = '<div style="white-space: nowrap; text-align: center; margin-bottom: 0.25em;">' + rhtml + '</div>';
  1169. }
  1170. for(var k = 0; k < ps.recipes.length; ++k) {
  1171. var hh = ps.recipes[k].html;
  1172. if(!ps.recipesUnlocked[k]) {
  1173. hh = '<s>' + hh + '</s>';
  1174. }
  1175. rhtml += '<div style="white-space: nowrap; margin-top: 0.5em;">' + hh + '</div>';
  1176. }
  1177. if(rhtml) {
  1178. return tt.replace(/<\/div>$/, '<div class="line"></div>' + rhtml +
  1179. '<div style="margin-top: 0.5em; text-align: center;">' +
  1180. '<small>(M) = mature, (AA) = any age, OO = other outcomes</small></div></div>');
  1181. } else {
  1182. return tt;
  1183. }
  1184. }
  1185. }
  1186. }
  1187.  
  1188. var buildPanelHook = function() {
  1189. var wrapper = this.AcharvaksAgronomicon.wrapper;
  1190. if(this.AcharvaksAgronomicon.oldBuildPanel.call(this) === false) {
  1191. return false;
  1192. } else {
  1193. if(wrapper.plantsUnlockable) {
  1194. for(var key in wrapper.plantStatus) {
  1195. if(wrapper.plantStatus[key].status > PLANT_LOCKED &&
  1196. wrapper.plantStatus[key].status < PLANT_UNLOCKED) {
  1197. var el = document.getElementById('gardenSeed-' + this.plants[key].id);
  1198. if(el) {
  1199. var elc = el.cloneNode(true); // We need to remove event listeners
  1200. elc.style.opacity = 0.3;
  1201. elc.classList.remove('locked');
  1202. el.parentNode.replaceChild(elc, el);
  1203. }
  1204. }
  1205. }
  1206. }
  1207. }
  1208. }
  1209.  
  1210. var unlockSeedHook = function(me) {
  1211. if(!this.AcharvaksAgronomicon.oldUnlockSeed(me)) {
  1212. return false;
  1213. } else {
  1214. this.AcharvaksAgronomicon.wrapper.recalculatePlantStatus();
  1215. this.buildPanel(); // TODO: maybe optimize in the future
  1216. }
  1217. }
  1218.  
  1219. var lockSeedHook = function(me) {
  1220. if(!this.AcharvaksAgronomicon.oldLockSeed(me)) {
  1221. return false;
  1222. } else {
  1223. this.AcharvaksAgronomicon.wrapper.recalculatePlantStatus();
  1224. this.buildPanel(); // TODO: maybe optimize in the future
  1225. }
  1226. }
  1227.  
  1228. // computeMatures seems the best way to find out when the garden ticks
  1229. var computeMaturesHook = function() {
  1230. this.AcharvaksAgronomicon.oldComputeMatures.call(this);
  1231. this.AcharvaksAgronomicon.newTick = true;
  1232. }
  1233.  
  1234. // Callbacks are called with no `this`
  1235. var drawCallback = function() {
  1236. if(Game.drawT % 10 === 0) {
  1237. // The original garden updates this just like here
  1238. var wrapper = Agronomicon.mainGardenWrapper;
  1239. if(wrapper && wrapper.plantsUnlockable) {
  1240. var tmp = document.getElementById('gardenSeedsUnlocked');
  1241. if(tmp) {
  1242. var garden = wrapper.garden;
  1243. tmp.innerHTML='Seeds <small>('+ garden.plantsUnlockedN+'/'+garden.plantsN+' + ' +
  1244. wrapper.plantsUnlockable + ')</small>';
  1245. }
  1246. }
  1247. }
  1248. }
  1249.  
  1250. var saveCallback = function() {
  1251. var str = JSON.stringify({version: VERSION, revision: REVISION, isDev: IS_DEV,
  1252. alerts: [!!Game.prefs.AcharvaksAgronomicon_IPIDAlert, !!Game.prefs.AcharvaksAgronomicon_NPSAlert,
  1253. !!Game.prefs.AcharvaksAgronomicon_MDAlert]});
  1254. if(window.localStorage) {
  1255. window.localStorage.setItem('AcharvaksAgronomicon', str);
  1256. if(Game.prefs.AcharvaksAgronomicon_IgnoreVersionMismatch) {
  1257. window.localStorage.setItem('AcharvaksAgronomicon_IgnoreMismatchForVersion',
  1258. Game.version + '|' + Agronomicon.version + '|' + Agronomicon.revision);
  1259. } else {
  1260. window.localStorage.removeItem('AcharvaksAgronomicon_IgnoreMismatchForVersion');
  1261. }
  1262. }
  1263. }
  1264.  
  1265. // Add Agronomicon options to the game's option menu (hook on Game)
  1266. var UpdateMenuHook = function() {
  1267. Agronomicon.oldUpdateMenu.call(this);
  1268. if(Game.onMenu === 'prefs') {
  1269. var str = '<div class="title">Cookie Clicker Agronomicon</div>' +
  1270. '<div class="listing">' +
  1271. Game.WriteButton('AcharvaksAgronomicon_IPIDAlert', 'AcharvaksAgronomicon_IPIDAlertButton',
  1272. 'IPID-Alert: ON', 'IPID-Alert: OFF') +
  1273. '<label>Play a sound if Interesting Plant In Danger ' +
  1274. '[<a href="#" onclick="PlaySound(AcharvaksAgronomicon.SOUND2_URL);">listen</a>]</label><br>' +
  1275. "<small>A plant is considered Interesting if you lack its seed, if it's mature and you lack its upgrade, " +
  1276. "or if it's a Juicy Queenbeet. Danger is a chance of death or contamination.</small>" +
  1277. '</div><div class="listing">' +
  1278. Game.WriteButton('AcharvaksAgronomicon_NPSAlert', 'AcharvaksAgronomicon_NPSAlertButton',
  1279. 'New plant species alert: ON', 'New plant species alert: OFF') +
  1280. '<label>Play a sound whenever a new plant species appears in the garden ' +
  1281. '[<a href="#" onclick="PlaySound(AcharvaksAgronomicon.SOUND1_URL);">listen</a>]</label>' +
  1282. '</div><div class="listing">' +
  1283. Game.WriteButton('AcharvaksAgronomicon_MDAlert', 'AcharvaksAgronomicon_MDAlertButton',
  1284. 'Any mature plant in danger alert: ON', 'Any mature plant in danger alert: OFF') +
  1285. '<label>Play a sound whenever any mature plant is in danger ' +
  1286. '[<a href="#" onclick="PlaySound(AcharvaksAgronomicon.SOUND2_URL);">listen</a>]</label>' +
  1287. '</div><div class="listing"' +
  1288. '<ul><li>Agronomicon version: ' + Agronomicon.versionString + '</li></ul>' +
  1289. '</div>';
  1290.  
  1291. if(Agronomicon.isVersionMismatch) {
  1292. str += '<div style="color: red; margin: 1em 0em 0.5em 1em;">Agronomicon has been loaded despite version mismatch!</div>' +
  1293. '<div style="margin-left: 4em;">' +
  1294. Game.WriteButton('AcharvaksAgronomicon_IgnoreVersionMismatch',
  1295. 'AcharvaksAgronomicon_IgnoreVersionMismatchButton',
  1296. 'Always ignore mismatch for this version: ON',
  1297. 'Always ignore mismatch for this version: OFF') +
  1298. '</div>';
  1299. }
  1300.  
  1301. var div = document.createElement('div');
  1302. div.innerHTML = str;
  1303. var menu = document.getElementById('menu');
  1304. if(menu) {
  1305. menu = menu.getElementsByClassName('subsection')[0];
  1306. if(menu) {
  1307. var padding = menu.getElementsByTagName('div');
  1308. padding = padding[padding.length - 1];
  1309. if(padding) {
  1310. menu.insertBefore(div, padding);
  1311. } else {
  1312. menu.appendChild(div);
  1313. }
  1314. }
  1315. }
  1316. }
  1317. }
  1318.  
  1319.  
  1320. // ======= MOD INJECTION =======
  1321.  
  1322.  
  1323. /**
  1324. * Initialize this mod.
  1325. *
  1326. * NOTE: if you're yourself developing a mod for Cookie Clicker and want to somehow
  1327. * interface with the Agronomicon, your mod can get notified when Agronomicon is
  1328. * about to be initialized or has already been initialized. To do so:
  1329. *
  1330. * 1. Set window.AcharvaksAgronomicon to a new object (check if it already exists first).
  1331. * 2. Set AcharvaksAgronomicon.preloadHooks and AcharvaksAgronomicon.postloadHooks to new arrays
  1332. * (again, check if they've already been set first).
  1333. * 3. If you want to be notified before Agronomicon loads, add a function to .preloadHooks. This function
  1334. * will be called with a certain object as an argument (see below). It must return true, else Agronomicon
  1335. * will abort loading.
  1336. * 4. If you want to be notified after Agronomicon loads, add a function to .postloadHooks. It will be called
  1337. * with no arguments and its return value will be ignored. Note that .postloadHooks will be called after
  1338. * the initialization finishes, even if the player has not yet unlocked the garden minigame.
  1339. * 5. If you want to prevent the Agronomicon from doing anything on its own and want to just use its functions,
  1340. * set AcharvaksAgronomicon.APIOnly to true.
  1341. *
  1342. * Note that Agronomicon does not call its own functions via AcharvaksAgronomicon, so you can't easily replace them
  1343. * (except for AcharvaksAgronomicon.wrapGarden and AcharvaksAgronomicon.GardenWrapper)
  1344. *
  1345. * WARNING: this is a beta version. Everything may change yet. It probably will, in fact.
  1346. */
  1347. var initialize = function() {
  1348. try {
  1349. // Version check, preload hooks, setting up global variables
  1350. var version_string = '' + VERSION + '.' + REVISION + (IS_DEV ? '-next' : '') + (IS_BETA ? ' (beta)' : '');
  1351. if(window.AcharvaksAgronomicon != undefined && window.AcharvaksAgronomicon.isLoaded) {
  1352. alert('Cookie Agronomicon is already loaded (version ' + window.AcharvaksAgronomicon.versionString + ')');
  1353. return;
  1354. }
  1355. // Game must have been initialized at this point
  1356. var mismatch = false;
  1357. var ignoreMismatchFor = null;
  1358. if(Game.version != VERSION) {
  1359. mismatch = true;
  1360. if(localStorage) {
  1361. ignoreMismatchFor = localStorage.getItem('AcharvaksAgronomicon_IgnoreMismatchForVersion');
  1362. }
  1363. if(ignoreMismatchFor !== Game.version + '|' + VERSION + '|' + REVISION) {
  1364. var conf = confirm('Cookie Agronomicon ' + version_string
  1365. + ' is intended for Cookie Clicker ' + VERSION + ', but you are running '
  1366. + Game.version + '. Loading the mod may break the game.\n\nProceed anyway?');
  1367. if(!conf) {
  1368. return;
  1369. }
  1370. alert('Loading Agronomicon. You can choose to always ignore this version mismatch in the settings.');
  1371. Game.prefs.AcharvaksAgronomicon_IgnoreVersionMismatch = false;
  1372. } else {
  1373. Game.prefs.AcharvaksAgronomicon_IgnoreVersionMismatch = true;
  1374. }
  1375. } else if(localStorage) {
  1376. // Reset if it's set for a previous version
  1377. Game.prefs.AcharvaksAgronomicon_IgnoreVersionMismatch = false;
  1378. if(localStorage) {
  1379. localStorage.removeItem('AcharvaksAgronomicon_IgnoreMismatchForVersion');
  1380. }
  1381. }
  1382. if(window.AcharvaksAgronomicon === undefined) {
  1383. Agronomicon = {};
  1384. window.AcharvaksAgronomicon = Agronomicon;
  1385. } else {
  1386. if(window.AcharvaksAgronomicon.preloadHooks) {
  1387. // Run preload hooks, if other mods have installed any. If a hook returns false, abort loading.
  1388. var tmp = {version: VERSION, revision: REVISION, isDev: IS_DEV, versionString: version_string};
  1389. for(var hook in window.AcharvaksAgronomicon) {
  1390. if(!hook(tmp)) {
  1391. alert('Cookie Agronomicon has been prevented from loading by another mod');
  1392. return;
  1393. }
  1394. }
  1395. }
  1396. Agronomicon = window.AcharvaksAgronomicon;
  1397. }
  1398. Agronomicon.isLoaded = true;
  1399. Agronomicon.version = VERSION;
  1400. Agronomicon.revision = REVISION;
  1401. Agronomicon.isDev = IS_DEV;
  1402. Agronomicon.isBeta = IS_BETA;
  1403. Agronomicon.versionString = version_string;
  1404. Agronomicon.SOUND1_URL = SOUND1_URL;
  1405. Agronomicon.SOUND2_URL = SOUND2_URL;
  1406. Agronomicon.isVersionMismatch = mismatch;
  1407.  
  1408. if(Agronomicon.GardenWrapper === undefined) {
  1409. Agronomicon.GardenWrapper = GardenWrapper;
  1410. }
  1411. if(Agronomicon.wrapGarden === undefined) {
  1412. Agronomicon.wrapGarden = wrapGarden;
  1413. }
  1414.  
  1415. if(Agronomicon.weedKey === undefined) {
  1416. Agronomicon.weedKey = 'meddleweed';
  1417. Agronomicon.weedProb = 0.002;
  1418. }
  1419. if(Agronomicon.autoUnlocked === undefined) {
  1420. Agronomicon.autoUnlocked = {'bakerWheat': true};
  1421. }
  1422. if(Agronomicon.deathDrops === undefined) {
  1423. Agronomicon.deathDrops = {'meddleweed': [['brownMold','crumbspore'], 0, 0.2]}; // plant -> [[possible results, prob_a, prob_b]]
  1424. // total prob = prob_a + prob_p * (age / 100)
  1425. }
  1426. if(Agronomicon.plantUpgrades === undefined) {
  1427. Agronomicon.plantUpgrades = {
  1428. bakerWheat: ['Wheat slims', 0.001],
  1429. greenRot: ['Green yeast digestives', 0.005],
  1430. elderwort: ['Elderwort biscuits', 0.01],
  1431. bakeberry: ['Bakeberry cookies', 0.015],
  1432. drowsyfern: ['Fern tea', 0.01],
  1433. duketater: ['Duketater cookies', 0.005],
  1434. ichorpuff: ['Ichor syrup', 0.005],
  1435. };
  1436. }
  1437. if(Agronomicon.recipes === undefined) {
  1438. Agronomicon.recipes = RECIPES;
  1439. }
  1440.  
  1441. // Initializing settings
  1442. // TODO: load saved settings
  1443. Agronomicon.percentagePrecision = 4;
  1444.  
  1445. // Overtaking the garden
  1446. if(!Agronomicon.APIOnly) {
  1447. if(Game.Objects['Farm'].minigameLoaded) {
  1448. Agronomicon.wrapGarden(Game.Objects['Farm'].minigame, true);
  1449. } else {
  1450. Agronomicon.oldGameScriptLoaded = Game.scriptLoaded;
  1451. Game.scriptLoaded = scriptLoadedHook;
  1452. }
  1453. Agronomicon.oldUpdateMenu = Game.UpdateMenu;
  1454. Game.UpdateMenu = UpdateMenuHook;
  1455. Game.customDraw.push(drawCallback);
  1456. if(localStorage) {
  1457. var str = localStorage.getItem('AcharvaksAgronomicon');
  1458. if(str) {
  1459. var settings = JSON.parse(str);
  1460. if(settings.version > VERSION || settings.revision > REVISION) {
  1461. alert('Saved settings are from a newer version of Agronomicon, they will be reset');
  1462. } else {
  1463. Game.prefs.AcharvaksAgronomicon_IPIDAlert = settings.alerts[0];
  1464. Game.prefs.AcharvaksAgronomicon_NPSAlert = settings.alerts[1];
  1465. Game.prefs.AcharvaksAgronomicon_MDAlert = settings.alerts[2];
  1466. }
  1467. }
  1468. }
  1469. Game.customSave.push(saveCallback);
  1470. if(Game.onMenu === 'prefs') {
  1471. Game.UpdateMenu();
  1472. }
  1473. }
  1474.  
  1475. // Call postload hooks, if any
  1476. if(Agronomicon.postloadHooks) {
  1477. for(var i = 0; i < Agronomicon.postloadHooks.length; ++i) {
  1478. (Agronomicon.postloadHooks[i])(Agronomicon);
  1479. }
  1480. }
  1481.  
  1482. var msg = 'Cookie Clicker Agronomicon loaded, version ' + version_string;
  1483. if(Game.prefs.popups) {
  1484. Game.Popup(msg);
  1485. } else {
  1486. Game.Notify(msg, '', '', 5, true);
  1487. }
  1488. Game.Win('Third-party');
  1489. }
  1490. catch(e) {
  1491. alert('Error loading Cookie Agronomicon: ' + e);
  1492. throw e;
  1493. }
  1494. }
  1495.  
  1496. // When garden is loaded, prepare to take over
  1497. var scriptLoadedHook = function(who, script) {
  1498. Agronomicon.oldGameScriptLoaded.call(this, who, script);
  1499. if(!Agronomicon.gardenWrapped && who.name === 'Farm') {
  1500. Agronomicon.wrapGarden(Game.Objects['Farm'].minigame, true);
  1501. }
  1502. }
  1503.  
  1504. /**
  1505. * To be called after the garden minigame is launched (via AcharvaksAgronomicon.wrapGarden)
  1506. */
  1507. var wrapGarden = function(garden_minigame, set_main) {
  1508. var gwrapper = new Agronomicon.GardenWrapper(garden_minigame, Agronomicon.autoUnlocked,
  1509. Agronomicon.weedKey, Agronomicon.weedProb, Agronomicon.deathDrops,
  1510. Agronomicon.recipes, Agronomicon.plantUpgrades,
  1511. Agronomicon.percentagePrecision);
  1512.  
  1513. garden_minigame.AcharvaksAgronomicon = {
  1514. oldBuildPlot: garden_minigame.buildPlot,
  1515. oldComputeEffs: garden_minigame.computeEffs,
  1516. oldBuildPanel: garden_minigame.buildPanel,
  1517. oldTileTooltip: garden_minigame.tileTooltip,
  1518. oldGetPlantDesc: garden_minigame.getPlantDesc,
  1519. oldSeedTooltip: garden_minigame.seedTooltip,
  1520. oldUnlockSeed: garden_minigame.unlockSeed,
  1521. oldLockSeed: garden_minigame.lockSeed,
  1522. oldComputeMatures: garden_minigame.computeMatures,
  1523. wrapper: gwrapper,
  1524. newTick: false,
  1525. };
  1526.  
  1527. garden_minigame.buildPlot = buildPlotHook;
  1528. garden_minigame.computeEffs = computeEffsHook;
  1529. garden_minigame.buildPanel = buildPanelHook;
  1530. garden_minigame.tileTooltip = tileTooltipHook;
  1531. garden_minigame.getPlantDesc = getPlantDescHook;
  1532. garden_minigame.seedTooltip = seedTooltipHook;
  1533. garden_minigame.unlockSeed = unlockSeedHook;
  1534. garden_minigame.lockSeed = lockSeedHook;
  1535. garden_minigame.computeMatures = computeMaturesHook;
  1536. garden_minigame.toRebuild = true;
  1537. garden_minigame.buildPlot();
  1538. garden_minigame.buildPanel();
  1539.  
  1540. if(set_main) {
  1541. Agronomicon.mainGardenWrapper = gwrapper;
  1542. }
  1543. }
  1544.  
  1545. // Alert audio files - TODO: maybe replace, allow custom URLs
  1546. // They were created by myself (Acharvak) as MIDI, then converted to MP3
  1547. var SOUND1_URL = 'data:audio/mpeg;base64,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15+7t2NIhXT6vVC38ZCHtzPzrVUOyUehU7s09Vu1b/mYleud/+MoxNEVEAplnhBEAtWmrWm0kZVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVAmn+SSQABfenZur9/o2u9P/7/tbRWW+v13dvYt7f6LGf/+MoxNQV1G5+XhBEvrP6W0/X9CpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqiFG3JLbbsAAfqHu5U6VeItt0ss7EssezuWPZ3DsRLBUJB2RJeGkxLiU7hr/Ep0tiLlud6j2o98sGodK1Dzr6w0Ij2JXEQ0i/+MoxKQJyAZ1nghEAtKnQ6Jf+VoKyO223XbAABYQh4uouDgLgoMEagJiQXEoqCoKBCyFwWZey3805RR6C0UjTjRIEehuO17m5RpwkCAhZjkZGSz9goIHY6GTLJUP/JZf7LY5GrKGDAwjo8pEeasFDAwYR0Mjsln8s//stmR7WNY/9nkasoIGDBOhl/L+rWSw/+MoxMoTYAZyXghEAsjKWz////lkuZMstlQ/n/mrAwQKIOW+r+VV1UxBTUUzLjk5LjVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxP8ljBmSXkmGVVVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MoxHwAAANIAAAAAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV';
  1548.  
  1549. var SOUND2_URL = 'data:audio/mpeg;base64,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/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MoxHwAAANIAAAAAKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq';
  1550.  
  1551. initialize();
  1552. })();
Add Comment
Please, Sign In to add comment