mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
v1.4.01
This commit is contained in:
parent
e7b4d0e39a
commit
72d124d95d
26 changed files with 713 additions and 498 deletions
50
modules/ui/battle-screen.js
Normal file
50
modules/ui/battle-screen.js
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
"use strict";
|
||||
function showBattleScreen(attacker, defender) {
|
||||
if (customization) return;
|
||||
closeDialogs(".stable");
|
||||
|
||||
const battle = {name:"Battle", attackers:[attacker], defenders:[defender]};
|
||||
const battleAttackers = document.getElementById("battleAttackers");
|
||||
const battleDefenders = document.getElementById("battleDefenders");
|
||||
addHeaders();
|
||||
addRegiment(battleAttackers, attacker);
|
||||
addRegiment(battleDefenders, defender);
|
||||
|
||||
$("#battleScreen").dialog({
|
||||
title: battle.name, resizable: false, width: fitContent(), close: closeBattleScreen,
|
||||
position: {my: "center", at: "center", of: "#map"}
|
||||
});
|
||||
|
||||
if (modules.showBattleScreen) return;
|
||||
modules.showBattleScreen = true;
|
||||
|
||||
// add listeners
|
||||
//document.getElementById("regimentNameRestore").addEventListener("click", restoreName);
|
||||
|
||||
function addHeaders() {
|
||||
document.getElementById("battleScreen").querySelectorAll("th").forEach(el => el.remove());
|
||||
const attackers = battleAttackers.querySelector("tr");
|
||||
const defenders = battleDefenders.querySelector("tr");
|
||||
let headers = "<th></th>";
|
||||
|
||||
for (const u of options.military) {
|
||||
const label = capitalize(u.name.replace(/_/g, ' '));
|
||||
headers += `<th data-tip="${label}">${u.icon}</th>`;
|
||||
}
|
||||
|
||||
headers += "<th>Total</th>";
|
||||
attackers.insertAdjacentHTML("beforebegin", headers);
|
||||
defenders.insertAdjacentHTML("beforebegin", headers);
|
||||
}
|
||||
|
||||
function addRegiment(div, regiment) {
|
||||
const reg = document.createElement("div");
|
||||
reg.innerHTML = regiment.name;
|
||||
div.append(reg);
|
||||
}
|
||||
|
||||
function closeBattleScreen() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -215,6 +215,8 @@ function editBiomes() {
|
|||
|
||||
function addCustomBiome() {
|
||||
const b = biomesData, i = biomesData.i.length;
|
||||
if (i > 254) {tip("Maximum number of biomes reached (255), data cleansing is required", false, "error"); return;}
|
||||
|
||||
b.i.push(i);
|
||||
b.color.push(getRandomColor());
|
||||
b.habitability.push(50);
|
||||
|
|
|
|||
|
|
@ -294,13 +294,14 @@ function editBurg(id) {
|
|||
|
||||
function openMFCG(seed) {
|
||||
if (!seed && burg.MFCGlink) {openURL(burg.MFCGlink); return;}
|
||||
const cells = pack.cells;
|
||||
const name = elSelected.text();
|
||||
const size = Math.max(Math.min(rn(burg.population), 65), 6);
|
||||
|
||||
const s = burg.MFCG || defSeed;
|
||||
const cell = burg.cell;
|
||||
const hub = +pack.cells.road[cell] > 50;
|
||||
const river = pack.cells.r[cell] ? 1 : 0;
|
||||
const hub = +cells.road[cell] > 50;
|
||||
const river = cells.r[cell] ? 1 : 0;
|
||||
|
||||
const coast = +burg.port;
|
||||
const citadel = +burg.citadel;
|
||||
|
|
@ -309,8 +310,31 @@ function editBurg(id) {
|
|||
const temple = +burg.temple;
|
||||
const shanty = +burg.shanty;
|
||||
|
||||
const sea = coast && cells.haven[burg.cell] ? getSeaDirections(burg.cell) : "";
|
||||
function getSeaDirections(i) {
|
||||
const p1 = cells.p[i];
|
||||
const p2 = cells.p[cells.haven[i]];
|
||||
let deg = Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180 / Math.PI - 90;
|
||||
if (deg < 0) deg += 360;
|
||||
let norm = rn(normalize(deg, 0, 360) * 8) / 4;
|
||||
if (norm === 2) norm = 0;
|
||||
return "sea="+norm;
|
||||
// debug.selectAll("*").remove();
|
||||
// pack.burgs.filter(b => b.port).forEach(b => {
|
||||
// var p1 = pack.cells.p[b.cell];
|
||||
// var p2 = pack.cells.p[pack.cells.haven[b.cell]];
|
||||
// var deg = Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180 / Math.PI - 90;
|
||||
// if (deg < 0) deg += 360;
|
||||
// var norm = rn(normalize(deg, 0, 360) * 8) / 4;
|
||||
// if (norm === 2) norm = 0;
|
||||
// debug.append("line").attr("x1", p1[0]).attr("y1", p1[1]).attr("x2", p2[0]).attr("y2", p2[1]).attr("stroke", "red").attr("stroke-width", .2);
|
||||
// debug.append("circle").attr("cx", b.x).attr("cy", b.y).attr("r", .4);
|
||||
// debug.append("text").attr("x", b.x+1).attr("y", b.y).attr("font-size", 2).text(rn(norm, 2));
|
||||
// });
|
||||
}
|
||||
|
||||
const site = "http://fantasycities.watabou.ru/";
|
||||
const url = `${site}?name=${name}&size=${size}&seed=${s}&hub=${hub}&random=0&continuous=0&river=${river}&coast=${coast}&citadel=${citadel}&plaza=${plaza}&temple=${temple}&walls=${walls}&shantytown=${shanty}`;
|
||||
const url = `${site}?name=${name}&size=${size}&seed=${s}&hub=${hub}&random=0&continuous=0&river=${river}&coast=${coast}&citadel=${citadel}&plaza=${plaza}&temple=${temple}&walls=${walls}&shantytown=${shanty}${sea}`;
|
||||
openURL(url);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ function clicked() {
|
|||
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 === "ice") editIce();
|
||||
else if (parent.id === "terrain") editReliefIcon();
|
||||
else if (parent.id === "markers") editMarker();
|
||||
else if (grand.id === "coastline") editCoastline();
|
||||
|
|
@ -581,4 +582,43 @@ function highlightElement(element) {
|
|||
let y = box.y + box.height / 2;
|
||||
if (tr[1]) y += tr[1];
|
||||
if (scale >= 2) zoomTo(x, y, scale, 1600);
|
||||
}
|
||||
|
||||
function selectIcon(initial, callback) {
|
||||
if (!callback) return;
|
||||
$("#iconSelector").dialog();
|
||||
|
||||
const table = document.getElementById("iconTable");
|
||||
const input = document.getElementById("iconInput");
|
||||
input.value = initial;
|
||||
|
||||
if (!table.innerHTML) {
|
||||
const icons = ["⚔️","🏹","🐴","💣","🌊","🎯","⚓","🔮","📯","⚒️","🛡️","👑","⚜️",
|
||||
"☠️","🎆","🗡️","🔪","⛏️","🔥","🩸","💧","🐾","🎪","🏰","🏯","⛓️","❤️","💘","💜","📜","🔔",
|
||||
"🔱","💎","🌈","🌠","✨","💥","☀️","🌙","⚡","❄️","♨️","🎲","🚨","🌉","🗻","🌋","🧱",
|
||||
"⚖️","✂️","🎵","👗","🎻","🎨","🎭","⛲","💉","📖","📕","🎁","💍","⏳","🕸️","⚗️","☣️","☢️",
|
||||
"🔰","🎖️","🚩","🏳️","🏴","💪","✊","👊","🤜","🤝","🙏","🧙","🧙♀️","💂","🤴","🧛","🧟","🧞","🧝","👼",
|
||||
"👻","👺","👹","🦄","🐲","🐉","🐎","🦓","🐺","🦊","🐱","🐈","🦁","🐯","🐅","🐆","🐕","🦌","🐵","🐒","🦍",
|
||||
"🦅","🕊️","🐓","🦇","🦜","🐦","🦉","🐮","🐄","🐂","🐃","🐷","🐖","🐗","🐏","🐑","🐐","🐫","🦒","🐘","🦏","🐭","🐁","🐀",
|
||||
"🐹","🐰","🐇","🦔","🐸","🐊","🐢","🦎","🐍","🐳","🐬","🦈","🐠","🐙","🦑","🐌","🦋","🐜","🐝","🐞","🦗","🕷️","🦂","🦀",
|
||||
"🌳","🌲","🎄","🌴","🍂","🍁","🌵","☘️","🍀","🌿","🌱","🌾","🍄","🌽","🌸","🌹","🌻",
|
||||
"🍒","🍏","🍇","🍉","🍅","🍓","🥔","🥕","🥩","🍗","🍞","🍻","🍺","🍲","🍷"
|
||||
];
|
||||
|
||||
let row = "";
|
||||
for (let i=0; i < icons.length; i++) {
|
||||
if (i%17 === 0) row = table.insertRow(i/17|0);
|
||||
const cell = row.insertCell(i%17);
|
||||
cell.innerHTML = icons[i];
|
||||
}
|
||||
}
|
||||
|
||||
table.onclick = e => {if (e.target.tagName === "TD") {input.value = e.target.innerHTML; callback(input.value)}};
|
||||
table.onmouseover = e => {if (e.target.tagName === "TD") tip(`Click to select ${e.target.innerHTML} icon`)};
|
||||
|
||||
$("#iconSelector").dialog({width: fitContent(), title: "Select Icon",
|
||||
buttons: {
|
||||
Apply: function() {callback(input.value||"⠀"); $(this).dialog("close")},
|
||||
Close: function() {callback(initial); $(this).dialog("close")}}
|
||||
});
|
||||
}
|
||||
|
|
@ -106,6 +106,7 @@ function showMapTooltip(point, e, i, g) {
|
|||
if (group === "lakes" && !land) {tip(`${capitalize(subgroup)} lake. Click to edit`); return;}
|
||||
if (group === "coastline") {tip("Click to edit the coastline"); return;}
|
||||
if (group === "zones") {tip(path[path.length-8].dataset.description); return;}
|
||||
if (group === "ice") {tip("Click to edit the Ice"); return;}
|
||||
|
||||
// covering elements
|
||||
if (layerIsOn("togglePrec") && land) tip("Annual Precipitation: "+ getFriendlyPrecipitation(i)); else
|
||||
|
|
@ -403,6 +404,7 @@ document.addEventListener("keyup", event => {
|
|||
else if (key === 85) toggleRoutes(); // "U" to toggle Routes layer
|
||||
else if (key === 84) toggleTemp(); // "T" to toggle Temperature layer
|
||||
else if (key === 78) togglePopulation(); // "N" to toggle Population layer
|
||||
else if (key === 74) toggleIce(); // "J" to toggle Ice layer
|
||||
else if (key === 65) togglePrec(); // "A" to toggle Precipitation layer
|
||||
else if (key === 76) toggleLabels(); // "L" to toggle Labels layer
|
||||
else if (key === 73) toggleIcons(); // "I" to toggle Icons layer
|
||||
|
|
|
|||
|
|
@ -965,7 +965,7 @@ function editHeightmap() {
|
|||
closeDialogs("#imageConverter");
|
||||
|
||||
$("#imageConverter").dialog({
|
||||
title: "Image Converter", minHeight: "auto", width: "19.5em", resizable: false,
|
||||
title: "Image Converter", maxHeight: svgHeight*.75, minHeight: "auto", width: "19.5em", resizable: false,
|
||||
position: {my: "right top", at: "right-10 top+10", of: "svg"},
|
||||
beforeClose: closeImageConverter
|
||||
});
|
||||
|
|
|
|||
105
modules/ui/ice-editor.js
Normal file
105
modules/ui/ice-editor.js
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
"use strict";
|
||||
function editIce() {
|
||||
if (customization) return;
|
||||
closeDialogs(".stable");
|
||||
if (!layerIsOn("toggleIce")) toggleIce();
|
||||
|
||||
elSelected = d3.select(d3.event.target);
|
||||
const type = elSelected.attr("type") ? "Glacier" : "Iceberg";
|
||||
document.getElementById("iceRandomize").style.display = type === "Glacier" ? "none" : "inline-block";
|
||||
document.getElementById("iceSize").style.display = type === "Glacier" ? "none" : "inline-block";
|
||||
if (type === "Iceberg") document.getElementById("iceSize").value = +elSelected.attr("size");
|
||||
ice.selectAll("*").classed("draggable", true).call(d3.drag().on("drag", dragElement));
|
||||
|
||||
$("#iceEditor").dialog({
|
||||
title: "Edit "+type, resizable: false,
|
||||
position: {my: "center top+60", at: "top", of: d3.event, collision: "fit"},
|
||||
close: closeEditor
|
||||
});
|
||||
|
||||
if (modules.editIce) return;
|
||||
modules.editIce = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("iceEditStyle").addEventListener("click", () => editStyle("ice"));
|
||||
document.getElementById("iceRandomize").addEventListener("click", randomizeShape);
|
||||
document.getElementById("iceSize").addEventListener("input", changeSize);
|
||||
document.getElementById("iceNew").addEventListener("click", toggleAdd);
|
||||
document.getElementById("iceRemove").addEventListener("click", removeIce);
|
||||
|
||||
function randomizeShape() {
|
||||
const c = grid.points[+elSelected.attr("cell")];
|
||||
const s = +elSelected.attr("size");
|
||||
const i = ra(grid.cells.i), cn = grid.points[i];
|
||||
const poly = getGridPolygon(i).map(p => [p[0]-cn[0], p[1]-cn[1]]);
|
||||
const points = poly.map(p => [rn(c[0] + p[0] * s, 2), rn(c[1] + p[1] * s, 2)]);
|
||||
elSelected.attr("points", points);
|
||||
}
|
||||
|
||||
function changeSize() {
|
||||
const c = grid.points[+elSelected.attr("cell")];
|
||||
const s = +elSelected.attr("size");
|
||||
const flat = elSelected.attr("points").split(",").map(el => +el);
|
||||
const pairs = [];
|
||||
while (flat.length) pairs.push(flat.splice(0,2));
|
||||
const poly = pairs.map(p => [(p[0]-c[0]) / s, (p[1]-c[1]) / s]);
|
||||
const size = +this.value;
|
||||
const points = poly.map(p => [rn(c[0] + p[0] * size, 2), rn(c[1] + p[1] * size, 2)]);
|
||||
elSelected.attr("points", points).attr("size", size);
|
||||
}
|
||||
|
||||
function toggleAdd() {
|
||||
document.getElementById("iceNew").classList.toggle("pressed");
|
||||
if (document.getElementById("iceNew").classList.contains("pressed")) {
|
||||
viewbox.style("cursor", "crosshair").on("click", addIcebergOnClick);
|
||||
tip("Click on map to create an iceberg. Hold Shift to add multiple", true);
|
||||
} else {
|
||||
clearMainTip();
|
||||
viewbox.on("click", clicked).style("cursor", "default");
|
||||
}
|
||||
}
|
||||
|
||||
function addIcebergOnClick() {
|
||||
const point = d3.mouse(this);
|
||||
const i = findGridCell(point[0], point[1]);
|
||||
const c = grid.points[i];
|
||||
const s = +document.getElementById("iceSize").value;
|
||||
|
||||
const points = getGridPolygon(i).map(p => [(p[0] + (c[0]-p[0]) / s)|0, (p[1] + (c[1]-p[1]) / s)|0]);
|
||||
const iceberg = ice.append("polygon").attr("points", points).attr("cell", i).attr("size", s);
|
||||
iceberg.call(d3.drag().on("drag", dragElement));
|
||||
if (d3.event.shiftKey === false) toggleAdd();
|
||||
}
|
||||
|
||||
function removeIce() {
|
||||
const type = elSelected.attr("type") ? "Glacier" : "Iceberg";
|
||||
alertMessage.innerHTML = `Are you sure you want to remove the ${type}?`;
|
||||
$("#alert").dialog({resizable: false, title: "Remove "+type,
|
||||
buttons: {
|
||||
Remove: function() {
|
||||
$(this).dialog("close");
|
||||
elSelected.remove();
|
||||
$("#iceEditor").dialog("close");
|
||||
},
|
||||
Cancel: function() {$(this).dialog("close");}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function dragElement() {
|
||||
const tr = parseTransform(this.getAttribute("transform"));
|
||||
const dx = +tr[0] - d3.event.x, dy = +tr[1] - d3.event.y;
|
||||
|
||||
d3.event.on("drag", function() {
|
||||
const x = d3.event.x, y = d3.event.y;
|
||||
this.setAttribute("transform", `translate(${(dx+x)},${(dy+y)})`);
|
||||
});
|
||||
}
|
||||
|
||||
function closeEditor() {
|
||||
ice.selectAll("*").classed("draggable", false).call(d3.drag().on("drag", null));
|
||||
clearMainTip();
|
||||
iceNew.classList.remove("pressed");
|
||||
unselect();
|
||||
}
|
||||
}
|
||||
|
|
@ -32,10 +32,10 @@ function getDefaultPresets() {
|
|||
"cultural": ["toggleBorders", "toggleCultures", "toggleIcons", "toggleLabels", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||
"religions": ["toggleBorders", "toggleIcons", "toggleLabels", "toggleReligions", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||
"provinces": ["toggleBorders", "toggleIcons", "toggleProvinces", "toggleRivers", "toggleScaleBar"],
|
||||
"biomes": ["toggleBiomes", "toggleRivers", "toggleScaleBar"],
|
||||
"biomes": ["toggleBiomes", "toggleIce", "toggleRivers", "toggleScaleBar"],
|
||||
"heightmap": ["toggleHeight", "toggleRivers"],
|
||||
"physical": ["toggleCoordinates", "toggleHeight", "toggleRivers", "toggleScaleBar"],
|
||||
"poi": ["toggleBorders", "toggleHeight", "toggleIcons", "toggleMarkers", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||
"poi": ["toggleBorders", "toggleHeight", "toggleIce", "toggleIcons", "toggleMarkers", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||
"military": ["toggleBorders", "toggleIcons", "toggleLabels", "toggleMilitary", "toggleRivers", "toggleRoutes", "toggleScaleBar", "toggleStates"],
|
||||
"landmass": ["toggleScaleBar"]
|
||||
}
|
||||
|
|
@ -345,7 +345,7 @@ function drawBiomes() {
|
|||
const edgeVerticle = cells.v[i].find(v => vertices.c[v].some(i => cells.biome[i] !== b));
|
||||
const chain = connectVertices(edgeVerticle, b);
|
||||
if (chain.length < 3) continue;
|
||||
const points = chain.map(v => vertices.p[v]);
|
||||
const points = clipPoly(chain.map(v => vertices.p[v]), 1);
|
||||
paths[b] += "M" + points.join("L") + "Z";
|
||||
}
|
||||
|
||||
|
|
@ -456,38 +456,77 @@ function drawCells() {
|
|||
cells.append("path").attr("d", path);
|
||||
}
|
||||
|
||||
function drawSeaIce() {
|
||||
const seaIce = viewbox.append("g").attr("id", "seaIce").attr("fill", "#e8f0f6").attr("stroke", "#e8f0f6").attr("filter", "url(#dropShadow05)");//.attr("opacity", .8);
|
||||
for (const i of grid.cells.i) {
|
||||
const t = grid.cells.temp[i] ;
|
||||
if (t > 2) continue;
|
||||
if (t > -5 && grid.cells.h[i] >= 20) continue;
|
||||
if (t < -5) drawpolygon(i);
|
||||
if (P(normalize(t, -5.5, 2.5))) continue; // t[-5; 2]
|
||||
const size = t < -14 ? 0 : t > -6 ? (7 + t) / 10 : (15 + t) / 100; // [0; 1], where 0 = full size, 1 = zero size
|
||||
resizePolygon(i, rn(size * (.2 + rand() * .9), 2));
|
||||
function toggleIce() {
|
||||
if (!layerIsOn("toggleIce")) {
|
||||
turnButtonOn("toggleIce");
|
||||
$('#ice').fadeIn();
|
||||
if (event && isCtrlClick(event)) editStyle("ice");
|
||||
} else {
|
||||
if (event && isCtrlClick(event)) {editStyle("ice"); return;}
|
||||
$('#ice').fadeOut();
|
||||
turnButtonOff("toggleIce");
|
||||
}
|
||||
}
|
||||
|
||||
// -9: .06
|
||||
// -8: .07
|
||||
// -7: .08
|
||||
// -6: .09
|
||||
function drawIce() {
|
||||
const cells = grid.cells, vertices = grid.vertices, n = cells.i.length, temp = cells.temp, h = cells.h;
|
||||
const used = new Uint8Array(cells.i.length);
|
||||
Math.seedrandom(seed);
|
||||
|
||||
// -5: .2
|
||||
// -4: .3
|
||||
// -3: .4
|
||||
// -2: .5
|
||||
// -1: .6
|
||||
// 0: .7
|
||||
const shieldMin = -6; // min temp to form ice shield (glacier)
|
||||
const icebergMax = 2; // max temp to form an iceberg
|
||||
|
||||
function drawpolygon(i) {
|
||||
seaIce.append("polygon").attr("points", getGridPolygon(i));
|
||||
for (const i of grid.cells.i) {
|
||||
const t = temp[i];
|
||||
if (t > icebergMax) continue; // too warm: no ice
|
||||
if (t > shieldMin && h[i] >= 20) continue; // non-glacier land: no ice
|
||||
|
||||
if (t <= shieldMin) {
|
||||
// very cold: ice shield
|
||||
if (used[i]) continue; // already rendered
|
||||
const onborder = cells.c[i].some(n => temp[n] > shieldMin);
|
||||
if (!onborder) continue; // need to start from onborder cell
|
||||
const vertex = cells.v[i].find(v => vertices.c[v].some(i => temp[i] > shieldMin));
|
||||
const chain = connectVertices(vertex);
|
||||
if (chain.length < 3) continue;
|
||||
const points = clipPoly(chain.map(v => vertices.p[v]));
|
||||
ice.append("polygon").attr("points", points).attr("type", "iceShield");
|
||||
continue;
|
||||
}
|
||||
|
||||
// mildly cold: iceberd
|
||||
if (P(normalize(t, -7, 2.5))) continue; // t[-5; 2] cold: skip some cells
|
||||
if (grid.features[cells.f[i]].type === "lake") continue; // lake: no icebers
|
||||
let size = (6.5 + t) / 10; // iceberg size: 0 = full size, 1 = zero size
|
||||
if (cells.t[i] === -1) size *= 1.3; // coasline: smaller icebers
|
||||
size = Math.min(size * (.4 + rand() * 1.2), .95); // randomize iceberd size
|
||||
resizePolygon(i, size);
|
||||
}
|
||||
|
||||
function resizePolygon(i, s) {
|
||||
const c = grid.points[i];
|
||||
const points = getGridPolygon(i).map(p => [p[0] + (c[0]-p[0]) * s, p[1] + (c[1]-p[1]) * s]);
|
||||
seaIce.append("polygon").attr("points", points);
|
||||
const points = getGridPolygon(i).map(p => [(p[0] + (c[0]-p[0]) * s)|0, (p[1] + (c[1]-p[1]) * s)|0]);
|
||||
ice.append("polygon").attr("points", points).attr("cell", i).attr("size", rn(1-s, 2));
|
||||
}
|
||||
|
||||
// connect vertices to chain
|
||||
function connectVertices(start) {
|
||||
const chain = []; // vertices chain to form a path
|
||||
for (let i=0, current = start; i === 0 || current !== start && i < 20000; i++) {
|
||||
const prev = last(chain); // previous vertex in chain
|
||||
chain.push(current); // add current vertex to sequence
|
||||
const c = vertices.c[current]; // cells adjacent to vertex
|
||||
c.filter(c => temp[c] <= shieldMin).forEach(c => used[c] = 1);
|
||||
const c0 = c[0] >= n || temp[c[0]] > shieldMin;
|
||||
const c1 = c[1] >= n || temp[c[1]] > shieldMin;
|
||||
const c2 = c[2] >= n || temp[c[2]] > shieldMin;
|
||||
const v = vertices.v[current]; // neighboring vertices
|
||||
if (v[0] !== prev && c0 !== c1) current = v[0];
|
||||
else if (v[1] !== prev && c1 !== c2) current = v[1];
|
||||
else if (v[2] !== prev && c0 !== c2) current = v[2];
|
||||
if (current === chain[chain.length - 1]) {console.error("Next vertex is not found"); break;}
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ function editMarker() {
|
|||
document.getElementById("markerIconSize").addEventListener("input", changeIconSize);
|
||||
document.getElementById("markerIconShiftX").addEventListener("input", changeIconShiftX);
|
||||
document.getElementById("markerIconShiftY").addEventListener("input", changeIconShiftY);
|
||||
document.getElementById("markerIconCustom").addEventListener("input", applyCustomUnicodeIcon);
|
||||
document.getElementById("markerIconSelect").addEventListener("click", selectMarkerIcon);
|
||||
|
||||
document.getElementById("markerStyle").addEventListener("click", toggleStyleSection);
|
||||
document.getElementById("markerSize").addEventListener("input", changeMarkerSize);
|
||||
|
|
@ -73,13 +73,7 @@ function editMarker() {
|
|||
markerIconFill.value = icon.attr("fill");
|
||||
|
||||
markerToggleBubble.className = symbol.select("circle").attr("opacity") === "0" ? "icon-info" : "icon-info-circled";
|
||||
|
||||
const table = document.getElementById("markerIconTable");
|
||||
let selected = table.getElementsByClassName("selected");
|
||||
if (selected.length) selected[0].removeAttribute("class");
|
||||
selected = document.querySelectorAll("#markerIcon" + icon.text().codePointAt());
|
||||
if (selected.length) selected[0].className = "selected";
|
||||
markerIconCustom.value = selected.length ? "" : icon.text();
|
||||
markerIconSelect.innerHTML = icon.text();
|
||||
}
|
||||
|
||||
function toggleGroupSection() {
|
||||
|
|
@ -162,242 +156,20 @@ function editMarker() {
|
|||
if (markerIconSection.style.display === "inline-block") {
|
||||
markerEditor.querySelectorAll("button:not(#markerIcon)").forEach(b => b.style.display = "inline-block");
|
||||
markerIconSection.style.display = "none";
|
||||
markerIconSelect.style.display = "none";
|
||||
} else {
|
||||
markerEditor.querySelectorAll("button:not(#markerIcon)").forEach(b => b.style.display = "none");
|
||||
markerIconSection.style.display = "inline-block";
|
||||
if (!markerIconTable.innerHTML) drawIconsList();
|
||||
markerIconSelect.style.display = "inline-block";
|
||||
}
|
||||
}
|
||||
|
||||
function drawIconsList() {
|
||||
const icons = [
|
||||
// emoticons in FF:
|
||||
["2693", "⚓", "Anchor"],
|
||||
["26EA", "⛪", "Church"],
|
||||
["1F3EF", "🏯", "Japanese Castle"],
|
||||
["1F3F0", "🏰", "Castle"],
|
||||
["1F5FC", "🗼", "Tower"],
|
||||
["1F3E0", "🏠", "House"],
|
||||
["1F3AA", "🎪", "Tent"],
|
||||
["1F3E8", "🏨", "Hotel"],
|
||||
["1F4B0", "💰", "Money bag"],
|
||||
["1F6A8", "🚨", "Revolving Light"],
|
||||
["1F309", "🌉", "Bridge at Night"],
|
||||
["1F5FB", "🗻", "Mountain"],
|
||||
["1F30B", "🌋", "Volcano"],
|
||||
["270A", "✊", "Raised Fist"],
|
||||
["1F44A", "👊", "Oncoming Fist"],
|
||||
["1F4AA", "💪", "Flexed Biceps"],
|
||||
["1F47C", "👼", "Baby Angel"],
|
||||
["1F40E", "🐎", "Horse"],
|
||||
["1F434", "🐴", "Horse Face"],
|
||||
["1F42E", "🐮", "Cow"],
|
||||
["1F43A", "🐺", "Wolf Face"],
|
||||
["1F435", "🐵", "Monkey face"],
|
||||
["1F437", "🐷", "Pig face"],
|
||||
["1F414", "🐔", "Chicken"],
|
||||
["1F411", "🐑", "Ewe"],
|
||||
["1F42B", "🐫", "Camel"],
|
||||
["1F418", "🐘", "Elephant"],
|
||||
["1F422", "🐢", "Turtle"],
|
||||
["1F40C", "🐌", "Snail"],
|
||||
["1F40D", "🐍", "Snake"],
|
||||
["1F41D", "🐝", "Honeybee"],
|
||||
["1F41C", "🐜", "Ant"],
|
||||
["1F41B", "🐛", "Bug"],
|
||||
["1F426", "🐦", "Bird"],
|
||||
["1F438", "🐸", "Frog Face"],
|
||||
["1F433", "🐳", "Whale"],
|
||||
["1F42C", "🐬", "Dolphin"],
|
||||
["1F420", "🐟", "Fish"],
|
||||
["1F480", "💀", "Skull"],
|
||||
["1F432", "🐲", "Dragon Head"],
|
||||
["1F479", "👹", "Ogre"],
|
||||
["1F47A", "👺", "Goblin"],
|
||||
["1F47B", "👻", "Ghost"],
|
||||
["1F47E", "👾", "Alien"],
|
||||
["1F383", "🎃", "Jack-O-Lantern"],
|
||||
["1F384", "🎄", "Christmas Tree"],
|
||||
["1F334", "🌴", "Palm"],
|
||||
["1F335", "🌵", "Cactus"],
|
||||
["2618", "☘️", "Shamrock"],
|
||||
["1F340", "🍀", "Four Leaf Clover"],
|
||||
["1F341", "🍁", "Maple Leaf"],
|
||||
["1F33F", "🌿", "Herb"],
|
||||
["1F33E", "🌾", "Sheaf"],
|
||||
["1F344", "🍄", "Mushroom"],
|
||||
["1F374", "🍴", "Fork and knife"],
|
||||
["1F372", "🍲", "Food"],
|
||||
["1F35E", "🍞", "Bread"],
|
||||
["1F357", "🍗", "Poultry leg"],
|
||||
["1F347", "🍇", "Grapes"],
|
||||
["1F34F", "🍏", "Apple"],
|
||||
["1F352", "🍒", "Cherries"],
|
||||
["1F36F", "🍯", "Honey pot"],
|
||||
["1F37A", "🍺", "Beer"],
|
||||
["1F37B", "🍻", "Beers"],
|
||||
["1F377", "🍷", "Wine glass"],
|
||||
["1F3BB", "🎻", "Violin"],
|
||||
["1F3B8", "🎸", "Guitar"],
|
||||
["1F52A", "🔪", "Knife"],
|
||||
["1F52B", "🔫", "Pistol"],
|
||||
["1F4A3", "💣", "Bomb"],
|
||||
["1F4A5", "💥", "Collision"],
|
||||
["1F4A8", "💨", "Dashing away"],
|
||||
["1F301", "🌁", "Foggy"],
|
||||
["2744", "❄️", "Snowflake"],
|
||||
["26A1", "⚡", "Electricity"],
|
||||
["1F320", "🌠", "Shooting star"],
|
||||
["1F319", "🌙", "Crescent moon"],
|
||||
["1F525", "🔥", "Fire"],
|
||||
["1F4A7", "💧", "Droplet"],
|
||||
["1F30A", "🌊", "Wave"],
|
||||
["23F0", "⏰", "Alarm Clock"],
|
||||
["231B", "⌛", "Hourglass"],
|
||||
["1F3C6", "🏆", "Goblet"],
|
||||
["26F2", "⛲", "Fountain"],
|
||||
["26F5", "⛵", "Sailboat"],
|
||||
["26FA", "⛺", "Campfire"],
|
||||
["2764", "❤", "Red Heart"],
|
||||
["1F498", "💘", "Heart With Arrow"],
|
||||
["1F489", "💉", "Syringe"],
|
||||
["1F4D5", "📕", "Closed Book"],
|
||||
["1F4D6", "📚", "Books"],
|
||||
["1F381", "🎁", "Wrapped Gift"],
|
||||
["1F3AF", "🎯", "Archery"],
|
||||
["1F52E", "🔮", "Magic ball"],
|
||||
["1F3AD", "🎭", "Performing arts"],
|
||||
["1F3A8", "🎨", "Artist palette"],
|
||||
["1F457", "👗", "Dress"],
|
||||
["1F392", "🎒", "Backpack"],
|
||||
["1F451", "👑", "Crown"],
|
||||
["1F48D", "💍", "Ring"],
|
||||
["1F48E", "💎", "Gem"],
|
||||
["1F514", "🔔", "Bell"],
|
||||
["1F3B2", "🎲", "Die"],
|
||||
// black and white icons in FF:
|
||||
["26A0", "⚠", "Alert"],
|
||||
["2317", "⌗", "Hash"],
|
||||
["2318", "⌘", "POI"],
|
||||
["2307", "⌇", "Wavy"],
|
||||
["27F1", "⟱", "Downwards Quadruple"],
|
||||
["21E6", "⇦", "Left arrow"],
|
||||
["21E7", "⇧", "Top arrow"],
|
||||
["21E8", "⇨", "Right arrow"],
|
||||
["21E9", "⇩", "Left arrow"],
|
||||
["21F6", "⇶", "Three arrows"],
|
||||
["2699", "⚙", "Gear"],
|
||||
["269B", "⚛", "Atom"],
|
||||
["2680", "⚀", "Die1"],
|
||||
["2681", "⚁", "Die2"],
|
||||
["2682", "⚂", "Die3"],
|
||||
["2683", "⚃", "Die4"],
|
||||
["2684", "⚄", "Die5"],
|
||||
["2685", "⚅", "Die6"],
|
||||
["26B4", "⚴", "Pallas"],
|
||||
["26B5", "⚵", "Juno"],
|
||||
["26B6", "⚶", "Vesta"],
|
||||
["26B7", "⚷", "Chiron"],
|
||||
["26B8", "⚸", "Lilith"],
|
||||
["263F", "☿", "Mercury"],
|
||||
["2640", "♀", "Venus"],
|
||||
["2641", "♁", "Earth"],
|
||||
["2642", "♂", "Mars"],
|
||||
["2643", "♃", "Jupiter"],
|
||||
["2644", "♄", "Saturn"],
|
||||
["2645", "♅", "Uranus"],
|
||||
["2646", "♆", "Neptune"],
|
||||
["2647", "♇", "Pluto"],
|
||||
["26B3", "⚳", "Ceres"],
|
||||
["2654", "♔", "Chess king"],
|
||||
["2655", "♕", "Chess queen"],
|
||||
["2656", "♖", "Chess rook"],
|
||||
["2657", "♗", "Chess bishop"],
|
||||
["2658", "♘", "Chess knight"],
|
||||
["2659", "♙", "Chess pawn"],
|
||||
["2660", "♠", "Spade"],
|
||||
["2663", "♣", "Club"],
|
||||
["2665", "♥", "Heart"],
|
||||
["2666", "♦", "Diamond"],
|
||||
["2698", "⚘", "Flower"],
|
||||
["2625", "☥", "Ankh"],
|
||||
["2626", "☦", "Orthodox"],
|
||||
["2627", "☧", "Chi Rho"],
|
||||
["2628", "☨", "Lorraine"],
|
||||
["2629", "☩", "Jerusalem"],
|
||||
["2670", "♰", "Syriac cross"],
|
||||
["2020", "†", "Dagger"],
|
||||
["262A", "☪", "Muslim"],
|
||||
["262D", "☭", "Soviet"],
|
||||
["262E", "☮", "Peace"],
|
||||
["262F", "☯", "Yin yang"],
|
||||
["26A4", "⚤", "Heterosexuality"],
|
||||
["26A2", "⚢", "Female homosexuality"],
|
||||
["26A3", "⚣", "Male homosexuality"],
|
||||
["26A5", "⚥", "Male and female"],
|
||||
["26AD", "⚭", "Rings"],
|
||||
["2690", "⚐", "White flag"],
|
||||
["2691", "⚑", "Black flag"],
|
||||
["263C", "☼", "Sun"],
|
||||
["263E", "☾", "Moon"],
|
||||
["2668", "♨", "Hot springs"],
|
||||
["2600", "☀", "Black sun"],
|
||||
["2601", "☁", "Cloud"],
|
||||
["2602", "☂", "Umbrella"],
|
||||
["2603", "☃", "Snowman"],
|
||||
["2604", "☄", "Comet"],
|
||||
["2605", "★", "Black star"],
|
||||
["2606", "☆", "White star"],
|
||||
["269D", "⚝", "Outlined star"],
|
||||
["2618", "☘", "Shamrock"],
|
||||
["21AF", "↯", "Lightning"],
|
||||
["269C", "⚜", "FleurDeLis"],
|
||||
["2622", "☢", "Radiation"],
|
||||
["2623", "☣", "Biohazard"],
|
||||
["2620", "☠", "Skull"],
|
||||
["2638", "☸", "Dharma"],
|
||||
["2624", "☤", "Caduceus"],
|
||||
["2695", "⚕", "Aeculapius staff"],
|
||||
["269A", "⚚", "Hermes staff"],
|
||||
["2697", "⚗", "Alembic"],
|
||||
["266B", "♫", "Music"],
|
||||
["2702", "✂", "Scissors"],
|
||||
["2696", "⚖", "Scales"],
|
||||
["2692", "⚒", "Hammer and pick"],
|
||||
["2694", "⚔", "Swords"]
|
||||
];
|
||||
|
||||
const table = document.getElementById("markerIconTable");
|
||||
table.addEventListener("click", selectIcon, false);
|
||||
table.addEventListener("mouseover", hoverIcon, false);
|
||||
let row = "";
|
||||
|
||||
for (let i=0; i < icons.length; i++) {
|
||||
if (i%16 === 0) row = table.insertRow(0);
|
||||
const cell = row.insertCell(0);
|
||||
const icon = String.fromCodePoint(parseInt(icons[i][0], 16));
|
||||
cell.innerHTML = icon;
|
||||
cell.id = "markerIcon" + icon.codePointAt();
|
||||
cell.dataset.desc = icons[i][2];
|
||||
}
|
||||
}
|
||||
|
||||
function selectIcon(e) {
|
||||
if (e.target !== e.currentTarget) {
|
||||
const table = document.getElementById("markerIconTable");
|
||||
const selected = table.getElementsByClassName("selected");
|
||||
if (selected.length) selected[0].removeAttribute("class");
|
||||
e.target.className = "selected";
|
||||
function selectMarkerIcon() {
|
||||
selectIcon(this.innerHTML, v => {
|
||||
this.innerHTML = v;
|
||||
const id = elSelected.attr("data-id");
|
||||
const icon = e.target.innerHTML;
|
||||
d3.select("#defs-markers").select(id).select("text").text(icon);
|
||||
}
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
function hoverIcon(e) {
|
||||
if (e.target !== e.currentTarget) tip(e.target.innerHTML + " " + e.target.dataset.desc);
|
||||
e.stopPropagation();
|
||||
d3.select("#defs-markers").select(id).select("text").text(v);
|
||||
});
|
||||
}
|
||||
|
||||
function changeIconSize() {
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ function overviewMilitary() {
|
|||
position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {
|
||||
Apply: applyMilitaryOptions,
|
||||
Add: () => addUnitLine({name: "custom"+militaryOptionsTable.rows.length, rural: .2, urban: .5, crew: 1, type: "melee"}),
|
||||
Add: () => addUnitLine({icon: "🛡️", name: "custom"+militaryOptionsTable.rows.length, rural: .2, urban: .5, crew: 1, power: 1, type: "melee"}),
|
||||
Restore: restoreDefaultUnits,
|
||||
Cancel: function() {$(this).dialog("close");}
|
||||
}, open: function() {
|
||||
|
|
@ -200,19 +200,20 @@ function overviewMilitary() {
|
|||
}
|
||||
|
||||
function addUnitLine(u) {
|
||||
const row = `<tr>
|
||||
const row = document.createElement("tr");
|
||||
row.innerHTML = `<td><button type="button" data-tip="Click to select unit icon">${u.icon||" "}</button></td>
|
||||
<td><input data-tip="Type unit name. If name is changed for existing unit, old unit will be replaced" value="${u.name}"></td>
|
||||
<td><input data-tip="Enter conscription percentage for rural population" type="number" min=0 max=100 step=.01 value="${u.rural}"></td>
|
||||
<td><input data-tip="Enter conscription percentage for urban population" type="number" min=0 max=100 step=.01 value="${u.urban}"></td>
|
||||
<td><input data-tip="Enter average number of people in crew" type="number" min=1 step=1 value="${u.crew}"></td>
|
||||
<td><input data-tip="Enter average number of people in crew (used for total personnel calculation)" type="number" min=1 step=1 value="${u.crew}"></td>
|
||||
<td><input data-tip="Enter military power (used for battle simulation)" type="number" min=0 step=.1 value="${u.power}"></td>
|
||||
<td><select data-tip="Select unit type to apply special rules on forces recalculation">${types.map(t => `<option ${u.type === t ? "selected" : ""} value="${t}">${t}</option>`).join(" ")}</select></td>
|
||||
<td data-tip="Check if unit is separate and can be stacked only with units of the same type">
|
||||
<input id="${u.name}Separate" type="checkbox" class="checkbox" ${u.separate ? "checked" : ""}>
|
||||
<label for="${u.name}Separate" class="checkbox-label"></label>
|
||||
</td>
|
||||
<td data-tip="Remove the unit"><span data-tip="Remove unit type" class="icon-trash-empty pointer" onclick="this.parentElement.parentElement.remove();"></span></td>
|
||||
</tr>`;
|
||||
table.insertAdjacentHTML("beforeend", row);
|
||||
<label for="${u.name}Separate" class="checkbox-label"></label></td>
|
||||
<td data-tip="Remove the unit"><span data-tip="Remove unit type" class="icon-trash-empty pointer" onclick="this.parentElement.parentElement.remove();"></span></td>`;
|
||||
row.querySelector("button").addEventListener("click", function(e) {selectIcon(this.innerHTML, v => this.innerHTML = v)});
|
||||
table.appendChild(row);
|
||||
}
|
||||
|
||||
function restoreDefaultUnits() {
|
||||
|
|
@ -230,8 +231,14 @@ function overviewMilitary() {
|
|||
|
||||
$("#militaryOptions").dialog("close");
|
||||
options.military = unitLines.map((r, i) => {
|
||||
const [name, rural, urban, crew, type, separate] = Array.from(r.querySelectorAll("input, select")).map(d => d.type === "checkbox" ? d.checked : d.value);
|
||||
return {name:names[i], rural:+rural||0, urban:+urban||0, crew:+crew||0, type, separate:+separate||0};
|
||||
const [icon, name, rural, urban, crew, power, type, separate] = Array.from(r.querySelectorAll("input, select, button")).map(d => {
|
||||
let value = d.value;
|
||||
if (d.type === "number") value = +d.value || 0;
|
||||
if (d.type === "checkbox") value = +d.checked || 0;
|
||||
if (d.type === "button") value = d.innerHTML || "⠀";
|
||||
return value;
|
||||
});
|
||||
return {icon, name:names[i], rural, urban, crew, power, type, separate};
|
||||
});
|
||||
localStorage.setItem("military", JSON.stringify(options.military));
|
||||
Military.generate();
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ function editRegiment(selector) {
|
|||
|
||||
$("#regimentEditor").dialog({
|
||||
title: "Edit Regiment", resizable: false, close: closeEditor,
|
||||
position: {my: "left top", at: "left+10 top+10", of: "#map"},
|
||||
close: closeEditor
|
||||
position: {my: "left top", at: "left+10 top+10", of: "#map"}
|
||||
});
|
||||
|
||||
if (modules.editRegiment) return;
|
||||
|
|
@ -27,6 +26,7 @@ function editRegiment(selector) {
|
|||
document.getElementById("regimentName").addEventListener("change", changeName);
|
||||
document.getElementById("regimentEmblem").addEventListener("input", changeEmblem);
|
||||
document.getElementById("regimentEmblemSelect").addEventListener("click", selectEmblem);
|
||||
document.getElementById("regimentAttack").addEventListener("click", toggleAttack);
|
||||
document.getElementById("regimentRegenerateLegend").addEventListener("click", regenerateLegend);
|
||||
document.getElementById("regimentLegend").addEventListener("click", editLegend);
|
||||
document.getElementById("regimentSplit").addEventListener("click", splitRegiment);
|
||||
|
|
@ -92,50 +92,15 @@ function editRegiment(selector) {
|
|||
elSelected.dataset.name = reg.name = document.getElementById("regimentName").value = name;
|
||||
}
|
||||
|
||||
function selectEmblem() {
|
||||
selectIcon(regimentEmblem.value, v => {regimentEmblem.value = v; changeEmblem()});
|
||||
}
|
||||
|
||||
function changeEmblem() {
|
||||
const emblem = document.getElementById("regimentEmblem").value;
|
||||
regiment().icon = elSelected.querySelector(".regimentIcon").innerHTML = emblem;
|
||||
}
|
||||
|
||||
function selectEmblem() {
|
||||
const emblems = ["⚔️","🏹","🐴","💣","🌊","🎯","⚓","🔮","📯","🛡️","👑",
|
||||
"☠️","🎆","🗡️","⛏️","🔥","🐾","🎪","🏰","⚜️","⛓️","❤️","📜","🔱","🌈","🌠","💥","☀️","🍀",
|
||||
"🔰","🕸️","⚗️","☣️","☢️","🎖️","⚕️","☸️","✡️","🚩","🏳️","🏴","🌈","💪","✊","👊","🤜","🤝","🙏","🧙","💂","🤴","🧛","🧟","🧞","🧝",
|
||||
"🦄","🐲","🐉","🐎","🦓","🐺","🦊","🐱","🐈","🦁","🐯","🐅","🐆","🐕","🦌","🐵","🐒","🦍",
|
||||
"🦅","🕊️","🐓","🦇","🐦","🦉","🐮","🐄","🐂","🐃","🐷","🐖","🐗","🐏","🐑","🐐","🐫","🦒","🐘","🦏",
|
||||
"🐭","🐁","🐀","🐹","🐰","🐇","🦔","🐸","🐊","🐢","🦎","🐍","🐳","🐬","🦈","🐙","🦑","🐌","🦋","🐜","🐝","🐞","🦗","🕷️","🦂","🦀"];
|
||||
|
||||
alertMessage.innerHTML = "";
|
||||
const container = document.createElement("div");
|
||||
container.style.userSelect = "none";
|
||||
container.style.cursor = "pointer";
|
||||
container.style.fontSize = "2em";
|
||||
container.style.width = "47vw";
|
||||
container.innerHTML = emblems.map(i => `<span>${i}</span>`).join("");
|
||||
container.addEventListener("mouseover", e => showTip(e), false);
|
||||
container.addEventListener("click", e => clickEmblem(e), false);
|
||||
alertMessage.appendChild(container);
|
||||
|
||||
$("#alert").dialog({
|
||||
resizable: false, width: fitContent(), title: "Select emblem",
|
||||
buttons: {
|
||||
Emojipedia: function() {openURL("https://emojipedia.org/");},
|
||||
Close: function() {$(this).dialog("close");}
|
||||
}
|
||||
});
|
||||
|
||||
function showTip(e) {
|
||||
if (e.target.tagName !== "SPAN") return;
|
||||
tip(`Click to select ${e.target.innerHTML} emblem`);
|
||||
}
|
||||
|
||||
function clickEmblem(e) {
|
||||
if (e.target.tagName !== "SPAN") return;
|
||||
document.getElementById("regimentEmblem").value = e.target.innerHTML;
|
||||
changeEmblem();
|
||||
}
|
||||
}
|
||||
|
||||
function changeUnit() {
|
||||
const u = this.dataset.u;
|
||||
const reg = regiment();
|
||||
|
|
@ -200,6 +165,60 @@ function editRegiment(selector) {
|
|||
toggleAdd();
|
||||
}
|
||||
|
||||
function toggleAttack() {
|
||||
document.getElementById("regimentAttack").classList.toggle("pressed");
|
||||
if (document.getElementById("regimentAttack").classList.contains("pressed")) {
|
||||
viewbox.style("cursor", "crosshair").on("click", attackRegimentOnClick);
|
||||
tip("Click on another regiment to initiate battle", true);
|
||||
armies.selectAll(":scope > g").classed("draggable", false);
|
||||
} else {
|
||||
clearMainTip();
|
||||
armies.selectAll(":scope > g").classed("draggable", true);
|
||||
viewbox.on("click", clicked).style("cursor", "default");
|
||||
}
|
||||
}
|
||||
|
||||
function attackRegimentOnClick() {
|
||||
const target = d3.event.target, regSelected = target.parentElement, army = regSelected.parentElement;
|
||||
const oldState = +elSelected.dataset.state, newState = +regSelected.dataset.state;
|
||||
|
||||
if (army.parentElement.id !== "armies") {tip("Please click on a regiment to attack", false, "error"); return;}
|
||||
if (regSelected === elSelected) {tip("Regiment cannot attack itself", false, "error"); return;}
|
||||
if (oldState === newState) {tip("Cannot attack fraternal regiment", false, "error"); return;}
|
||||
|
||||
const attacker = regiment();
|
||||
const defender = pack.states[regSelected.dataset.state].military.find(r => r.i == regSelected.dataset.id);
|
||||
if (!attacker.a || !defender.a) {tip("Regiment has no troops to battle", false, "error"); return;}
|
||||
|
||||
// move attacked to defender
|
||||
const duration = Math.hypot(attacker.x - defender.x, attacker.y - defender.y) * 6;
|
||||
const x = attacker.x = defender.x;
|
||||
const y = attacker.y = defender.y + 8;
|
||||
|
||||
const size = +armies.attr("box-size");
|
||||
const w = attacker.n ? size * 4 : size * 6;
|
||||
const h = size * 2;
|
||||
const x1 = x => rn(x - w / 2, 2);
|
||||
const y1 = y => rn(y - size, 2);
|
||||
|
||||
const move = d3.transition().duration(duration).ease(d3.easeSinInOut);
|
||||
const attack = d3.transition().delay(duration).duration(800).ease(d3.easeSinInOut).on("end", () => showBattleScreen(attacker, defender));
|
||||
|
||||
d3.select(elSelected.querySelector("rect")).transition(move).attr("x", x1(x)).attr("y", y1(y));
|
||||
d3.select(elSelected.querySelector("text")).transition(move).attr("x", x).attr("y", y);
|
||||
d3.select(elSelected.querySelectorAll("rect")[1]).transition(move).attr("x", x1(x)-h).attr("y", y1(y));
|
||||
d3.select(elSelected.querySelector(".regimentIcon")).transition(move).attr("x", x1(x)-size).attr("y", y);
|
||||
|
||||
// battle icon
|
||||
svg.append("text").attr("x", window.innerWidth/2).attr("y", window.innerHeight/2)
|
||||
.text("⚔️").attr("font-size", 0).attr("opacity", 1)
|
||||
.style("dominant-baseline", "central").style("text-anchor", "middle")
|
||||
.transition(attack).attr("font-size", 1000).attr("opacity", 0).remove();
|
||||
|
||||
tip("", false);
|
||||
$("#regimentEditor").dialog("close");
|
||||
}
|
||||
|
||||
function toggleAttach() {
|
||||
document.getElementById("regimentAttach").classList.toggle("pressed");
|
||||
if (document.getElementById("regimentAttach").classList.contains("pressed")) {
|
||||
|
|
@ -289,7 +308,7 @@ function editRegiment(selector) {
|
|||
const x1 = x => rn(x - w / 2, 2);
|
||||
const y1 = y => rn(y - size, 2);
|
||||
|
||||
const baseRect = this.querySelectorAll("rect")[0];
|
||||
const baseRect = this.querySelector("rect");
|
||||
const text = this.querySelector("text");
|
||||
const iconRect = this.querySelectorAll("rect")[1];
|
||||
const icon = this.querySelector(".regimentIcon");
|
||||
|
|
@ -330,6 +349,7 @@ function editRegiment(selector) {
|
|||
armies.selectAll("g>g").call(d3.drag().on("drag", null));
|
||||
viewbox.selectAll("g#regimentBase").remove();
|
||||
document.getElementById("regimentAdd").classList.remove("pressed");
|
||||
document.getElementById("regimentAttack").classList.remove("pressed");
|
||||
document.getElementById("regimentAttach").classList.remove("pressed");
|
||||
restoreDefaultEvents();
|
||||
elSelected = null;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -65,6 +65,7 @@ function processFeatureRegeneration(event, button) {
|
|||
if (button === "regenerateProvinces") regenerateProvinces(); else
|
||||
if (button === "regenerateReligions") regenerateReligions(); else
|
||||
if (button === "regenerateMilitary") regenerateMilitary(); else
|
||||
if (button === "regenerateIce") regenerateIce(); else
|
||||
if (button === "regenerateMarkers") regenerateMarkers(event); else
|
||||
if (button === "regenerateZones") regenerateZones(event);
|
||||
}
|
||||
|
|
@ -86,7 +87,7 @@ function recalculatePopulation() {
|
|||
if (!b.i || b.removed) return;
|
||||
const i = b.cell;
|
||||
|
||||
b.population = rn(Math.max((pack.cells.s[i] + pack.cells.road[i]) / 8 + b.i / 1000 + i % 100 / 1000, .1), 3);
|
||||
b.population = rn(Math.max((pack.cells.s[i] + pack.cells.road[i] / 2) / 8 + b.i / 1000 + i % 100 / 1000, .1), 3);
|
||||
if (b.capital) b.population = b.population * 1.3; // increase capital population
|
||||
if (b.port) b.population = b.population * 1.3; // increase port population
|
||||
b.population = rn(b.population * gauss(2,3,.6,20,3), 3);
|
||||
|
|
@ -249,6 +250,12 @@ function regenerateMilitary() {
|
|||
if (document.getElementById("militaryOverviewRefresh").offsetParent) militaryOverviewRefresh.click();
|
||||
}
|
||||
|
||||
function regenerateIce() {
|
||||
if (!layerIsOn("toggleIce")) toggleIce();
|
||||
ice.selectAll("*").remove();
|
||||
drawIce();
|
||||
}
|
||||
|
||||
function regenerateMarkers(event) {
|
||||
if (isCtrlClick(event)) prompt("Please provide markers number multiplier", {default:1, step:.01, min:0, max:100}, v => addNumberOfMarkers(v));
|
||||
else addNumberOfMarkers(gauss(1, .5, .3, 5, 2));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue