cookie clicker but bigger
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

588 linhas
14 KiB

  1. "use strict";
  2. let belongings = {};
  3. let ownedUpgrades = {};
  4. let effects = {};
  5. let remainingUpgrades = [];
  6. let resources = {};
  7. let updateRate = 60;
  8. let currentProductivity = {};
  9. let lastTime = 0;
  10. function calculateProductivity() {
  11. let productivity = 0;
  12. for (const [key, value] of Object.entries(belongings)) {
  13. productivity += productivityOf(key);
  14. }
  15. for (let effect of effects["prod-all"]) {
  16. if (ownedUpgrades[effect.parent]) {
  17. productivity = effect.apply(productivity);
  18. }
  19. }
  20. return productivity;
  21. }
  22. // here's where upgrades will go :3
  23. function productivityMultiplierOf(type) {
  24. let base = 1;
  25. for (let effect of effects["prod"]) {
  26. if (ownedUpgrades[effect.parent] && effect.target == type) {
  27. base = effect.apply(base);
  28. }
  29. }
  30. return base;
  31. }
  32. function productivityOf(type) {
  33. let baseProd = buildings[type].prod;
  34. let prod = baseProd * productivityMultiplierOf(type);
  35. return prod * belongings[type].count;
  36. }
  37. function costOfBuilding(type) {
  38. let baseCost = buildings[type].cost
  39. let countCost = baseCost * Math.pow(1.15, belongings[type].count);
  40. return Math.round(countCost);
  41. }
  42. function buyBuilding(type) {
  43. let cost = costOfBuilding(type);
  44. if (resources.food >= cost) {
  45. belongings[type].count += 1;
  46. resources.food -= cost;
  47. }
  48. updateProductivity();
  49. }
  50. // update stuff
  51. function updateDisplay() {
  52. let newTime = performance.now();
  53. let delta = newTime - lastTime;
  54. lastTime = newTime;
  55. updateProductivity();
  56. addResources(delta);
  57. displayResources();
  58. displayBuildings();
  59. displayUpgrades();
  60. setTimeout(updateDisplay, 1000/updateRate);
  61. }
  62. function updateProductivity() {
  63. currentProductivity["food"] = calculateProductivity();
  64. }
  65. function addResources(delta) {
  66. resources.food += currentProductivity["food"] * delta / 1000;
  67. }
  68. function displayResources() {
  69. document.title = "Gorge - " + round(resources.food) + " food";
  70. document.getElementById("resource-food").innerText = "Food: " + render(resources.food);
  71. document.getElementById("productivity").innerText = round(calculateProductivity(), 1) + " food/sec";
  72. }
  73. function displayBuildings() {
  74. for (const [key, value] of Object.entries(belongings)) {
  75. if (!belongings[key].visible) {
  76. if (resources.food * 10 >= costOfBuilding(key)) {
  77. unlockBuilding(key);
  78. } else {
  79. continue;
  80. }
  81. belongings[key].visible = true;
  82. document.querySelector("#building-" + key).classList.remove("hidden");
  83. }
  84. let button = document.querySelector("#building-" + key);
  85. document.querySelector("#building-" + key + " > .building-button-name").innerText = value.count + " " + (value.count == 1 ? buildings[key].name : buildings[key].plural);
  86. document.querySelector("#building-" + key + " > .building-button-cost").innerText = costOfBuilding(key) + " food";
  87. if (costOfBuilding(key) > resources.food) {
  88. button.classList.add("building-button-disabled");
  89. } else {
  90. button.classList.remove("building-button-disabled");
  91. }
  92. }
  93. }
  94. function canAfford(cost) {
  95. for (const [resource, amount] of Object.entries(cost)) {
  96. if (resources[resource] < amount) {
  97. return false;
  98. }
  99. }
  100. return true;
  101. }
  102. function spend(cost) {
  103. for (const [resource, amount] of Object.entries(cost)) {
  104. resources[resource] -= amount;
  105. }
  106. }
  107. function displayUpgrades() {
  108. for (let id of remainingUpgrades) {
  109. let button = document.querySelector("#upgrade-" + id);
  110. if (ownedUpgrades[id]) {
  111. button.style.display = "none";
  112. continue;
  113. }
  114. if (upgradeReachable(id)) {
  115. button.classList.remove("hidden");
  116. } else {
  117. button.classList.add("hidden");
  118. }
  119. if (upgradeAvailable(id)) {
  120. button.classList.remove("upgrade-button-inactive");
  121. } else {
  122. button.classList.add("upgrade-button-inactive");
  123. }
  124. }
  125. // now we throw out stuff
  126. for (let i = remainingUpgrades.length-1; i >= 0; i--) {
  127. if (ownedUpgrades[remainingUpgrades[i]]) {
  128. remainingUpgrades.splice(i, 1);
  129. }
  130. }
  131. }
  132. function buyUpgrade(id) {
  133. if (ownedUpgrades[id]) {
  134. return;
  135. }
  136. let upgrade = upgrades[id];
  137. if (!upgradeAvailable(id)) {
  138. return;
  139. }
  140. spend(upgrade.cost);
  141. ownedUpgrades[id] = true;
  142. }
  143. function eatMicro() {
  144. resources.food += productivityMultiplierOf("micro");
  145. }
  146. // setup stuff lol
  147. // we'll initialize the dict of buildings we can own
  148. function setup() {
  149. initializeData();
  150. createButtons();
  151. createDisplays();
  152. registerListeners();
  153. load();
  154. unlockAtStart();
  155. }
  156. function unlockAtStart() {
  157. unlockBuilding("micro");
  158. }
  159. function unlockBuilding(id) {
  160. belongings[id].visible = true;
  161. document.querySelector("#building-" + id).classList.remove("hidden");
  162. }
  163. function initializeData() {
  164. for (const [key, value] of Object.entries(buildings)) {
  165. belongings[key] = {};
  166. belongings[key].count = 0;
  167. belongings[key].visible = false;
  168. }
  169. for (const [key, value] of Object.entries(resourceTypes)) {
  170. currentProductivity[key] = 0;
  171. }
  172. for (const [id, upgrade] of Object.entries(upgrades)) {
  173. ownedUpgrades[id] = false;
  174. for (let effect of upgrade.effects) {
  175. if (effects[effect.type] === undefined) {
  176. effects[effect.type] = [];
  177. }
  178. // copy the data and add an entry for the upgrade id that owns the effect
  179. let newEffect = {};
  180. for (const [key, value] of Object.entries(effect)) {
  181. newEffect[key] = value;
  182. }
  183. newEffect.parent = id;
  184. // unfortunate name collision here
  185. // I'm using apply() to pass on any number of arguments to the
  186. // apply() function of the effect type
  187. newEffect.apply = function(...args) { return effect_types[effect.type].apply.apply(null, [effect].concat(args)); }
  188. effects[effect.type].push(newEffect);
  189. }
  190. }
  191. }
  192. function registerListeners() {
  193. document.querySelector("#tasty-micro").addEventListener("click", eatMicro);
  194. document.querySelector("#save").addEventListener("click", save);
  195. document.querySelector("#reset").addEventListener("click", reset);
  196. }
  197. function createButtons() {
  198. createBuildings();
  199. createUpgrades();
  200. }
  201. function createBuildings() {
  202. let container = document.querySelector("#buildings-area");
  203. for (const [key, value] of Object.entries(buildings)) {
  204. let button = document.createElement("div");
  205. button.classList.add("building-button");
  206. button.classList.add("hidden");
  207. button.id = "building-" + key;
  208. let buttonName = document.createElement("div");
  209. buttonName.classList.add("building-button-name");
  210. let buttonCost = document.createElement("div");
  211. buttonCost.classList.add("building-button-cost");
  212. button.appendChild(buttonName);
  213. button.appendChild(buttonCost);
  214. button.addEventListener("mousemove", function(e) { buildingTooltip(key, e); });
  215. button.addEventListener("mouseleave", function() { buildingTooltipRemove(); });
  216. button.addEventListener("click", function() { buyBuilding(key); });
  217. button.addEventListener("click", function(e) { buildingTooltip(key, e); });
  218. container.appendChild(button);
  219. }
  220. }
  221. // do we have previous techs and at least one of each building?
  222. function upgradeReachable(id) {
  223. if (ownedUpgrades[id]) {
  224. return false;
  225. }
  226. for (const [type, reqs] of Object.entries(upgrades[id].prereqs)) {
  227. if (type == "buildings") {
  228. for (const [building, amount] of Object.entries(reqs)) {
  229. if (belongings[building].count == 0) {
  230. return false;
  231. }
  232. }
  233. }
  234. else if (type == "upgrades") {
  235. for (let upgrade of reqs) {
  236. if (!ownedUpgrades[upgrade]) {
  237. return false;
  238. }
  239. }
  240. }
  241. }
  242. return true;
  243. }
  244. function upgradeAvailable(id) {
  245. if (!upgradeReachable(id)) {
  246. return false;
  247. }
  248. if (!canAfford(upgrades[id].cost)) {
  249. return false;
  250. }
  251. for (const [type, reqs] of Object.entries(upgrades[id].prereqs)) {
  252. if (type == "buildings") {
  253. for (const [building, amount] of Object.entries(upgrades[id].prereqs[type])) {
  254. if (belongings[building].count < amount) {
  255. return false;
  256. }
  257. }
  258. } else if (type == "productivity") {
  259. for (const [key, value] of Object.entries(reqs)) {
  260. if (currentProductivity[key] < value) {
  261. return false;
  262. }
  263. }
  264. }
  265. }
  266. return true;
  267. }
  268. function createUpgrades() {
  269. let container = document.querySelector("#upgrades-list");
  270. for (const [key, value] of Object.entries(upgrades)) {
  271. remainingUpgrades.push(key);
  272. let button = document.createElement("div");
  273. button.classList.add("upgrade-button");
  274. button.classList.add("hidden");
  275. button.id = "upgrade-" + key;
  276. let buttonName = document.createElement("div");
  277. buttonName.classList.add("upgrade-button-name");
  278. buttonName.innerText = value.name;
  279. button.appendChild(buttonName);
  280. button.addEventListener("mousemove", function(e) { upgradeTooltip(key, e); });
  281. button.addEventListener("mouseleave", function() { upgradeTooltipRemove(); });
  282. button.addEventListener("click", function() { buyUpgrade(key); });
  283. container.appendChild(button);
  284. }
  285. }
  286. function createDisplays() {
  287. let resourceList = document.querySelector("#resource-list");
  288. for (const [key, value] of Object.entries(resourceTypes)) {
  289. resources[key] = 0;
  290. let line = document.createElement("div");
  291. line.id = "resource-" + key;
  292. resourceList.appendChild(line);
  293. }
  294. }
  295. function renderLine(line) {
  296. let div = document.createElement("div");
  297. div.innerText = line.text;
  298. if (line.valid !== undefined) {
  299. if (line.valid) {
  300. div.classList.add("cost-met");
  301. } else {
  302. div.classList.add("cost-unmet");
  303. }
  304. }
  305. return div;
  306. }
  307. function renderLines(lines) {
  308. let divs = [];
  309. for (let line of lines) {
  310. divs.push(renderLine(line));
  311. }
  312. return divs;
  313. }
  314. function renderCost(cost) {
  315. let list = [];
  316. list.push({
  317. "text": "Cost:"
  318. });
  319. for (const [key, value] of Object.entries(cost)) {
  320. list.push({
  321. "text": value + " " + resourceTypes[key].name,
  322. "valid": resources[key] >= value
  323. });
  324. }
  325. return renderLines(list);
  326. }
  327. function renderPrereqs(prereqs) {
  328. let list = [];
  329. list.push({
  330. "text": "Own:"
  331. });
  332. for (const [key, value] of Object.entries(prereqs)) {
  333. if (key == "buildings") {
  334. for (const [id, amount] of Object.entries(prereqs.buildings)) {
  335. list.push({
  336. "text": buildings[id].name + " x" + amount,
  337. "valid": belongings[id].count >= amount
  338. });
  339. }
  340. } else if (key == "productivity") {
  341. for (const [id, amount] of Object.entries(prereqs.productivity)) {
  342. list.push({
  343. "text": amount + " " + resourceTypes[id].name + "/s",
  344. "valid": currentProductivity[id] >= amount
  345. });
  346. }
  347. }
  348. }
  349. return renderLines(list);
  350. }
  351. function renderEffects(effectList) {
  352. let list = [];
  353. for (let effect of effectList) {
  354. list.push({"text": effect_types[effect.type].desc(effect)});
  355. }
  356. return renderLines(list);
  357. }
  358. function fillTooltip(type, field, content) {
  359. let item = document.querySelector("#" + type + "-tooltip-" + field);
  360. if (typeof(content) === "string") {
  361. item.innerText = content;
  362. } else {
  363. replaceChildren(item, content);
  364. }
  365. }
  366. function upgradeTooltip(id, event) {
  367. let tooltip = document.querySelector("#upgrade-tooltip");
  368. tooltip.style.setProperty("display", "inline-block");
  369. fillTooltip("upgrade", "name", upgrades[id].name);
  370. fillTooltip("upgrade", "desc", upgrades[id].desc);
  371. fillTooltip("upgrade", "effect", renderEffects(upgrades[id].effects));
  372. fillTooltip("upgrade", "cost", renderCost(upgrades[id].cost));
  373. fillTooltip("upgrade", "prereqs", renderPrereqs(upgrades[id].prereqs));
  374. let yOffset = tooltip.parentElement.getBoundingClientRect().y;
  375. let yTrans = Math.round(event.clientY - yOffset);
  376. tooltip.style.setProperty("transform", "translate(-220px, " + yTrans + "px)");
  377. }
  378. function upgradeTooltipRemove() {
  379. let tooltip = document.querySelector("#upgrade-tooltip");
  380. tooltip.style.setProperty("display", "none");
  381. }
  382. function prodSummary(id) {
  383. let list = [];
  384. list.push(
  385. {"text": "Each " + buildings[id].name + " produces " + round(productivityMultiplierOf(id) * buildings[id].prod,1) + " food/sec"}
  386. );
  387. list.push(
  388. {"text": "Your " + belongings[id].count + " " + (belongings[id].count == 1 ? buildings[id].name + " is": buildings[id].plural + " are") + " producing " + round(productivityOf(id),1) + " food/sec"}
  389. );
  390. let percentage = round(100 * productivityOf(id) / currentProductivity["food"], 2);
  391. if (isNaN(percentage)) {
  392. percentage = 0;
  393. }
  394. list.push(
  395. {"text": "(" + percentage + "% of all food)"}
  396. );
  397. return renderLines(list);
  398. }
  399. function buildingTooltip(id, event) {
  400. let tooltip = document.querySelector("#building-tooltip");
  401. tooltip.style.setProperty("display", "inline-block");
  402. fillTooltip("building", "name", buildings[id].name);
  403. fillTooltip("building", "desc", buildings[id].desc);
  404. fillTooltip("building", "cost", costOfBuilding(id) + " food");
  405. fillTooltip("building", "prod", prodSummary(id));
  406. let yOffset = tooltip.parentElement.getBoundingClientRect().y;
  407. let xPos = tooltip.parentElement.getBoundingClientRect().x - 450;
  408. let yPos = event.clientY;
  409. tooltip.style.setProperty("transform", "translate(" + xPos + "px, " + yPos + "px)")
  410. }
  411. function buildingTooltipRemove() {
  412. let tooltip = document.querySelector("#building-tooltip");
  413. tooltip.style.setProperty("display", "none");
  414. }
  415. window.onload = function() {
  416. setup();
  417. lastTime = performance.now();
  418. setTimeout(updateDisplay, 1000/updateRate);
  419. }
  420. function save() {
  421. let storage = window.localStorage;
  422. storage.setItem("save-version", "0.0.1");
  423. storage.setItem("ownedUpgrades", JSON.stringify(ownedUpgrades));
  424. storage.setItem("resources", JSON.stringify(resources));
  425. storage.setItem("belongings", JSON.stringify(belongings));
  426. }
  427. function load() {
  428. let storage = window.localStorage;
  429. if (!storage.getItem("save-version")) {
  430. return;
  431. }
  432. ownedUpgrades = JSON.parse(storage.getItem("ownedUpgrades"));
  433. resources = JSON.parse(storage.getItem("resources"));
  434. belongings = JSON.parse(storage.getItem("belongings"));
  435. }
  436. function reset() {
  437. window.localStorage.clear();
  438. }