This commit is contained in:
Azgaar 2020-06-21 18:11:51 +03:00
parent 882d024e24
commit 2d00b3c25b
6 changed files with 111 additions and 82 deletions

View file

@ -1217,6 +1217,16 @@ div.slider .ui-slider-handle {
overflow-y: auto;
}
.overflow {
max-width: 93vw;
overflow: auto;
max-height: 75vh;
}
.overflow > div {
width: max-content;
}
div.header > div {
font-weight: bold;
font-size: .9em;
@ -1855,7 +1865,9 @@ div#notesHeader {
}
div#notesBody {
padding: 0 10px;
padding: 0 1em;
max-height: 80vh;
overflow: auto;
}
svg.button {

View file

@ -2450,7 +2450,7 @@
<button id="regimentEmblemSelect" style="padding: 0; width: 4.5em">select</button>
</div>
<div id="regimentComposition" style="padding: .1em"></div>
<div id="regimentComposition" class="table"></div>
</div>
<div id="regimentBottom">
@ -2465,7 +2465,7 @@
</div>
<div id="battleScreen" class="dialog stable" style="display: none">
<div id="battleBody">
<div id="battleBody" class="overflow>
<template id="battlePhases_field">
<button data-tip="Skirmish phase. Ranged units excel" data-phase="skirmish" class="icon-button-skirmish"></button>
<button data-tip="Melee phase. Melee units excel" data-phase="melee" class="icon-button-melee"></button>
@ -3450,16 +3450,18 @@
</div>
<div id="militaryOverview" class="dialog stable" style="display: none">
<div id="militaryHeader" class="header">
<div data-tip="State name. Click to sort" style="left:1.8em; width: 7.4em;" class="sortable alphabetically" data-sortby="state">State&nbsp;</div>
<div data-tip="Total military personnel (considering crew). Click to sort" id="militaryTotal" class="sortable icon-sort-number-down" data-sortby="total">Total&nbsp;</div>
<div data-tip="State population. Click to sort" style="width: 6.5em; margin-left: -1em" class="sortable" data-sortby="population">Population&nbsp;</div>
<div data-tip="Military personnel rate (% of state population). Depends on war alert. Click to sort" style="width: 3.7em" class="sortable" data-sortby="rate">Rate&nbsp;</div>
<div data-tip="War Alert. Modifier to military forces number, depends of political situation. Click to sort" class="sortable" data-sortby="alert">War Alert&nbsp;</div>
<div class="overflow">
<div id="militaryHeader" class="header">
<div data-tip="State name. Click to sort" style="margin-left: 1.8em; width: 5.6em" class="sortable alphabetically" data-sortby="state">State&nbsp;</div>
<div data-tip="Total military personnel (considering crew). Click to sort" id="militaryTotal" class="sortable icon-sort-number-down" data-sortby="total">Total&nbsp;</div>
<div data-tip="State population. Click to sort" style="width: 6.5em; margin-left: -1em" class="sortable" data-sortby="population">Population&nbsp;</div>
<div data-tip="Military personnel rate (% of state population). Depends on war alert. Click to sort" style="width: 3.7em" class="sortable" data-sortby="rate">Rate&nbsp;</div>
<div data-tip="War Alert. Modifier to military forces number, depends of political situation. Click to sort" class="sortable" data-sortby="alert">War Alert&nbsp;</div>
</div>
<div id="militaryBody" data-type="absolute"></div>
</div>
<div id="militaryBody" class="table" data-type="absolute"></div>
<div id="militaryFooter" class="totalLine">
<div data-tip="States number" style="margin-left: 4px">States:&nbsp;<span id="militaryFooterStates">0</span></div>
<div data-tip="Total military forces" style="margin-left: 14px">Total forces:&nbsp;<span id="militaryFooterForcesTotal">0</span></div>
@ -3480,14 +3482,16 @@
</div>
<div id="regimentsOverview" class="dialog stable" style="display: none">
<div id="regimentsHeader" class="header">
<div data-tip="State name. Click to sort" style="left:1.8em; width: 9em" class="sortable alphabetically" data-sortby="state">State&nbsp;</div>
<div data-tip="Regiment emblem and name. Click to sort by name" style="width: 12em" class="sortable alphabetically" data-sortby="name">Name&nbsp;</div>
<div data-tip="Total military personnel (not considering crew). Click to sort" style="margin-left: .8em" id="regimentsTotal" class="sortable icon-sort-number-down" data-sortby="total">Total&nbsp;</div>
<div class="overflow">
<div id="regimentsHeader" class="header">
<div data-tip="State name. Click to sort" style="left:1.8em; width: 9em" class="sortable alphabetically" data-sortby="state">State&nbsp;</div>
<div data-tip="Regiment emblem and name. Click to sort by name" style="width: 12em" class="sortable alphabetically" data-sortby="name">Name&nbsp;</div>
<div data-tip="Total military personnel (not considering crew). Click to sort" style="margin-left: .8em" id="regimentsTotal" class="sortable icon-sort-number-down" data-sortby="total">Total&nbsp;</div>
</div>
<div id="regimentsBody" data-type="absolute"></div>
</div>
<div id="regimentsBody" class="table" data-type="absolute"></div>
<div id="regimentsBottom">
<button id="regimentsOverviewRefresh" data-tip="Refresh the overview screen" class="icon-cw"></button>
<button id="regimentsPercentage" data-tip="Toggle percentage / absolute values views" class="icon-percent"></button>
@ -3498,22 +3502,24 @@
</div>
<div id="militaryOptions" class="dialog stable" style="display: none">
<table id="militaryOptionsTable">
<thead>
<tr>
<th data-tip="Unit icon">Icon</th>
<th data-tip="Unit name. If name is changed for existing unit, old unit will be replaced">Unit name</th>
<th data-tip="Conscription percentage for rural population">Rural</th>
<th data-tip="Conscription percentage for urban population">Urban</th>
<th data-tip="Average number of people in crew (used for total personnel calculation)">Crew</th>
<th data-tip="Unit military power (used for battle simulation)">Power</th>
<th data-tip="Unit type to apply special rules on forces recalculation">Type</th>
<th data-tip="Check if unit is separate and can be stacked only with units of the same type">Sep.</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="table">
<table id="militaryOptionsTable">
<thead>
<tr>
<th data-tip="Unit icon">Icon</th>
<th data-tip="Unit name. If name is changed for existing unit, old unit will be replaced">Unit name</th>
<th data-tip="Conscription percentage for rural population">Rural</th>
<th data-tip="Conscription percentage for urban population">Urban</th>
<th data-tip="Average number of people in crew (used for total personnel calculation)">Crew</th>
<th data-tip="Unit military power (used for battle simulation)">Power</th>
<th data-tip="Unit type to apply special rules on forces recalculation">Type</th>
<th data-tip="Check if unit is separate and can be stacked only with units of the same type">Sep.</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<div id="styleSaver" class="dialog stable textual" style="display: none">

View file

@ -341,7 +341,8 @@ function showWelcomeMessage() {
This version is compatible with ${changelog}, loaded <i>.map</i> files will be auto-updated.
<ul>${post}
<li>Battle simulation</li>
<li>Military forces changes (${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Military-Forces", "detailed description")})</li>
<li>Battle simulation (${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Battle-Simulator", "detailed description")})</li>
<li>Ice layer and Ice editor</li>
<li>Route and River Elevation profile</li>
<li>Image Converter enhancement</li>

View file

@ -24,7 +24,6 @@ function showEPForRiver(node) {
function showElevationProfile(data, routeLen, isRiver) {
// data is an array of cell indexes, routeLen is the distance (in actual metres/feet), isRiver should be true for rivers, false otherwise
document.getElementById("epScaleRange").addEventListener("change", draw);
document.getElementById("epCurve").addEventListener("change", draw);
document.getElementById("epSave").addEventListener("click", downloadCSV);
@ -36,30 +35,25 @@ function showElevationProfile(data, routeLen, isRiver) {
});
// prevent river graphs from showing rivers as flowing uphill - remember the general slope
var slope = 0;
let slope = 0;
if (isRiver) {
if (pack.cells.h[data[0]] < pack.cells.h[data[data.length-1]]) {
slope = 1; // up-hill
} else if (pack.cells.h[data[0]] > pack.cells.h[data[data.length-1]]) {
slope = -1; // down-hill
slope = -1; // down-hill
}
}
const chartWidth = window.innerWidth-180;
const chartHeight = 300; // height of our land/sea profile, excluding the biomes data below
const xOffset = 80;
const yOffset = 80; // this is our drawing starting point from top-left (y = 0) of SVG
const chartWidth = window.innerWidth-180, chartHeight = 300; // height of our land/sea profile, excluding the biomes data below
const xOffset = 80, yOffset = 80; // this is our drawing starting point from top-left (y = 0) of SVG
const biomesHeight = 40;
let lastBurgIndex = 0;
let lastBurgCell = 0;
let burgCount = 0;
let chartData = {biome:[], burg:[], cell:[], height:[], mi:1000000, ma:0, mih: 100, mah: 0, points:[] }
for (let i=0, prevB=0, prevH=-1; i<data.length; i++) {
let chartData = {biome:[], burg:[], cell:[], height:[], mi:1000000, ma:0, mih: 100, mah: 0, points:[]};
for (let i=0, prevB=0, prevH=-1; i <data.length; i++) {
let cell = data[i];
let h = pack.cells.h[cell];
if (h < 20) h = 20;
@ -72,7 +66,6 @@ function showElevationProfile(data, routeLen, isRiver) {
}
}
prevH = h;
// river up-hill checks stop here
let b = pack.cells.burg[cell];
if (b == prevB) b = 0;
@ -100,7 +93,7 @@ function showElevationProfile(data, routeLen, isRiver) {
function downloadCSV() {
let data = "Point,X,Y,Cell,Height,Height value,Population,Burg,Burg population,Biome,Biome color,Culture,Culture color,Religion,Religion color,Province,Province color,State,State color\n"; // headers
for (let k=0; k<chartData.points.length; k++) {
for (let k=0; k < chartData.points.length; k++) {
let cell = chartData.cell[k];
let burg = pack.cells.burg[cell];
let biome = pack.cells.biome[cell];
@ -143,26 +136,26 @@ function showElevationProfile(data, routeLen, isRiver) {
}
function draw() {
chartData.points = [];
chartData.points = [];
let heightScale = 100 / parseInt(epScaleRange.value);
heightScale *= 0.90; // curves cause the heights to go slightly higher, adjust here
heightScale *= .9; // curves cause the heights to go slightly higher, adjust here
const xscale = d3.scaleLinear().domain([0, data.length]).range([0, chartWidth]);
const yscale = d3.scaleLinear().domain([0, chartData.ma * heightScale]).range([chartHeight, 0]);
for (let i=0; i<data.length; i++) {
chartData.points.push([xscale(i) + xOffset, yscale(chartData.height[i]) + yOffset]);
}
document.getElementById("elevationGraph").innerHTML = "";
const chart = d3.select("#elevationGraph").append("svg").attr("width", chartWidth+120).attr("height", chartHeight+yOffset+biomesHeight).attr("id", "elevationSVG").attr("class", "epbackground");
// arrow-head definition
chart.append("defs").append("marker").attr("id", "arrowhead").attr("orient", "auto").attr("markerWidth", "2").attr("markerHeight", "4").attr("refX", "0.1").attr("refY", "2").append("path").attr("d", "M0,0 V4 L2,2 Z").attr("fill", "darkgray");
let colors = getColorScheme();
var landdef = chart.select("defs").append("linearGradient").attr("id", "landdef").attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%");
const landdef = chart.select("defs").append("linearGradient").attr("id", "landdef").attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%");
if (chartData.mah == chartData.mih) {
landdef.append("stop").attr("offset", "0%").attr("style", "stop-color:" + getColor(chartData.mih, colors) + ";stop-opacity:1");
@ -173,7 +166,7 @@ function showElevationProfile(data, routeLen, isRiver) {
landdef.append("stop").attr("offset", perc*100 + "%").attr("style", "stop-color:" + getColor(k, colors) + ";stop-opacity:1");
}
}
// land
let curve = d3.line().curve(d3.curveBasis); // see https://github.com/d3/d3-shape#curves
let epCurveIndex = parseInt(epCurve.selectedIndex);
@ -185,35 +178,35 @@ function showElevationProfile(data, routeLen, isRiver) {
case 4 : curve = d3.line().curve(d3.curveMonotoneX); break;
case 5 : curve = d3.line().curve(d3.curveNatural); break;
}
// copy the points so that we can add extra straight pieces, else we get curves at the ends of the chart
let extra = chartData.points.slice();
var path = curve(extra);
let path = curve(extra);
// this completes the right-hand side and bottom of our land "polygon"
path += " L" + parseInt(xscale(extra.length) + +xOffset) + "," + parseInt(extra[extra.length-1][1]);
path += " L" + parseInt(xscale(extra.length) + +xOffset) + "," + parseInt(yscale(0) + +yOffset);
path += " L" + parseInt(xscale(0) + +xOffset) +"," + parseInt(yscale(0) + +yOffset);
path += "Z";
chart.append("g").attr("id", "epland").append("path").attr("d", path).attr("stroke", "purple").attr("stroke-width", "0").attr("fill", "url(#landdef)");
path += "Z";
chart.append("g").attr("id", "epland").append("path").attr("d", path).attr("stroke", "purple").attr("stroke-width", "0").attr("fill", "url(#landdef)");
// biome / heights
let g = chart.append("g").attr("id", "epbiomes");
const hu = heightUnit.value;
for(var k=0; k<chartData.points.length; k++) {
for(let k=0; k < chartData.points.length; k++) {
const x = chartData.points[k][0];
const y = yOffset + chartHeight;
const c = biomesData.color[chartData.biome[k]];
const dataTip = biomesData.name[chartData.biome[k]]+" (" + chartData.height[k] + " " + hu + ", cell " + chartData.cell[k] + ")";
g.append("rect").attr("stroke", c).attr("fill", c).attr("x", x).attr("y", y).attr("width", xscale(1)).attr("height", 15).attr("data-tip", dataTip);
}
const xAxis = d3.axisBottom(xscale).ticks(10).tickFormat(function(d){ return (rn(d / chartData.points.length * routeLen) + " " + distanceUnitInput.value);});
const yAxis = d3.axisLeft(yscale).ticks(5).tickFormat(function(d) { return d + " " + hu; });
const xGrid = d3.axisBottom(xscale).ticks(10).tickSize(-chartHeight).tickFormat("");
const yGrid = d3.axisLeft(yscale).ticks(5).tickSize(-chartWidth).tickFormat("");
chart.append("g")
.attr("id", "epxaxis")
.attr("transform", "translate(" + xOffset + "," + parseInt(chartHeight + +yOffset + 20) + ")")
@ -223,12 +216,12 @@ function showElevationProfile(data, routeLen, isRiver) {
.attr("transform", function(d) {
return "rotate(0)" // used to rotate labels, - anti-clockwise, + clockwise
});
chart.append("g")
.attr("id", "epyaxis")
.attr("transform", "translate(" + parseInt(+xOffset-10) + "," + parseInt(+yOffset) + ")")
.call(yAxis);
// add the X gridlines
chart.append("g")
.attr("id", "epxgrid")
@ -236,38 +229,36 @@ function showElevationProfile(data, routeLen, isRiver) {
.attr("stroke-dasharray", "4 1")
.attr("transform", "translate(" + xOffset + "," + parseInt(chartHeight + +yOffset) + ")")
.call(xGrid);
// add the Y gridlines
chart.append("g")
chart.append("g")
.attr("id", "epygrid")
.attr("class", "epgrid")
.attr("stroke-dasharray", "4 1")
.attr("transform", "translate(" + xOffset + "," + yOffset + ")")
.call(yGrid);
// draw city labels - try to avoid putting labels over one another
// draw city labels - try to avoid putting labels over one another
g = chart.append("g").attr("id", "epburglabels");
var y1 = 0;
var add = 15;
let y1 = 0;
const add = 15;
let xwidth = chartData.points[1][0] - chartData.points[0][0];
for (let k=0; k<chartData.points.length; k++)
{
if (chartData.burg[k] > 0) {
let b = chartData.burg[k];
let x1 = chartData.points[k][0]; // left side of graph by default
if (k > 0) x1 += xwidth/2; // center it if not first
if (k == chartData.points.length-1)
x1 = chartWidth + xOffset; // right part of graph
if (k == chartData.points.length-1) x1 = chartWidth + xOffset; // right part of graph
y1+=add;
if (y1 >= yOffset) { y1 = add; }
var d1 = 0;
if (y1 >= yOffset) y1 = add;
// burg name
g.append("text").attr("id", "ep" + b).attr("class", "epburglabel").attr("x", x1).attr("y", y1).attr("text-anchor", "middle");
document.getElementById("ep" + b).innerHTML = pack.burgs[b].name;
// arrow from burg name to graph line
g.append("path").attr("id", "eparrow" + b).attr("d", "M" + x1.toString() + "," + (y1+3).toString() + "L" + x1.toString() + "," + parseInt(chartData.points[k][1]-3).toString()).attr("stroke", "darkgray").attr("fill", "lightgray").attr("stroke-width", "1").attr("marker-end", "url(#arrowhead)");
}
@ -284,4 +275,3 @@ function showElevationProfile(data, routeLen, isRiver) {
modules.elevation = false;
}
}

View file

@ -71,7 +71,7 @@ document.getElementById("options").querySelector("div.tab").addEventListener("cl
// show popup with a list of Patreon supportes (updated manually, to be replaced with API call)
function showSupporters() {
const supporters = "Aaron Meyer, Ahmad Amerih, AstralJacks, aymeric, Billy Dean Goehring, Branndon Edwards, Chase Mayers, Curt Flood, cyninge, Dino Princip, E.M. White, es, Fondue, Fritjof Olsson, Gatsu, Johan Fröberg, Jonathan Moore, Joseph Miranda, Kate, KC138, Luke Nelson, Markus Finster, Massimo Vella, Mikey, Nathan Mitchell, Paavi1, Pat, Ryan Westcott, Sasquatch, Shawn Spencer, Sizz_TV, Timothée CALLET, UTG community, Vlad Tomash, Wil Sisney, William Merriott, Xariun, Gun Metal Games, Scott Marner, Spencer Sherman, Valerii Matskevych, Alloyed Clavicle, Stewart Walsh, Ruthlyn Mollett (Javan), Benjamin Mair-Pratt, Diagonath, Alexander Thomas, Ashley Wilson-Savoury, William Henry, Preston Brooks, JOSHUA QUALTIERI, Hilton Williams, Katharina Haase, Hisham Bedri, Ian arless, Karnat, Bird, Kevin, Jessica Thomas, Steve Hyatt, Logicspren, Alfred García, Jonathan Killstring, John Ackley, Invad3r233, Norbert Žigmund, Jennifer, PoliticsBuff, _gfx_, Maggie, Connor McMartin, Jared McDaris, BlastWind, Franc Casanova Ferrer, Dead & Devil, Michael Carmody, Valerie Elise, naikibens220, Jordon Phillips, William Pucs, The Dungeon Masters, Brady R Rathbun, J, Shadow, Matthew Tiffany, Huw Williams, Joseph Hamilton, FlippantFeline, Tamashi Toh, kms, Stephen Herron, MidnightMoon, Whakomatic x, Barished, Aaron bateson, Brice Moss, Diklyquill, PatronUser, Michael Greiner, Steven Bennett, Jacob Harrington, Miguel C., Reya C., Giant Monster Games, Noirbard, Brian Drennen, Ben Craigie, Alex Smolin, Endwords, Joshua E Goodwin, SirTobit , Allen S. Rout, Allen Bull Bear, Pippa Mitchell, R K, G0atfather, Ryan Lege, Caner Oleas Pekgönenç, Bradley Edwards, Tertiary , Austin Miller, Jesse Holmes, Jan Dvořák, Marten F, Erin D. Smale";
const supporters = "Aaron Meyer, Ahmad Amerih, AstralJacks, aymeric, Billy Dean Goehring, Branndon Edwards, Chase Mayers, Curt Flood, cyninge, Dino Princip, E.M. White, es, Fondue, Fritjof Olsson, Gatsu, Johan Fröberg, Jonathan Moore, Joseph Miranda, Kate, KC138, Luke Nelson, Markus Finster, Massimo Vella, Mikey, Nathan Mitchell, Paavi1, Pat, Ryan Westcott, Sasquatch, Shawn Spencer, Sizz_TV, Timothée CALLET, UTG community, Vlad Tomash, Wil Sisney, William Merriott, Xariun, Gun Metal Games, Scott Marner, Spencer Sherman, Valerii Matskevych, Alloyed Clavicle, Stewart Walsh, Ruthlyn Mollett (Javan), Benjamin Mair-Pratt, Diagonath, Alexander Thomas, Ashley Wilson-Savoury, William Henry, Preston Brooks, JOSHUA QUALTIERI, Hilton Williams, Katharina Haase, Hisham Bedri, Ian arless, Karnat, Bird, Kevin, Jessica Thomas, Steve Hyatt, Logicspren, Alfred García, Jonathan Killstring, John Ackley, Invad3r233, Norbert Žigmund, Jennifer, PoliticsBuff, _gfx_, Maggie, Connor McMartin, Jared McDaris, BlastWind, Franc Casanova Ferrer, Dead & Devil, Michael Carmody, Valerie Elise, naikibens220, Jordon Phillips, William Pucs, The Dungeon Masters, Brady R Rathbun, J, Shadow, Matthew Tiffany, Huw Williams, Joseph Hamilton, FlippantFeline, Tamashi Toh, kms, Stephen Herron, MidnightMoon, Whakomatic x, Barished, Aaron bateson, Brice Moss, Diklyquill, PatronUser, Michael Greiner, Steven Bennett, Jacob Harrington, Miguel C., Reya C., Giant Monster Games, Noirbard, Brian Drennen, Ben Craigie, Alex Smolin, Endwords, Joshua E Goodwin, SirTobit , Allen S. Rout, Allen Bull Bear, Pippa Mitchell, R K, G0atfather, Ryan Lege, Caner Oleas Pekgönenç, Bradley Edwards, Tertiary , Austin Miller, Jesse Holmes, Jan Dvořák, Marten F, Erin D. Smale, Maxwell Hill, Drunken_Legends, rob bee, Jesse Holmes, YYako, Detocroix";
alertMessage.innerHTML = "<ul style='column-count: 3; column-gap: 2em'>" + supporters.split(", ").sort().map(n => `<li>${n}</li>`).join("") + "</ul>";
$("#alert").dialog({resizable: false, title: "Patreon Supporters", width: "30vw", position: {my: "center", at: "center", of: "svg"}});
}

View file

@ -446,11 +446,31 @@ function editStates() {
pack.cells.province.forEach((pr, i) => {if(pr === p) pack.cells.province[i] = 0;});
});
// remove military
pack.states[state].military.forEach(m => {
const id = `regiment${state}-${m.i}`;
const index = notes.findIndex(n => n.id === id);
if (index != -1) notes.splice(index, 1);
});
armies.select("g#army"+state).remove();
const military = pack.states[elSelected.dataset.state].military;
const regIndex = military.indexOf(regiment());
if (regIndex === -1) return;
military.splice(regIndex, 1);
const index = notes.findIndex(n => n.id === elSelected.id);
if (index != -1) notes.splice(index, 1);
elSelected.remove();
const capital = pack.states[state].capital;
pack.burgs[capital].capital = 0;
pack.burgs[capital].state = 0;
moveBurgToGroup(capital, "towns");
// clean state object
pack.states[state].military = [];
debug.selectAll(".highlight").remove();
if (!layerIsOn("toggleStates")) toggleStates(); else drawStates();
if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders();