cookie clicker but bigger
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

1040 строки
25 KiB

  1. "use strict";
  2. const belongings = {};
  3. const ownedUpgrades = {};
  4. const remainingUpgrades = [];
  5. let showOwnedUpgrades = false;
  6. const effects = {};
  7. const resources = {};
  8. let updateRate = 60;
  9. const currentProductivity = {};
  10. let clickBonus = 0;
  11. let clickVictim = "micro";
  12. let lastTime = 0;
  13. let controlHeld = false;
  14. let shiftHeld = false;
  15. let mouseTarget = undefined;
  16. const activePowerups = [];
  17. function tickPowerups(delta) {
  18. const powerupList = document.querySelector("#powerup-list");
  19. let changed = false;
  20. // I love mutating arrays as I traverse them.
  21. for (let i = activePowerups.length - 1; i >= 0; i--) {
  22. activePowerups[i].lifetime -= delta;
  23. if (activePowerups[i].lifetime <= 0) {
  24. const entry = activePowerups[i];
  25. setTimeout(() => {
  26. powerupList.removeChild(entry.element);
  27. }, 1000);
  28. entry.element.classList.add("powerup-entry-done");
  29. activePowerups.splice(i, 1);
  30. changed = true;
  31. } else {
  32. const frac = (activePowerups[i].powerup.duration - activePowerups[i].lifetime) / (activePowerups[i].powerup.duration);
  33. activePowerups[i].element.style.setProperty("--progress", frac * 100 + "%")
  34. }
  35. }
  36. if (changed) {
  37. updateAll();
  38. }
  39. }
  40. function addPowerup(powerup) {
  41. const powerupList = document.querySelector("#powerup-list");
  42. const powerupEntry = document.createElement("div");
  43. powerupEntry.classList.add("powerup-entry");
  44. powerupEntry.innerText = powerup.name;
  45. powerupList.appendChild(powerupEntry);
  46. activePowerups.push({ powerup: powerup, lifetime: powerup.duration, element: powerupEntry });
  47. updateAll();
  48. }
  49. function applyGlobalProdBonuses(productivity) {
  50. for (let effect of effects["prod-all"]) {
  51. if (ownedUpgrades[effect.parent]) {
  52. productivity = effect.apply(productivity);
  53. }
  54. }
  55. return productivity;
  56. }
  57. function calculateProductivity() {
  58. let productivity = 0;
  59. for (const [key, value] of Object.entries(belongings)) {
  60. productivity += productivityOf(key);
  61. }
  62. return productivity;
  63. }
  64. // here's where upgrades will go :3
  65. function productivityMultiplierOf(type) {
  66. let base = 1;
  67. for (let effect of effects["prod"]) {
  68. if (ownedUpgrades[effect.parent] && effect.target == type) {
  69. base = effect.apply(base);
  70. }
  71. }
  72. for (let effect of effects["helper"]) {
  73. if (ownedUpgrades[effect.parent] && effect.helped == type) {
  74. base = effect.apply(base, belongings[effect.helper].count);
  75. }
  76. }
  77. return base;
  78. }
  79. function productivityOf(type) {
  80. let baseProd = buildings[type].prod;
  81. let prod = baseProd * productivityMultiplierOf(type);
  82. prod = applyGlobalProdBonuses(prod);
  83. return prod * belongings[type].count;
  84. }
  85. function costOfBuilding(type, count = 1) {
  86. let total = 0;
  87. while (count > 0) {
  88. let baseCost = buildings[type].cost;
  89. let countCost = baseCost * Math.pow(1.15, belongings[type].count + count - 1);
  90. total += countCost;
  91. count--;
  92. }
  93. return Math.round(total);
  94. }
  95. function buildingCount() {
  96. if (controlHeld) {
  97. return 10;
  98. } else if (shiftHeld) {
  99. return 5;
  100. } else {
  101. return 1;
  102. }
  103. }
  104. function buyBuilding(type, e) {
  105. const count = buildingCount();
  106. let cost = costOfBuilding(type, count);
  107. if (resources.food >= cost) {
  108. belongings[type].count += count;
  109. resources.food -= cost;
  110. }
  111. updateProductivity();
  112. updateClickBonus();
  113. }
  114. function updateAll() {
  115. updateProductivity();
  116. updateClickBonus();
  117. updateClickVictim();
  118. }
  119. // update stuff
  120. function updateDisplay() {
  121. let newTime = performance.now();
  122. let delta = newTime - lastTime;
  123. lastTime = newTime;
  124. addResources(delta);
  125. displayResources();
  126. displayBuildings();
  127. displayUpgrades(showOwnedUpgrades);
  128. tickPowerups(delta);
  129. setTimeout(updateDisplay, 1000 / updateRate);
  130. }
  131. function updateProductivity() {
  132. currentProductivity["food"] = calculateProductivity();
  133. activePowerups.forEach(entry => {
  134. const powerup = entry.powerup;
  135. const state = {
  136. ownedUpgrades: ownedUpgrades,
  137. resources: resources,
  138. currentProductivity: currentProductivity,
  139. belongings: belongings
  140. };
  141. console.log(currentProductivity);
  142. powerup.effect(state);
  143. console.log(currentProductivity);
  144. })
  145. }
  146. function addResources(delta) {
  147. for (const [resource, amount] of Object.entries(currentProductivity)) {
  148. resources[resource] += amount * delta / 1000;
  149. }
  150. }
  151. function displayResources() {
  152. document.title = "Gorge - " + round(resources.food) + " food";
  153. replaceChildren(document.querySelector("#resource-list"), renderResources());
  154. }
  155. function renderResources() {
  156. let list = [];
  157. for (const [key, value] of Object.entries(resources)) {
  158. let line1 = render(value, 3, 0) + " " + resourceTypes[key].name;
  159. let line2 = render(currentProductivity[key], 1, 1) + " " + resourceTypes[key].name + "/sec";
  160. list.push({ "text": line1, "class": "resource-quantity" });
  161. list.push({ "text": line2, "class": "resource-rate" });
  162. }
  163. return renderLines(list);
  164. }
  165. function displayBuildings() {
  166. const count = buildingCount();
  167. for (const [key, value] of Object.entries(belongings)) {
  168. if (!belongings[key].visible) {
  169. if (resources.food * 10 >= costOfBuilding(key)) {
  170. unlockBuilding(key);
  171. } else {
  172. continue;
  173. }
  174. belongings[key].visible = true;
  175. document.querySelector("#building-" + key).classList.remove("hidden");
  176. }
  177. let button = document.querySelector("#building-" + key);
  178. let name = document.querySelector("#building-" + key + " > .building-button-name");
  179. let cost = document.querySelector("#building-" + key + " > .building-button-cost");
  180. name.innerText = value.count + " " + (value.count == 1 ? buildings[key].name : buildings[key].plural);
  181. cost.innerText = render(costOfBuilding(key, count)) + " food";
  182. if (costOfBuilding(key, count) > resources.food) {
  183. button.classList.add("building-button-disabled");
  184. cost.classList.add("building-button-cost-invalid");
  185. } else {
  186. button.classList.remove("building-button-disabled");
  187. cost.classList.add("building-button-cost-valid");
  188. }
  189. }
  190. }
  191. function canAfford(cost) {
  192. for (const [resource, amount] of Object.entries(cost)) {
  193. if (resources[resource] < amount) {
  194. return false;
  195. }
  196. }
  197. return true;
  198. }
  199. function spend(cost) {
  200. for (const [resource, amount] of Object.entries(cost)) {
  201. resources[resource] -= amount;
  202. }
  203. }
  204. function switchShowOwnedUpgrades() {
  205. if (showOwnedUpgrades) {
  206. document.querySelector("#upgrades").innerText = "Upgrades";
  207. } else {
  208. document.querySelector("#upgrades").innerText = "Owned Upgrades";
  209. }
  210. showOwnedUpgrades = !showOwnedUpgrades;
  211. }
  212. function displayUpgrades(owned) {
  213. if (owned) {
  214. Object.entries(ownedUpgrades).forEach(([key, val]) => {
  215. let button = document.querySelector("#upgrade-" + key);
  216. if (val) {
  217. button.classList.remove("hidden");
  218. } else {
  219. button.classList.add("hidden");
  220. }
  221. });
  222. }
  223. else {
  224. for (let id of remainingUpgrades) {
  225. let button = document.querySelector("#upgrade-" + id);
  226. if (ownedUpgrades[id]) {
  227. button.classList.add("hidden");
  228. continue;
  229. }
  230. if (upgradeReachable(id)) {
  231. button.classList.remove("hidden");
  232. } else {
  233. button.classList.add("hidden");
  234. }
  235. if (upgradeAvailable(id)) {
  236. button.classList.remove("upgrade-button-inactive");
  237. } else {
  238. button.classList.add("upgrade-button-inactive");
  239. }
  240. }
  241. // we aren't trimming the list of upgrades now
  242. // because we need to switch between owned and unowned upgrades
  243. // - thus we need to be able to show or hide anything
  244. /*
  245. for (let i = remainingUpgrades.length-1; i >= 0; i--) {
  246. if (ownedUpgrades[remainingUpgrades[i]]) {
  247. remainingUpgrades.splice(i, 1);
  248. }
  249. }*/
  250. }
  251. }
  252. function updateClickBonus() {
  253. let bonus = 0;
  254. for (let effect of effects["click"]) {
  255. if (ownedUpgrades[effect.parent]) {
  256. bonus = effect.apply(bonus, currentProductivity["food"]);
  257. }
  258. }
  259. clickBonus = bonus;
  260. }
  261. function updateClickVictim() {
  262. for (let effect of effects["click-victim"]) {
  263. if (ownedUpgrades[effect.parent]) {
  264. clickVictim = effect.id;
  265. document.querySelector("#tasty-micro").innerText = "Eat " + buildings[effect.id].name;
  266. }
  267. }
  268. }
  269. function buyUpgrade(id, e) {
  270. if (ownedUpgrades[id]) {
  271. return;
  272. }
  273. let upgrade = upgrades[id];
  274. if (!upgradeAvailable(id)) {
  275. return;
  276. }
  277. spend(upgrade.cost);
  278. ownedUpgrades[id] = true;
  279. let text = "Bought " + upgrade.name + "!";
  280. clickPopup(text, "upgrade", [e.clientX, e.clientY]);
  281. updateProductivity();
  282. updateClickBonus();
  283. updateClickVictim();
  284. }
  285. function eatPrey() {
  286. const add = buildings[clickVictim]["prod"] * 10 * productivityMultiplierOf(clickVictim) + clickBonus;
  287. resources.food += add;
  288. return add;
  289. }
  290. // setup stuff lol
  291. // we'll initialize the dict of buildings we can own
  292. function setup() {
  293. // create static data
  294. createTemplateUpgrades();
  295. // prepare dynamic stuff
  296. initializeData();
  297. createButtons();
  298. createDisplays();
  299. registerListeners();
  300. load();
  301. unlockAtStart();
  302. updateAll();
  303. }
  304. function unlockAtStart() {
  305. unlockBuilding("micro");
  306. for (const [key, value] of Object.entries(belongings)) {
  307. if (belongings[key].visible) {
  308. unlockBuilding(key);
  309. }
  310. }
  311. }
  312. function unlockBuilding(id) {
  313. belongings[id].visible = true;
  314. document.querySelector("#building-" + id).classList.remove("hidden");
  315. }
  316. function initializeData() {
  317. for (const [key, value] of Object.entries(buildings)) {
  318. belongings[key] = {};
  319. belongings[key].count = 0;
  320. belongings[key].visible = false;
  321. }
  322. for (const [key, value] of Object.entries(resourceTypes)) {
  323. resources[key] = 0;
  324. currentProductivity[key] = 0;
  325. }
  326. for (const [id, upgrade] of Object.entries(upgrades)) {
  327. ownedUpgrades[id] = false;
  328. for (let effect of upgrade.effects) {
  329. if (effects[effect.type] === undefined) {
  330. effects[effect.type] = [];
  331. }
  332. // copy the data and add an entry for the upgrade id that owns the effect
  333. let newEffect = {};
  334. for (const [key, value] of Object.entries(effect)) {
  335. newEffect[key] = value;
  336. }
  337. newEffect.parent = id;
  338. // unfortunate name collision here
  339. // I'm using apply() to pass on any number of arguments to the
  340. // apply() function of the effect type
  341. newEffect.apply = function (...args) { return effect_types[effect.type].apply.apply(null, [effect].concat(args)); }
  342. effects[effect.type].push(newEffect);
  343. }
  344. }
  345. }
  346. function registerListeners() {
  347. document.querySelector("#tasty-micro").addEventListener("click", (e) => {
  348. const add = eatPrey();
  349. const text = "+" + round(add, 1) + " food";
  350. const gulp = "*glp*";
  351. clickPopup(text, "food", [e.clientX, e.clientY]);
  352. clickPopup(gulp, "gulp", [e.clientX, e.clientY]);
  353. });
  354. document.querySelector("#save").addEventListener("click", save);
  355. document.querySelector("#reset").addEventListener("click", reset);
  356. document.querySelector("#upgrades").addEventListener("click", switchShowOwnedUpgrades);
  357. document.addEventListener("keydown", e => {
  358. shiftHeld = e.shiftKey;
  359. controlHeld = e.ctrlKey;
  360. if (mouseTarget)
  361. mouseTarget.dispatchEvent(new Event("mousemove"));
  362. return true;
  363. });
  364. document.addEventListener("keyup", e => {
  365. shiftHeld = e.shiftKey;
  366. controlHeld = e.ctrlKey;
  367. if (mouseTarget)
  368. mouseTarget.dispatchEvent(new Event("mousemove"));
  369. return true;
  370. });
  371. }
  372. function createButtons() {
  373. createBuildings();
  374. createUpgrades();
  375. }
  376. function createBuildings() {
  377. let container = document.querySelector("#buildings-list");
  378. for (const [key, value] of Object.entries(buildings)) {
  379. let button = document.createElement("div");
  380. button.classList.add("building-button");
  381. button.classList.add("hidden");
  382. button.id = "building-" + key;
  383. let buttonName = document.createElement("div");
  384. buttonName.classList.add("building-button-name");
  385. let buttonCost = document.createElement("div");
  386. buttonCost.classList.add("building-button-cost");
  387. let buildingIcon = document.createElement("i");
  388. buildingIcon.classList.add("fas");
  389. buildingIcon.classList.add(value.icon);
  390. button.appendChild(buttonName);
  391. button.appendChild(buttonCost);
  392. button.appendChild(buildingIcon);
  393. button.addEventListener("mousemove", function (e) { mouseTarget = button; buildingTooltip(key, e); });
  394. button.addEventListener("mouseleave", function () { mouseTarget = undefined; buildingTooltipRemove(); });
  395. button.addEventListener("click", function (e) { buyBuilding(key, e); });
  396. button.addEventListener("click", function (e) { buildingTooltip(key, e); });
  397. container.appendChild(button);
  398. }
  399. }
  400. // do we have previous techs and at least one of each building?
  401. function upgradeReachable(id) {
  402. if (ownedUpgrades[id]) {
  403. return false;
  404. }
  405. if (upgrades[id].prereqs !== undefined) {
  406. for (const [type, reqs] of Object.entries(upgrades[id].prereqs)) {
  407. if (type == "buildings") {
  408. for (const [building, amount] of Object.entries(reqs)) {
  409. if (belongings[building].count == 0) {
  410. return false;
  411. }
  412. }
  413. }
  414. else if (type == "upgrades") {
  415. for (let upgrade of reqs) {
  416. if (!ownedUpgrades[upgrade]) {
  417. return false;
  418. }
  419. }
  420. }
  421. }
  422. }
  423. return true;
  424. }
  425. function upgradeAvailable(id) {
  426. if (!upgradeReachable(id)) {
  427. return false;
  428. }
  429. if (!canAfford(upgrades[id].cost)) {
  430. return false;
  431. }
  432. if (upgrades[id].prereqs !== undefined) {
  433. for (const [type, reqs] of Object.entries(upgrades[id].prereqs)) {
  434. if (type == "buildings") {
  435. for (const [building, amount] of Object.entries(upgrades[id].prereqs[type])) {
  436. if (belongings[building].count < amount) {
  437. return false;
  438. }
  439. }
  440. } else if (type == "productivity") {
  441. for (const [key, value] of Object.entries(reqs)) {
  442. if (currentProductivity[key] < value) {
  443. return false;
  444. }
  445. }
  446. }
  447. }
  448. }
  449. return true;
  450. }
  451. function createUpgrades() {
  452. let container = document.querySelector("#upgrades-list");
  453. for (const [key, value] of Object.entries(upgrades)) {
  454. remainingUpgrades.push(key);
  455. let button = document.createElement("div");
  456. button.classList.add("upgrade-button");
  457. button.classList.add("hidden");
  458. button.id = "upgrade-" + key;
  459. let buttonName = document.createElement("div");
  460. buttonName.classList.add("upgrade-button-name");
  461. buttonName.innerText = value.name;
  462. let upgradeIcon = document.createElement("i");
  463. upgradeIcon.classList.add("fas");
  464. upgradeIcon.classList.add(value.icon);
  465. button.appendChild(buttonName);
  466. button.appendChild(upgradeIcon);
  467. button.addEventListener("mouseenter", function (e) { mouseTarget = button; upgradeTooltip(key, e); });
  468. button.addEventListener("mousemove", function (e) { mouseTarget = button; upgradeTooltip(key, e); });
  469. button.addEventListener("mouseleave", function () { mouseTarget = undefined; upgradeTooltipRemove(); });
  470. button.addEventListener("click", function (e) { buyUpgrade(key, e); });
  471. container.appendChild(button);
  472. }
  473. }
  474. function createDisplays() {
  475. // nop
  476. }
  477. function renderLine(line) {
  478. let div = document.createElement("div");
  479. div.innerText = line.text;
  480. if (line.valid !== undefined) {
  481. if (line.valid) {
  482. div.classList.add("cost-met");
  483. } else {
  484. div.classList.add("cost-unmet");
  485. }
  486. }
  487. if (line.class !== undefined) {
  488. for (let entry of line.class.split(",")) {
  489. div.classList.add(entry);
  490. }
  491. }
  492. return div;
  493. }
  494. function renderLines(lines) {
  495. let divs = [];
  496. for (let line of lines) {
  497. divs.push(renderLine(line));
  498. }
  499. return divs;
  500. }
  501. function renderCost(cost) {
  502. let list = [];
  503. list.push({
  504. "text": "Cost:"
  505. });
  506. for (const [key, value] of Object.entries(cost)) {
  507. list.push({
  508. "text": render(value, 0) + " " + resourceTypes[key].name,
  509. "valid": resources[key] >= value
  510. });
  511. }
  512. return renderLines(list);
  513. }
  514. function renderPrereqs(prereqs) {
  515. let list = [];
  516. if (prereqs === undefined) {
  517. return renderLines(list);
  518. }
  519. list.push({
  520. "text": "Own:"
  521. });
  522. for (const [key, value] of Object.entries(prereqs)) {
  523. if (key == "buildings") {
  524. for (const [id, amount] of Object.entries(prereqs.buildings)) {
  525. list.push({
  526. "text": buildings[id].name + " x" + render(amount, 0),
  527. "valid": belongings[id].count >= amount
  528. });
  529. }
  530. } else if (key == "productivity") {
  531. for (const [id, amount] of Object.entries(prereqs.productivity)) {
  532. list.push({
  533. "text": render(amount, 0) + " " + resourceTypes[id].name + "/s",
  534. "valid": currentProductivity[id] >= amount
  535. });
  536. }
  537. }
  538. }
  539. return renderLines(list);
  540. }
  541. function renderEffects(effectList) {
  542. let list = [];
  543. for (let effect of effectList) {
  544. list.push({ "text": effect_types[effect.type].desc(effect) });
  545. }
  546. return renderLines(list);
  547. }
  548. function clickPopup(text, type, location) {
  549. const div = document.createElement("div");
  550. div.textContent = text;
  551. div.classList.add("click-popup-" + type);
  552. var direction;
  553. if (type == "food") {
  554. direction = -150;
  555. } else if (type == "gulp") {
  556. direction = -150;
  557. } else if (type == "upgrade") {
  558. direction = -50;
  559. } else if (type == "info") {
  560. direction = 0;
  561. }
  562. direction *= Math.random() * 0.5 + 1;
  563. direction = Math.round(direction) + "px"
  564. div.style.setProperty("--target", direction)
  565. div.style.left = location[0] + "px";
  566. div.style.top = location[1] + "px";
  567. const body = document.querySelector("body");
  568. body.appendChild(div);
  569. setTimeout(() => {
  570. body.removeChild(div);
  571. }, 2000);
  572. }
  573. function doNews() {
  574. const state = {
  575. ownedUpgrades: ownedUpgrades,
  576. resources: resources,
  577. currentProductivity: currentProductivity,
  578. belongings: belongings
  579. };
  580. let options = [];
  581. news.forEach(entry => {
  582. if (entry.condition(state)) {
  583. options = options.concat(entry.lines);
  584. }
  585. });
  586. const choice = Math.floor(Math.random() * options.length);
  587. showNews(options[choice](state));
  588. setTimeout(() => {
  589. doNews();
  590. }, 15000 + Math.random() * 2500);
  591. }
  592. function showNews(text) {
  593. const div = document.createElement("div");
  594. div.textContent = text;
  595. div.classList.add("news-text");
  596. const body = document.querySelector("body");
  597. body.appendChild(div);
  598. setTimeout(() => {
  599. body.removeChild(div);
  600. }, 10000);
  601. }
  602. function doPowerup() {
  603. const lifetime = 10000;
  604. const button = document.createElement("div");
  605. const left = Math.round(Math.random() * 50 + 25) + "%";
  606. const top = Math.round(Math.random() * 50 + 25) + "%";
  607. button.classList.add("powerup");
  608. button.style.setProperty("--lifetime", lifetime / 1000 + "s");
  609. button.style.setProperty("--leftpos", left);
  610. button.style.setProperty("--toppos", top);
  611. const body = document.querySelector("body");
  612. body.appendChild(button);
  613. const choices = [];
  614. Object.entries(powerups).forEach(([key, val]) => {
  615. choices.push(key);
  616. });
  617. const choice = Math.floor(Math.random() * choices.length);
  618. const powerup = powerups[choices[choice]];
  619. const icon = document.createElement("div");
  620. icon.classList.add("fas");
  621. icon.classList.add(powerup.icon);
  622. button.appendChild(icon);
  623. const remove = setTimeout(() => {
  624. body.removeChild(button);
  625. }, lifetime);
  626. let delay = 60000 + Math.random() * 30000;
  627. for (let effect of effects["powerup-freq"]) {
  628. if (ownedUpgrades[effect.parent]) {
  629. delay = effect.apply(delay);
  630. }
  631. }
  632. setTimeout(() => {
  633. doPowerup();
  634. }, delay);
  635. const state = {
  636. ownedUpgrades: ownedUpgrades,
  637. resources: resources,
  638. currentProductivity: currentProductivity,
  639. belongings: belongings
  640. };
  641. button.addEventListener("mousedown", e => {
  642. if (powerup.duration !== undefined) {
  643. addPowerup(powerup);
  644. } else {
  645. powerup.effect(state);
  646. }
  647. powerup.popup(powerup, e);
  648. button.classList.add("powerup-clicked");
  649. resources.food += 1000;
  650. clearTimeout(remove);
  651. setTimeout(() => {
  652. body.removeChild(button);
  653. }, 500);
  654. });
  655. }
  656. function fillTooltip(type, field, content) {
  657. let item = document.querySelector("#" + type + "-tooltip-" + field);
  658. if (typeof (content) === "string") {
  659. item.innerText = content;
  660. } else {
  661. replaceChildren(item, content);
  662. }
  663. }
  664. function upgradeTooltip(id, event) {
  665. let tooltip = document.querySelector("#upgrade-tooltip");
  666. tooltip.style.setProperty("display", "inline-block");
  667. fillTooltip("upgrade", "name", upgrades[id].name);
  668. fillTooltip("upgrade", "desc", upgrades[id].desc);
  669. fillTooltip("upgrade", "effect", renderEffects(upgrades[id].effects));
  670. fillTooltip("upgrade", "cost", renderCost(upgrades[id].cost));
  671. fillTooltip("upgrade", "prereqs", renderPrereqs(upgrades[id].prereqs));
  672. let yOffset = tooltip.parentElement.getBoundingClientRect().y;
  673. let tooltipSize = tooltip.getBoundingClientRect().height;
  674. let yTrans = Math.round(event.clientY - yOffset);
  675. var body = document.body,
  676. html = document.documentElement;
  677. var height = Math.max(window.innerHeight);
  678. yTrans = Math.min(yTrans, height - tooltipSize - 150);
  679. tooltip.style.setProperty("transform", "translate(-220px, " + yTrans + "px)");
  680. }
  681. function upgradeTooltipRemove() {
  682. let tooltip = document.querySelector("#upgrade-tooltip");
  683. tooltip.style.setProperty("display", "none");
  684. }
  685. function prodSummary(id) {
  686. let list = [];
  687. list.push(
  688. { "text": "Each " + buildings[id].name + " produces " + round(productivityMultiplierOf(id) * buildings[id].prod, 1) + " food/sec" }
  689. );
  690. list.push(
  691. { "text": "Your " + render(belongings[id].count) + " " + (belongings[id].count == 1 ? buildings[id].name + " is" : buildings[id].plural + " are") + " producing " + round(productivityOf(id), 1) + " food/sec" }
  692. );
  693. let percentage = round(100 * productivityOf(id) / currentProductivity["food"], 2);
  694. if (isNaN(percentage)) {
  695. percentage = 0;
  696. }
  697. list.push(
  698. { "text": "(" + percentage + "% of all food)" }
  699. );
  700. return renderLines(list);
  701. }
  702. function buildingTooltip(id, event) {
  703. let tooltip = document.querySelector("#building-tooltip");
  704. tooltip.style.setProperty("display", "inline-block");
  705. const count = buildingCount();
  706. fillTooltip("building", "name", (count != 1 ? count + "x " : "") + buildings[id].name);
  707. fillTooltip("building", "desc", buildings[id].desc);
  708. fillTooltip("building", "cost", render(costOfBuilding(id, count)) + " food");
  709. fillTooltip("building", "prod", prodSummary(id));
  710. let xPos = tooltip.parentElement.getBoundingClientRect().x - 450;
  711. // wow browsers are bad
  712. var body = document.body,
  713. html = document.documentElement;
  714. var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
  715. let yPos = Math.min(event.clientY, height - 200);
  716. tooltip.style.setProperty("transform", "translate(" + xPos + "px, " + yPos + "px)")
  717. }
  718. function buildingTooltipRemove() {
  719. let tooltip = document.querySelector("#building-tooltip");
  720. tooltip.style.setProperty("display", "none");
  721. }
  722. window.onload = function () {
  723. setup();
  724. lastTime = performance.now();
  725. doNews();
  726. doPowerup();
  727. setTimeout(updateDisplay, 1000 / updateRate);
  728. setTimeout(autosave, 60000);
  729. }
  730. function autosave() {
  731. saveGame();
  732. let x = window.innerWidth / 2;
  733. let y = window.innerHeight * 9 / 10;
  734. clickPopup("Autosaving...", "info", [x, y]);
  735. setTimeout(autosave, 60000);
  736. }
  737. function save(e) {
  738. saveGame();
  739. clickPopup("Saved!", "info", [e.clientX, e.clientY]);
  740. }
  741. function saveGame() {
  742. try {
  743. let storage = window.localStorage;
  744. storage.setItem("save-version", "0.0.1");
  745. storage.setItem("ownedUpgrades", JSON.stringify(ownedUpgrades));
  746. storage.setItem("resources", JSON.stringify(resources));
  747. storage.setItem("belongings", JSON.stringify(belongings));
  748. } catch (e) {
  749. clickPopup("Can't save - no access to local storage.", "info", [window.innerWidth / 2, window.innerHeight / 5]);
  750. }
  751. }
  752. function load() {
  753. try {
  754. let storage = window.localStorage;
  755. if (!storage.getItem("save-version")) {
  756. return;
  757. }
  758. let newOwnedUpgrades = JSON.parse(storage.getItem("ownedUpgrades"));
  759. for (const [key, value] of Object.entries(newOwnedUpgrades)) {
  760. ownedUpgrades[key] = value;
  761. }
  762. let newResources = JSON.parse(storage.getItem("resources"));
  763. for (const [key, value] of Object.entries(newResources)) {
  764. resources[key] = value;
  765. }
  766. let newBelongings = JSON.parse(storage.getItem("belongings"));
  767. for (const [key, value] of Object.entries(newBelongings)) {
  768. belongings[key] = value;
  769. }
  770. } catch (e) {
  771. clickPopup("Can't load - no access to local storage.", "info", [window.innerWidth / 2, window.innerHeight / 5]);
  772. }
  773. }
  774. function reset() {
  775. window.localStorage.clear();
  776. }