burgs placement change + resource style

This commit is contained in:
Azgaar 2021-07-07 21:17:39 +03:00
parent dc6528665a
commit b7545d2805
6 changed files with 687 additions and 479 deletions

View file

@ -907,6 +907,15 @@
</tr> </tr>
</tbody> </tbody>
<tbody id="styleResources">
<tr data-tip="Show or hide circle around resource icons">
<td>
<input id="styleResourcesCircle" class="checkbox" type="checkbox">
<label for="styleResourcesCircle" class="checkbox-label">Show circle</label>
</td>
</tr>
</tbody>
<tbody id="styleVisibility"> <tbody id="styleVisibility">
<tr data-tip="Allow system to hide labels if their size in too small or too big on that scale"> <tr data-tip="Allow system to hide labels if their size in too small or too big on that scale">
<td colspan=2> <td colspan=2>

57
main.js
View file

@ -357,7 +357,7 @@ function applyDefaultBiomesSystem() {
'Wetland' 'Wetland'
]; ];
const color = ['#466eab', '#fbe79f', '#b5b887', '#d2d082', '#c8d68f', '#b6d95d', '#29bc56', '#7dcb35', '#409c43', '#4b6b32', '#96784b', '#d5e7eb', '#0b9131']; const color = ['#466eab', '#fbe79f', '#b5b887', '#d2d082', '#c8d68f', '#b6d95d', '#29bc56', '#7dcb35', '#409c43', '#4b6b32', '#96784b', '#d5e7eb', '#0b9131'];
const habitability = [0, 4, 10, 22, 30, 50, 100, 80, 90, 12, 4, 0, 12]; const habitability = [0, 4, 10, 25, 40, 60, 100, 80, 90, 12, 4, 0, 12];
const iconsDensity = [0, 3, 2, 120, 120, 120, 120, 150, 150, 100, 5, 0, 150]; const iconsDensity = [0, 3, 2, 120, 120, 120, 120, 150, 150, 100, 5, 0, 150];
const icons = [ const icons = [
{}, {},
@ -1411,45 +1411,62 @@ function getBiomeId(moisture, temperature, height) {
return biomesData.biomesMartix[m][t]; return biomesData.biomesMartix[m][t];
} }
// assess cells suitability to calculate population and rand cells for culture center and burgs placement // assess cells suitability to calculate population and rang cells for culture center and burgs placement
function rankCells() { function rankCells() {
TIME && console.time('rankCells'); TIME && console.time('rankCells');
const {cells, features} = pack; const {cells, features} = pack;
cells.s = new Int16Array(cells.i.length); // cell suitability array cells.s = new Int16Array(cells.i.length); // cell suitability array
cells.pop = new Float32Array(cells.i.length); // cell population array cells.pop = new Float32Array(cells.i.length); // cell population array
const flMean = d3.median(cells.fl.filter((f) => f)) || 0, const flMean = d3.median(cells.fl.filter((f) => f)) || 0;
flMax = d3.max(cells.fl) + d3.max(cells.conf); // to normalize flux const flMax = d3.max(cells.fl) + d3.max(cells.conf); // to normalize flux
const areaMean = d3.mean(cells.area); // to adjust population by cell area const areaMean = d3.mean(cells.area); // to adjust population by cell area
const getResValue = (i) => (cells.resource[i] ? Resources.get(cells.resource[i])?.value : 0);
const resBonuses = [];
const POP_BALANCER = 1.5; // contant to ballance population to desired number not changing ranking
for (const i of cells.i) { for (const i of cells.i) {
if (cells.b[i]) continue; // avoid adding burgs on map border
if (cells.h[i] < 20) continue; // no population in water if (cells.h[i] < 20) continue; // no population in water
let s = +biomesData.habitability[cells.biome[i]]; // base suitability derived from biome habitability let s = biomesData.habitability[cells.biome[i]] / 10; // base suitability derived from biome habitability
if (!s) continue; // uninhabitable biomes has 0 suitability if (!s) continue; // uninhabitable biomes has 0 suitability
if (flMean) s += normalize(cells.fl[i] + cells.conf[i], flMean, flMax) * 250; // big rivers and confluences are valued
s -= (cells.h[i] - 50) / 5; // low elevation is valued, high is not; if (flMean) s += normalize(cells.fl[i] + cells.conf[i], flMean, flMax) * 50; // big rivers and confluences are valued
s -= (cells.h[i] - 50) / 25; // low elevation is valued, high is not;
if (cells.t[i] === 1) { if (cells.t[i] === 1) {
if (cells.r[i]) s += 15; // estuary is valued if (cells.r[i]) s += 3; // estuary is valued
const feature = features[cells.f[cells.haven[i]]]; const {type, group} = features[cells.f[cells.haven[i]]];
if (feature.type === 'lake') { if (type === 'lake') {
if (feature.group === 'freshwater') s += 30; if (group === 'freshwater') s += 5;
else if (feature.group == 'salt') s += 10; else if (group == 'salt') s += 2;
else if (feature.group == 'frozen') s += 1; else if (group == 'dry') s -= 1;
else if (feature.group == 'dry') s -= 5; else if (group == 'sinkhole') s -= 1;
else if (feature.group == 'sinkhole') s -= 5; else if (group == 'lava') s -= 6;
else if (feature.group == 'lava') s -= 30;
} else { } else {
s += 5; // ocean coast is valued s += 1; // ocean coast is valued
if (cells.harbor[i] === 1) s += 20; // safe sea harbor is valued if (cells.harbor[i] === 1) s += 4; // safe sea harbor is valued
} }
} }
cells.s[i] = s / 5; // general population rate // add bonus for resources around
const cellRes = getResValue(i);
const neibRes = d3.mean(cells.c[i].map((c) => getResValue(c)));
const resBonus = (cellRes ? cellRes + 10 : 0) + neibRes;
resBonuses.push(resBonus);
// cell rural population is suitability adjusted by cell area // cell rural population is suitability adjusted by cell area
cells.pop[i] = cells.s[i] > 0 ? (cells.s[i] * cells.area[i]) / areaMean : 0; cells.pop[i] = s > 0 ? (s * POP_BALANCER * cells.area[i]) / areaMean : 0;
cells.s[i] = s + resBonus;
debug.append('text').attr('x', cells.p[i][0]).attr('y', cells.p[i][1]).text(cells.s[i]);
} }
console.log(resBonuses);
console.log(d3.max(resBonuses));
console.log(d3.mean(resBonuses));
console.log(d3.median(resBonuses.map((v) => rn(v))));
TIME && console.timeEnd('rankCells'); TIME && console.timeEnd('rankCells');
} }

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
})(this, function () { })(this, function () {
'use strict'; 'use strict';
// TO-DO // TODO
// apply logic on heightmap edit // apply logic on heightmap edit
// apply logic on burgs regeneration // apply logic on burgs regeneration
// apply logic on population recalculation // apply logic on population recalculation
@ -13,48 +13,47 @@
let cells, cellId; let cells, cellId;
const getDefault = function () { const getDefault = function () {
// model: cells eligibility function; chance: chance to get rosource in model-eligible cell
return [ return [
{i: 1, name: 'Wood', category: 'Construction', icon: 'resource-wood', color: '#966F33', value: 5, chance: 10, model: 'Any_forest', bonus: {fleet: 2, defence: 1}}, {i: 1, name: 'Wood', category: 'Construction', icon: 'resource-wood', color: '#966F33', value: 2, chance: 4, model: 'Any_forest', bonus: {fleet: 2, defence: 1}},
{i: 2, name: 'Stone', category: 'Construction', icon: 'resource-stone', color: '#979EA2', value: 4, chance: 7, model: 'Hills', bonus: {prestige: 1, defence: 2}}, {i: 2, name: 'Stone', category: 'Construction', icon: 'resource-stone', color: '#979EA2', value: 2, chance: 4, model: 'Hills', bonus: {prestige: 1, defence: 2}},
{i: 3, name: 'Marble', category: 'Construction', icon: 'resource-marble', color: '#d6d0bf', value: 15, chance: 1, model: 'Mountains', bonus: {prestige: 2}}, {i: 3, name: 'Marble', category: 'Construction', icon: 'resource-marble', color: '#d6d0bf', value: 7, chance: 1, model: 'Mountains', bonus: {prestige: 2}},
{i: 4, name: 'Iron', category: 'Ore', icon: 'resource-iron', color: '#5D686E', value: 8, chance: 8, model: 'Mountains_and_wetlands', bonus: {artillery: 1, infantry: 1, defence: 1}}, {i: 4, name: 'Iron', category: 'Ore', icon: 'resource-iron', color: '#5D686E', value: 4, chance: 4, model: 'Mountains_and_wetlands', bonus: {artillery: 1, infantry: 1, defence: 1}},
{i: 5, name: 'Copper', category: 'Ore', icon: 'resource-copper', color: '#b87333', value: 10, chance: 3, model: 'Mountains', bonus: {artillery: 2, defence: 1, prestige: 1}}, {i: 5, name: 'Copper', category: 'Ore', icon: 'resource-copper', color: '#b87333', value: 5, chance: 3, model: 'Mountains', bonus: {artillery: 2, defence: 1, prestige: 1}},
{i: 6, name: 'Lead', category: 'Ore', icon: 'resource-lead', color: '#454343', value: 8, chance: 3, model: 'Mountains', bonus: {artillery: 1, defence: 1}}, {i: 6, name: 'Lead', category: 'Ore', icon: 'resource-lead', color: '#454343', value: 4, chance: 3, model: 'Mountains', bonus: {artillery: 1, defence: 1}},
{i: 7, name: 'Silver', category: 'Ore', icon: 'resource-silver', color: '#C0C0C0', value: 15, chance: 3, model: 'Mountains', bonus: {prestige: 2}}, {i: 7, name: 'Silver', category: 'Ore', icon: 'resource-silver', color: '#C0C0C0', value: 8, chance: 3, model: 'Mountains', bonus: {prestige: 2}},
{i: 8, name: 'Gold', category: 'Ore', icon: 'resource-gold', color: '#d4af37', value: 30, chance: 1, model: 'Headwaters', bonus: {prestige: 3}}, {i: 8, name: 'Gold', category: 'Ore', icon: 'resource-gold', color: '#d4af37', value: 15, chance: 1, model: 'Headwaters', bonus: {prestige: 3}},
{i: 9, name: 'Grain', category: 'Food', icon: 'resource-grain', color: '#F5DEB3', value: 1, chance: 15, model: 'Biome_habitability', bonus: {population: 4}}, {i: 9, name: 'Grain', category: 'Food', icon: 'resource-grain', color: '#F5DEB3', value: 1, chance: 4, model: 'More_habitable', bonus: {population: 4}},
{i: 10, name: 'Cattle', category: 'Food', icon: 'resource-cattle', color: '#56b000', value: 2, chance: 10, model: 'Pastures_and_temperate_forest', bonus: {population: 2}}, {i: 10, name: 'Cattle', category: 'Food', icon: 'resource-cattle', color: '#56b000', value: 2, chance: 4, model: 'Pastures_and_temperate_forest', bonus: {population: 2}},
{i: 11, name: 'Fish', category: 'Food', icon: 'resource-fish', color: '#7fcdff', value: 1, chance: 5, model: 'Marine_and_rivers', bonus: {population: 2}}, {i: 11, name: 'Fish', category: 'Food', icon: 'resource-fish', color: '#7fcdff', value: 1, chance: 2, model: 'Marine_and_rivers', bonus: {population: 2}},
{i: 12, name: 'Game', category: 'Food', icon: 'resource-game', color: '#c38a8a', value: 2, chance: 3, model: 'Any_forest', bonus: {archers: 2, population: 1}}, {i: 12, name: 'Game', category: 'Food', icon: 'resource-game', color: '#c38a8a', value: 2, chance: 3, model: 'Any_forest', bonus: {archers: 2, population: 1}},
{i: 13, name: 'Wine', category: 'Food', icon: 'resource-wine', color: '#963e48', value: 3, chance: 4, model: 'Tropical_forests', bonus: {population: 1, prestige: 1}}, {i: 13, name: 'Wine', category: 'Food', icon: 'resource-wine', color: '#963e48', value: 2, chance: 3, model: 'Tropical_forests', bonus: {population: 1, prestige: 1}},
{i: 14, name: 'Olives', category: 'Food', icon: 'resource-olives', color: '#BDBD7D', value: 3, chance: 4, model: 'Tropical_forests', bonus: {population: 1}}, {i: 14, name: 'Olives', category: 'Food', icon: 'resource-olives', color: '#BDBD7D', value: 2, chance: 3, model: 'Tropical_forests', bonus: {population: 1}},
{i: 15, name: 'Honey', category: 'Food', icon: 'resource-honey', color: '#DCBC66', value: 4, chance: 3, model: 'Temperate_and_boreal_forests', bonus: {population: 1}}, {i: 15, name: 'Honey', category: 'Food', icon: 'resource-honey', color: '#DCBC66', value: 2, chance: 3, model: 'Temperate_and_boreal_forests', bonus: {population: 1}},
{i: 16, name: 'Salt', category: 'Food', icon: 'resource-salt', color: '#E5E4E5', value: 5, chance: 4, model: 'Arid_land_and_salt_lakes', bonus: {population: 1, defence: 1}}, {i: 16, name: 'Salt', category: 'Food', icon: 'resource-salt', color: '#E5E4E5', value: 3, chance: 3, model: 'Arid_land_and_salt_lakes', bonus: {population: 1, defence: 1}},
{i: 17, name: 'Dates', category: 'Food', icon: 'resource-dates', color: '#dbb2a3', value: 3, chance: 3, model: 'Hot_desert', bonus: {population: 1}}, {i: 17, name: 'Dates', category: 'Food', icon: 'resource-dates', color: '#dbb2a3', value: 2, chance: 2, model: 'Hot_desert', bonus: {population: 1}},
{i: 18, name: 'Horses', category: 'Supply', icon: 'resource-horses', color: '#ba7447', value: 10, chance: 6, model: 'Grassland_and_cold_desert', bonus: {cavalry: 2}}, {i: 18, name: 'Horses', category: 'Supply', icon: 'resource-horses', color: '#ba7447', value: 5, chance: 4, model: 'Grassland_and_cold_desert', bonus: {cavalry: 2}},
{i: 19, name: 'Elephants', category: 'Supply', icon: 'resource-elephants', color: '#C5CACD', value: 15, chance: 2, model: 'Hot_biomes', bonus: {cavalry: 1}}, {i: 19, name: 'Elephants', category: 'Supply', icon: 'resource-elephants', color: '#C5CACD', value: 7, chance: 2, model: 'Hot_biomes', bonus: {cavalry: 1}},
{i: 20, name: 'Camels', category: 'Supply', icon: 'resource-camels', color: '#C19A6B', value: 13, chance: 4, model: 'Deserts', bonus: {cavalry: 1}}, {i: 20, name: 'Camels', category: 'Supply', icon: 'resource-camels', color: '#C19A6B', value: 7, chance: 3, model: 'Deserts', bonus: {cavalry: 1}},
{i: 21, name: 'Hemp', category: 'Material', icon: 'resource-hemp', color: '#069a06', value: 2, chance: 4, model: 'Deciduous_forests', bonus: {fleet: 2}}, {i: 21, name: 'Hemp', category: 'Material', icon: 'resource-hemp', color: '#069a06', value: 2, chance: 3, model: 'Deciduous_forests', bonus: {fleet: 2}},
{i: 22, name: 'Pearls', category: 'Luxury', icon: 'resource-pearls', color: '#EAE0C8', value: 35, chance: 3, model: 'Tropical_waters', bonus: {prestige: 1}}, {i: 22, name: 'Pearls', category: 'Luxury', icon: 'resource-pearls', color: '#EAE0C8', value: 16, chance: 2, model: 'Tropical_waters', bonus: {prestige: 1}},
{i: 23, name: 'Gemstones', category: 'Luxury', icon: 'resource-gemstones', color: '#e463e4', value: 35, chance: 2, model: 'Mountains', bonus: {prestige: 1}}, {i: 23, name: 'Gemstones', category: 'Luxury', icon: 'resource-gemstones', color: '#e463e4', value: 17, chance: 2, model: 'Mountains', bonus: {prestige: 1}},
{i: 24, name: 'Dyes', category: 'Luxury', icon: 'resource-dyes', color: '#fecdea', value: 15, chance: 0.5, model: 'Habitable_biome_or_marine', bonus: {prestige: 1}}, {i: 24, name: 'Dyes', category: 'Luxury', icon: 'resource-dyes', color: '#fecdea', value: 6, chance: 0.5, model: 'Habitable_biome_or_marine', bonus: {prestige: 1}},
{i: 25, name: 'Incense', category: 'Luxury', icon: 'resource-incense', color: '#ebe5a7', value: 25, chance: 2, model: 'Hot_desert_and_tropical_forest', bonus: {prestige: 2}}, {i: 25, name: 'Incense', category: 'Luxury', icon: 'resource-incense', color: '#ebe5a7', value: 12, chance: 2, model: 'Hot_desert_and_tropical_forest', bonus: {prestige: 2}},
{i: 26, name: 'Silk', category: 'Luxury', icon: 'resource-silk', color: '#e0f0f8', value: 30, chance: 1, model: 'Tropical_rainforest', bonus: {prestige: 2}}, {i: 26, name: 'Silk', category: 'Luxury', icon: 'resource-silk', color: '#e0f0f8', value: 15, chance: 1, model: 'Tropical_rainforest', bonus: {prestige: 2}},
{i: 27, name: 'Spices', category: 'Luxury', icon: 'resource-spices', color: '#e99c75', value: 30, chance: 2, model: 'Tropical_rainforest', bonus: {prestige: 2}}, {i: 27, name: 'Spices', category: 'Luxury', icon: 'resource-spices', color: '#e99c75', value: 15, chance: 2, model: 'Tropical_rainforest', bonus: {prestige: 2}},
{i: 28, name: 'Amber', category: 'Luxury', icon: 'resource-amber', color: '#e68200', value: 15, chance: 2, model: 'Foresty_seashore', bonus: {prestige: 1}}, {i: 28, name: 'Amber', category: 'Luxury', icon: 'resource-amber', color: '#e68200', value: 7, chance: 2, model: 'Foresty_seashore', bonus: {prestige: 1}},
{i: 29, name: 'Furs', category: 'Material', icon: 'resource-furs', color: '#8a5e51', value: 13, chance: 2, model: 'Boreal_forests', bonus: {prestige: 1}}, {i: 29, name: 'Furs', category: 'Material', icon: 'resource-furs', color: '#8a5e51', value: 6, chance: 2, model: 'Boreal_forests', bonus: {prestige: 1}},
{i: 30, name: 'Sheep', category: 'Material', icon: 'resource-sheeps', color: '#53b574', value: 2, chance: 5, model: 'Pastures_and_temperate_forest', bonus: {infantry: 1}}, {i: 30, name: 'Sheep', category: 'Material', icon: 'resource-sheeps', color: '#53b574', value: 2, chance: 3, model: 'Pastures_and_temperate_forest', bonus: {infantry: 1}},
{i: 31, name: 'Slaves', category: 'Supply', icon: 'resource-slaves', color: '#757575', value: 10, chance: 3, model: 'Less_habitable_seashore', bonus: {population: 2}}, {i: 31, name: 'Slaves', category: 'Supply', icon: 'resource-slaves', color: '#757575', value: 5, chance: 2, model: 'Less_habitable_seashore', bonus: {population: 2}},
{i: 32, name: 'Tar', category: 'Material', icon: 'resource-tar', color: '#727272', value: 3, chance: 3, model: 'Any_forest', bonus: {fleet: 1}}, {i: 32, name: 'Tar', category: 'Material', icon: 'resource-tar', color: '#727272', value: 2, chance: 3, model: 'Any_forest', bonus: {fleet: 1}},
{i: 33, name: 'Saltpeter', category: 'Material', icon: 'resource-saltpeter', color: '#e6e3e3', value: 8, chance: 2, model: 'Biome_habitability', bonus: {artillery: 3}}, {i: 33, name: 'Saltpeter', category: 'Material', icon: 'resource-saltpeter', color: '#e6e3e3', value: 3, chance: 2, model: 'Less_habitable_biomes', bonus: {artillery: 3}},
{i: 34, name: 'Coal', category: 'Material', icon: 'resource-coal', color: '#36454f', value: 2, chance: 7, model: 'Hills', bonus: {artillery: 2}}, {i: 34, name: 'Coal', category: 'Material', icon: 'resource-coal', color: '#36454f', value: 2, chance: 3, model: 'Hills', bonus: {artillery: 2}},
{i: 35, name: 'Oil', category: 'Material', icon: 'resource-oil', color: '#565656', value: 5, chance: 2, model: 'Less_habitable_biomes', bonus: {artillery: 1}}, {i: 35, name: 'Oil', category: 'Material', icon: 'resource-oil', color: '#565656', value: 3, chance: 2, model: 'Less_habitable_biomes', bonus: {artillery: 1}},
{i: 36, name: 'Tropical timber', category: 'Luxury', icon: 'resource-tropicalTimber', color: '#a45a52', value: 20, chance: 2, model: 'Tropical_rainforest', bonus: {prestige: 1}}, {i: 36, name: 'Tropical timber', category: 'Luxury', icon: 'resource-tropicalTimber', color: '#a45a52', value: 10, chance: 2, model: 'Tropical_rainforest', bonus: {prestige: 1}},
{i: 37, name: 'Whales', category: 'Food', icon: 'resource-whales', color: '#cccccc', value: 2, chance: 2, model: 'Arctic_waters', bonus: {population: 1}}, {i: 37, name: 'Whales', category: 'Food', icon: 'resource-whales', color: '#cccccc', value: 2, chance: 3, model: 'Arctic_waters', bonus: {population: 1}},
{i: 38, name: 'Sugar', category: 'Food', icon: 'resource-sugar', color: '#7abf87', value: 3, chance: 3, model: 'Tropical_rainforest', bonus: {population: 1}}, {i: 38, name: 'Sugar', category: 'Food', icon: 'resource-sugar', color: '#7abf87', value: 3, chance: 3, model: 'Tropical_rainforest', bonus: {population: 1}},
{i: 39, name: 'Tea', category: 'Luxury', icon: 'resource-tea', color: '#d0f0c0', value: 10, chance: 3, model: 'Hilly_tropical_rainforest', bonus: {prestige: 1}}, {i: 39, name: 'Tea', category: 'Luxury', icon: 'resource-tea', color: '#d0f0c0', value: 5, chance: 3, model: 'Hilly_tropical_rainforest', bonus: {prestige: 1}},
{i: 40, name: 'Tobacco', category: 'Luxury', icon: 'resource-tobacco', color: '#6D5843', value: 10, chance: 2, model: 'Tropical_rainforest', bonus: {prestige: 1}} {i: 40, name: 'Tobacco', category: 'Luxury', icon: 'resource-tobacco', color: '#6D5843', value: 5, chance: 2, model: 'Tropical_rainforest', bonus: {prestige: 1}}
]; ];
}; };
@ -66,8 +65,8 @@
Mountains: 'minHeight(60) || (minHeight(40) && nth(10))', Mountains: 'minHeight(60) || (minHeight(40) && nth(10))',
Mountains_and_wetlands: 'minHeight(60) || (biome(12) && nth(8))', Mountains_and_wetlands: 'minHeight(60) || (biome(12) && nth(8))',
Headwaters: 'river() && minHeight(40)', Headwaters: 'river() && minHeight(40)',
Biome_habitability: 'habitability()', More_habitable: 'minHabitability(20) && habitability()',
Marine_and_rivers: 'type("ocean", "freshwater", "salt") || (river() && shore(1, 2))', Marine_and_rivers: 'shore(-1) && (type("ocean", "freshwater", "salt") || (river() && shore(1, 2)))',
Pastures_and_temperate_forest: '(biome(3, 4) && !elevation()) || (biome(6) && random(70)) || (biome(5) && nth(5))', Pastures_and_temperate_forest: '(biome(3, 4) && !elevation()) || (biome(6) && random(70)) || (biome(5) && nth(5))',
Tropical_forests: 'biome(5, 7)', Tropical_forests: 'biome(5, 7)',
Arid_land_and_salt_lakes: 'type("salt", "dry") || (biome(1, 2) && random(70)) || (biome(12) && nth(10))', Arid_land_and_salt_lakes: 'type("salt", "dry") || (biome(1, 2) && random(70)) || (biome(12) && nth(10))',
@ -80,19 +79,19 @@
Tropical_waters: 'shore(-1) && minTemp(18)', Tropical_waters: 'shore(-1) && minTemp(18)',
Hilly_tropical_rainforest: 'minHeight(40) && biome(7)', Hilly_tropical_rainforest: 'minHeight(40) && biome(7)',
Subtropical_waters: 'shore(-1) && minTemp(14)', Subtropical_waters: 'shore(-1) && minTemp(14)',
Habitable_biome_or_marine: 'shore(-1) || habitable()', Habitable_biome_or_marine: 'shore(-1) || minHabitability(1)',
Foresty_seashore: 'shore(1) && biome(6, 7, 8, 9)', Foresty_seashore: 'shore(1) && biome(6, 7, 8, 9)',
Boreal_forests: 'biome(9) || (biome(10) && nth(2)) || (biome(6, 8) && nth(5)) || (biome(12) && nth(10))', Boreal_forests: 'biome(9) || (biome(10) && nth(2)) || (biome(6, 8) && nth(5)) || (biome(12) && nth(10))',
Less_habitable_seashore: 'shore(1) && habitable() && !habitability()', Less_habitable_seashore: 'shore(1) && minHabitability(1) && !habitability()',
Less_habitable_biomes: 'habitable() && !habitability()', Less_habitable_biomes: 'minHabitability(1) && !habitability()',
Arctic_waters: 'biome(0) && maxTemp(7)' Arctic_waters: 'shore(-1) && biome(0) && maxTemp(7)'
}; };
const methods = { const methods = {
random: (number) => number >= 100 || (number > 0 && number / 100 > Math.random()), random: (number) => number >= 100 || (number > 0 && number / 100 > Math.random()),
nth: (number) => !(cellId % number), nth: (number) => !(cellId % number),
habitable: () => biomesData.habitability[pack.cells.biome[cellId]], minHabitability: (min) => biomesData.habitability[pack.cells.biome[cellId]] >= min,
habitability: () => biomesData.habitability[cells.biome[cellId]] / 100 > Math.random(), habitability: () => biomesData.habitability[cells.biome[cellId]] > Math.random() * 100,
elevation: () => pack.cells.h[cellId] / 100 > Math.random(), elevation: () => pack.cells.h[cellId] / 100 > Math.random(),
biome: (...biomes) => biomes.includes(pack.cells.biome[cellId]), biome: (...biomes) => biomes.includes(pack.cells.biome[cellId]),
minHeight: (heigh) => pack.cells.h[cellId] >= heigh, minHeight: (heigh) => pack.cells.h[cellId] >= heigh,
@ -106,7 +105,7 @@
const allMethods = '{' + Object.keys(methods).join(', ') + '}'; const allMethods = '{' + Object.keys(methods).join(', ') + '}';
const generate = function () { const generate = function () {
console.time('generateResources'); TIME && console.time('generateResources');
cells = pack.cells; cells = pack.cells;
cells.resource = new Uint8Array(cells.i.length); // resources array [0, 255] cells.resource = new Uint8Array(cells.i.length); // resources array [0, 255]
const resourceMaxCells = Math.ceil((200 * cells.i.length) / 5000); const resourceMaxCells = Math.ceil((200 * cells.i.length) / 5000);
@ -128,7 +127,7 @@
for (const resource of pack.resources) { for (const resource of pack.resources) {
if (resource.cells >= resourceMaxCells) continue; if (resource.cells >= resourceMaxCells) continue;
if (resource.cells >= resource.chance && rnd > resource.chance) continue; if (resource.cells ? rnd > resource.chance : Math.random() * 100 > resource.chance) continue;
if (!resource.fn({...methods})) continue; if (!resource.fn({...methods})) continue;
cells.resource[i] = resource.i; cells.resource[i] = resource.i;
@ -138,7 +137,7 @@
} }
pack.resources.sort((a, b) => (a.i > b.i ? 1 : -1)).forEach((r) => delete r.fn); pack.resources.sort((a, b) => (a.i > b.i ? 1 : -1)).forEach((r) => delete r.fn);
console.timeEnd('generateResources'); TIME && console.timeEnd('generateResources');
}; };
const getStroke = (color) => d3.color(color).darker(2).hex(); const getStroke = (color) => d3.color(color).darker(2).hex();

View file

@ -1655,6 +1655,7 @@ function toggleResources(event) {
function drawResources() { function drawResources() {
console.time('drawResources'); console.time('drawResources');
const someArePinned = pack.resources.some((resource) => resource.pinned); const someArePinned = pack.resources.some((resource) => resource.pinned);
const drawCircle = +goods.attr('data-circle');
let resourcesHTML = ''; let resourcesHTML = '';
for (const i of pack.cells.i) { for (const i of pack.cells.i) {
@ -1664,11 +1665,17 @@ function drawResources() {
const [x, y] = pack.cells.p[i]; const [x, y] = pack.cells.p[i];
const stroke = Resources.getStroke(resource.color); const stroke = Resources.getStroke(resource.color);
if (!drawCircle) {
resourcesHTML += `<use href="#${resource.icon}" x="${x - 3}" y="${y - 3}" width="6" height="6"/>`;
continue;
}
resourcesHTML += `<g> resourcesHTML += `<g>
<circle data-i="${resource.i}" cx=${x} cy=${y} r="3" fill="${resource.color}" stroke="${stroke}" /> <circle data-i="${resource.i}" cx=${x} cy=${y} r="3" fill="${resource.color}" stroke="${stroke}" />
<use href="#${resource.icon}" x="${x - 3}" y="${y - 3}" width="6" height="6"/> <use href="#${resource.icon}" x="${x - 3}" y="${y - 3}" width="6" height="6"/>
</g>`; </g>`;
} }
goods.html(resourcesHTML); goods.html(resourcesHTML);
console.timeEnd('drawResources'); console.timeEnd('drawResources');
} }

View file

@ -251,6 +251,13 @@ function selectStyleElement() {
styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr('stroke-width') || 1; styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr('stroke-width') || 1;
} }
if (sel === 'goods') {
styleStrokeWidth.style.display = 'block';
styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr('stroke-width') || '';
styleResources.style.display = 'block';
styleResourcesCircle.checked = +el.attr('data-circle');
}
// update group options // update group options
styleGroupSelect.options.length = 0; // remove all options styleGroupSelect.options.length = 0; // remove all options
if (['routes', 'labels', 'coastline', 'lakes', 'anchors', 'burgIcons', 'borders'].includes(sel)) { if (['routes', 'labels', 'coastline', 'lakes', 'anchors', 'burgIcons', 'borders'].includes(sel)) {
@ -677,6 +684,12 @@ styleEmblemsStateSizeInput.addEventListener('input', drawEmblems);
styleEmblemsProvinceSizeInput.addEventListener('input', drawEmblems); styleEmblemsProvinceSizeInput.addEventListener('input', drawEmblems);
styleEmblemsBurgSizeInput.addEventListener('input', drawEmblems); styleEmblemsBurgSizeInput.addEventListener('input', drawEmblems);
styleResourcesCircle.addEventListener('change', function () {
goods.attr('data-circle', +this.checked);
goods.selectAll('*').remove();
drawResources();
});
// request a URL to image to be used as a texture // request a URL to image to be used as a texture
function textureProvideURL() { function textureProvideURL() {
alertMessage.innerHTML = `Provide an image URL to be used as a texture: alertMessage.innerHTML = `Provide an image URL to be used as a texture:
@ -913,7 +926,7 @@ function applyDefaultStyle() {
fogging.attr('opacity', 0.98).attr('fill', '#30426f'); fogging.attr('opacity', 0.98).attr('fill', '#30426f');
emblems.attr('opacity', 0.9).attr('stroke-width', 1).attr('filter', null); emblems.attr('opacity', 0.9).attr('stroke-width', 1).attr('filter', null);
goods.attr('opacity', 1).attr('fill', '#000').attr('stroke', '#000').attr('stroke-width', 0.32).attr('filter', 'url(#dropShadow01)'); goods.attr('opacity', 1).attr('data-circle', 1).attr('fill', '#000').attr('stroke', '#000').attr('stroke-width', 0.32).attr('filter', 'url(#dropShadow01)');
} }
// apply style settings in JSON // apply style settings in JSON