Browse Source

Change everything to use a dictionary of costs, rather than just numbers

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 possible
tags/v0.0.6
Fen Dweller 5 years ago
parent
commit
5738659d8e
No known key found for this signature in database GPG Key ID: E80B35A6F11C3656
4 changed files with 196 additions and 69 deletions
  1. +125
    -41
      constants.js
  2. +47
    -28
      gorge.js
  3. +3
    -0
      numbers.js
  4. +21
    -0
      util.js

+ 125
- 41
constants.js View File

@@ -2,168 +2,249 @@


const resourceTypes = { const resourceTypes = {
"food": { "food": {
name: "Food"
name: "food"
} }
} }

deepFreeze(resourceTypes);

const buildings = { const buildings = {
"micro": { "micro": {
"name": "Micro", "name": "Micro",
"plural": "Micros", "plural": "Micros",
"desc": "A tasty, squirmy treat.", "desc": "A tasty, squirmy treat.",
"cost": 1e1,
"prod": 1e-1 / 1,
"cost": {
"food": 1e1
},
"prod": {
"food": 1e-1 / 1
},
"icon": "fa-universal-access" "icon": "fa-universal-access"
}, },
"anthro": { "anthro": {
"name": "Anthro", "name": "Anthro",
"plural": "Anthros", "plural": "Anthros",
"desc": "Something more substantial to sate your hunger.", "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" "icon": "fa-male"
}, },
"car": { "car": {
"name": "Car", "name": "Car",
"plural": "Cars", "plural": "Cars",
"desc": "Crunchy shell, tasty center.", "desc": "Crunchy shell, tasty center.",
"cost": 1.2e3,
"prod": 1e1 / 1.2,
"cost": {
"food": 1.2e3
},
"prod": {
"food": 1e1 / 1.2
},
"icon": "fa-car" "icon": "fa-car"
}, },
"bus": { "bus": {
"name": "Bus", "name": "Bus",
"plural": "Buses", "plural": "Buses",
"desc": "Probably the worst place to be when a macro is aroud.", "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" "icon": "fa-bus"
}, },
"house": { "house": {
"name": "House", "name": "House",
"plural": "Houses", "plural": "Houses",
"desc": "Home sweet home - but it doesn't taste sweet?", "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" "icon": "fa-home"
}, },
"apartment": { "apartment": {
"name": "Apartment", "name": "Apartment",
"plural": "Apartments", "plural": "Apartments",
"desc": "More snacks, less packaging.", "desc": "More snacks, less packaging.",
"cost": 1.8e6,
"prod": 1e4 / 1.5,
"cost": {
"food": 1.8e6
},
"prod": {
"food": 1e4 / 1.5
},
"icon": "fa-building" "icon": "fa-building"
}, },
"block": { "block": {
"name": "Block", "name": "Block",
"plural": "Blocks", "plural": "Blocks",
"desc": "A whole pile of buildings.", "desc": "A whole pile of buildings.",
"cost": 2e7,
"prod": 1e5 / 1.6,
"cost": {
"food": 2e7
},
"prod": {
"food": 1e5 / 1.6
},
"icon": "fa-warehouse" "icon": "fa-warehouse"
}, },
"town": { "town": {
"name": "Town", "name": "Town",
"plural": "Towns", "plural": "Towns",
"desc": "'Tourist trap' has never been this literal.", "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" "icon": "fa-store"
}, },
"city": { "city": {
"name": "City", "name": "City",
"plural": "Cities", "plural": "Cities",
"desc": "Please no sitty on our city.", "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" "icon": "fa-city"
}, },
"metro": { "metro": {
"name": "Metropolis", "name": "Metropolis",
"plural": "Metropolises", "plural": "Metropolises",
"desc": "A big ol' city. Tasty, too.", "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" "icon": "fa-landmark"
}, },
"county": { "county": {
"name": "County", "name": "County",
"plural": "Counties", "plural": "Counties",
"desc": "Why salt the land when you can slurp it?", "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" "icon": "fa-map"
}, },
"state": { "state": {
"name": "State", "name": "State",
"plural": "States", "plural": "States",
"desc": "The United States is made up of...43 states - no, 42...", "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" "icon": "fa-map-signs"
}, },
"country": { "country": {
"name": "Country", "name": "Country",
"plural": "Countries", "plural": "Countries",
"desc": "One nation, under paw.", "desc": "One nation, under paw.",
"cost": 3.2e13,
"prod": 1e11 / 2.2,
"cost": {
"food": 3.2e13
},
"prod": {
"food": 1e11 / 2.2
},
"icon": "fa-flag" "icon": "fa-flag"
}, },
"continent": { "continent": {
"name": "Continent", "name": "Continent",
"plural": "Continents", "plural": "Continents",
"desc": "Earth-shattering appetite!", "desc": "Earth-shattering appetite!",
"cost": 3.4e14,
"prod": 1e12 / 2.3,
"cost": {
"food": 3.4e14
},
"prod": {
"food": 1e12 / 2.3
},
"icon": "fa-mountain" "icon": "fa-mountain"
}, },
"planet": { "planet": {
"name": "Planet", "name": "Planet",
"plural": "Planets", "plural": "Planets",
"desc": "Earth appetite!", "desc": "Earth appetite!",
"cost": 3.6e15,
"prod": 1e13 / 2.4,
"cost": {
"food": 3.6e15
},
"prod": {
"food": 1e13 / 2.4
},
"icon": "fa-globe-europe" "icon": "fa-globe-europe"
}, },
"solar-system": { "solar-system": {
"name": "Solar System", "name": "Solar System",
"plural": "Solar Systems", "plural": "Solar Systems",
"desc": "Earths appetite!", "desc": "Earths appetite!",
"cost": 3.8e16,
"prod": 1e14 / 2.5,
"cost": {
"food": 3.8e16
},
"prod": {
"food": 1e14 / 2.5
},
"icon": "fa-meteor" "icon": "fa-meteor"
}, },
"galaxy": { "galaxy": {
"name": "Galaxy", "name": "Galaxy",
"plural": "Galaxies", "plural": "Galaxies",
"desc": "In a galaxy far, far down your gullet...", "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" "icon": "fa-sun"
}, },
"universe": { "universe": {
"name": "Universe", "name": "Universe",
"plural": "Universes", "plural": "Universes",
"desc": "Into the you-verse.", "desc": "Into the you-verse.",
"cost": 4.2e18,
"prod": 1e16 / 2.7,
"cost": {
"food": 4.2e18
},
"prod": {
"food": 1e16 / 2.7
},
"icon": "fa-asterisk" "icon": "fa-asterisk"
}, },
"multiverse": { "multiverse": {
"name": "Multiverse", "name": "Multiverse",
"plural": "Multiverses", "plural": "Multiverses",
"desc": "This is getting very silly.", "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" "icon": "fa-infinity"
} }
} }


