mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
v1.0.47
This commit is contained in:
parent
5320279289
commit
729d91c053
11 changed files with 557 additions and 193 deletions
|
|
@ -106,7 +106,7 @@
|
|||
const score = new Int16Array(cells.s.map(s => s * gauss(1,3,0,20,3))); // a bit randomized cell score for towns placement
|
||||
const sorted = cells.i.filter(i => !cells.burg[i] && score[i] > 0 && cells.culture[i]).sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes
|
||||
|
||||
const desiredNumber = manorsInput.value == 1000 ? rn(sorted.length / 8 / densityInput.value ** .8) : manorsInput.valueAsNumber;
|
||||
const desiredNumber = manorsInput.value == 1000 ? rn(sorted.length / 8 / (grid.points.length / 10000) ** .8) : manorsInput.valueAsNumber;
|
||||
const burgsNumber = Math.min(desiredNumber, sorted.length); // towns to generate
|
||||
let burgsAdded = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,13 +18,14 @@ function clicked() {
|
|||
const el = d3.event.target;
|
||||
if (!el || !el.parentElement || !el.parentElement.parentElement) return;
|
||||
const parent = el.parentElement, grand = parent.parentElement;
|
||||
if (parent.id === "rivers") editRiver(); else
|
||||
if (grand.id === "routes") editRoute(); else
|
||||
if (el.tagName === "tspan" && grand.parentNode.parentNode.id === "labels") editLabel(); else
|
||||
if (grand.id === "burgLabels") editBurg(); else
|
||||
if (grand.id === "burgIcons") editBurg(); else
|
||||
if (parent.id === "terrain") editReliefIcon(); else
|
||||
if (parent.id === "markers") editMarker();
|
||||
if (parent.id === "rivers") editRiver();
|
||||
else if (grand.id === "routes") editRoute();
|
||||
else if (el.tagName === "tspan" && grand.parentNode.parentNode.id === "labels") editLabel();
|
||||
else if (grand.id === "burgLabels") editBurg();
|
||||
else if (grand.id === "burgIcons") editBurg();
|
||||
else if (parent.id === "terrain") editReliefIcon();
|
||||
else if (parent.id === "markers") editMarker();
|
||||
// else if (grand.id === "lakes") editLake();
|
||||
}
|
||||
|
||||
// clear elSelected variable
|
||||
|
|
|
|||
86
modules/ui/lakes-editor.js
Normal file
86
modules/ui/lakes-editor.js
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
"use strict";
|
||||
function editLake() {
|
||||
if (customization) return;
|
||||
closeDialogs(".stable");
|
||||
|
||||
$("#routeEditor").dialog({
|
||||
title: "Edit Route", resizable: false,
|
||||
position: {my: "center top+20", at: "top", of: d3.event, collision: "fit"},
|
||||
close: closeLakesEditor
|
||||
});
|
||||
|
||||
const node = d3.event.target;
|
||||
debug.append("g").attr("id", "controlPoints");
|
||||
elSelected = d3.select(node).on("click", addInterimControlPoint);
|
||||
drawControlPoints(node);
|
||||
//selectRouteGroup(elSelected.node());
|
||||
|
||||
if (modules.editLake) return;
|
||||
modules.editLake = true;
|
||||
|
||||
// add listeners
|
||||
|
||||
|
||||
function drawControlPoints(node) {
|
||||
const l = node.getTotalLength();
|
||||
const increment = l / Math.ceil(l / 10);
|
||||
for (let i=0; i <= l; i += increment) {addControlPoint(node.getPointAtLength(i));}
|
||||
}
|
||||
|
||||
function addControlPoint(point) {
|
||||
debug.select("#controlPoints").append("circle")
|
||||
.attr("cx", point.x).attr("cy", point.y).attr("r", .8)
|
||||
.call(d3.drag().on("drag", dragControlPoint))
|
||||
//.on("click", clickControlPoint);
|
||||
}
|
||||
|
||||
function addInterimControlPoint() {
|
||||
const point = d3.mouse(this);
|
||||
|
||||
const dists = [];
|
||||
debug.select("#controlPoints").selectAll("circle").each(function() {
|
||||
const x = +this.getAttribute("cx");
|
||||
const y = +this.getAttribute("cy");
|
||||
dists.push((point[0] - x) ** 2 + (point[1] - y) ** 2);
|
||||
});
|
||||
|
||||
let index = dists.length;
|
||||
if (dists.length > 1) {
|
||||
const sorted = dists.slice(0).sort((a, b) => a-b);
|
||||
const closest = dists.indexOf(sorted[0]);
|
||||
const next = dists.indexOf(sorted[1]);
|
||||
if (closest <= next) index = closest+1; else index = next+1;
|
||||
}
|
||||
|
||||
const before = ":nth-child(" + (index + 1) + ")";
|
||||
debug.select("#controlPoints").insert("circle", before)
|
||||
.attr("cx", point[0]).attr("cy", point[1]).attr("r", .8)
|
||||
.call(d3.drag().on("drag", dragControlPoint))
|
||||
.on("click", clickControlPoint);
|
||||
|
||||
redrawLake();
|
||||
}
|
||||
|
||||
function dragControlPoint() {
|
||||
this.setAttribute("cx", d3.event.x);
|
||||
this.setAttribute("cy", d3.event.y);
|
||||
redrawLake();
|
||||
}
|
||||
|
||||
function redrawLake() {
|
||||
lineGen.curve(d3.curveCatmullRom.alpha(.1));
|
||||
const points = [];
|
||||
debug.select("#controlPoints").selectAll("circle").each(function() {
|
||||
points.push([this.getAttribute("cx"), this.getAttribute("cy")]);
|
||||
});
|
||||
|
||||
elSelected.attr("d", round(lineGen(points)));
|
||||
}
|
||||
|
||||
function closeLakesEditor() {
|
||||
elSelected.on("click", null);
|
||||
clearMainTip();
|
||||
debug.select("#controlPoints").remove();
|
||||
unselect();
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ function editProvinces() {
|
|||
if (cl.contains("zoneFill")) changeFill(el); else
|
||||
if (cl.contains("icon-fleur")) provinceOpenCOA(p); else
|
||||
if (cl.contains("icon-star-empty")) capitalZoomIn(p); else
|
||||
if (cl.contains("icon-flag-empty")) declareProvinceIndependence(p); else
|
||||
if (cl.contains("icon-pin")) focusOn(p, cl); else
|
||||
if (cl.contains("icon-trash-empty")) removeProvince(p);
|
||||
if (cl.contains("hoverButton") && cl.contains("stateName")) regenerateName(p, line); else
|
||||
|
|
@ -106,6 +107,7 @@ function editProvinces() {
|
|||
|
||||
const stateName = pack.states[p.state].name;
|
||||
const capital = p.burg ? pack.burgs[p.burg].name : '';
|
||||
const separable = p.burg && p.burg !== pack.states[p.state].capital;
|
||||
const focused = defs.select("#fog #focusProvince"+p.i).size();
|
||||
lines += `<div class="states" data-id=${p.i} data-name=${p.name} data-form=${p.formName} data-color="${p.color}" data-capital="${capital}" data-state="${stateName}" data-area=${area} data-population=${population}>
|
||||
<svg data-tip="Province fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${p.color}" class="zoneFill"></svg>
|
||||
|
|
@ -121,6 +123,7 @@ function editProvinces() {
|
|||
<div data-tip="Province area" class="biomeArea hide">${si(area) + unit}</div>
|
||||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||
<div data-tip="${populationTip}" class="culturePopulation hide">${si(population)}</div>
|
||||
<span data-tip="Declare province independence" class="icon-flag-empty ${separable ? '' : 'placeholder'} hide"></span>
|
||||
<span data-tip="Toggle province focus" class="icon-pin ${focused?'':' inactive'} hide"></span>
|
||||
<span data-tip="Remove the province" class="icon-trash-empty hide"></span>
|
||||
</div>`;
|
||||
|
|
@ -202,6 +205,67 @@ function editProvinces() {
|
|||
zoomTo(x, y, 8, 2000);
|
||||
}
|
||||
|
||||
function declareProvinceIndependence(p) {
|
||||
const states = pack.states, provinces = pack.provinces, cells = pack.cells;
|
||||
const oldState = pack.provinces[p].state;
|
||||
const newState = pack.states.length;
|
||||
|
||||
// turn burg into a capital
|
||||
const burg = provinces[p].burg;
|
||||
if (!burg) return;
|
||||
pack.burgs[burg].capital = true;
|
||||
pack.burgs[burg].state = newState;
|
||||
moveBurgToGroup(burg, "cities");
|
||||
|
||||
// difine new state attributes
|
||||
const center = provinces[p].center;
|
||||
const culture = cells.culture[center];
|
||||
const name = provinces[p].name;
|
||||
const color = getRandomColor();
|
||||
|
||||
// update cells
|
||||
cells.i.filter(i => cells.province[i] === p).forEach(i => {
|
||||
cells.province[i] = 0;
|
||||
cells.state[i] = newState;
|
||||
});
|
||||
|
||||
// update diplomacy and reverse relations
|
||||
const diplomacy = states.map(s => {
|
||||
if (!s.i) return "x";
|
||||
let relations = states[oldState].diplomacy[s.i]; // relations between Nth state and old overlord
|
||||
if (s.i === oldState) relations = "Enemy"; // new state is Enemy to its old overlord
|
||||
else if (relations === "Ally") relations = "Suspicion";
|
||||
else if (relations === "Friendly") relations = "Suspicion";
|
||||
else if (relations === "Suspicion") relations = "Neutral";
|
||||
else if (relations === "Enemy") relations = "Friendly";
|
||||
else if (relations === "Rival") relations = "Friendly";
|
||||
else if (relations === "Vassal") relations = "Suspicion";
|
||||
else if (relations === "Suzerain") relations = "Enemy";
|
||||
s.diplomacy.push(relations);
|
||||
return relations;
|
||||
});
|
||||
diplomacy.push("x");
|
||||
states[0].diplomacy.push([`Independance declaration`, `${name} declared its independance from ${states[oldState].name}`]);
|
||||
|
||||
// create new state
|
||||
states.push({i:newState, name, diplomacy, provinces:[], color, expansionism:.5, capital:burg, type:"Generic", center, culture});
|
||||
BurgsAndStates.collectStatistics();
|
||||
BurgsAndStates.defineStateForms([newState]);
|
||||
|
||||
if (layerIsOn("toggleProvinces")) toggleProvinces();
|
||||
if (!layerIsOn("toggleStates")) toggleStates(); else drawStates();
|
||||
if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders();
|
||||
BurgsAndStates.drawStateLabels([newState, oldState]);
|
||||
|
||||
// remove old province
|
||||
unfocus(p);
|
||||
if (states[oldState].provinces.includes(p)) states[oldState].provinces.splice(states[oldState].provinces.indexOf(p), 1);
|
||||
provinces[p].removed = true;
|
||||
|
||||
closeDialogs();
|
||||
editStates();
|
||||
}
|
||||
|
||||
function focusOn(p, cl) {
|
||||
const inactive = cl.contains("inactive");
|
||||
cl.toggle("inactive");
|
||||
|
|
|
|||
|
|
@ -643,7 +643,7 @@ function editStates() {
|
|||
const provCells = cells.i.filter(i => cells.province[i] === p);
|
||||
const provStates = [...new Set(provCells.map(i => cells.state[i]))];
|
||||
|
||||
// assign province its center owner; if center is neutral, remove province
|
||||
// assign province to its center owner; if center is neutral, remove province
|
||||
const owner = cells.state[provinces[p].center];
|
||||
if (owner) {
|
||||
const name = provinces[p].name;
|
||||
|
|
@ -715,46 +715,73 @@ function editStates() {
|
|||
}
|
||||
|
||||
function addState() {
|
||||
const states = pack.states, burgs = pack.burgs, cells = pack.cells;
|
||||
const point = d3.mouse(this);
|
||||
const center = findCell(point[0], point[1]);
|
||||
if (pack.cells.h[center] < 20) {tip("You cannot place state into the water. Please click on a land cell", false, "error"); return;}
|
||||
let burg = pack.cells.burg[center];
|
||||
if (burg && pack.burgs[burg].capital) {tip("Existing capital cannot be selected as a new state capital! Select other cell", false, "error"); return;}
|
||||
if (cells.h[center] < 20) {tip("You cannot place state into the water. Please click on a land cell", false, "error"); return;}
|
||||
let burg = cells.burg[center];
|
||||
if (burg && burgs[burg].capital) {tip("Existing capital cannot be selected as a new state capital! Select other cell", false, "error"); return;}
|
||||
if (!burg) burg = addBurg(point); // add new burg
|
||||
|
||||
const oldState = cells.state[center];
|
||||
const newState = states.length;
|
||||
|
||||
// turn burg into a capital
|
||||
pack.burgs[burg].capital = true;
|
||||
pack.burgs[burg].state = pack.states.length;
|
||||
burgs[burg].capital = true;
|
||||
burgs[burg].state = newState;
|
||||
moveBurgToGroup(burg, "cities");
|
||||
|
||||
if (d3.event.shiftKey === false) exitAddStateMode();
|
||||
|
||||
const i = pack.states.length;
|
||||
const culture = pack.cells.culture[center];
|
||||
const basename = center%5 === 0 ? pack.burgs[burg].name : Names.getCulture(culture);
|
||||
const culture = cells.culture[center];
|
||||
const basename = center%5 === 0 ? burgs[burg].name : Names.getCulture(culture);
|
||||
const name = Names.getState(basename, culture);
|
||||
const color = d3.color(d3.scaleSequential(d3.interpolateRainbow)(Math.random())).hex();
|
||||
const diplomacy = pack.states.map(s => s.i ? "Neutral" : "x")
|
||||
diplomacy.push("x");
|
||||
pack.states.forEach(s => {if (s.i) {s.diplomacy.push("Neutral");}});
|
||||
const provinces = [];
|
||||
const color = getRandomColor();
|
||||
|
||||
const affected = [pack.states.length, pack.cells.state[center]];
|
||||
// update diplomacy and reverse relations
|
||||
const diplomacy = states.map(s => {
|
||||
if (!s.i) return "x";
|
||||
if (!oldState) {
|
||||
s.diplomacy.push("Neutral");
|
||||
return "Neutral";
|
||||
}
|
||||
|
||||
pack.cells.state[center] = pack.states.length;
|
||||
pack.cells.c[center].forEach(c => {
|
||||
if (pack.cells.h[c] < 20) return;
|
||||
if (pack.cells.burg[c]) return;
|
||||
affected.push(pack.cells.state[c]);
|
||||
pack.cells.state[c] = pack.states.length;
|
||||
let relations = states[oldState].diplomacy[s.i]; // relations between Nth state and old overlord
|
||||
if (s.i === oldState) relations = "Enemy"; // new state is Enemy to its old overlord
|
||||
else if (relations === "Ally") relations = "Suspicion";
|
||||
else if (relations === "Friendly") relations = "Suspicion";
|
||||
else if (relations === "Suspicion") relations = "Neutral";
|
||||
else if (relations === "Enemy") relations = "Friendly";
|
||||
else if (relations === "Rival") relations = "Friendly";
|
||||
else if (relations === "Vassal") relations = "Suspicion";
|
||||
else if (relations === "Suzerain") relations = "Enemy";
|
||||
s.diplomacy.push(relations);
|
||||
return relations;
|
||||
});
|
||||
pack.states.push({i, name, diplomacy, provinces, color, expansionism:.5, capital:burg, type:"Generic", center, culture});
|
||||
BurgsAndStates.collectStatistics();
|
||||
BurgsAndStates.defineStateForms([i]);
|
||||
diplomacy.push("x");
|
||||
states[0].diplomacy.push([`Independance declaration`, `${name} declared its independance from ${states[oldState].name}`]);
|
||||
|
||||
const affectedStates = [newState, oldState];
|
||||
const affectedProvinces = [cells.province[center]];
|
||||
cells.state[center] = newState;
|
||||
cells.province[center] = 0;
|
||||
cells.c[center].forEach(c => {
|
||||
if (cells.h[c] < 20) return;
|
||||
if (cells.burg[c]) return;
|
||||
affectedStates.push(cells.state[c]);
|
||||
affectedProvinces.push(cells.province[c]);
|
||||
cells.state[c] = newState;
|
||||
cells.province[c] = 0;
|
||||
});
|
||||
states.push({i:newState, name, diplomacy, provinces:[], color, expansionism:.5, capital:burg, type:"Generic", center, culture});
|
||||
BurgsAndStates.collectStatistics();
|
||||
BurgsAndStates.defineStateForms([newState]);
|
||||
adjustProvinces([...new Set(affectedProvinces)]);
|
||||
|
||||
if (layerIsOn("toggleProvinces")) toggleProvinces();
|
||||
if (!layerIsOn("toggleStates")) toggleStates(); else drawStates();
|
||||
if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders();
|
||||
BurgsAndStates.drawStateLabels(affected);
|
||||
BurgsAndStates.drawStateLabels([...new Set(affectedStates)]);
|
||||
statesEditorAddLines();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,8 @@ function processFeatureRegeneration(button) {
|
|||
if (button === "regenerateStates") regenerateStates(); else
|
||||
if (button === "regenerateProvinces") regenerateProvinces(); else
|
||||
if (button === "regenerateReligions") regenerateReligions(); else
|
||||
if (button === "regenerateMarkers") regenerateMarkers();
|
||||
if (button === "regenerateMarkers") regenerateMarkers(); else
|
||||
if (button === "regenerateZones") regenerateZones();
|
||||
}
|
||||
|
||||
function regenerateRivers() {
|
||||
|
|
@ -94,7 +95,7 @@ function regenerateBurgs() {
|
|||
|
||||
const score = new Int16Array(cells.s.map(s => s * Math.random())); // cell score for capitals placement
|
||||
const sorted = cells.i.filter(i => score[i] > 0 && cells.culture[i]).sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes
|
||||
const burgsCount = manorsInput.value == 1000 ? rn(sorted.length / 10 / densityInput.value ** .8) + states.length : +manorsInput.value + states.length;
|
||||
const burgsCount = manorsInput.value == 1000 ? rn(sorted.length / 10 / (grid.points.length / 10000) ** .8) + states.length : +manorsInput.value + states.length;
|
||||
const spacing = (graphWidth + graphHeight) / 200 / (burgsCount / 500); // base min distance between towns
|
||||
|
||||
for (let i=0; i < sorted.length && burgs.length < burgsCount; i++) {
|
||||
|
|
@ -219,6 +220,12 @@ function regenerateMarkers() {
|
|||
addMarkers(gauss(1, .5, .3, 5, 2));
|
||||
}
|
||||
|
||||
function regenerateZones() {
|
||||
zones.selectAll("g").remove(); // remove existing zones
|
||||
addZones(gauss(1, .5, .6, 5, 2));
|
||||
if (document.getElementById("zonesEditorRefresh").offsetParent) zonesEditorRefresh.click();
|
||||
}
|
||||
|
||||
function unpressClickToAddButton() {
|
||||
addFeature.querySelectorAll("button.pressed").forEach(b => b.classList.remove("pressed"));
|
||||
restoreDefaultEvents();
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ function editZones() {
|
|||
});
|
||||
|
||||
// add listeners
|
||||
document.getElementById("zonesEditorRefresh").addEventListener("click", zonesEditorAddLines);
|
||||
document.getElementById("zonesEditStyle").addEventListener("click", () => editStyle("zones"));
|
||||
document.getElementById("zonesLegend").addEventListener("click", toggleLegend);
|
||||
document.getElementById("zonesPercentage").addEventListener("click", togglePercentageMode);
|
||||
|
|
|
|||
|
|
@ -388,6 +388,11 @@ function getAdjective(string) {
|
|||
if (Math.random() < .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
|
||||
if (string.slice(-2) === "an") return string;
|
||||
if (string.slice(-3) === "ese") return string;
|
||||
if (string.slice(-1) === "i") return string;
|
||||
|
||||
const end = string.slice(-1); // last letter of string
|
||||
if (end === "a") return string += "n";
|
||||
if (end === "i") return string += "an";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue