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

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