|  | "use strict";
let belongings = {};
let ownedUpgrades = {};
let effects = {};
let remainingUpgrades = [];
let resources = {};
let updateRate = 60;
let currentProductivity = {};
let lastTime = 0;
function calculateProductivity() {
  let productivity = 0;
  for (const [key, value] of Object.entries(belongings)) {
      productivity += productivityOf(key);
  }
  for (let effect of effects["prod-all"]) {
    if (ownedUpgrades[effect.parent]) {
      productivity = effect.apply(productivity);
    }
  }
  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);
    }
  }
  return base;
}
function productivityOf(type) {
  let baseProd = buildings[type].prod;
  let prod = baseProd * productivityMultiplierOf(type);
  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();
}
// update stuff
function updateDisplay() {
  let newTime = performance.now();
  let delta = newTime - lastTime;
  lastTime = newTime;
  updateProductivity();
  addResources(delta);
  displayResources();
  displayBuildings();
  displayUpgrades();
  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 = round(value) + " " + resourceTypes[key].name;
    let line2 = round(currentProductivity[key],1) + " " + resourceTypes[key].name + "/sec";
    list.push({"text": line1});
    list.push({"text": line2});
  }
  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);
    document.querySelector("#building-" + key + " > .building-button-name").innerText = value.count + " " + (value.count == 1 ? buildings[key].name : buildings[key].plural);
    document.querySelector("#building-" + key + " > .building-button-cost").innerText = costOfBuilding(key) + " food";
    if (costOfBuilding(key) > resources.food) {
      button.classList.add("building-button-disabled");
    } else {
      button.classList.remove("building-button-disabled");
    }
  }
}
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 displayUpgrades() {
  for (let id of remainingUpgrades) {
    let button = document.querySelector("#upgrade-" + id);
    if (ownedUpgrades[id]) {
      button.style.display = "none";
      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");
    }
  }
  // now we throw out stuff
  for (let i = remainingUpgrades.length-1; i >= 0; i--) {
    if (ownedUpgrades[remainingUpgrades[i]]) {
      remainingUpgrades.splice(i, 1);
    }
  }
}
function buyUpgrade(id) {
  if (ownedUpgrades[id]) {
    return;
  }
  let upgrade = upgrades[id];
  if (!upgradeAvailable(id)) {
    return;
  }
  spend(upgrade.cost);
  ownedUpgrades[id] = true;
}
function eatMicro() {
  resources.food += productivityMultiplierOf("micro");
}
// setup stuff lol
// we'll initialize the dict of buildings we can own
function setup() {
  initializeData();
  createButtons();
  createDisplays();
  registerListeners();
  load();
  unlockAtStart();
}
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", eatMicro);
  document.querySelector("#save").addEventListener("click", save);
  document.querySelector("#reset").addEventListener("click", reset);
}
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");
    button.appendChild(buttonName);
    button.appendChild(buttonCost);
    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;
  }
  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;
  }
  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;
    button.appendChild(buttonName);
    button.addEventListener("mousemove", function(e) { upgradeTooltip(key, e); });
    button.addEventListener("mouseleave", function() { upgradeTooltipRemove(); });
    button.addEventListener("click", function() { buyUpgrade(key); });
    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");
    }
  }
  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": value + " " + resourceTypes[key].name,
      "valid": resources[key] >= value
    });
  }
  return renderLines(list);
}
function renderPrereqs(prereqs) {
  let 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" + amount,
          "valid": belongings[id].count >= amount
        });
      }
    } else if (key == "productivity") {
      for (const [id, amount] of Object.entries(prereqs.productivity)) {
        list.push({
          "text": amount + " " + 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 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 " + 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", costOfBuilding(id) + " food");
  fillTooltip("building", "prod", prodSummary(id));
  let yOffset = tooltip.parentElement.getBoundingClientRect().y;
  let xPos = tooltip.parentElement.getBoundingClientRect().x - 450;
  let yPos = event.clientY;
  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);
}
function save() {
  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;
  }
  ownedUpgrades = JSON.parse(storage.getItem("ownedUpgrades"));
  resources = JSON.parse(storage.getItem("resources"));
  belongings = JSON.parse(storage.getItem("belongings"));
}
function reset() {
  window.localStorage.clear();
}
 |