diff --git a/index.html b/index.html index 75df1f56..2fb90f09 100644 --- a/index.html +++ b/index.html @@ -1520,14 +1520,14 @@

Map settings (new map to apply):

- +
Canvas size - width - - height - + width + + height + diff --git a/main.js b/main.js index 35dfa0e8..3bb562fe 100644 --- a/main.js +++ b/main.js @@ -499,6 +499,7 @@ function generate() { markFeatures(); openNearSeaLakes(); OceanLayers(); + defineMapSize(); calculateMapCoordinates(); calculateTemperatures(); generatePrecipitation(); @@ -676,6 +677,38 @@ function openNearSeaLakes() { console.timeEnd("openLakes"); } +// define map size and position based on template and random factor +function defineMapSize() { + const [size, latitude] = getSizeAndLatitude(); + if (!locked("mapSize")) mapSizeOutput.value = mapSizeInput.value = size; + if (!locked("latitude")) latitudeOutput.value = latitudeInput.value = latitude; + + function getSizeAndLatitude() { + const template = document.getElementById("templateInput").value; // heightmap template + const part = grid.features.some(f => f.land && f.border); // if land goes over map borders + const max = part ? 85 : 100; // max size + const lat = part ? gauss(P(.5) ? 30 : 70, 15, 15, 85) : gauss(50, 20, 0, 100); // latiture shift + + if (!part) { + if (template === "Pangea") return [100, 50]; + if (template === "Shattered" && P(.8)) return [100, 50]; + if (template === "Continents" && P(.7)) return [100, 50]; + if (template === "Archipelago" && P(.35)) return [100, 50]; + if (template === "High Island" && P(.3)) return [100, 50]; + if (template === "Low Island" && P(.1)) return [100, 50]; + } + + if (template === "Pangea") return [gauss(75, 20, 30, max), lat]; + if (template === "Volcano") return [gauss(40, 25, 10, max), lat]; + if (template === "Mediterranean") return [gauss(40, 30, 15, 80), lat]; + if (template === "Peninsula") return [gauss(15, 15, 5, 80), lat]; + if (template === "Isthmus") return [gauss(20, 20, 3, 80), lat]; + if (template === "Atoll") return [gauss(10, 10, 2, max), lat]; + + return [gauss(50, 20, 15, max), lat]; // Continents, Archipelago, High Island, Low Island + } +} + // calculate map position on globe function calculateMapCoordinates() { const size = +document.getElementById("mapSizeOutput").value; @@ -1158,7 +1191,7 @@ function addMarkers(number = 1) { .attr("data-size", 1).attr("width", 30).attr("height", 30); const height = getFriendlyHeight([x, y]); const proper = Names.getCulture(cells.culture[cell]); - const name = Math.random() < .3 ? "Mount " + proper : Math.random() > .3 ? proper + " Volcano" : proper; + const name = P(.3) ? "Mount " + proper : Math.random() > .3 ? proper + " Volcano" : proper; notes.push({id, name, legend:`Active volcano. Height: ${height}`}); count--; } @@ -1234,7 +1267,7 @@ function addMarkers(number = 1) { const burg = pack.burgs[cells.burg[cell]]; const river = pack.rivers.find(r => r.i === pack.cells.r[cell]); const riverName = river ? `${river.name} ${river.type}` : "river"; - const name = river && Math.random() < .2 ? river.name : burg.name; + const name = river && P(.2) ? river.name : burg.name; notes.push({id, name:`${name} Bridge`, legend:`A stone bridge over the ${riverName} near ${burg.name}`}); count--; } @@ -1261,8 +1294,8 @@ function addMarkers(number = 1) { .attr("data-x", x).attr("data-y", y).attr("x", x - 15).attr("y", y - 30) .attr("data-size", 1).attr("width", 30).attr("height", 30); - const type = Math.random() > .7 ? "inn" : "tavern"; - const name = Math.random() < .5 ? ra(color) + " " + ra(animal) : Math.random() < .6 ? ra(adj) + " " + ra(animal) : ra(adj) + " " + capitalize(type); + const type = P(.3) ? "inn" : "tavern"; + const name = P(.5) ? ra(color) + " " + ra(animal) : P(.6) ? ra(adj) + " " + ra(animal) : ra(adj) + " " + capitalize(type); notes.push({id, name: "The " + name, legend:`A big and famous roadside ${type}`}); } }() @@ -1375,7 +1408,7 @@ function addZones(number = 1) { const cellsArray = [], queue = [cell], power = rand(5, 30); while (queue.length) { - const q = Math.random() < .4 ? queue.shift() : queue.pop(); + const q = P(.4) ? queue.shift() : queue.pop(); cellsArray.push(q); if (cellsArray.length > power) break; @@ -1543,7 +1576,7 @@ function addZones(number = 1) { const cellsArray = [], queue = [cell], power = rand(10, 30); while (queue.length) { - const q = Math.random() < .5 ? queue.shift() : queue.pop(); + const q = P(.5) ? queue.shift() : queue.pop(); cellsArray.push(q); if (cellsArray.length > power) break; cells.c[q].forEach(e => { @@ -1564,7 +1597,7 @@ function addZones(number = 1) { const cellsArray = [], queue = [cell], power = rand(3, 15); while (queue.length) { - const q = Math.random() < .3 ? queue.shift() : queue.pop(); + const q = P(.3) ? queue.shift() : queue.pop(); cellsArray.push(q); if (cellsArray.length > power) break; cells.c[q].forEach(e => { @@ -1668,10 +1701,11 @@ function showStatistics() { const template = templateInput.value; const templateRandom = locked("template") ? "" : "(random)"; const stats = ` Seed: ${seed} - Size: ${graphWidth}x${graphHeight} + Canvas size: ${graphWidth}x${graphHeight} Template: ${template} ${templateRandom} Points: ${grid.points.length} Cells: ${pack.cells.i.length} + Map size: ${mapSizeOutput.value}% States: ${pack.states.length-1} Provinces: ${pack.provinces.length-1} Burgs: ${pack.burgs.length-1} diff --git a/modules/burgs-and-states.js b/modules/burgs-and-states.js index 2d628a16..c8a175d8 100644 --- a/modules/burgs-and-states.js +++ b/modules/burgs-and-states.js @@ -186,13 +186,13 @@ const defineBurgFeatures = function() { pack.burgs.filter(b => b.i && !b.removed).forEach(b => { const pop = b.population; - b.citadel = b.capital || pop > 50 && Math.random() < .75 || Math.random() < .5 ? 1 : 0; - b.plaza = pop > 50 || pop > 30 && Math.random() < .75 || pop > 10 && Math.random() < .5 || Math.random() < .25 ? 1 : 0; - b.walls = b.capital || pop > 30 || pop > 20 && Math.random() < .75 || pop > 10 && Math.random() < .5 || Math.random() < .2 ? 1 : 0; - b.shanty = pop > 30 || pop > 20 && Math.random() < .75 || b.walls && Math.random() < .75 ? 1 : 0; + b.citadel = b.capital || pop > 50 && P(.75) || P(.5) ? 1 : 0; + b.plaza = pop > 50 || pop > 30 && P(.75) || pop > 10 && P(.5) || P(.25) ? 1 : 0; + b.walls = b.capital || pop > 30 || pop > 20 && P(.75) || pop > 10 && P(.5) || P(.2) ? 1 : 0; + b.shanty = pop > 30 || pop > 20 && P(.75) || b.walls && P(.75) ? 1 : 0; const religion = pack.cells.religion[b.cell]; const theocracy = pack.states[b.state].form === "Theocracy"; - b.temple = religion && theocracy || pop > 50 || pop > 35 && Math.random() < .75 || pop > 20 && Math.random() < .5 ? 1 : 0; + b.temple = religion && theocracy || pop > 50 || pop > 35 && P(.75) || pop > 20 && P(.5) ? 1 : 0; }); } @@ -642,7 +642,7 @@ let status = naval ? rw(navals) : neib ? rw(neibs) : neibOfNeib ? rw(neibsOfNeibs) : rw(far); // add Vassal - if (neib && Math.random() < .8 && states[f].area > areaMean && states[t].area < areaMean && states[f].area / states[t].area > 2) status = "Vassal"; + if (neib && P(.8) && states[f].area > areaMean && states[t].area < areaMean && states[f].area / states[t].area > 2) status = "Vassal"; states[f].diplomacy[t] = status === "Vassal" ? "Suzerain" : status; states[t].diplomacy[f] = status; } @@ -707,7 +707,7 @@ ad.forEach((r, d) => { if (r !== "Ally" || states[d].diplomacy.includes("Vassal") || defenders.includes(d)) return; const name = states[d].name; - if (states[d].diplomacy[defender] !== "Rival" && (Math.random() < .2 || ap <= dp * 1.2)) {war.push(`${an}'s ally ${name} avoided entering the war`); return;} + if (states[d].diplomacy[defender] !== "Rival" && (P(.2) || ap <= dp * 1.2)) {war.push(`${an}'s ally ${name} avoided entering the war`); return;} const allies = states[d].diplomacy.map((r, d) => r === "Ally" ? d : 0).filter(d => d); if (allies.some(ally => defenders.includes(ally))) {war.push(`${an}'s ally ${name} did not join the war as its allies are in war on both sides`); return;}; @@ -759,7 +759,7 @@ for (const s of states) { if (list && !list.includes(s.i)) continue; const religion = pack.cells.religion[s.center]; - const theocracy = religion && pack.religions[religion].expansion === "state" || (Math.random() < .1 && pack.religions[religion].type === "Organized"); + const theocracy = religion && pack.religions[religion].expansion === "state" || (P(.1) && pack.religions[religion].type === "Organized"); s.form = theocracy ? "Theocracy" : s.type === "Naval" ? ra(navalArray) : ra(genericArray); s.formName = selectForm(s); s.fullName = getFullName(s); @@ -767,14 +767,14 @@ function selectForm(s) { const base = pack.cultures[s.culture].base; - if (s.type === "Nomadic" && Math.random() < .3) return "Horde"; // some nomadic states + if (s.type === "Nomadic" && P(.3)) return "Horde"; // some nomadic states if (s.form === "Monarchy") { const form = monarchy[expTiers[s.i]]; // Default name depends on exponent tier, some culture bases have special names for tiers if (s.diplomacy) { if (form === "Duchy" && s.neighbors.length > 1 && rand(6) < s.neighbors.length && s.diplomacy.includes("Vassal")) return "Marches"; // some vassal dutchies on borderland - if (Math.random() < .3 && s.diplomacy.includes("Vassal")) return "Protectorate"; // some vassals + if (P(.3) && s.diplomacy.includes("Vassal")) return "Protectorate"; // some vassals } if (base === 16 && (form === "Empire" || form === "Kingdom")) return "Sultanate"; // Turkic @@ -797,7 +797,7 @@ s.name = pack.burgs[s.capital].name; return "Free City"; } - if (Math.random() < .3) return "City-state"; + if (P(.3)) return "City-state"; } return rw(republic); } @@ -861,7 +861,7 @@ const center = stateBurgs[i].cell; const burg = stateBurgs[i].i; const c = stateBurgs[i].culture; - const name = Math.random() < .5 ? Names.getState(Names.getCultureShort(c), c) : stateBurgs[i].name; + const name = P(.5) ? Names.getState(Names.getCultureShort(c), c) : stateBurgs[i].name; const formName = rw(form); form[formName] += 5; const fullName = name + " " + formName; @@ -950,12 +950,12 @@ // generate "wild" province name const c = cells.culture[center]; - const name = burgCell && Math.random() < .5 ? burgs[burg].name : Names.getState(Names.getCultureShort(c), c); + const name = burgCell && P(.5) ? burgs[burg].name : Names.getState(Names.getCultureShort(c), c); const f = pack.features[cells.f[center]]; const provCells = stateNoProvince.filter(i => cells.province[i] === province); const singleIsle = provCells.length === f.cells && !provCells.find(i => cells.f[i] !== f.i); const isleGroup = !singleIsle && !provCells.find(i => pack.features[cells.f[i]].group !== "isle"); - const colony = !singleIsle && !isleGroup && Math.random() < .5 && !isPassable(s.center, center); + const colony = !singleIsle && !isleGroup && P(.5) && !isPassable(s.center, center); const formName = singleIsle ? "Island" : isleGroup ? "Islands" : colony ? "Colony" : rw(forms["Wild"]); const fullName = name + " " + formName; const color = getMixedColor(s.color); diff --git a/modules/cultures-generator.js b/modules/cultures-generator.js index 386ccc62..22823c83 100644 --- a/modules/cultures-generator.js +++ b/modules/cultures-generator.js @@ -92,7 +92,7 @@ if (cells.h[i] > 50) return "Highland"; // no penalty for hills and moutains, high for other elevations const f = pack.features[cells.f[cells.haven[i]]]; // feature if (f.type === "lake" && f.cells > 5) return "Lake" // low water cross penalty and high for non-along-coastline growth - if ((f.cells < 10 && cells.harbor[i]) || (cells.harbor[i] === 1 && Math.random() < .5)) return "Naval"; // low water cross penalty and high for non-along-coastline growth + if ((f.cells < 10 && cells.harbor[i]) || (cells.harbor[i] === 1 && P(.5))) return "Naval"; // low water cross penalty and high for non-along-coastline growth if (cells.r[i] && cells.fl[i] > 100) return "River"; // no River cross penalty, penalty for non-River growth const b = cells.biome[i]; if (b === 4 || b === 1 || b === 2) return "Nomadic"; // high penalty in forest biomes and near coastline diff --git a/modules/heightmap-generator.js b/modules/heightmap-generator.js index af152b0b..28643976 100644 --- a/modules/heightmap-generator.js +++ b/modules/heightmap-generator.js @@ -14,17 +14,17 @@ const input = document.getElementById("templateInput"); if (!locked("template")) { const templates = { - "Volcano": 5, + "Volcano": 3, "High Island": 22, - "Low Island": 10, + "Low Island": 9, "Continents": 20, - "Archipelago": 30, + "Archipelago": 25, "Mediterranean":3, "Peninsula": 3, - "Pangea": 2, + "Pangea": 5, "Isthmus": 2, "Atoll": 1, - "Shattered": 2}; + "Shattered": 7}; input.value = rw(templates); } @@ -235,7 +235,7 @@ const addHill = function(count, height, rangeX, rangeY) { count = getNumberInRange(count); const power = getBlobPower(); - while (count >= 1 || Math.random() < count) {addOneHill(); count--;} + while (count > 0) {addOneHill(); count--;} function addOneHill() { const change = new Uint8Array( cells.h.length); @@ -268,7 +268,7 @@ const addPit = function(count, height, rangeX, rangeY) { count = getNumberInRange(count); - while (count >= 1 || Math.random() < count) {addOnePit(); count--;} + while (count > 0) {addOnePit(); count--;} function addOnePit() { const used = new Uint8Array(cells.h.length); @@ -301,7 +301,7 @@ const addRange = function(count, height, rangeX, rangeY) { count = getNumberInRange(count); const power = getLinePower(); - while (count >= 1 || Math.random() < count) {addOneRange(); count--;} + while (count > 0) {addOneRange(); count--;} function addOneRange() { const used = new Uint8Array(cells.h.length); @@ -376,7 +376,7 @@ const addTrough = function(count, height, rangeX, rangeY) { count = getNumberInRange(count); const power = getLinePower(); - while (count >= 1 || Math.random() < count) {addOneTrough(); count--;} + while (count > 0) {addOneTrough(); count--;} function addOneTrough() { const used = new Uint8Array(cells.h.length); @@ -455,7 +455,7 @@ const addStrait = function(width, direction = "vertical") { width = Math.min(getNumberInRange(width), grid.cellsX/3); - if (width < 1 && Math.random() < width) return; + if (width < 1 && P(width)) return; const used = new Uint8Array(cells.h.length); const vert = direction === "vertical"; const startX = vert ? Math.floor(Math.random() * graphWidth * .4 + graphWidth * .3) : 5; diff --git a/modules/names-generator.js b/modules/names-generator.js index 0ace981e..6a3c3c4f 100644 --- a/modules/names-generator.js +++ b/modules/names-generator.js @@ -137,17 +137,17 @@ if (base === 5 && ["sk", "ev", "ov"].includes(name.slice(-2))) name = name.slice(0,-2); // remove -sk/-ev/-ov for Ruthenian else if (base === 12) return vowel(name.slice(-1)) ? name : name + "u"; // Japanese ends on any vowel or -u - else if (base === 18 && Math.random() < .4) name = vowel(name.slice(0,1).toLowerCase()) ? "Al" + name.toLowerCase() : "Al " + name; // Arabic starts with -Al + else if (base === 18 && P(.4)) name = vowel(name.slice(0,1).toLowerCase()) ? "Al" + name.toLowerCase() : "Al " + name; // Arabic starts with -Al // no suffix for fantasy bases if (base > 32 && base < 42) return name; // define if suffix should be used if (name.length > 3 && vowel(name.slice(-1))) { - if (vowel(name.slice(-2,-1)) && Math.random() < .85) name = name.slice(0,-2); // 85% for vv - else if (Math.random() < .7) name = name.slice(0,-1); // ~60% for cv + if (vowel(name.slice(-2,-1)) && P(.85)) name = name.slice(0,-2); // 85% for vv + else if (P(.7)) name = name.slice(0,-1); // ~60% for cv else return name; - } else if (Math.random() < .4) return name; // 60% for cc and vc + } else if (P(.4)) return name; // 60% for cc and vc // define suffix let suffix = "ia"; // standard suffix @@ -187,17 +187,17 @@ const getMapName = function(force) { if (!force && locked("mapName")) return; if (force && locked("mapName")) unlock("mapName"); - const base = Math.random() < .7 ? 2 : Math.random() < .5 ? rand(0, 6) : rand(0, 31); + const base = P(.7) ? 2 : P(.5) ? rand(0, 6) : rand(0, 31); if (!nameBases[base]) {tip("Namebase is not found", false, "error"); return ""}; const min = nameBases[base].min-1; const max = Math.max(nameBases[base].max-3, min); const baseName = getBase(base, min, max, "", 0); - const name = Math.random() < .7 ? addSuffix(baseName) : baseName; + const name = P(.7) ? addSuffix(baseName) : baseName; mapName.value = name; } function addSuffix(name) { - const suffix = Math.random() < .8 ? "ia" : "land"; + const suffix = P(.8) ? "ia" : "land"; if (suffix === "ia" && name.length > 6) name = name.slice(0, -(name.length-3)); else if (suffix === "land" && name.length > 6) name = name.slice(0, -(name.length-5)); return validateSuffix(name, suffix); diff --git a/modules/ocean-layers.js b/modules/ocean-layers.js index aabcdb6e..b72bc5ad 100644 --- a/modules/ocean-layers.js +++ b/modules/ocean-layers.js @@ -49,9 +49,9 @@ function randomizeOutline() { const limits = []; - let odd = 0.2 + let odd = .2 for (let l = -9; l < 0; l++) { - if (Math.random() < odd) {odd = 0.2; limits.push(l);} + if (P(odd)) {odd = .2; limits.push(l);} else {odd *= 2;} } return limits; diff --git a/modules/river-generator.js b/modules/river-generator.js index f2593984..7f4bbe0b 100644 --- a/modules/river-generator.js +++ b/modules/river-generator.js @@ -178,13 +178,13 @@ const sin = Math.sin(angle), cos = Math.cos(angle); const serpentine = 1 / (s + 1) + 0.3; const meandr = serpentine + Math.random() * rndFactor; - if (Math.random() < 0.5) side *= -1; // change meandring direction in 50% + if (P(.5)) side *= -1; // change meandring direction in 50% const dist2 = (eX - sX) ** 2 + (eY - sY) ** 2; // if dist2 is big or river is small add extra points at 1/3 and 2/3 of segment if (dist2 > 64 || (dist2 > 16 && segments.length < 6)) { const p1x = (sX * 2 + eX) / 3 + side * -sin * meandr; const p1y = (sY * 2 + eY) / 3 + side * cos * meandr; - if (Math.random() < 0.2) side *= -1; // change 2nd extra point meandring direction in 20% + if (P(.2)) side *= -1; // change 2nd extra point meandring direction in 20% const p2x = (sX + eX * 2) / 3 + side * sin * meandr; const p2y = (sY + eY * 2) / 3 + side * cos * meandr; riverEnhanced.push([p1x, p1y], [p2x, p2y]); diff --git a/modules/ui/layers.js b/modules/ui/layers.js index 27201082..cb879003 100644 --- a/modules/ui/layers.js +++ b/modules/ui/layers.js @@ -947,7 +947,7 @@ function drawCoordinates() { const desired = +coordinates.attr("data-size"); // desired label size coordinates.attr("font-size", Math.max(rn(desired / scale ** .8, 2), .1)); // actual label size - const graticule = d3.geoGraticule().extent([[mapCoordinates.lonW, mapCoordinates.latN], [mapCoordinates.lonE, mapCoordinates.latS]]) + const graticule = d3.geoGraticule().extent([[mapCoordinates.lonW, mapCoordinates.latN], [mapCoordinates.lonE+.1, mapCoordinates.latS+.1]]) .stepMajor([400, 400]).stepMinor([step, step]); const projection = d3.geoEquirectangular().fitSize([graphWidth, graphHeight], graticule()); diff --git a/modules/ui/options.js b/modules/ui/options.js index 557d8003..cd81d757 100644 --- a/modules/ui/options.js +++ b/modules/ui/options.js @@ -318,16 +318,13 @@ function randomizeOptions() { if (!locked("power")) powerInput.value = powerOutput.value = gauss(3, 2, 0, 10); if (!locked("neutral")) neutralInput.value = neutralOutput.value = rn(1 + Math.random(), 1); if (!locked("cultures")) culturesInput.value = culturesOutput.value = gauss(12, 3, 5, 30); - if (!locked("culturesSet")) culturesSet.value = ra(Array.from(culturesSet.options)).value; - changeCultureSet(); + if (!locked("culturesSet")) randomizeCultureSet(); // 'Configure World' settings if (!locked("prec")) precInput.value = precOutput.value = gauss(120, 20, 5, 500); const tMax = +temperatureEquatorOutput.max, tMin = +temperatureEquatorOutput.min; // temperature extremes if (!locked("temperatureEquator")) temperatureEquatorOutput.value = temperatureEquatorInput.value = rand(tMax-6, tMax); if (!locked("temperaturePole")) temperaturePoleOutput.value = temperaturePoleInput.value = rand(tMin, tMin+10); - if (!locked("mapSize")) mapSizeOutput.value = mapSizeInput.value = gauss(50, 20, 15, 100); - if (!locked("latitude")) latitudeOutput.value = latitudeInput.value = gauss(50, 20, 15, 100); // 'Units Editor' settings const US = navigator.language === "en-US"; @@ -338,6 +335,21 @@ function randomizeOptions() { if (!stored("temperatureScale")) temperatureScale.value = US ? "°F" : "°C"; } +// select culture set pseudo-randomly +function randomizeCultureSet() { + const sets = { + "world": 25, + "european": 20, + "oriental": 10, + "english": 10, + "antique": 5, + "highFantasy": 22, + "darkFantasy": 6, + "random": 2}; + culturesSet.value = rw(sets); + changeCultureSet(); +} + // remove all saved data from LocalStorage and reload the page function restoreDefaultOptions() { localStorage.clear(); diff --git a/modules/ui/states-editor.js b/modules/ui/states-editor.js index 7e05a4f6..40a5723b 100644 --- a/modules/ui/states-editor.js +++ b/modules/ui/states-editor.js @@ -749,8 +749,7 @@ function editStates() { const center = burgCell ? burgCell : provCells[0]; const burg = burgCell ? cells.burg[burgCell] : 0; - const name = burgCell && Math.random() < .7 - ? getAdjective(pack.burgs[burg].name) + const name = burgCell && P(.7) ? getAdjective(pack.burgs[burg].name) : getAdjective(states[state].name) + " " + provinces[initProv].name.split(" ").slice(-1)[0]; const formName = name.split(" ").length > 1 ? provinces[initProv].formName : rw(form); const fullName = name + " " + formName; diff --git a/modules/utils.js b/modules/utils.js index 77f1463f..de3c4cbe 100644 --- a/modules/utils.js +++ b/modules/utils.js @@ -217,11 +217,17 @@ function convertTemperature(c) { // random number in a range function rand(min, max) { - if (min === undefined && !max === undefined) return Math.random(); + if (min === undefined && max === undefined) return Math.random(); if (max === undefined) {max = min; min = 0;} return Math.floor(Math.random() * (max - min + 1)) + min; } +// probability shorthand +function P(probability) { + return Math.random() < probability; +} + +// random number (normal or gaussian distribution) function gauss(expected = 100, deviation = 30, min = 0, max = 300, round = 0) { return rn(Math.max(Math.min(d3.randomNormal(expected, deviation)(), max), min), round); } @@ -380,7 +386,7 @@ function getAdjective(string) { // special cases for some suffixes if (string.length > 8 && string.slice(-6) === "orszag") return string.slice(0, -6); if (string.length > 6 && string.slice(-4) === "stan") return string.slice(0, -4); - if (Math.random() < .5 && string.slice(-4) === "land") return string + "ic"; + if (P(.5) && string.slice(-4) === "land") return string + "ic"; if (string.slice(-4) === " Guo") string = string.slice(0, -4); // don't change is name ends on suffix @@ -447,9 +453,9 @@ function lim(v) { // get number from string in format "1-3" or "2" or "0.5" function getNumberInRange(r) { if (typeof r !== "string") {console.error("The value should be a string", r); return 0;} - if (!isNaN(+r)) return +r; + if (!isNaN(+r)) return ~~r + +P(r - ~~r); const sign = r[0] === "-" ? -1 : 1; - if (isNaN(+r[0])) r = r.slice(1); + if (isNaN(+r[0])) r = r.slice(1); const range = r.includes("-") ? r.split("-") : null; if (!range) {console.error("Cannot parse the number. Check the format", r); return 0;} const count = rand(range[0] * sign, +range[1]);