|
- "use strict";
-
- let belongings = {};
-
- let ownedUpgrades = {};
- let remainingUpgrades = [];
- let showOwnedUpgrades = false;
-
- let effects = {};
-
-
- let resources = {};
-
- let updateRate = 60;
-
- let currentProductivity = {};
- let clickBonus = 0;
-
- let lastTime = 0;
-
- function applyGlobalProdBonuses(productivity) {
- for (let effect of effects["prod-all"]) {
-
- if (ownedUpgrades[effect.parent]) {
- productivity = effect.apply(productivity);
- }
- }
-
- return productivity;
- }
-
- function calculateProductivity() {
- let productivity = 0;
-
- for (const [key, value] of Object.entries(belongings)) {
- productivity += productivityOf(key);
- }
-
- return productivity;
- }
-
- // here's where upgrades will go :3
-
- function productivityMultiplierOf(type) {
- let base = 1;
-
- for (let effect of effects["prod"]) {
-
- if (ownedUpgrades[effect.parent] && effect.target == type) {
- base = effect.apply(base);
- }
- }
-
- for (let effect of effects["helper"]) {
-
- if (ownedUpgrades[effect.parent] && effect.helped == type) {
- base = effect.apply(base, belongings[effect.helper].count);
- }
- }
-
- return base;
- }
-
- function productivityOf(type) {
- let baseProd = buildings[type].prod;
-
- let prod = baseProd * productivityMultiplierOf(type);
-
- prod = applyGlobalProdBonuses(prod);
-
- return prod * belongings[type].count;
- }
-
- function costOfBuilding(type) {
- let baseCost = buildings[type].cost
- let countCost = baseCost * Math.pow(1.15, belongings[type].count);
-
- return Math.round(countCost);
- }
-
- function buyBuilding(type) {
- let cost = costOfBuilding(type);
-
- if (resources.food >= cost) {
- belongings[type].count += 1;
- resources.food -= cost;
- }
-
- updateProductivity();
- updateClickBonus();
-
- }
- // update stuff
-
- function updateDisplay() {
-
- let newTime = performance.now();
- let delta = newTime - lastTime;
- lastTime = newTime;
-
- addResources(delta);
- displayResources();
- displayBuildings();
- displayUpgrades(showOwnedUpgrades);
-
- setTimeout(updateDisplay, 1000/updateRate);
- }
-
- function updateProductivity() {
- currentProductivity["food"] = calculateProductivity();
- }
-
- function addResources(delta) {
- for (const [resource, amount] of Object.entries(currentProductivity)) {
- resources[resource] += amount * delta / 1000;
- }
- }
-
- function displayResources() {
- document.title = "Gorge - " + round(resources.food) + " food";
-
- replaceChildren(document.querySelector("#resource-list"), renderResources());
- }
-
- function renderResources() {
- let list = [];
-
- for (const [key, value] of Object.entries(resources)) {
-
- let line1 = render(value, 3, 0) + " " + resourceTypes[key].name;
- let line2 = render(currentProductivity[key], 1, 1) + " " + resourceTypes[key].name + "/sec";
-
- list.push({"text": line1, "class": "resource-quantity"});
- list.push({"text": line2, "class": "resource-rate"});
- }
-
- return renderLines(list);
- }
-
- function displayBuildings() {
- for (const [key, value] of Object.entries(belongings)) {
-
- if (!belongings[key].visible) {
- if (resources.food * 10 >= costOfBuilding(key)) {
- unlockBuilding(key);
- } else {
- continue;
- }
- belongings[key].visible = true;
- document.querySelector("#building-" + key).classList.remove("hidden");
- }
-
- let button = document.querySelector("#building-" + key);
-
- let name = document.querySelector("#building-" + key + " > .building-button-name");
- let cost = document.querySelector("#building-" + key + " > .building-button-cost");
-
- name.innerText = value.count + " " + (value.count == 1 ? buildings[key].name : buildings[key].plural);
- cost.innerText = render(costOfBuilding(key)) + " food";
-
- if (costOfBuilding(key) > resources.food) {
- button.classList.add("building-button-disabled");
- cost.classList.add("building-button-cost-invalid");
- } else {
- button.classList.remove("building-button-disabled");
- cost.classList.add("building-button-cost-valid");
- }
- }
- }
-
- function canAfford(cost) {
- for (const [resource, amount] of Object.entries(cost)) {
- if (resources[resource] < amount) {
- return false;
- }
- }
-
- return true;
- }
-
- function spend(cost) {
- for (const [resource, amount] of Object.entries(cost)) {
- resources[resource] -= amount;
- }
- }
-
- function switchShowOwnedUpgrades() {
- if (showOwnedUpgrades) {
- document.querySelector("#upgrades").innerText = "Upgrades";
- } else {
- document.querySelector("#upgrades").innerText = "Owned Upgrades";
- }
-
- showOwnedUpgrades = !showOwnedUpgrades;
- }
-
- function displayUpgrades(owned) {
- if (owned) {
- Object.entries(ownedUpgrades).forEach(([key, val]) => {
- let button = document.querySelector("#upgrade-" + key);
- if (val) {
- button.classList.remove("hidden");
- } else {
- button.classList.add("hidden");
- }
- });
- }
- else {
- for (let id of remainingUpgrades) {
- let button = document.querySelector("#upgrade-" + id);
-
- if (ownedUpgrades[id]) {
- button.classList.add("hidden");
- continue;
- }
- if (upgradeReachable(id)) {
- button.classList.remove("hidden");
- } else {
- button.classList.add("hidden");
- }
- if (upgradeAvailable(id)) {
- button.classList.remove("upgrade-button-inactive");
- } else {
- button.classList.add("upgrade-button-inactive");
- }
- }
-
- // we aren't trimming the list of upgrades now
- // because we need to switch between owned and unowned upgrades
- // - thus we need to be able to show or hide anything
- /*
- for (let i = remainingUpgrades.length-1; i >= 0; i--) {
- if (ownedUpgrades[remainingUpgrades[i]]) {
- remainingUpgrades.splice(i, 1);
- }
- }*/
- }
- }
-
- function updateClickBonus() {
- let bonus = 0;
-
- for (let effect of effects["click"]) {
- if (ownedUpgrades[effect.parent]) {
- bonus = effect.apply(bonus, currentProductivity["food"]);
- }
- }
-
- clickBonus = bonus;
- }
-
- function buyUpgrade(id, e) {
- if (ownedUpgrades[id]) {
- return;
- }
-
- let upgrade = upgrades[id];
-
- if (!upgradeAvailable(id)) {
- return;
- }
-
- spend(upgrade.cost);
-
- ownedUpgrades[id] = true;
-
- let text = "Bought " + upgrade.name + "!";
-
- clickPopup(text, "upgrade", [e.clientX, e.clientY]);
-
- updateProductivity();
- updateClickBonus();
- }
-
- function eatMicro() {
- const add = productivityMultiplierOf("micro") + clickBonus;
- resources.food += add;
- return add;
- }
-
- // setup stuff lol
-
- // we'll initialize the dict of buildings we can own
-
- function setup() {
-
- // create static data
-
- createTemplateUpgrades();
-
- // prepare dynamic stuff
-
- initializeData();
- createButtons();
- createDisplays();
- registerListeners();
- load();
- unlockAtStart();
- updateProductivity();
-
- }
-
- function unlockAtStart() {
- unlockBuilding("micro");
-
- for (const [key, value] of Object.entries(belongings)) {
- if (belongings[key].visible) {
- unlockBuilding(key);
- }
- }
- }
-
- function unlockBuilding(id) {
- belongings[id].visible = true;
- document.querySelector("#building-" + id).classList.remove("hidden");
- }
-
- function initializeData() {
- for (const [key, value] of Object.entries(buildings)) {
- belongings[key] = {};
- belongings[key].count = 0;
- belongings[key].visible = false;
- }
-
- for (const [key, value] of Object.entries(resourceTypes)) {
- resources[key] = 0;
- currentProductivity[key] = 0;
- }
-
- for (const [id, upgrade] of Object.entries(upgrades)) {
- ownedUpgrades[id] = false;
-
- for (let effect of upgrade.effects) {
- if (effects[effect.type] === undefined) {
- effects[effect.type] = [];
- }
-
- // copy the data and add an entry for the upgrade id that owns the effect
-
- let newEffect = {};
-
- for (const [key, value] of Object.entries(effect)) {
- newEffect[key] = value;
- }
-
- newEffect.parent = id;
-
- // unfortunate name collision here
- // I'm using apply() to pass on any number of arguments to the
- // apply() function of the effect type
-
- newEffect.apply = function(...args) { return effect_types[effect.type].apply.apply(null, [effect].concat(args)); }
-
- effects[effect.type].push(newEffect);
- }
-
- }
- }
-
- function registerListeners() {
- document.querySelector("#tasty-micro").addEventListener("click", (e) => {
- const add = eatMicro();
- const text = "+" + round(add, 1) + " food";
- const gulp = "*glp*";
- clickPopup(text, "food", [e.clientX, e.clientY]);
- clickPopup(gulp, "gulp", [e.clientX, e.clientY]);
- });
-
- document.querySelector("#save").addEventListener("click", save);
-
- document.querySelector("#reset").addEventListener("click", reset);
-
- document.querySelector("#upgrades").addEventListener("click", switchShowOwnedUpgrades);
- }
-
- function createButtons() {
- createBuildings();
- createUpgrades();
- }
-
- function createBuildings() {
- let container = document.querySelector("#buildings-area");
-
- for (const [key, value] of Object.entries(buildings)) {
- let button = document.createElement("div");
- button.classList.add("building-button");
- button.classList.add("hidden");
- button.id = "building-" + key;
- let buttonName = document.createElement("div");
- buttonName.classList.add("building-button-name");
- let buttonCost = document.createElement("div");
- buttonCost.classList.add("building-button-cost");
-
- let buildingIcon = document.createElement("img");
- buildingIcon.classList.add("building-icon");
- buildingIcon.src = "images/buildings/" + key + ".svg";
-
- button.appendChild(buttonName);
- button.appendChild(buttonCost);
- button.appendChild(buildingIcon);
- button.addEventListener("mousemove", function(e) { buildingTooltip(key, e); });
- button.addEventListener("mouseleave", function() { buildingTooltipRemove(); });
- button.addEventListener("click", function() { buyBuilding(key); });
- button.addEventListener("click", function(e) { buildingTooltip(key, e); });
-
- container.appendChild(button);
- }
- }
-
- // do we have previous techs and at least one of each building?
-
- function upgradeReachable(id) {
-
- if (ownedUpgrades[id]) {
- return false;
- }
-
- if (upgrades[id].prereqs !== undefined ){
- for (const [type, reqs] of Object.entries(upgrades[id].prereqs)) {
- if (type == "buildings") {
- for (const [building, amount] of Object.entries(reqs)) {
- if (belongings[building].count == 0) {
- return false;
- }
- }
- }
- else if (type == "upgrades") {
- for (let upgrade of reqs) {
- if (!ownedUpgrades[upgrade]) {
- return false;
- }
- }
- }
- }
- }
-
-
- return true;
- }
- function upgradeAvailable(id) {
- if (!upgradeReachable(id)) {
- return false;
- }
-
- if (!canAfford(upgrades[id].cost)) {
- return false;
- }
-
- if (upgrades[id].prereqs !== undefined) {
- for (const [type, reqs] of Object.entries(upgrades[id].prereqs)) {
- if (type == "buildings") {
- for (const [building, amount] of Object.entries(upgrades[id].prereqs[type])) {
- if (belongings[building].count < amount) {
- return false;
- }
- }
- } else if (type == "productivity") {
- for (const [key, value] of Object.entries(reqs)) {
- if (currentProductivity[key] < value) {
- return false;
- }
- }
- }
- }
- }
-
-
- return true;
- }
-
- function createUpgrades() {
- let container = document.querySelector("#upgrades-list");
-
- for (const [key, value] of Object.entries(upgrades)) {
-
- remainingUpgrades.push(key);
-
- let button = document.createElement("div");
- button.classList.add("upgrade-button");
- button.classList.add("hidden");
- button.id = "upgrade-" + key;
- let buttonName = document.createElement("div");
- buttonName.classList.add("upgrade-button-name");
- buttonName.innerText = value.name;
-
- let upgradeIcon = document.createElement("img");
- upgradeIcon.src = "images/" + value.icon + ".svg";
- upgradeIcon.classList.add("upgrade-icon");
-
- button.appendChild(buttonName);
- button.appendChild(upgradeIcon);
-
- button.addEventListener("mousemove", function(e) { upgradeTooltip(key, e); });
- button.addEventListener("mouseleave", function() { upgradeTooltipRemove(); });
- button.addEventListener("click", function(e) { buyUpgrade(key, e); });
-
- container.appendChild(button);
- }
- }
-
- function createDisplays() {
- // nop
- }
-
- function renderLine(line) {
- let div = document.createElement("div");
- div.innerText = line.text;
-
- if (line.valid !== undefined) {
- if (line.valid) {
- div.classList.add("cost-met");
- } else {
- div.classList.add("cost-unmet");
- }
- }
-
- if (line.class !== undefined) {
- for (let entry of line.class.split(",")) {
- div.classList.add(entry);
- }
- }
-
- return div;
- }
-
- function renderLines(lines) {
- let divs = [];
-
- for (let line of lines) {
- divs.push(renderLine(line));
- }
-
- return divs;
- }
-
- function renderCost(cost) {
- let list = [];
-
- list.push({
- "text": "Cost:"
- });
-
- for (const [key, value] of Object.entries(cost)) {
- list.push({
- "text": render(value,0) + " " + resourceTypes[key].name,
- "valid": resources[key] >= value
- });
- }
-
- return renderLines(list);
- }
-
- function renderPrereqs(prereqs) {
- let list = [];
-
- if (prereqs === undefined) {
- return renderLines(list);
- }
-
- list.push({
- "text": "Own:"
- });
-
- for (const [key, value] of Object.entries(prereqs)) {
- if (key == "buildings") {
- for (const [id, amount] of Object.entries(prereqs.buildings)) {
- list.push({
- "text": buildings[id].name + " x" + render(amount,0),
- "valid": belongings[id].count >= amount
- });
- }
- } else if (key == "productivity") {
- for (const [id, amount] of Object.entries(prereqs.productivity)) {
- list.push({
- "text": render(amount,0) + " " + resourceTypes[id].name + "/s",
- "valid": currentProductivity[id] >= amount
- });
- }
- }
- }
-
- return renderLines(list);
- }
-
- function renderEffects(effectList) {
- let list = [];
-
- for (let effect of effectList) {
- list.push({"text": effect_types[effect.type].desc(effect)});
- }
-
- return renderLines(list);
- }
-
- function clickPopup(text, type, location) {
- const div = document.createElement("div");
- div.textContent = text;
-
- div.classList.add("click-popup-" + type);
-
- var direction;
-
- if (type == "food") {
- direction = -150;
- } else if (type == "gulp") {
- direction = -150;
- } else if (type == "upgrade") {
- direction = -50;
- } else if (type == "info") {
- direction = 0;
- }
-
- direction *= Math.random() * 0.5 + 1;
-
- direction = Math.round(direction) + "px"
-
- div.style.setProperty("--target", direction)
-
- div.style.left = location[0] + "px";
- div.style.top = location[1] + "px";
-
- const body = document.querySelector("body");
-
- body.appendChild(div);
-
- setTimeout(() => {
- body.removeChild(div);
- }, 2000);
- }
-
- function fillTooltip(type, field, content) {
- let item = document.querySelector("#" + type + "-tooltip-" + field);
- if (typeof(content) === "string") {
- item.innerText = content;
- } else {
- replaceChildren(item, content);
- }
- }
-
- function upgradeTooltip(id, event) {
-
- let tooltip = document.querySelector("#upgrade-tooltip");
- tooltip.style.setProperty("display", "inline-block");
-
- fillTooltip("upgrade", "name", upgrades[id].name);
- fillTooltip("upgrade", "desc", upgrades[id].desc);
- fillTooltip("upgrade", "effect", renderEffects(upgrades[id].effects));
- fillTooltip("upgrade", "cost", renderCost(upgrades[id].cost));
- fillTooltip("upgrade", "prereqs", renderPrereqs(upgrades[id].prereqs));
-
- let yOffset = tooltip.parentElement.getBoundingClientRect().y;
-
- let yTrans = Math.round(event.clientY - yOffset);
- tooltip.style.setProperty("transform", "translate(-220px, " + yTrans + "px)");
- }
-
- function upgradeTooltipRemove() {
- let tooltip = document.querySelector("#upgrade-tooltip");
-
- tooltip.style.setProperty("display", "none");
- }
-
- function prodSummary(id) {
- let list = [];
-
- list.push(
- {"text": "Each " + buildings[id].name + " produces " + round(productivityMultiplierOf(id) * buildings[id].prod,1) + " food/sec"}
- );
-
- list.push(
- {"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"}
- );
-
- let percentage = round(100 * productivityOf(id) / currentProductivity["food"], 2);
-
- if (isNaN(percentage)) {
- percentage = 0;
- }
- list.push(
- {"text": "(" + percentage + "% of all food)"}
- );
-
- return renderLines(list);
- }
-
- function buildingTooltip(id, event) {
-
- let tooltip = document.querySelector("#building-tooltip");
- tooltip.style.setProperty("display", "inline-block");
-
- fillTooltip("building", "name", buildings[id].name);
- fillTooltip("building", "desc", buildings[id].desc);
- fillTooltip("building", "cost", render(costOfBuilding(id)) + " food");
- fillTooltip("building", "prod", prodSummary(id));
-
- let yOffset = tooltip.parentElement.getBoundingClientRect().y;
-
- let xPos = tooltip.parentElement.getBoundingClientRect().x - 450;
-
- // wow browsers are bad
-
- var body = document.body,
- html = document.documentElement;
-
- var height = Math.max( body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight );
-
- let yPos = Math.min(event.clientY, height - 200);
-
- tooltip.style.setProperty("transform", "translate(" + xPos + "px, " + yPos + "px)")
- }
-
- function buildingTooltipRemove() {
- let tooltip = document.querySelector("#building-tooltip");
-
- tooltip.style.setProperty("display", "none");
- }
-
- window.onload = function() {
- setup();
-
- lastTime = performance.now();
-
- setTimeout(updateDisplay, 1000/updateRate);
-
- setTimeout(autosave, 60000);
- }
-
- function autosave() {
- saveGame();
- let x = window.innerWidth / 2;
- let y = window.innerHeight * 9 / 10;
- clickPopup("Autosaving...", "info", [x, y]);
- setTimeout(autosave, 60000);
- }
-
- function save(e) {
- saveGame();
- clickPopup("Saved!", "info", [e.clientX, e.clientY]);
- }
-
- function saveGame() {
- let storage = window.localStorage;
-
- storage.setItem("save-version", "0.0.1");
-
- storage.setItem("ownedUpgrades", JSON.stringify(ownedUpgrades));
-
- storage.setItem("resources", JSON.stringify(resources));
-
- storage.setItem("belongings", JSON.stringify(belongings));
- }
-
- function load() {
- let storage = window.localStorage;
-
- if (!storage.getItem("save-version")) {
- return;
- }
-
- let newOwnedUpgrades = JSON.parse(storage.getItem("ownedUpgrades"));
-
- for (const [key, value] of Object.entries(newOwnedUpgrades)) {
- ownedUpgrades[key] = value;
- }
-
- let newResources = JSON.parse(storage.getItem("resources"));
-
- for (const [key, value] of Object.entries(newResources)) {
- resources[key] = value;
- }
-
- let newBelongings = JSON.parse(storage.getItem("belongings"));
-
- for (const [key, value] of Object.entries(newBelongings)) {
- belongings[key] = value;
- }
- }
-
- function reset() {
- window.localStorage.clear();
- }
|