cookie clicker but bigger
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

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