cookie clicker but bigger
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

456 行
11 KiB

  1. "use strict";
  2. let belongings = {};
  3. let ownedUpgrades = {};
  4. let remainingUpgrades = [];
  5. let resources = {};
  6. let updateRate = 60;
  7. function calculateProductivity() {
  8. let productivity = 0;
  9. for (const [key, value] of Object.entries(belongings)) {
  10. productivity += productivityOf(key);
  11. }
  12. return productivity;
  13. }
  14. // here's where upgrades will go :3
  15. function productivityMultiplierOf(type) {
  16. let base = 1;
  17. for (const [key, value] of Object.entries(upgrades)) {
  18. if (!ownedUpgrades[key]) {
  19. continue;
  20. }
  21. if (value.effect.type == "prod-2x") {
  22. if (value.effect.target == type) {
  23. base *= 2;
  24. }
  25. }
  26. }
  27. return base;
  28. }
  29. function productivityOf(type) {
  30. let baseProd = buildings[type].prod;
  31. let prod = baseProd * productivityMultiplierOf(type);
  32. return prod * belongings[type].count;
  33. }
  34. function costOfBuilding(type) {
  35. let baseCost = buildings[type].cost
  36. let countCost = baseCost * Math.pow(1.15, belongings[type].count);
  37. return Math.round(countCost);
  38. }
  39. function buyBuilding(type) {
  40. let cost = costOfBuilding(type);
  41. if (resources.food >= cost) {
  42. belongings[type].count += 1;
  43. resources.food -= cost;
  44. }
  45. }
  46. // update stuff
  47. function updateDisplay() {
  48. addResources();
  49. displayResources();
  50. displayBuildings();
  51. displayUpgrades();
  52. setTimeout(updateDisplay, 1000/updateRate);
  53. }
  54. function addResources() {
  55. resources.food += calculateProductivity() * 1 / updateRate;
  56. }
  57. function displayResources() {
  58. document.getElementById("resource-food").innerText = "Food: " + render(resources.food);
  59. document.getElementById("productivity").innerText = (Math.round(calculateProductivity() * 10) / 10) + " food/sec";
  60. }
  61. function displayBuildings() {
  62. for (const [key, value] of Object.entries(belongings)) {
  63. if (!belongings[key].visible) {
  64. if (resources.food * 10 >= costOfBuilding(key)) {
  65. unlockBuilding(key);
  66. } else {
  67. continue;
  68. }
  69. belongings[key].visible = true;
  70. document.querySelector("#building-" + key).classList.remove("hidden");
  71. }
  72. let button = document.querySelector("#building-" + key);
  73. document.querySelector("#building-" + key + " > .building-button-name").innerText = value.count + " " + (value.count == 1 ? buildings[key].name : buildings[key].plural);
  74. document.querySelector("#building-" + key + " > .building-button-cost").innerText = costOfBuilding(key) + " food";
  75. if (costOfBuilding(key) > resources.food) {
  76. button.classList.add("building-button-disabled");
  77. } else {
  78. button.classList.remove("building-button-disabled");
  79. }
  80. }
  81. }
  82. function canAfford(cost) {
  83. for (const [resource, amount] of Object.entries(cost)) {
  84. if (resources[resource] < amount) {
  85. return false;
  86. }
  87. }
  88. return true;
  89. }
  90. function spend(cost) {
  91. for (const [resource, amount] of Object.entries(cost)) {
  92. resources[resource] -= amount;
  93. }
  94. }
  95. function displayUpgrades() {
  96. for (let id of remainingUpgrades) {
  97. let button = document.querySelector("#upgrade-" + id);
  98. if (ownedUpgrades[id]) {
  99. button.style.display = "none";
  100. continue;
  101. }
  102. if (upgradeReachable(id)) {
  103. button.classList.remove("hidden");
  104. } else {
  105. button.classList.add("hidden");
  106. }
  107. if (upgradeAvailable(id)) {
  108. button.classList.remove("upgrade-button-inactive");
  109. } else {
  110. button.classList.add("upgrade-button-inactive");
  111. }
  112. }
  113. // now we throw out stuff
  114. for (let i = remainingUpgrades.length-1; i >= 0; i--) {
  115. if (ownedUpgrades[remainingUpgrades[i]]) {
  116. remainingUpgrades.splice(i, 1);
  117. }
  118. }
  119. }
  120. function buyUpgrade(id) {
  121. if (ownedUpgrades[id]) {
  122. return;
  123. }
  124. let upgrade = upgrades[id];
  125. if (!canAfford(upgrade.cost)) {
  126. return;
  127. }
  128. spend(upgrade.cost);
  129. ownedUpgrades[id] = true;
  130. }
  131. function eatMicro() {
  132. resources.food += productivityMultiplierOf("micro");
  133. }
  134. // setup stuff lol
  135. // we'll initialize the dict of buildings we can own
  136. function setup() {
  137. initializeData();
  138. createButtons();
  139. createDisplays();
  140. registerListeners();
  141. unlockAtStart();
  142. }
  143. function unlockAtStart() {
  144. unlockBuilding("micro");
  145. }
  146. function unlockBuilding(id) {
  147. belongings[id].visible = true;
  148. document.querySelector("#building-" + id).classList.remove("hidden");
  149. }
  150. function initializeData() {
  151. for (const [key, value] of Object.entries(buildings)) {
  152. belongings[key] = {};
  153. belongings[key].count = 0;
  154. belongings[key].visible = false;
  155. }
  156. for (const [key, value] of Object.entries(upgrades)) {
  157. ownedUpgrades[key] = false;
  158. }
  159. }
  160. function registerListeners() {
  161. document.querySelector("#tasty-micro").addEventListener("click", eatMicro);
  162. }
  163. function createButtons() {
  164. createBuildings();
  165. createUpgrades();
  166. }
  167. function createBuildings() {
  168. let container = document.querySelector("#buildings-area");
  169. for (const [key, value] of Object.entries(buildings)) {
  170. let button = document.createElement("div");
  171. button.classList.add("building-button");
  172. button.classList.add("hidden");
  173. button.id = "building-" + key;
  174. let buttonName = document.createElement("div");
  175. buttonName.classList.add("building-button-name");
  176. let buttonCost = document.createElement("div");
  177. buttonCost.classList.add("building-button-cost");
  178. button.appendChild(buttonName);
  179. button.appendChild(buttonCost);
  180. button.addEventListener("mousemove", function(e) { buildingTooltip(key, e); });
  181. button.addEventListener("mouseleave", function() { buildingTooltipRemove(); });
  182. button.addEventListener("click", function() { buyBuilding(key); });
  183. button.addEventListener("click", function(e) { buildingTooltip(key, e); });
  184. container.appendChild(button);
  185. }
  186. }
  187. // do we have previous techs and at least one of each building?
  188. function upgradeReachable(id) {
  189. if (ownedUpgrades[id]) {
  190. return false;
  191. }
  192. for (const [type, reqs] of Object.entries(upgrades[id].prereqs)) {
  193. if (type == "buildings") {
  194. for (const [building, amount] of Object.entries(reqs)) {
  195. if (belongings[building].count == 0) {
  196. return false;
  197. }
  198. }
  199. }
  200. else if (type == "upgrades") {
  201. for (let upgrade of reqs) {
  202. if (!ownedUpgrades[upgrade]) {
  203. return false;
  204. }
  205. }
  206. }
  207. }
  208. return true;
  209. }
  210. function upgradeAvailable(id) {
  211. if (!upgradeReachable(id)) {
  212. return false;
  213. }
  214. if (!canAfford(upgrades[id].cost)) {
  215. return false;
  216. }
  217. for (const [type, reqs] of Object.entries(upgrades[id].prereqs)) {
  218. if (type == "buildings") {
  219. for (const [building, amount] of Object.entries(upgrades[id].prereqs[type])) {
  220. if (belongings[building].count < amount) {
  221. return false;
  222. }
  223. }
  224. }
  225. }
  226. return true;
  227. }
  228. function createUpgrades() {
  229. let container = document.querySelector("#upgrades-list");
  230. for (const [key, value] of Object.entries(upgrades)) {
  231. remainingUpgrades.push(key);
  232. let button = document.createElement("div");
  233. button.classList.add("upgrade-button");
  234. button.classList.add("hidden");
  235. button.id = "upgrade-" + key;
  236. let buttonName = document.createElement("div");
  237. buttonName.classList.add("upgrade-button-name");
  238. buttonName.innerText = value.name;
  239. button.appendChild(buttonName);
  240. button.addEventListener("mousemove", function(e) { upgradeTooltip(key, e); });
  241. button.addEventListener("mouseleave", function() { upgradeTooltipRemove(); });
  242. button.addEventListener("click", function() { buyUpgrade(key); });
  243. container.appendChild(button);
  244. }
  245. }
  246. function createDisplays() {
  247. let resourceList = document.querySelector("#resource-list");
  248. for (const [key, value] of Object.entries(resourceTypes)) {
  249. resources[key] = 0;
  250. let line = document.createElement("div");
  251. line.id = "resource-" + key;
  252. resourceList.appendChild(line);
  253. }
  254. }
  255. function renderLine(line) {
  256. let div = document.createElement("div");
  257. div.innerText = line.text;
  258. if (line.valid !== undefined) {
  259. if (line.valid) {
  260. div.classList.add("cost-met");
  261. } else {
  262. div.classList.add("cost-unmet");
  263. }
  264. }
  265. return div;
  266. }
  267. function renderLines(lines) {
  268. let divs = [];
  269. for (let line of lines) {
  270. divs.push(renderLine(line));
  271. }
  272. return divs;
  273. }
  274. function renderCost(cost) {
  275. let list = [];
  276. list.push({
  277. "text": "Cost:"
  278. });
  279. for (const [key, value] of Object.entries(cost)) {
  280. list.push({
  281. "text": value + " " + resourceTypes[key].name,
  282. "valid": resources[key] >= value
  283. });
  284. }
  285. return renderLines(list);
  286. }
  287. function renderPrereqs(prereqs) {
  288. let list = [];
  289. list.push({
  290. "text": "Own:"
  291. });
  292. for (const [key, value] of Object.entries(prereqs)) {
  293. if (key == "buildings") {
  294. for (const [id, amount] of Object.entries(prereqs.buildings)) {
  295. list.push({
  296. "text": buildings[id].name + " x" + amount,
  297. "valid": belongings[id].count >= amount
  298. });
  299. }
  300. }
  301. }
  302. return renderLines(list);
  303. }
  304. function fillTooltip(type, field, content) {
  305. let item = document.querySelector("#" + type + "-tooltip-" + field);
  306. if (typeof(content) === "string") {
  307. item.innerText = content;
  308. } else {
  309. replaceChildren(item, content);
  310. }
  311. }
  312. function upgradeTooltip(id, event) {
  313. let tooltip = document.querySelector("#upgrade-tooltip");
  314. tooltip.style.setProperty("display", "inline-block");
  315. fillTooltip("upgrade", "name", upgrades[id].name);
  316. fillTooltip("upgrade", "desc", upgrades[id].desc);
  317. fillTooltip("upgrade", "effect", upgrade_types[upgrades[id].effect.type].desc(buildings[upgrades[id].effect.target].name));
  318. fillTooltip("upgrade", "cost", renderCost(upgrades[id].cost));
  319. fillTooltip("upgrade", "prereqs", renderPrereqs(upgrades[id].prereqs));
  320. let yOffset = tooltip.parentElement.getBoundingClientRect().y;
  321. let yTrans = Math.round(event.clientY - yOffset);
  322. tooltip.style.setProperty("transform", "translate(-220px, " + yTrans + "px)");
  323. }
  324. function upgradeTooltipRemove() {
  325. let tooltip = document.querySelector("#upgrade-tooltip");
  326. tooltip.style.setProperty("display", "none");
  327. }
  328. function buildingTooltip(id, event) {
  329. let tooltip = document.querySelector("#building-tooltip");
  330. tooltip.style.setProperty("display", "inline-block");
  331. fillTooltip("building", "name", buildings[id].name);
  332. fillTooltip("building", "desc", buildings[id].desc);
  333. fillTooltip("building", "cost", costOfBuilding(id) + " food");
  334. let yOffset = tooltip.parentElement.getBoundingClientRect().y;
  335. let xPos = tooltip.parentElement.getBoundingClientRect().x - 450;
  336. let yPos = event.clientY;
  337. tooltip.style.setProperty("transform", "translate(" + xPos + "px, " + yPos + "px)")
  338. }
  339. function buildingTooltipRemove() {
  340. let tooltip = document.querySelector("#building-tooltip");
  341. tooltip.style.setProperty("display", "none");
  342. }
  343. window.onload = function() {
  344. setup();
  345. setTimeout(updateDisplay, 1000/updateRate);
  346. }