diff --git a/constants.js b/constants.js index 0c0d454..2854de4 100644 --- a/constants.js +++ b/constants.js @@ -2,168 +2,249 @@ const resourceTypes = { "food": { - name: "Food" + name: "food" } } + +deepFreeze(resourceTypes); + const buildings = { "micro": { "name": "Micro", "plural": "Micros", "desc": "A tasty, squirmy treat.", - "cost": 1e1, - "prod": 1e-1 / 1, + "cost": { + "food": 1e1 + }, + "prod": { + "food": 1e-1 / 1 + }, "icon": "fa-universal-access" }, "anthro": { "name": "Anthro", "plural": "Anthros", "desc": "Something more substantial to sate your hunger.", - "cost": 1e2, - "prod": 1e0 / 1.1, + "cost": { + "food": 1e2 + }, + "prod": { + "food": 1e0 / 1.1 + }, "icon": "fa-male" }, "car": { "name": "Car", "plural": "Cars", "desc": "Crunchy shell, tasty center.", - "cost": 1.2e3, - "prod": 1e1 / 1.2, + "cost": { + "food": 1.2e3 + }, + "prod": { + "food": 1e1 / 1.2 + }, "icon": "fa-car" }, "bus": { "name": "Bus", "plural": "Buses", "desc": "Probably the worst place to be when a macro is aroud.", - "cost": 1.4e4, - "prod": 1e2 / 1.3, + "cost": { + "food": 1.4e4 + }, + "prod": { + "food": 1e2 / 1.3 + }, "icon": "fa-bus" }, "house": { "name": "House", "plural": "Houses", "desc": "Home sweet home - but it doesn't taste sweet?", - "cost": 1.6e5, - "prod": 1e3 / 1.4, + "cost": { + "food": 1.6e5 + }, + "prod": { + "food": 1e3 / 1.4 + }, "icon": "fa-home" }, "apartment": { "name": "Apartment", "plural": "Apartments", "desc": "More snacks, less packaging.", - "cost": 1.8e6, - "prod": 1e4 / 1.5, + "cost": { + "food": 1.8e6 + }, + "prod": { + "food": 1e4 / 1.5 + }, "icon": "fa-building" }, "block": { "name": "Block", "plural": "Blocks", "desc": "A whole pile of buildings.", - "cost": 2e7, - "prod": 1e5 / 1.6, + "cost": { + "food": 2e7 + }, + "prod": { + "food": 1e5 / 1.6 + }, "icon": "fa-warehouse" }, "town": { "name": "Town", "plural": "Towns", "desc": "'Tourist trap' has never been this literal.", - "cost": 2.2e8, - "prod": 1e6 / 1.7, + "cost": { + "food": 2.2e8 + }, + "prod": { + "food": 1e6 / 1.7 + }, "icon": "fa-store" }, "city": { "name": "City", "plural": "Cities", "desc": "Please no sitty on our city.", - "cost": 2.4e9, - "prod": 1e7 / 1.8, + "cost": { + "food": 2.4e9 + }, + "prod": { + "food": 1e7 / 1.8 + }, "icon": "fa-city" }, "metro": { "name": "Metropolis", "plural": "Metropolises", "desc": "A big ol' city. Tasty, too.", - "cost": 2.6e10, - "prod": 1e8 / 1.9, + "cost": { + "food": 2.6e10 + }, + "prod": { + "food": 1e8 / 1.9 + }, "icon": "fa-landmark" }, "county": { "name": "County", "plural": "Counties", "desc": "Why salt the land when you can slurp it?", - "cost": 2.8e11, - "prod": 1e9 / 2, + "cost": { + "food": 2.8e11 + }, + "prod": { + "food": 1e9 / 2 + }, "icon": "fa-map" }, "state": { "name": "State", "plural": "States", "desc": "The United States is made up of...43 states - no, 42...", - "cost": 3e12, - "prod": 1e10 / 2.1, + "cost": { + "food": 3e12 + }, + "prod": { + "food": 1e10 / 2.1 + }, "icon": "fa-map-signs" }, "country": { "name": "Country", "plural": "Countries", "desc": "One nation, under paw.", - "cost": 3.2e13, - "prod": 1e11 / 2.2, + "cost": { + "food": 3.2e13 + }, + "prod": { + "food": 1e11 / 2.2 + }, "icon": "fa-flag" }, "continent": { "name": "Continent", "plural": "Continents", "desc": "Earth-shattering appetite!", - "cost": 3.4e14, - "prod": 1e12 / 2.3, + "cost": { + "food": 3.4e14 + }, + "prod": { + "food": 1e12 / 2.3 + }, "icon": "fa-mountain" }, "planet": { "name": "Planet", "plural": "Planets", "desc": "Earth appetite!", - "cost": 3.6e15, - "prod": 1e13 / 2.4, + "cost": { + "food": 3.6e15 + }, + "prod": { + "food": 1e13 / 2.4 + }, "icon": "fa-globe-europe" }, "solar-system": { "name": "Solar System", "plural": "Solar Systems", "desc": "Earths appetite!", - "cost": 3.8e16, - "prod": 1e14 / 2.5, + "cost": { + "food": 3.8e16 + }, + "prod": { + "food": 1e14 / 2.5 + }, "icon": "fa-meteor" }, "galaxy": { "name": "Galaxy", "plural": "Galaxies", "desc": "In a galaxy far, far down your gullet...", - "cost": 4.0e17, - "prod": 1e15 / 2.6, + "cost": { + "food": 4.0e17 + }, + "prod": { + "food": 1e15 / 2.6 + }, "icon": "fa-sun" }, "universe": { "name": "Universe", "plural": "Universes", "desc": "Into the you-verse.", - "cost": 4.2e18, - "prod": 1e16 / 2.7, + "cost": { + "food": 4.2e18 + }, + "prod": { + "food": 1e16 / 2.7 + }, "icon": "fa-asterisk" }, "multiverse": { "name": "Multiverse", "plural": "Multiverses", "desc": "This is getting very silly.", - "cost": 4.4e19, - "prod": 1e17 / 2.8, + "cost": { + "food": 4.4e19 + }, + "prod": { + "food": 1e17 / 2.8 + }, "icon": "fa-infinity" } } +deepFreeze(buildings); + const effect_types = { "prod": { "apply": function (effect, productivity) { - return productivity * effect.amount; + scaleCost(productivity, effect); }, "desc": function (effect) { return round(effect.amount, 2) + "x food production from " + buildings[effect.target].plural; @@ -171,7 +252,7 @@ const effect_types = { }, "prod-all": { "apply": function (effect, productivity) { - return productivity * effect.amount; + scaleCost(productivity, effect); }, "desc": function (effect) { return round((effect.amount - 1) * 100) + "% increase to food production"; @@ -208,6 +289,8 @@ const effect_types = { } } +deepFreeze(effect_types); + let upgrades = { } @@ -219,6 +302,7 @@ function createTemplateUpgrades() { createHelperUpgrades(); createClickVictimUpgrades(); createPowerupFreqUpgrades(); + deepFreeze(upgrades); } const prodUpgradeCounts = [1, 5, 10, 25, 50, 75, 100]; diff --git a/gorge.js b/gorge.js index b6981b0..4d95b37 100644 --- a/gorge.js +++ b/gorge.js @@ -13,6 +13,7 @@ const resources = {}; let updateRate = 30; const currentProductivity = {}; + let clickBonus = 0; let clickVictim = "micro"; @@ -66,7 +67,10 @@ function addPowerup(powerup) { updateAll(); } -function applyGlobalProdBonuses(productivity) { +function getGlobalProdBonus() { + + let productivity = 1; + for (let effect of effects["prod-all"]) { if (ownedUpgrades[effect.parent]) { @@ -78,10 +82,10 @@ function applyGlobalProdBonuses(productivity) { } function calculateProductivity() { - let productivity = 0; + let productivity = makeCost(); for (const [key, value] of Object.entries(belongings)) { - productivity += productivityOf(key); + productivity = addCost(productivity, productivityOf(key)); } return productivity; @@ -110,27 +114,42 @@ function productivityMultiplierOf(type) { } function productivityOf(type) { - let baseProd = buildings[type].prod; + let baseProd = makeCost(buildings[type].prod); - let prod = baseProd * productivityMultiplierOf(type); + baseProd.food *= productivityMultiplierOf(type); - prod = applyGlobalProdBonuses(prod); + baseProd.food *= getGlobalProdBonus(); + + baseProd.food *= belongings[type].count; + + return baseProd; +} - return prod * belongings[type].count; +function makeCost(source) { + const empty = mapObject(resourceTypes, () => 0); + Object.preventExtensions(empty); + return {...empty, ...source}; +} + +function addCost(cost1, cost2) { + return Object.keys(resourceTypes).reduce((o, k) => ({ ...o, [k]: cost1[k] + cost2[k]}), {}); +} + +function scaleCost(cost, scale) { + return Object.keys(resourceTypes).reduce((o, k) => ({ ...o, [k]: cost[k] * scale}), {}); } function costOfBuilding(type, count = 1) { - let total = 0; + let total = makeCost(); while (count > 0) { - let baseCost = buildings[type].cost; - let countCost = baseCost * Math.pow(1.15, belongings[type].count + count - 1); - total += countCost; + let baseCost = makeCost(buildings[type].cost); + baseCost.food *= Math.pow(1.15, belongings[type].count + count - 1); + total = addCost(total, baseCost); count--; } - - return Math.round(total); + return mapObject(total, round); } function buildingCount() { @@ -148,9 +167,9 @@ function buyBuilding(type, e) { let cost = costOfBuilding(type, count); - if (resources.food >= cost) { + if (canAfford(cost)) { + spend(cost); belongings[type].count += count; - resources.food -= cost; } updateProductivity(); @@ -181,7 +200,7 @@ function updateDisplay() { } function updateProductivity() { - currentProductivity["food"] = calculateProductivity(); + Object.assign(currentProductivity, calculateProductivity()); activePowerups.forEach(entry => { const powerup = entry.powerup; @@ -193,11 +212,7 @@ function updateProductivity() { belongings: belongings }; - console.log(currentProductivity); - powerup.effect(state); - - console.log(currentProductivity); }) } @@ -233,7 +248,9 @@ function displayBuildings() { for (const [key, value] of Object.entries(belongings)) { if (!belongings[key].visible) { - if (resources.food * 10 >= costOfBuilding(key)) { + if (resources.food * 10 >= costOfBuilding(key).food) { + unlockBuilding(key); + } if (belongings[key].count > 0) { unlockBuilding(key); } else { continue; @@ -247,15 +264,17 @@ function displayBuildings() { let name = document.querySelector("#building-" + key + " > .building-button-name"); let cost = document.querySelector("#building-" + key + " > .building-button-cost"); + const buildingCost = costOfBuilding(key, count); + name.innerText = value.count + " " + (value.count == 1 ? buildings[key].name : buildings[key].plural); - cost.innerText = render(costOfBuilding(key, count)) + " food"; + cost.innerText = render(buildingCost.food) + " food"; - if (costOfBuilding(key, count) > resources.food) { - button.classList.add("building-button-disabled"); - cost.classList.add("building-button-cost-invalid"); - } else { + if (canAfford(buildingCost)) { button.classList.remove("building-button-disabled"); cost.classList.add("building-button-cost-valid"); + } else { + button.classList.add("building-button-disabled"); + cost.classList.add("building-button-cost-invalid"); } } } @@ -375,7 +394,7 @@ function buyUpgrade(id, e) { } function eatPrey() { - const add = buildings[clickVictim]["prod"] * 10 * productivityMultiplierOf(clickVictim) + clickBonus; + const add = buildings[clickVictim]["prod"].food * 10 * productivityMultiplierOf(clickVictim) + clickBonus; resources.food += add; return add; } @@ -938,7 +957,7 @@ function buildingTooltip(id, event) { fillTooltip("building", "name", (count != 1 ? count + "x " : "") + buildings[id].name); fillTooltip("building", "desc", buildings[id].desc); - fillTooltip("building", "cost", render(costOfBuilding(id, count)) + " food"); + fillTooltip("building", "cost", render(costOfBuilding(id, count).food) + " food"); fillTooltip("building", "prod", prodSummary(id)); let xPos = tooltip.parentElement.getBoundingClientRect().x - 450; diff --git a/numbers.js b/numbers.js index 35a8caf..53ddf83 100644 --- a/numbers.js +++ b/numbers.js @@ -3,6 +3,9 @@ function render(val, places = 1, smallPlaces = 0) { } function numberText(val, places = 1, smallPlaces = 0) { + if (isNaN(val)) { + throw new RangeError("Invalid number: " + val); + } if (val < 1000) { return round(val, smallPlaces); } diff --git a/util.js b/util.js index fe4e471..c9b90aa 100644 --- a/util.js +++ b/util.js @@ -18,3 +18,24 @@ function removeChildren(element) { function round(val, places = 0) { return Math.round(val * Math.pow(10, places)) / Math.pow(10, places); } + +function mapObject(obj, func) { + return Object.keys(obj).reduce((o, k) => ({ ...o, [k]: func(obj[k])}), {}); +} + +function deepFreeze(object) { + + // Retrieve the property names defined on object + var propNames = Object.getOwnPropertyNames(object); + + // Freeze properties before freezing self + + for (let name of propNames) { + let value = object[name]; + + object[name] = value && typeof value === "object" ? + deepFreeze(value) : value; + } + + return Object.freeze(object); +}