deepFreeze(buildings);

const effect_types = { const effect_types = {
"prod": { "prod": {
"apply": function (effect, productivity) { "apply": function (effect, productivity) {
return productivity * effect.amount;
scaleCost(productivity, effect);
}, },
"desc": function (effect) { "desc": function (effect) {
return round(effect.amount, 2) + "x food production from " + buildings[effect.target].plural; return round(effect.amount, 2) + "x food production from " + buildings[effect.target].plural;
@@ -171,7 +252,7 @@ const effect_types = {
}, },
"prod-all": { "prod-all": {
"apply": function (effect, productivity) { "apply": function (effect, productivity) {
return productivity * effect.amount;
scaleCost(productivity, effect);
}, },
"desc": function (effect) { "desc": function (effect) {
return round((effect.amount - 1) * 100) + "% increase to food production"; return round((effect.amount - 1) * 100) + "% increase to food production";
@@ -208,6 +289,8 @@ const effect_types = {
} }
} }


deepFreeze(effect_types);

let upgrades = { let upgrades = {


} }
@@ -219,6 +302,7 @@ function createTemplateUpgrades() {
createHelperUpgrades(); createHelperUpgrades();
createClickVictimUpgrades(); createClickVictimUpgrades();
createPowerupFreqUpgrades(); createPowerupFreqUpgrades();
deepFreeze(upgrades);
} }


const prodUpgradeCounts = [1, 5, 10, 25, 50, 75, 100]; const prodUpgradeCounts = [1, 5, 10, 25, 50, 75, 100];


+ 47
- 28
gorge.js View File

@@ -13,6 +13,7 @@ const resources = {};
let updateRate = 30; let updateRate = 30;


const currentProductivity = {}; const currentProductivity = {};

let clickBonus = 0; let clickBonus = 0;
let clickVictim = "micro"; let clickVictim = "micro";


@@ -66,7 +67,10 @@ function addPowerup(powerup) {
updateAll(); updateAll();
} }


function applyGlobalProdBonuses(productivity) {
function getGlobalProdBonus() {

let productivity = 1;

for (let effect of effects["prod-all"]) { for (let effect of effects["prod-all"]) {


if (ownedUpgrades[effect.parent]) { if (ownedUpgrades[effect.parent]) {
@@ -78,10 +82,10 @@ function applyGlobalProdBonuses(productivity) {
} }


function calculateProductivity() { function calculateProductivity() {
let productivity = 0;
let productivity = makeCost();


for (const [key, value] of Object.entries(belongings)) { for (const [key, value] of Object.entries(belongings)) {
productivity += productivityOf(key);
productivity = addCost(productivity, productivityOf(key));
} }


return productivity; return productivity;
@@ -110,27 +114,42 @@ function productivityMultiplierOf(type) {
} }


function productivityOf(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) { function costOfBuilding(type, count = 1) {
let total = 0;
let total = makeCost();


while (count > 0) { 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--; count--;
} }



return Math.round(total);
return mapObject(total, round);
} }


function buildingCount() { function buildingCount() {
@@ -148,9 +167,9 @@ function buyBuilding(type, e) {


let cost = costOfBuilding(type, count); let cost = costOfBuilding(type, count);


if (resources.food >= cost) {
if (canAfford(cost)) {
spend(cost);
belongings[type].count += count; belongings[type].count += count;
resources.food -= cost;
} }


updateProductivity(); updateProductivity();
@@ -181,7 +200,7 @@ function updateDisplay() {
} }


function updateProductivity() { function updateProductivity() {
currentProductivity["food"] = calculateProductivity();
Object.assign(currentProductivity, calculateProductivity());


activePowerups.forEach(entry => { activePowerups.forEach(entry => {
const powerup = entry.powerup; const powerup = entry.powerup;
@@ -193,11 +212,7 @@ function updateProductivity() {
belongings: belongings belongings: belongings
}; };


console.log(currentProductivity);

powerup.effect(state); powerup.effect(state);

console.log(currentProductivity);
}) })
} }


@@ -233,7 +248,9 @@ function displayBuildings() {
for (const [key, value] of Object.entries(belongings)) { for (const [key, value] of Object.entries(belongings)) {


if (!belongings[key].visible) { 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); unlockBuilding(key);
} else { } else {
continue; continue;
@@ -247,15 +264,17 @@ function displayBuildings() {
let name = document.querySelector("#building-" + key + " > .building-button-name"); let name = document.querySelector("#building-" + key + " > .building-button-name");
let cost = document.querySelector("#building-" + key + " > .building-button-cost"); 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); 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"); button.classList.remove("building-button-disabled");
cost.classList.add("building-button-cost-valid"); 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() { function eatPrey() {
const add = buildings[clickVictim]["prod"] * 10 * productivityMultiplierOf(clickVictim) + clickBonus;
const add = buildings[clickVictim]["prod"].food * 10 * productivityMultiplierOf(clickVictim) + clickBonus;
resources.food += add; resources.food += add;
return add; return add;
} }
@@ -938,7 +957,7 @@ function buildingTooltip(id, event) {


fillTooltip("building", "name", (count != 1 ? count + "x " : "") + buildings[id].name); fillTooltip("building", "name", (count != 1 ? count + "x " : "") + buildings[id].name);
fillTooltip("building", "desc", buildings[id].desc); 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)); fillTooltip("building", "prod", prodSummary(id));


let xPos = tooltip.parentElement.getBoundingClientRect().x - 450; let xPos = tooltip.parentElement.getBoundingClientRect().x - 450;


+ 3
- 0
numbers.js View File

@@ -3,6 +3,9 @@ function render(val, places = 1, smallPlaces = 0) {
} }


function numberText(val, places = 1, smallPlaces = 0) { function numberText(val, places = 1, smallPlaces = 0) {
if (isNaN(val)) {
throw new RangeError("Invalid number: " + val);
}
if (val < 1000) { if (val < 1000) {
return round(val, smallPlaces); return round(val, smallPlaces);
} }


+ 21
- 0
util.js View File

@@ -18,3 +18,24 @@ function removeChildren(element) {
function round(val, places = 0) { function round(val, places = 0) {
return Math.round(val * Math.pow(10, places)) / Math.pow(10, places); 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);
}

Loading…
Cancel
Save