mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-24 04:51:24 +01:00
merge completed... now to fix all the bugs...
This commit is contained in:
commit
87c4d80fbc
3472 changed files with 466748 additions and 6517 deletions
|
|
@ -1,8 +1,8 @@
|
|||
"use strict";
|
||||
'use strict';
|
||||
class Battle {
|
||||
constructor(attacker, defender) {
|
||||
if (customization) return;
|
||||
closeDialogs(".stable");
|
||||
closeDialogs('.stable');
|
||||
customization = 13; // enter customization to avoid unwanted dialog closing
|
||||
|
||||
Battle.prototype.context = this; // store context
|
||||
|
|
@ -14,21 +14,21 @@ class Battle {
|
|||
this.defenders = {regiments: [], distances: [], morale: 100, casualties: 0, power: 0};
|
||||
|
||||
this.addHeaders();
|
||||
this.addRegiment("attackers", attacker);
|
||||
this.addRegiment("defenders", defender);
|
||||
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");
|
||||
this.calculateStrength('attackers');
|
||||
this.calculateStrength('defenders');
|
||||
this.getInitialMorale();
|
||||
|
||||
$("#battleScreen").dialog({
|
||||
$('#battleScreen').dialog({
|
||||
title: this.name,
|
||||
resizable: false,
|
||||
width: fitContent(),
|
||||
position: {my: "center", at: "center", of: "#map"},
|
||||
position: {my: 'center', at: 'center', of: '#map'},
|
||||
close: () => Battle.prototype.context.cancelResults()
|
||||
});
|
||||
|
||||
|
|
@ -36,42 +36,42 @@ 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", () => Battle.prototype.context.showNameSection());
|
||||
document.getElementById("battleNamePlace").addEventListener("change", ev => (Battle.prototype.context.place = ev.target.value));
|
||||
document.getElementById("battleNameFull").addEventListener("change", ev => Battle.prototype.context.changeName(ev));
|
||||
document.getElementById("battleNameCulture").addEventListener("click", () => Battle.prototype.context.generateName("culture"));
|
||||
document.getElementById("battleNameRandom").addEventListener("click", () => Battle.prototype.context.generateName("random"));
|
||||
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("battleWiki").addEventListener("click", () => wiki("Battle-Simulator"));
|
||||
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', () => Battle.prototype.context.showNameSection());
|
||||
document.getElementById('battleNamePlace').addEventListener('change', (ev) => (Battle.prototype.context.place = ev.target.value));
|
||||
document.getElementById('battleNameFull').addEventListener('change', (ev) => Battle.prototype.context.changeName(ev));
|
||||
document.getElementById('battleNameCulture').addEventListener('click', () => Battle.prototype.context.generateName('culture'));
|
||||
document.getElementById('battleNameRandom').addEventListener('click', () => Battle.prototype.context.generateName('random'));
|
||||
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('battleWiki').addEventListener('click', () => wiki('Battle-Simulator'));
|
||||
|
||||
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.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"));
|
||||
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.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'));
|
||||
}
|
||||
|
||||
defineType() {
|
||||
const attacker = this.attackers.regiments[0];
|
||||
const defender = this.defenders.regiments[0];
|
||||
const getType = () => {
|
||||
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);
|
||||
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 (attacker.n && defender.n) return "naval"; // attacker and defender are navals
|
||||
if (typesA.every(t => t === "aviation") && typesD.every(t => t === "aviation")) return "air"; // if attackers and defender have only aviation units
|
||||
if (attacker.n && !defender.n && typesA.some(t => t !== "naval")) return "landing"; // if attacked is naval with non-naval units and defender is not naval
|
||||
if (!defender.n && pack.burgs[pack.cells.burg[this.cell]].walls) return "siege"; // defender is in walled town
|
||||
if (P(0.1) && [5, 6, 7, 8, 9, 12].includes(pack.cells.biome[this.cell])) return "ambush"; // 20% if defenders are in forest or marshes
|
||||
return "field";
|
||||
if (attacker.n && defender.n) return 'naval'; // attacker and defender are navals
|
||||
if (typesA.every((t) => t === 'aviation') && typesD.every((t) => t === 'aviation')) return 'air'; // if attackers and defender have only aviation units
|
||||
if (attacker.n && !defender.n && typesA.some((t) => t !== 'naval')) return 'landing'; // if attacked is naval with non-naval units and defender is not naval
|
||||
if (!defender.n && pack.burgs[pack.cells.burg[this.cell]].walls) return 'siege'; // defender is in walled town
|
||||
if (P(0.1) && [5, 6, 7, 8, 9, 12].includes(pack.cells.biome[this.cell])) return 'ambush'; // 20% if defenders are in forest or marshes
|
||||
return 'field';
|
||||
};
|
||||
|
||||
this.type = getType();
|
||||
|
|
@ -79,25 +79,25 @@ class Battle {
|
|||
}
|
||||
|
||||
setType() {
|
||||
document.getElementById("battleType").className = "icon-button-" + this.type;
|
||||
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;
|
||||
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));
|
||||
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 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]);
|
||||
|
|
@ -105,28 +105,28 @@ class Battle {
|
|||
}
|
||||
|
||||
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(0.8) ? "Air Battle" : "Dogfight"}`;
|
||||
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(0.8) ? 'Air Battle' : 'Dogfight'}`;
|
||||
}
|
||||
|
||||
getTypeName() {
|
||||
if (this.type === "field") return "field battle";
|
||||
if (this.type === "naval") return "naval battle";
|
||||
if (this.type === "siege") return "siege";
|
||||
if (this.type === "ambush") return "ambush";
|
||||
if (this.type === "landing") return "landing";
|
||||
if (this.type === "air") return "battle";
|
||||
if (this.type === 'field') return 'field battle';
|
||||
if (this.type === 'naval') return 'naval battle';
|
||||
if (this.type === 'siege') return 'siege';
|
||||
if (this.type === 'ambush') return 'ambush';
|
||||
if (this.type === 'landing') return 'landing';
|
||||
if (this.type === 'air') return 'battle';
|
||||
}
|
||||
|
||||
addHeaders() {
|
||||
let headers = "<thead><tr><th></th><th></th>";
|
||||
let headers = '<thead><tr><th></th><th></th>';
|
||||
|
||||
for (const u of options.military) {
|
||||
const label = capitalize(u.name.replace(/_/g, " "));
|
||||
const label = capitalize(u.name.replace(/_/g, ' '));
|
||||
headers += `<th data-tip="${label}">${u.icon}</th>`;
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ class Battle {
|
|||
|
||||
const state = pack.states[regiment.state];
|
||||
const distance = (Math.hypot(this.y - regiment.by, this.x - regiment.bx) * distanceScaleInput.value) | 0; // distance between regiment and its base
|
||||
const color = state.color[0] === "#" ? state.color : "#999";
|
||||
const color = state.color[0] === '#' ? state.color : '#999';
|
||||
const icon = `<svg width="1.4em" height="1.4em" style="margin-bottom: -.6em">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="${color}" class="fillRect"></rect>
|
||||
<text x="0" y="1.04em" style="">${regiment.icon}</text></svg>`;
|
||||
|
|
@ -160,28 +160,28 @@ class Battle {
|
|||
casualties += `<td data-tip="Casualties" style="width: 2.5em; text-align: center; color: red">0</td></tr>`;
|
||||
survivors += `<td data-tip="Survivors" style="width: 2.5em; text-align: center; color: green">${regiment.a || 0}</td></tr>`;
|
||||
|
||||
const div = side === "attackers" ? battleAttackers : battleDefenders;
|
||||
div.innerHTML += body + initial + casualties + survivors + "</tbody>";
|
||||
const div = side === 'attackers' ? battleAttackers : battleDefenders;
|
||||
div.innerHTML += body + initial + casualties + survivors + '</tbody>';
|
||||
this[side].regiments.push(regiment);
|
||||
this[side].distances.push(distance);
|
||||
}
|
||||
|
||||
addSide() {
|
||||
const body = document.getElementById("regimentSelectorBody");
|
||||
const body = document.getElementById('regimentSelectorBody');
|
||||
const context = Battle.prototype.context;
|
||||
const regiments = pack.states
|
||||
.filter(s => s.military && !s.removed)
|
||||
.map(s => s.military)
|
||||
.filter((s) => s.military && !s.removed)
|
||||
.map((s) => s.military)
|
||||
.flat();
|
||||
const distance = reg => rn(Math.hypot(context.y - reg.y, context.x - reg.x) * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
||||
const isAdded = reg => context.defenders.regiments.some(r => r === reg) || context.attackers.regiments.some(r => r === reg);
|
||||
const distance = (reg) => rn(Math.hypot(context.y - reg.y, context.x - reg.x) * distanceScaleInput.value) + ' ' + distanceUnitInput.value;
|
||||
const isAdded = (reg) => context.defenders.regiments.some((r) => r === reg) || context.attackers.regiments.some((r) => r === reg);
|
||||
|
||||
body.innerHTML = regiments
|
||||
.map(r => {
|
||||
.map((r) => {
|
||||
const s = pack.states[r.state],
|
||||
added = isAdded(r),
|
||||
dist = added ? "0 " + distanceUnitInput.value : distance(r);
|
||||
return `<div ${added ? "class='inactive'" : ""} data-s=${s.i} data-i=${r.i} data-state=${s.name} data-regiment=${r.name}
|
||||
dist = added ? '0 ' + distanceUnitInput.value : distance(r);
|
||||
return `<div ${added ? "class='inactive'" : ''} data-s=${s.i} data-i=${r.i} data-state=${s.name} data-regiment=${r.name}
|
||||
data-total=${r.a} data-distance=${dist} data-tip="Click to select regiment">
|
||||
<svg width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" class="fillRect"></svg>
|
||||
<div style="width:6em">${s.name.slice(0, 11)}</div>
|
||||
|
|
@ -191,43 +191,43 @@ class Battle {
|
|||
<div style="width:4em">${dist}</div>
|
||||
</div>`;
|
||||
})
|
||||
.join("");
|
||||
.join('');
|
||||
|
||||
$("#regimentSelectorScreen").dialog({
|
||||
$('#regimentSelectorScreen').dialog({
|
||||
resizable: false,
|
||||
width: fitContent(),
|
||||
title: "Add regiment to the battle",
|
||||
position: {my: "left center", at: "right+10 center", of: "#battleScreen"},
|
||||
title: 'Add regiment to the battle',
|
||||
position: {my: 'left center', at: 'right+10 center', of: '#battleScreen'},
|
||||
close: addSideClosed,
|
||||
buttons: {
|
||||
"Add to attackers": () => addSideClicked("attackers"),
|
||||
"Add to defenders": () => addSideClicked("defenders"),
|
||||
Cancel: () => $("#regimentSelectorScreen").dialog("close")
|
||||
'Add to attackers': () => addSideClicked('attackers'),
|
||||
'Add to defenders': () => addSideClicked('defenders'),
|
||||
Cancel: () => $('#regimentSelectorScreen').dialog('close')
|
||||
}
|
||||
});
|
||||
|
||||
applySorting(regimentSelectorHeader);
|
||||
body.addEventListener("click", selectLine);
|
||||
body.addEventListener('click', selectLine);
|
||||
|
||||
function selectLine(ev) {
|
||||
if (ev.target.className === "inactive") {
|
||||
tip("Regiment is already in the battle", false, "error");
|
||||
if (ev.target.className === 'inactive') {
|
||||
tip('Regiment is already in the battle', false, 'error');
|
||||
return;
|
||||
}
|
||||
ev.target.classList.toggle("selected");
|
||||
ev.target.classList.toggle('selected');
|
||||
}
|
||||
|
||||
function addSideClicked(side) {
|
||||
const selected = body.querySelectorAll(".selected");
|
||||
const selected = body.querySelectorAll('.selected');
|
||||
if (!selected.length) {
|
||||
tip("Please select a regiment first", false, "error");
|
||||
tip('Please select a regiment first', false, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
$("#regimentSelectorScreen").dialog("close");
|
||||
selected.forEach(line => {
|
||||
$('#regimentSelectorScreen').dialog('close');
|
||||
selected.forEach((line) => {
|
||||
const state = pack.states[line.dataset.s];
|
||||
const regiment = state.military.find(r => r.i == +line.dataset.i);
|
||||
const regiment = state.military.find((r) => r.i == +line.dataset.i);
|
||||
Battle.prototype.addRegiment.call(context, side, regiment);
|
||||
Battle.prototype.calculateStrength.call(context, side);
|
||||
Battle.prototype.getInitialMorale.call(context);
|
||||
|
|
@ -235,7 +235,7 @@ class Battle {
|
|||
// move regiment
|
||||
const defenders = context.defenders.regiments,
|
||||
attackers = context.attackers.regiments;
|
||||
const shift = side === "attackers" ? attackers.length * -8 : (defenders.length - 1) * 8;
|
||||
const shift = side === 'attackers' ? attackers.length * -8 : (defenders.length - 1) * 8;
|
||||
regiment.px = regiment.x;
|
||||
regiment.py = regiment.y;
|
||||
Military.moveRegiment(regiment, defenders[0].x, defenders[0].y + shift);
|
||||
|
|
@ -243,34 +243,34 @@ class Battle {
|
|||
}
|
||||
|
||||
function addSideClosed() {
|
||||
body.innerHTML = "";
|
||||
body.removeEventListener("click", selectLine);
|
||||
body.innerHTML = '';
|
||||
body.removeEventListener('click', selectLine);
|
||||
}
|
||||
}
|
||||
|
||||
showNameSection() {
|
||||
document.querySelectorAll("#battleBottom > button").forEach(el => (el.style.display = "none"));
|
||||
document.getElementById("battleNameSection").style.display = "inline-block";
|
||||
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;
|
||||
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";
|
||||
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});
|
||||
$('#battleScreen').dialog({title: this.name});
|
||||
}
|
||||
|
||||
generateName(type) {
|
||||
const place = type === "culture" ? Names.getCulture(pack.cells.culture[this.cell], null, null, "") : Names.getBase(rand(nameBases.length - 1));
|
||||
document.getElementById("battleNamePlace").value = this.place = place;
|
||||
document.getElementById("battleNameFull").value = this.name = this.defineName();
|
||||
$("#battleScreen").dialog({title: this.name});
|
||||
const place = type === 'culture' ? Names.getCulture(pack.cells.culture[this.cell], null, null, '') : Names.getBase(rand(nameBases.length - 1));
|
||||
document.getElementById('battleNamePlace').value = this.place = place;
|
||||
document.getElementById('battleNameFull').value = this.name = this.defineName();
|
||||
$('#battleScreen').dialog({title: this.name});
|
||||
}
|
||||
|
||||
getJoinedForces(regiments) {
|
||||
|
|
@ -324,38 +324,38 @@ class Battle {
|
|||
const forces = this.getJoinedForces(this[side].regiments);
|
||||
const phase = this[side].phase;
|
||||
const adjuster = Math.max(populationRate / 10, 10); // population adjuster, by default 100
|
||||
this[side].power = d3.sum(options.military.map(u => (forces[u.name] || 0) * u.power * scheme[phase][u.type])) / adjuster;
|
||||
this[side].power = d3.sum(options.military.map((u) => (forces[u.name] || 0) * u.power * scheme[phase][u.type])) / adjuster;
|
||||
const UIvalue = this[side].power ? Math.max(this[side].power | 0, 1) : 0;
|
||||
document.getElementById("battlePower_" + side).innerHTML = UIvalue;
|
||||
document.getElementById('battlePower_' + side).innerHTML = UIvalue;
|
||||
}
|
||||
|
||||
getInitialMorale() {
|
||||
const powerFee = diff => Math.min(Math.max(100 - diff ** 1.5 * 10 + 10, 50), 100);
|
||||
const distanceFee = dist => Math.min(d3.mean(dist) / 50, 15);
|
||||
const powerFee = (diff) => minmax(100 - diff ** 1.5 * 10 + 10, 50, 100);
|
||||
const distanceFee = (dist) => Math.min(d3.mean(dist) / 50, 15);
|
||||
const powerDiff = this.defenders.power / this.attackers.power;
|
||||
this.attackers.morale = powerFee(powerDiff) - distanceFee(this.attackers.distances);
|
||||
this.defenders.morale = powerFee(1 / powerDiff) - distanceFee(this.defenders.distances);
|
||||
this.updateMorale("attackers");
|
||||
this.updateMorale("defenders");
|
||||
this.updateMorale('attackers');
|
||||
this.updateMorale('defenders');
|
||||
}
|
||||
|
||||
updateMorale(side) {
|
||||
const morale = document.getElementById("battleMorale_" + side);
|
||||
morale.dataset.tip = morale.dataset.tip.replace(morale.value, "");
|
||||
const morale = document.getElementById('battleMorale_' + side);
|
||||
morale.dataset.tip = morale.dataset.tip.replace(morale.value, '');
|
||||
morale.value = this[side].morale | 0;
|
||||
morale.dataset.tip += morale.value;
|
||||
}
|
||||
|
||||
randomize() {
|
||||
this.rollDie("attackers");
|
||||
this.rollDie("defenders");
|
||||
this.rollDie('attackers');
|
||||
this.rollDie('defenders');
|
||||
this.selectPhase();
|
||||
this.calculateStrength("attackers");
|
||||
this.calculateStrength("defenders");
|
||||
this.calculateStrength('attackers');
|
||||
this.calculateStrength('defenders');
|
||||
}
|
||||
|
||||
rollDie(side) {
|
||||
const el = document.getElementById("battleDie_" + side);
|
||||
const el = document.getElementById('battleDie_' + side);
|
||||
const prev = +el.innerHTML;
|
||||
do {
|
||||
el.innerHTML = rand(1, 6);
|
||||
|
|
@ -369,131 +369,131 @@ class Battle {
|
|||
const powerRatio = this.attackers.power / this.defenders.power;
|
||||
|
||||
const getFieldBattlePhase = () => {
|
||||
const prev = [this.attackers.phase || "skirmish", this.defenders.phase || "skirmish"]; // previous phase
|
||||
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"];
|
||||
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") {
|
||||
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])
|
||||
.filter((u) => u.type === 'ranged')
|
||||
.map((u) => u.name)
|
||||
.map((u) => forces[u])
|
||||
) / total; // ranged units
|
||||
if (P(ranged) || P(0.8 - i / 10)) return ["skirmish", "skirmish"];
|
||||
if (P(ranged) || P(0.8 - i / 10)) return ['skirmish', 'skirmish'];
|
||||
}
|
||||
|
||||
return ["melee", "melee"]; // default option
|
||||
return ['melee', 'melee']; // default option
|
||||
};
|
||||
|
||||
const getNavalBattlePhase = () => {
|
||||
const prev = [this.attackers.phase || "shelling", this.defenders.phase || "shelling"]; // previous phase
|
||||
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"];
|
||||
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 < 0.5 || (P(this.attackers.casualties) && powerRatio < 1)) return ["withdrawal", "chase"];
|
||||
if (powerRatio > 2 || (P(this.defenders.casualties) && powerRatio > 1)) return ["chase", "withdrawal"];
|
||||
if (!prev[0] === 'boarding') {
|
||||
if (powerRatio < 0.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 - 0.1)) return ["boarding", "boarding"];
|
||||
if (prev[0] === 'boarding' || P(i / 10 - 0.1)) return ['boarding', 'boarding'];
|
||||
|
||||
return ["shelling", "shelling"]; // default option
|
||||
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
|
||||
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 (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(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 (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
|
||||
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(0.9)) phase[0] = "bombardment";
|
||||
const machineryA = d3.sum(machinery.map((u) => attackers[u]));
|
||||
if (i && machineryA && P(0.9)) phase[0] = 'bombardment';
|
||||
|
||||
const defenders = this.getJoinedForces(this.defenders.regiments);
|
||||
const machineryD = d3.sum(machinery.map(u => defenders[u]));
|
||||
if (machineryD && P(0.9)) phase[1] = "bombardment";
|
||||
const machineryD = d3.sum(machinery.map((u) => defenders[u]));
|
||||
if (machineryD && P(0.9)) phase[1] = 'bombardment';
|
||||
|
||||
if (i && prev[1] !== "sortie" && machineryD < machineryA && P(0.25) && P(morale[1] / 70)) phase[1] = "sortie"; // defenders sortie
|
||||
if (i && prev[1] !== 'sortie' && machineryD < machineryA && P(0.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
|
||||
const prev = [this.attackers.phase || 'shock', this.defenders.phase || 'surprise']; // previous phase
|
||||
|
||||
if (prev[1] === "surprise" && P(1 - (powerRatio * i) / 5)) return ["shock", "surprise"];
|
||||
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"];
|
||||
if (P(1 - morale[0] / 25)) return ['retreat', 'pursue'];
|
||||
if (P(1 - morale[1] / 25)) return ['pursue', 'retreat'];
|
||||
|
||||
return ["melee", "melee"]; // default option
|
||||
return ['melee', 'melee']; // default option
|
||||
};
|
||||
|
||||
const getLandingPhase = () => {
|
||||
const prev = [this.attackers.phase || "landing", this.defenders.phase || "defense"]; // previous phase
|
||||
const prev = [this.attackers.phase || 'landing', this.defenders.phase || 'defense']; // previous phase
|
||||
|
||||
if (prev[1] === "waiting") return ["flee", "waiting"];
|
||||
if (prev[1] === "pursue") return ["flee", P(0.3) ? "pursue" : "waiting"];
|
||||
if (prev[1] === "retreat") return ["pursue", "retreat"];
|
||||
if (prev[1] === 'waiting') return ['flee', 'waiting'];
|
||||
if (prev[1] === 'pursue') return ['flee', P(0.3) ? 'pursue' : 'waiting'];
|
||||
if (prev[1] === 'retreat') return ['pursue', 'retreat'];
|
||||
|
||||
if (prev[0] === "landing") {
|
||||
const attackers = P(i / 2) ? "melee" : "landing";
|
||||
const defenders = i ? prev[1] : P(0.5) ? "defense" : "shock";
|
||||
if (prev[0] === 'landing') {
|
||||
const attackers = P(i / 2) ? 'melee' : 'landing';
|
||||
const defenders = i ? prev[1] : P(0.5) ? 'defense' : 'shock';
|
||||
return [attackers, defenders];
|
||||
}
|
||||
|
||||
if (P(1 - morale[0] / 40)) return ["flee", "pursue"]; // chance if moral < 40
|
||||
if (P(1 - morale[1] / 25)) return ["pursue", "retreat"]; // chance if moral < 25
|
||||
if (P(1 - morale[0] / 40)) return ['flee', 'pursue']; // chance if moral < 40
|
||||
if (P(1 - morale[1] / 25)) return ['pursue', 'retreat']; // chance if moral < 25
|
||||
|
||||
return ["melee", "melee"]; // default option
|
||||
return ['melee', 'melee']; // default option
|
||||
};
|
||||
|
||||
const getAirBattlePhase = () => {
|
||||
const prev = [this.attackers.phase || "maneuvering", this.defenders.phase || "maneuvering"]; // previous phase
|
||||
const prev = [this.attackers.phase || 'maneuvering', this.defenders.phase || 'maneuvering']; // previous phase
|
||||
|
||||
// chance if moral < 25
|
||||
if (P(1 - morale[0] / 25)) return ["retreat", "pursue"];
|
||||
if (P(1 - morale[1] / 25)) return ["pursue", "retreat"];
|
||||
if (P(1 - morale[0] / 25)) return ['retreat', 'pursue'];
|
||||
if (P(1 - morale[1] / 25)) return ['pursue', 'retreat'];
|
||||
|
||||
if (prev[0] === "maneuvering" && P(1 - i / 10)) return ["maneuvering", "maneuvering"];
|
||||
if (prev[0] === 'maneuvering' && P(1 - i / 10)) return ['maneuvering', 'maneuvering'];
|
||||
|
||||
return ["dogfight", "dogfight"]; // default option
|
||||
return ['dogfight', 'dogfight']; // default option
|
||||
};
|
||||
|
||||
const phase = (function (type) {
|
||||
switch (type) {
|
||||
case "field":
|
||||
case 'field':
|
||||
return getFieldBattlePhase();
|
||||
case "naval":
|
||||
case 'naval':
|
||||
return getNavalBattlePhase();
|
||||
case "siege":
|
||||
case 'siege':
|
||||
return getSiegePhase();
|
||||
case "ambush":
|
||||
case 'ambush':
|
||||
return getAmbushPhase();
|
||||
case "landing":
|
||||
case 'landing':
|
||||
return getLandingPhase();
|
||||
case "air":
|
||||
case 'air':
|
||||
return getAirBattlePhase();
|
||||
default:
|
||||
getFieldBattlePhase();
|
||||
|
|
@ -503,23 +503,23 @@ class Battle {
|
|||
this.attackers.phase = phase[0];
|
||||
this.defenders.phase = phase[1];
|
||||
|
||||
const buttonA = document.getElementById("battlePhase_attackers");
|
||||
buttonA.className = "icon-button-" + this.attackers.phase;
|
||||
const buttonA = document.getElementById('battlePhase_attackers');
|
||||
buttonA.className = 'icon-button-' + this.attackers.phase;
|
||||
buttonA.dataset.tip = buttonA.nextElementSibling.querySelector("[data-phase='" + phase[0] + "']").dataset.tip;
|
||||
|
||||
const buttonD = document.getElementById("battlePhase_defenders");
|
||||
buttonD.className = "icon-button-" + this.defenders.phase;
|
||||
const buttonD = document.getElementById('battlePhase_defenders');
|
||||
buttonD.className = 'icon-button-' + this.defenders.phase;
|
||||
buttonD.dataset.tip = buttonD.nextElementSibling.querySelector("[data-phase='" + phase[1] + "']").dataset.tip;
|
||||
}
|
||||
|
||||
run() {
|
||||
// validations
|
||||
if (!this.attackers.power) {
|
||||
tip("Attackers army destroyed", false, "warn");
|
||||
tip('Attackers army destroyed', false, 'warn');
|
||||
return;
|
||||
}
|
||||
if (!this.defenders.power) {
|
||||
tip("Defenders army destroyed", false, "warn");
|
||||
tip('Defenders army destroyed', false, 'warn');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -558,8 +558,8 @@ class Battle {
|
|||
const casualtiesA = (casualties * defense) / (attack + defense); // attackers casualties, ~5% per iteration
|
||||
const casualtiesD = (casualties * attack) / (attack + defense); // defenders casualties, ~5% per iteration
|
||||
|
||||
this.calculateCasualties("attackers", casualtiesA);
|
||||
this.calculateCasualties("defenders", casualtiesD);
|
||||
this.calculateCasualties('attackers', casualtiesA);
|
||||
this.calculateCasualties('defenders', casualtiesD);
|
||||
this.attackers.casualties += casualtiesA;
|
||||
this.defenders.casualties += casualtiesD;
|
||||
|
||||
|
|
@ -568,14 +568,14 @@ class Battle {
|
|||
this.defenders.morale = Math.max(this.defenders.morale - casualtiesD * 100 - 1, 0);
|
||||
|
||||
// update table values
|
||||
this.updateTable("attackers");
|
||||
this.updateTable("defenders");
|
||||
this.updateTable('attackers');
|
||||
this.updateTable('defenders');
|
||||
|
||||
// prepare for next iteration
|
||||
this.iteration += 1;
|
||||
this.selectPhase();
|
||||
this.calculateStrength("attackers");
|
||||
this.calculateStrength("defenders");
|
||||
this.calculateStrength('attackers');
|
||||
this.calculateStrength('defenders');
|
||||
}
|
||||
|
||||
calculateCasualties(side, casualties) {
|
||||
|
|
@ -591,9 +591,9 @@ class Battle {
|
|||
|
||||
updateTable(side) {
|
||||
for (const r of this[side].regiments) {
|
||||
const tbody = document.getElementById("battle" + r.state + "-" + r.i);
|
||||
const battleCasualties = tbody.querySelector(".battleCasualties");
|
||||
const battleSurvivors = tbody.querySelector(".battleSurvivors");
|
||||
const tbody = document.getElementById('battle' + r.state + '-' + r.i);
|
||||
const battleCasualties = tbody.querySelector('.battleCasualties');
|
||||
const battleSurvivors = tbody.querySelector('.battleSurvivors');
|
||||
|
||||
let index = 3; // index to find table element easily
|
||||
for (const u of options.military) {
|
||||
|
|
@ -615,35 +615,35 @@ class Battle {
|
|||
|
||||
const hideSection = function () {
|
||||
button.style.opacity = 1;
|
||||
div.style.display = "none";
|
||||
div.style.display = 'none';
|
||||
};
|
||||
if (div.style.display === "block") {
|
||||
if (div.style.display === 'block') {
|
||||
hideSection();
|
||||
return;
|
||||
}
|
||||
|
||||
button.style.opacity = 0.5;
|
||||
div.style.display = "block";
|
||||
div.style.display = 'block';
|
||||
|
||||
document.getElementsByTagName("body")[0].addEventListener("click", hideSection, {once: true});
|
||||
document.getElementsByTagName('body')[0].addEventListener('click', hideSection, {once: true});
|
||||
}
|
||||
|
||||
changeType(ev) {
|
||||
if (ev.target.tagName !== "BUTTON") return;
|
||||
if (ev.target.tagName !== 'BUTTON') return;
|
||||
this.type = ev.target.dataset.type;
|
||||
this.setType();
|
||||
this.selectPhase();
|
||||
this.calculateStrength("attackers");
|
||||
this.calculateStrength("defenders");
|
||||
this.calculateStrength('attackers');
|
||||
this.calculateStrength('defenders');
|
||||
this.name = this.defineName();
|
||||
$("#battleScreen").dialog({title: this.name});
|
||||
$('#battleScreen').dialog({title: this.name});
|
||||
}
|
||||
|
||||
changePhase(ev, side) {
|
||||
if (ev.target.tagName !== "BUTTON") return;
|
||||
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;
|
||||
const button = document.getElementById('battlePhase_' + side);
|
||||
button.className = 'icon-button-' + phase;
|
||||
button.dataset.tip = ev.target.dataset.tip;
|
||||
this.calculateStrength(side);
|
||||
}
|
||||
|
|
@ -654,34 +654,49 @@ class Battle {
|
|||
const relativeCasualties = this.defenders.casualties / (this.attackers.casualties + this.attackers.casualties);
|
||||
const battleStatus = getBattleStatus(relativeCasualties, maxCasualties);
|
||||
function getBattleStatus(relative, max) {
|
||||
if (isNaN(relative)) return ["standoff", "standoff"]; // if no casualties at all
|
||||
if (max < 0.05) return ["minor skirmishes", "minor skirmishes"];
|
||||
if (relative > 95) return ["attackers flawless victory", "disorderly retreat of defenders"];
|
||||
if (relative > 0.7) return ["attackers decisive victory", "defenders disastrous defeat"];
|
||||
if (relative > 0.6) return ["attackers victory", "defenders defeat"];
|
||||
if (relative > 0.4) return ["stalemate", "stalemate"];
|
||||
if (relative > 0.3) return ["attackers defeat", "defenders victory"];
|
||||
if (relative > 0.5) return ["attackers disastrous defeat", "decisive victory of defenders"];
|
||||
if (relative >= 0) return ["attackers disorderly retreat", "flawless victory of defenders"];
|
||||
return ["stalemate", "stalemate"]; // exception
|
||||
if (isNaN(relative)) return ['standoff', 'standoff']; // if no casualties at all
|
||||
if (max < 0.05) return ['minor skirmishes', 'minor skirmishes'];
|
||||
if (relative > 95) return ['attackers flawless victory', 'disorderly retreat of defenders'];
|
||||
if (relative > 0.7) return ['attackers decisive victory', 'defenders disastrous defeat'];
|
||||
if (relative > 0.6) return ['attackers victory', 'defenders defeat'];
|
||||
if (relative > 0.4) return ['stalemate', 'stalemate'];
|
||||
if (relative > 0.3) return ['attackers defeat', 'defenders victory'];
|
||||
if (relative > 0.5) return ['attackers disastrous defeat', 'decisive victory of defenders'];
|
||||
if (relative >= 0) return ['attackers disorderly retreat', 'flawless victory of defenders'];
|
||||
return ['stalemate', 'stalemate']; // exception
|
||||
}
|
||||
|
||||
this.attackers.regiments.forEach(r => applyResultForSide(r, "attackers"));
|
||||
this.defenders.regiments.forEach(r => applyResultForSide(r, "defenders"));
|
||||
this.attackers.regiments.forEach((r) => applyResultForSide(r, 'attackers'));
|
||||
this.defenders.regiments.forEach((r) => applyResultForSide(r, 'defenders'));
|
||||
|
||||
function applyResultForSide(r, side) {
|
||||
const id = "regiment" + r.state + "-" + r.i;
|
||||
const id = 'regiment' + r.state + '-' + r.i;
|
||||
|
||||
// add result to regiment note
|
||||
const note = notes.find(n => n.id === id);
|
||||
const note = notes.find((n) => n.id === id);
|
||||
if (note) {
|
||||
const status = side === "attackers" ? battleStatus[0] : battleStatus[1];
|
||||
const status = side === 'attackers' ? battleStatus[0] : battleStatus[1];
|
||||
const losses = r.a ? Math.abs(d3.sum(Object.values(r.casualties))) / r.a : 1;
|
||||
const regStatus = losses === 1 ? "is destroyed" : losses > 0.8 ? "is almost completely destroyed" : losses > 0.5 ? "suffered terrible losses" : losses > 0.3 ? "suffered severe losses" : losses > 0.2 ? "suffered heavy losses" : losses > 0.05 ? "suffered significant losses" : losses > 0 ? "suffered unsignificant losses" : "left the battle without loss";
|
||||
const regStatus =
|
||||
losses === 1
|
||||
? 'is destroyed'
|
||||
: losses > 0.8
|
||||
? 'is almost completely destroyed'
|
||||
: losses > 0.5
|
||||
? 'suffered terrible losses'
|
||||
: losses > 0.3
|
||||
? 'suffered severe losses'
|
||||
: losses > 0.2
|
||||
? 'suffered heavy losses'
|
||||
: losses > 0.05
|
||||
? 'suffered significant losses'
|
||||
: losses > 0
|
||||
? 'suffered unsignificant losses'
|
||||
: 'left the battle without loss';
|
||||
const casualties = Object.keys(r.casualties)
|
||||
.map(t => (r.casualties[t] ? `${Math.abs(r.casualties[t])} ${t}` : null))
|
||||
.filter(c => c);
|
||||
const casualtiesText = casualties.length ? " Casualties: " + list(casualties) + "." : "";
|
||||
.map((t) => (r.casualties[t] ? `${Math.abs(r.casualties[t])} ${t}` : null))
|
||||
.filter((c) => c);
|
||||
const casualtiesText = casualties.length ? ' Casualties: ' + list(casualties) + '.' : '';
|
||||
const legend = `\r\n\r\n${battleName} (${options.year} ${options.eraShort}): ${status}. The regiment ${regStatus}.${casualtiesText}`;
|
||||
note.legend += legend;
|
||||
}
|
||||
|
|
@ -691,57 +706,47 @@ class Battle {
|
|||
armies.select(`g#${id} > text`).text(Military.getTotal(r)); // update reg box
|
||||
}
|
||||
|
||||
// append battlefield marker
|
||||
void (function addMarkerSymbol() {
|
||||
if (svg.select("#defs-markers").select("#marker_battlefield").size()) return;
|
||||
const symbol = svg.select("#defs-markers").append("symbol").attr("id", "marker_battlefield").attr("viewBox", "0 0 30 30");
|
||||
symbol.append("path").attr("d", "M6,19 l9,10 L24,19").attr("fill", "#000000").attr("stroke", "none");
|
||||
symbol.append("circle").attr("cx", 15).attr("cy", 15).attr("r", 10).attr("fill", "#ffffff").attr("stroke", "#000000").attr("stroke-width", 1);
|
||||
symbol.append("text").attr("x", "50%").attr("y", "52%").attr("fill", "#000000").attr("stroke", "#3200ff").attr("stroke-width", 0).attr("font-size", "12px").attr("dominant-baseline", "central").text("⚔️");
|
||||
})();
|
||||
const i = last(pack.markers)?.i + 1 || 0;
|
||||
{
|
||||
// append battlefield marker
|
||||
const marker = {i, x: this.x, y: this.y, cell: this.cell, icon: '⚔️', type: 'battlefields', dy: 52};
|
||||
pack.markers.push(marker);
|
||||
const markerHTML = drawMarker(marker);
|
||||
document.getElementById('markers').insertAdjacentHTML('beforeend', markerHTML);
|
||||
}
|
||||
|
||||
const getSide = (regs, n) => (regs.length > 1 ? `${n ? "regiments" : "forces"} of ${list([...new Set(regs.map(r => pack.states[r.state].name))])}` : getAdjective(pack.states[regs[0].state].name) + " " + regs[0].name);
|
||||
const getLosses = casualties => Math.min(rn(casualties * 100), 100);
|
||||
const getSide = (regs, n) =>
|
||||
regs.length > 1 ? `${n ? 'regiments' : 'forces'} of ${list([...new Set(regs.map((r) => pack.states[r.state].name))])}` : getAdjective(pack.states[regs[0].state].name) + ' ' + regs[0].name;
|
||||
const getLosses = (casualties) => Math.min(rn(casualties * 100), 100);
|
||||
|
||||
const status = battleStatus[+P(0.7)];
|
||||
const result = `The ${this.getTypeName(this.type)} ended in ${status}`;
|
||||
const legend = `${this.name} took place in ${options.year} ${options.eraShort}. It was fought between ${getSide(this.attackers.regiments, 1)} and ${getSide(this.defenders.regiments, 0)}. ${result}.
|
||||
const legend = `${this.name} took place in ${options.year} ${options.eraShort}. It was fought between ${getSide(this.attackers.regiments, 1)} and ${getSide(
|
||||
this.defenders.regiments,
|
||||
0
|
||||
)}. ${result}.
|
||||
\r\nAttackers losses: ${getLosses(this.attackers.casualties)}%, defenders losses: ${getLosses(this.defenders.casualties)}%`;
|
||||
const id = getNextId("markerElement");
|
||||
notes.push({id, name: this.name, legend});
|
||||
notes.push({id: `marker${i}`, name: this.name, legend});
|
||||
|
||||
tip(`${this.name} is over. ${result}`, true, "success", 4000);
|
||||
tip(`${this.name} is over. ${result}`, true, 'success', 4000);
|
||||
|
||||
markers
|
||||
.append("use")
|
||||
.attr("id", id)
|
||||
.attr("xlink:href", "#marker_battlefield")
|
||||
.attr("data-id", "#marker_battlefield")
|
||||
.attr("data-x", this.x)
|
||||
.attr("data-y", this.y)
|
||||
.attr("x", this.x - 15)
|
||||
.attr("y", this.y - 30)
|
||||
.attr("data-size", 1)
|
||||
.attr("width", 30)
|
||||
.attr("height", 30);
|
||||
|
||||
$("#battleScreen").dialog("destroy");
|
||||
$('#battleScreen').dialog('destroy');
|
||||
this.cleanData();
|
||||
}
|
||||
|
||||
cancelResults() {
|
||||
// move regiments back to initial positions
|
||||
this.attackers.regiments.concat(this.defenders.regiments).forEach(r => Military.moveRegiment(r, r.px, r.py));
|
||||
$("#battleScreen").dialog("close");
|
||||
this.attackers.regiments.concat(this.defenders.regiments).forEach((r) => Military.moveRegiment(r, r.px, r.py));
|
||||
$('#battleScreen').dialog('close');
|
||||
this.cleanData();
|
||||
}
|
||||
|
||||
cleanData() {
|
||||
battleAttackers.innerHTML = battleDefenders.innerHTML = ""; // clean DOM
|
||||
battleAttackers.innerHTML = battleDefenders.innerHTML = ''; // clean DOM
|
||||
customization = 0; // exit edit mode
|
||||
|
||||
// clean temp data
|
||||
this.attackers.regiments.concat(this.defenders.regiments).forEach(r => {
|
||||
this.attackers.regiments.concat(this.defenders.regiments).forEach((r) => {
|
||||
delete r.px;
|
||||
delete r.py;
|
||||
delete r.casualties;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue