+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/ui/battle-screen.js b/modules/ui/battle-screen.js
index 70375f3e..b53789ef 100644
--- a/modules/ui/battle-screen.js
+++ b/modules/ui/battle-screen.js
@@ -7,16 +7,19 @@ class Battle {
customization = 13; // enter customization to avoid unwanted dialog closing
Battle.prototype.context = this; // store context
+ this.iteration = 0;
this.x = defender.x;
this.y = defender.y;
- this.name = this.getBattleName();
- this.iteration = 0;
- this.attackers = {regiments:[], distances:[], morale:100, casualties:0};
- this.defenders = {regiments:[], distances:[], morale:100, casualties:0};
+ this.cell = findCell(this.x, this.y);
+ this.attackers = {regiments:[], distances:[], morale:100, casualties:0, power:0};
+ this.defenders = {regiments:[], distances:[], morale:100, casualties:0, power:0};
this.addHeaders();
this.addRegiment("attackers", attacker);
this.addRegiment("defenders", defender);
+ this.place = this.definePlace();
+ this.defineType();
+ this.name = this.defineName();
this.randomize();
this.calculateStrength("attackers");
this.calculateStrength("defenders");
@@ -32,24 +35,81 @@ class Battle {
modules.Battle = true;
// add listeners
+ document.getElementById("battleType").addEventListener("click", ev => this.toggleChange(ev));
+ document.getElementById("battleType").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changeType(ev));
+ document.getElementById("battleNameShow").addEventListener("click", () => this.showNameSection());
+ document.getElementById("battleNamePlace").addEventListener("change", ev => this.place = ev.target.value);
+ document.getElementById("battleNameFull").addEventListener("change", ev => this.changeName(ev));
+ document.getElementById("battleNameCulture").addEventListener("click", () => this.generateName(Names.getCulture(pack.cells.culture[this.cell], null, null, "")));
+ document.getElementById("battleNameRandom").addEventListener("click", () => this.generateName(Names.getBase(rand(nameBases.length-1))));
+ document.getElementById("battleNameHide").addEventListener("click", this.hideNameSection);
document.getElementById("battleAddRegiment").addEventListener("click", this.addSide);
document.getElementById("battleRoll").addEventListener("click", () => Battle.prototype.context.randomize());
document.getElementById("battleRun").addEventListener("click", () => Battle.prototype.context.run());
document.getElementById("battleApply").addEventListener("click", () => Battle.prototype.context.applyResults());
document.getElementById("battleCancel").addEventListener("click", () => Battle.prototype.context.cancelResults());
- document.getElementById("battlePhase_attackers").addEventListener("click", ev => this.toggleChangePhase(ev, "attackers"));
+ document.getElementById("battlePhase_attackers").addEventListener("click", ev => this.toggleChange(ev));
document.getElementById("battlePhase_attackers").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "attackers"));
- document.getElementById("battlePhase_defenders").addEventListener("click", ev => this.toggleChangePhase(ev, "defenders"));
+ document.getElementById("battlePhase_defenders").addEventListener("click", ev => this.toggleChange(ev));
document.getElementById("battlePhase_defenders").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "defenders"));
document.getElementById("battleDie_attackers").addEventListener("click", () => Battle.prototype.context.rollDie("attackers"));
document.getElementById("battleDie_defenders").addEventListener("click", () => Battle.prototype.context.rollDie("defenders"));
}
- getBattleName() {
- const cell = findCell(this.x, this.y);
- const burg = pack.cells.burg[cell] ? pack.burgs[pack.cells.burg[cell]].name : null;
- return burg ? burg + " Battle" : Names.getCulture(pack.cells.culture[cell]) + " Battle"
+ defineType() {
+ const attacker = this.attackers.regiments[0];
+ const defender = this.defenders.regiments[0];
+ const getType = () => {
+ if (attacker.n && defender.n) return "naval"; // attacker and defender are navals
+ if (!defender.n && pack.burgs[pack.cells.burg[this.cell]].walls) return "siege"; // defender is in walled town
+ if (P(.1) && [5,6,7,8,9,12].includes(pack.cells.biome[this.cell])) return "ambush"; // 20% if defenders are in forest or marshes
+
+ const typesA = Object.keys(attacker.u).map(name => options.military.find(u => u.name === name).type);
+ const typesD = Object.keys(defender.u).map(name => options.military.find(u => u.name === name).type);
+
+ // if attacked is naval with non-naval units and defender is not naval
+ if (attacker.n && !defender.n && typesA.some(t => t !== "naval")) return "landing";
+
+ // if attacked and defender have only aviation units
+ if (typesA.every(t => t === "aviation") && typesD.every(t => t === "aviation")) return "air";
+
+ return "field";
+ }
+
+ this.type = getType();
+ this.setType();
+ }
+
+ setType() {
+ document.getElementById("battleType").className = "icon-button-" + this.type;
+
+ const sideSpecific = document.getElementById("battlePhases_"+this.type+"_attackers");
+ const attackers = sideSpecific ? sideSpecific.content : document.getElementById("battlePhases_"+this.type).content;
+ const defenders = sideSpecific ? document.getElementById("battlePhases_"+this.type+"_defenders").content : attackers;
+
+ document.getElementById("battlePhase_attackers").nextElementSibling.innerHTML = "";
+ document.getElementById("battlePhase_defenders").nextElementSibling.innerHTML = "";
+ document.getElementById("battlePhase_attackers").nextElementSibling.append(attackers.cloneNode(true));
+ document.getElementById("battlePhase_defenders").nextElementSibling.append(defenders.cloneNode(true));
+ }
+
+ definePlace() {
+ const cells = pack.cells, i = this.cell;
+ const burg = cells.burg[i] ? pack.burgs[cells.burg[i]].name : null;
+ const getRiver = i => {const river = pack.rivers.find(r => r.i === i); return river.name + " " + river.type};
+ const river = !burg && cells.r[i] ? getRiver(cells.r[i]) : null;
+ const proper = burg || river ? null : Names.getCulture(cells.culture[this.cell]);
+ return burg ? burg : river ? river : proper;
+ }
+
+ defineName() {
+ if (this.type === "field") return "Battle of " + this.place;
+ if (this.type === "naval") return "Naval Battle of " + this.place;
+ if (this.type === "siege") return "Siege of "+ this.place;
+ if (this.type === "ambush") return this.place + " Ambush";
+ if (this.type === "landing") return this.place + " Landing";
+ if (this.type === "air") return `${this.place} ${P(.8) ? "Air Battle" : "Dogfight"}`;
}
addHeaders() {
@@ -78,17 +138,17 @@ class Battle {
let initial = `
| ${icon} | ${regiment.name.slice(0, 24)} | `;
let casualties = `
| ${state.fullName.slice(0, 26)} | `;
- let survivors = `
| Distance to base: ${distance} ${distanceUnitInput.value} | `;
+ let survivors = `
| Distance to base: ${distance} ${distanceUnitInput.value} | `;
for (const u of options.military) {
- initial += `${regiment.u[u.name]||0} | `;
- casualties += `0 | `;
- survivors += `${regiment.u[u.name]||0} | `;
+ initial += `${regiment.u[u.name]||0} | `;
+ casualties += `0 | `;
+ survivors += `${regiment.u[u.name]||0} | `;
}
- initial += `${regiment.a||0} |
`;
- casualties += `
0 | `;
- survivors += `
${regiment.a||0} | `;
+ initial += `
${regiment.a||0} | `;
+ casualties += `
0 | `;
+ survivors += `
${regiment.a||0} | `;
const div = side === "attackers" ? battleAttackers : battleDefenders;
div.innerHTML += body + initial + casualties + survivors + "";
@@ -161,6 +221,30 @@ class Battle {
}
}
+ showNameSection() {
+ document.querySelectorAll("#battleBottom > button").forEach(el => el.style.display = "none");
+ document.getElementById("battleNameSection").style.display = "inline-block";
+
+ document.getElementById("battleNamePlace").value = this.place;
+ document.getElementById("battleNameFull").value = this.name;
+ }
+
+ hideNameSection() {
+ document.querySelectorAll("#battleBottom > button").forEach(el => el.style.display = "inline-block");
+ document.getElementById("battleNameSection").style.display = "none";
+ }
+
+ changeName(ev) {
+ this.name = ev.target.value;
+ $("#battleScreen").dialog({"title":this.name});
+ }
+
+ generateName(place) {
+ document.getElementById("battleNamePlace").value = this.place = place;
+ document.getElementById("battleNameFull").value = this.name = this.defineName();
+ $("#battleScreen").dialog({"title":this.name});
+ }
+
getJoinedForces(regiments) {
return regiments.reduce((a, b) => {
for (let k in b.survivors) {
@@ -173,10 +257,31 @@ class Battle {
calculateStrength(side) {
const scheme = {
+ // field battle phases
"skirmish": {"melee":.2, "ranged":2.4, "mounted":.1, "machinery":3, "naval":1, "armored":.2, "aviation":1.8, "magical":1.8}, // ranged excel
"melee": {"melee":2, "ranged":1.2, "mounted":1.5, "machinery":.5, "naval":.2, "armored":2, "aviation":.8, "magical":.8}, // melee excel
"pursue": {"melee":1, "ranged":1, "mounted":4, "machinery":.05, "naval":1, "armored":1, "aviation":1.5, "magical":.6}, // mounted excel
- "retreat": {"melee":.1, "ranged":.01, "mounted":.5, "machinery":.01, "naval":.2, "armored":.1, "aviation":.8, "magical":.05} // mounted excel
+ "retreat": {"melee":.1, "ranged":.01, "mounted":.5, "machinery":.01, "naval":.2, "armored":.1, "aviation":.8, "magical":.05}, // reduced
+
+ // naval battle phases
+ "shelling": {"melee":0, "ranged":.2, "mounted":0, "machinery":2, "naval":2, "armored":0, "aviation":.1, "magical":.5}, // naval and machinery excel
+ "boarding": {"melee":1, "ranged":.5, "mounted":.5, "machinery":0, "naval":.5, "armored":.4, "aviation":0, "magical":.2}, // melee excel
+ "chase": {"melee":0, "ranged":.15, "mounted":0, "machinery":1, "naval":1, "armored":0, "aviation":.15, "magical":.5}, // reduced
+ "withdrawal": {"melee":0, "ranged":.02, "mounted":0, "machinery":.5, "naval":.1, "armored":0, "aviation":.1, "magical":.3}, // reduced
+
+ // siege phases
+ "blockade": {"melee":.25, "ranged":.25, "mounted":.2, "machinery":.5, "naval":.2, "armored":.1, "aviation":.25, "magical":.25}, // no active actions
+ "sheltering": {"melee":.3, "ranged":.5, "mounted":.2, "machinery":.5, "naval":.2, "armored":.1, "aviation":.25, "magical":.25}, // no active actions
+ "sortie": {"melee":2, "ranged":.5, "mounted":1.2, "machinery":.2, "naval":.1, "armored":.5, "aviation":1, "magical":1}, // melee excel
+ "bombardment": {"melee":.2, "ranged":.5, "mounted":.2, "machinery":3, "naval":1, "armored":.5, "aviation":1, "magical":1}, // machinery excel
+ "storming": {"melee":1, "ranged":.6, "mounted":.5, "machinery":1, "naval":.1, "armored":.1, "aviation":.5, "magical":.5}, // melee excel
+ "defense": {"melee":2, "ranged":3, "mounted":1, "machinery":1, "naval":.1, "armored":1, "aviation":.5, "magical":1}, // ranged excel
+ "looting": {"melee":1.6, "ranged":1.6, "mounted":.5, "machinery":.2, "naval":.02, "armored":.2, "aviation":.1, "magical":.3}, // melee excel
+ "surrendering": {"melee":.1, "ranged":.1, "mounted":.05, "machinery":.01, "naval":.01, "armored":.02, "aviation":.01, "magical":.03}, // reduced
+
+ // ambush phases
+ "surprise": {"melee":2.2, "ranged":3, "mounted":1.5, "machinery":1.3, "naval":1, "armored":1.3, "aviation":1, "magical":2}, // increased
+ "shock": {"melee":.8, "ranged":.8, "mounted":.6, "machinery":.5, "naval":.5, "armored":.1, "aviation":.5, "magical":.7} // reduced
};
const forces = this.getJoinedForces(this[side].regiments);
@@ -215,34 +320,119 @@ class Battle {
rollDie(side) {
const el = document.getElementById("battleDie_"+side);
const prev = +el.innerHTML;
- do {el.innerHTML = rand(1, 6)} while (el.innerHTML === prev)
+ do {el.innerHTML = rand(1, 6)} while (el.innerHTML == prev)
this[side].die = +el.innerHTML;
}
selectPhase() {
- const phase = this.getPhase();
+ const i = this.iteration;
+ const morale = [this.attackers.morale, this.defenders.morale];
+ const powerRatio = this.attackers.power / this.defenders.power;
+
+ const getFieldBattlePhase = () => {
+ const prev = [this.attackers.phase || "skirmish", this.defenders.phase || "skirmish"]; // previous phase
+
+ // chance if moral < 25
+ if (P(1 - morale[0] / 25)) return ["retreat", "pursue"];
+ if (P(1 - morale[1] / 25)) return ["pursue", "retreat"];
+
+ // skirmish phase continuation depends on ranged forces number
+ if (prev[0] === "skirmish" && prev[1] === "skirmish") {
+ const forces = this.getJoinedForces(this.attackers.regiments.concat(this.defenders.regiments));
+ const total = d3.sum(Object.values(forces)); // total forces
+ const ranged = d3.sum(options.military.filter(u => u.type === "ranged").map(u => u.name).map(u => forces[u])) / total; // ranged units
+ if (P(ranged) || P(.8-i/10)) return ["skirmish", "skirmish"];
+ }
+
+ return ["melee", "melee"]; // default option
+ }
+
+ const getNavalBattlePhase = () => {
+ const prev = [this.attackers.phase || "shelling", this.defenders.phase || "shelling"]; // previous phase
+
+ if (prev[0] === "withdrawal") return ["withdrawal", "chase"];
+ if (prev[0] === "chase") return ["chase", "withdrawal"];
+
+ // withdrawal phase when power imbalanced
+ if (!prev[0] === "boarding") {
+ if (powerRatio < .5 || P(this.attackers.casualties) && powerRatio < 1) return ["withdrawal", "chase"];
+ if (powerRatio > 2 || P(this.defenders.casualties) && powerRatio > 1) return ["chase", "withdrawal"];
+ }
+
+ // boarding phase can start from 2nd iteration
+ if (prev[0] === "boarding" || P(i/10 - .1)) return ["boarding", "boarding"];
+
+ return ["shelling", "shelling"]; // default option
+ }
+
+ const getSiegePhase = () => {
+ const prev = [this.attackers.phase || "blockade", this.defenders.phase || "sheltering"]; // previous phase
+ let phase = ["blockade", "sheltering"] // default phase
+
+ if (prev[0] === "retreat" || prev[0] === "looting") return prev;
+
+ if (P(1 - morale[0] / 30) && powerRatio < 1) return ["retreat", "pursue"]; // attackers retreat chance if moral < 30
+ if (P(1 - morale[1] / 15)) return ["looting", "surrendering"]; // defenders surrendering chance if moral < 15
+
+ if (P((powerRatio-1) / 2)) return ["storming", "defense"]; // start storm
+
+ if (prev[0] !== "storming") {
+ const machinery = options.military.filter(u => u.type === "machinery").map(u => u.name); // machinery units
+
+ const attackers = this.getJoinedForces(this.attackers.regiments);
+ const machineryA = d3.sum(machinery.map(u => attackers[u]));
+ if (i && machineryA && P(.9)) phase[0] = "bombardment";
+
+ const defenders = this.getJoinedForces(this.defenders.regiments);
+ const machineryD = d3.sum(machinery.map(u => defenders[u]));
+ if (machineryD && P(.9)) phase[1] = "bombardment";
+
+ if (i && prev[1] !== "sortie" && machineryD < machineryA && P(.25) && P(morale[1]/70)) phase[1] = "sortie"; // defenders sortie
+ }
+
+ return phase;
+ }
+
+ const getAmbushPhase = () => {
+ const prev = [this.attackers.phase || "shock", this.defenders.phase || "surprise"]; // previous phase
+
+ if (prev[1] === "surprise" && P(1-powerRatio*i/5)) return ["shock", "surprise"];
+
+ // chance if moral < 25
+ if (P(1 - morale[0] / 25)) return ["retreat", "pursue"];
+ if (P(1 - morale[1] / 25)) return ["pursue", "retreat"];
+
+ return ["melee", "melee"]; // default option
+ }
+
+ const getLandingPhase = () => {
+ // ⚓ landing / 💫 shock OR 🛡️ defense, ⚔️ melee, 🏳️ retreat / 🐎 pursue
+
+ }
+
+ const getAirBattlePhase = () => {
+ // 🎯 maneuvering, 🐕 dogfight, 🏳️ retreat / 🐎 pursue
+
+ }
+
+ const phase = function(type) {
+ switch (type) {
+ case "field": return getFieldBattlePhase();
+ case "naval": return getNavalBattlePhase();
+ case "siege": return getSiegePhase();
+ case "ambush": return getAmbushPhase();
+ case "landing": return getLandingPhase();
+ case "air": return getAirBattlePhase();
+ default: getFieldBattlePhase();
+ }
+ }(this.type);
+
this.attackers.phase = phase[0];
this.defenders.phase = phase[1];
document.getElementById("battlePhase_attackers").className = "icon-button-" + this.attackers.phase;
document.getElementById("battlePhase_defenders").className = "icon-button-" + this.defenders.phase;
- }
-
- getPhase() {
- const i = this.iteration;
- const prev = [this.attackers.phase || "skirmish", this.defenders.phase || "skirmish"]; // previous phase
- const morale = [this.attackers.morale, this.defenders.morale];
-
- if (P(1 - morale[0] / 25)) return ["retreat", "pursue"];
- if (P(1 - morale[1] / 25)) return ["pursue", "retreat"];
-
- if (prev[0] === "skirmish" && prev[1] === "skirmish") {
- const forces = this.getJoinedForces(this.attackers.regiments.concat(this.defenders.regiments));
- const total = d3.sum(Object.values(forces)); // total forces
- const ranged = d3.sum(options.military.filter(u => u.type === "ranged").map(u => u.name).map(u => forces[u])) / total;
- if (ranged && (P(ranged) || P(.8-i/10))) return ["skirmish", "skirmish"];
- }
-
- return ["melee", "melee"]; // default option
+ document.getElementById("battlePhase_attackers").dataset.tip = battleBody.querySelector(".battlePhases > [data-phase='"+phase[0]+"']").dataset.tip;
+ document.getElementById("battlePhase_defenders").dataset.tip = battleBody.querySelector(".battlePhases > [data-phase='"+phase[1]+"']").dataset.tip;
}
run() {
@@ -253,8 +443,16 @@ class Battle {
// calculate casualties
const attack = this.attackers.power * (this.attackers.die / 10 + .4);
const defence = this.defenders.power * (this.defenders.die / 10 + .4);
- const phase = {"skirmish":.1, "melee":.2, "pursue":.3, "retreat":.3}; // casualties modifier for phase
- const casualties = Math.random() * phase[this.attackers.phase]; // total casualties, ~10% per iteration
+
+ // casualties modifier for phase
+ const phase = {
+ "skirmish":.1, "melee":.2, "pursue":.3, "retreat":.3,
+ "boarding":.2, "shelling":.1, "chase":.03, "withdrawal": .03,
+ "blockade":0, "sheltering":0, "sortie":.1, "bombardment":.05, "storming":.2, "defense":.2, "looting":.5, "surrendering":.5,
+ "surprise":.3, "shock":.3
+ };
+
+ const casualties = Math.random() * (Math.max(phase[this.attackers.phase], phase[this.defenders.phase])); // total casualties, ~10% per iteration
const casualtiesA = casualties * defence / (attack + defence); // attackers casualties, ~5% per iteration
const casualtiesD = casualties * attack / (attack + defence); // defenders casualties, ~5% per iteration
@@ -264,8 +462,8 @@ class Battle {
this.defenders.casualties += casualtiesD;
// change morale
- this.attackers.morale = Math.max(this.attackers.morale - casualtiesA * 100, 0);
- this.defenders.morale = Math.max(this.defenders.morale - casualtiesD * 100, 0);
+ this.attackers.morale = Math.max(this.attackers.morale - casualtiesA * 100 - 1, 0);
+ this.defenders.morale = Math.max(this.defenders.morale - casualtiesD * 100 - 1, 0);
// update table values
this.updateTable("attackers");
@@ -308,22 +506,37 @@ class Battle {
this.updateMorale(side);
}
- toggleChangePhase(ev, side) {
+ toggleChange(ev) {
ev.stopPropagation();
- const button = document.getElementById("battlePhase_"+side);
+ const button = ev.target;
const div = button.nextElementSibling;
+
+ const hideSection = function() {button.style.opacity = 1; div.style.display = "none"}
+ if (div.style.display === "block") {hideSection(); return}
+
button.style.opacity = .5;
div.style.display = "block";
- const hideSection = function() {button.style.opacity = 1; div.style.display = "none"}
document.getElementsByTagName("body")[0].addEventListener("click", hideSection, {once: true});
}
+ changeType(ev) {
+ if (ev.target.tagName !== "BUTTON") return;
+ this.type = ev.target.dataset.type;
+ this.setType();
+ this.selectPhase();
+ this.calculateStrength("attackers");
+ this.calculateStrength("defenders");
+ this.name = this.defineName();
+ $("#battleScreen").dialog({"title":this.name});
+ }
+
changePhase(ev, side) {
if (ev.target.tagName !== "BUTTON") return;
const phase = this[side].phase = ev.target.dataset.phase;
const button = document.getElementById("battlePhase_"+side);
button.className = "icon-button-" + phase;
+ button.dataset.tip = ev.target.dataset.tip;
this.calculateStrength(side);
}
diff --git a/modules/ui/burg-editor.js b/modules/ui/burg-editor.js
index da68ad20..b129df88 100644
--- a/modules/ui/burg-editor.js
+++ b/modules/ui/burg-editor.js
@@ -316,35 +316,12 @@ function editBurg(id) {
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;
- switch(norm) {
- case 0 : return "&southSea=1";
- case 0.25 : return "&southSea=1&westSea=1";
- case 0.50 : return "&westSea=1";
- case 0.75 : return "&westSea=1&northSea=1";
- case 1 : return "&northSea=1";
- case 1.25 : return "&northSea=1&eastSea=1";
- case 1.5 : return "&eastSea=1";
- case 1.75 : return "&eastSea=1&southSea=1";
- }
+ const norm = rn(normalize(deg, 0, 360) * 2, 2); // 0 = south, 0.5 = west, 1 = north, 1.5 = east
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}${sea}`;
+ const site = "http://fantasycities.watabou.ru/?random=0&continuous=0";
+ const url = `${site}&name=${name}&size=${size}&seed=${s}&hub=${hub}&river=${river}&coast=${coast}&citadel=${citadel}&plaza=${plaza}&temple=${temple}&walls=${walls}&shantytown=${shanty}${sea}`;
openURL(url);
}
}
diff --git a/modules/ui/options.js b/modules/ui/options.js
index 5fbef6a8..ec700197 100644
--- a/modules/ui/options.js
+++ b/modules/ui/options.js
@@ -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";
+ 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";
alertMessage.innerHTML = "
" + supporters.split(", ").sort().map(n => `- ${n}
`).join("") + "
";
$("#alert").dialog({resizable: false, title: "Patreon Supporters", width: "30vw", position: {my: "center", at: "center", of: "svg"}});
}