cookie clicker but bigger
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 

990 lines
23 KiB

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