This has broken quite a bit, since many parts of the game expect a scalar instead of a whole object. I have also been deep freezing constant configuration data and, generally, trying to make as much immutable as possibletags/v0.0.6
| @@ -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]; | |||
| @@ -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; | |||
| @@ -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); | |||
| } | |||
| @@ -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); | |||
| } | |||