Files
Battle-for-Tismo/client/js/modals/train.js
T

127 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// The city training modal: choose a unit to build and manage the city's queue.
import { groupDigits, hours } from "../../../shared/text_format.js";
import { ECONOMY } from "../../../shared/data.js";
export class TrainModal {
constructor() {
this.$modal = $("#modal-train");
this.$title = $("#train-title");
this.$list = $("#train-list");
this.$status = $("#train-status");
this.$queue = $("#train-queue");
this.$label = $("#train-label");
this.$progress = $("#train-progress");
this.cityId = 0;
this.gdp = 0;
this.protoNames = new Map();
this.onTrain = () => {};
this.onCancel = () => {};
this.$modal.find(".modal-close").on("click", () => this.close());
}
isShowing(cityId) {
return !this.$modal.hasClass("hidden") && this.cityId === cityId;
}
show(city, gdp, trainableUnits, queue = []) {
this.cityId = city.id;
this.gdp = gdp;
this.protoNames = new Map();
this.$title.text(`${city.name} — Train`);
this.$list.empty();
this.$status.addClass("hidden");
for (let index = 0; index < trainableUnits.length; index++) {
const proto = trainableUnits[index];
if (!proto.cost && proto.cost !== 0) continue;
const button = $("<button class='aero'></button>");
const duration = ECONOMY.trainingHours(gdp, proto.cost);
button.text(`${proto.name} — Cost ${groupDigits(proto.cost)} — ${hours(duration)}`);
const serverIndex = proto.serverIndex !== undefined ? proto.serverIndex : index;
this.protoNames.set(serverIndex, proto.name);
button.on("click", () => this.onTrain(city.id, serverIndex));
this.$list.append(button);
}
this.renderQueue(queue);
this.$modal.removeClass("hidden");
}
// Draws the city's training queue: one row per run of contiguous identical
// units, with the cumulated hours until each run is ready and a button to
// cancel its last unit. Cancelling refunds nothing.
renderQueue(queue) {
if (!this.$queue || !this.$queue.length) return;
this.$queue.empty();
if (!queue || queue.length === 0) {
this.$queue.append(
$("<div class='desc queue-empty'></div>").text("No units queued.")
);
return;
}
let cumulative = 0;
groupQueue(queue).forEach((group, groupIndex) => {
let firstReady = 0;
for (const index of group.indices) {
const entry = queue[index];
const elapsed = index === 0 ? (entry.elapsedHours || 0) : 0;
cumulative += Math.max(entry.totalHours - elapsed, 0);
if (index === group.indices[0]) firstReady = cumulative;
}
const count = group.indices.length;
const name = this.protoNames.get(group.protoIndex) || "Unit";
const label = count > 1 ? `${name} ×${count}` : name;
const $row = $("<div class='row-card queue-row'></div>");
const $info = $("<div class='info'></div>");
$info.append($("<div class='name'></div>").text(`${groupIndex + 1}. ${label}`));
$info.append(
$("<div class='desc'></div>").text(
count > 1
? `Next in ${hours(firstReady)} · all ready in ${hours(cumulative)}`
: `Ready in ${hours(cumulative)}`
)
);
const $actions = $("<div class='actions'></div>");
const $cancel = $("<button class='aero'></button>").text(count > 1 ? "Cancel last" : "Cancel");
$cancel.on("click", () => this.onCancel(this.cityId, group.indices[group.indices.length - 1]));
$actions.append($cancel);
$row.append($info, $actions);
this.$queue.append($row);
});
}
beginTraining(name, totalHours, elapsedHours = 0) {
this.$label.text(`Training ${name}…`);
this.$progress.css("width", `${Math.min(100, (elapsedHours / Math.max(totalHours, 0.0001)) * 100)}%`);
this.$status.removeClass("hidden");
}
setTrainingProgress(name, elapsedHours, totalHours) {
const remaining = Math.max(0, Math.ceil(totalHours - elapsedHours));
this.$label.text(`Training ${name}… (${remaining} h left)`);
this.$progress.css("width", `${Math.min(100, (elapsedHours / Math.max(totalHours, 0.0001)) * 100)}%`);
}
endTraining() {
this.$label.text("Training complete");
this.$progress.css("width", "100%");
}
close() {
this.$modal.addClass("hidden");
this.cityId = 0;
}
}
// Runs of contiguous queue entries with the same unit type, as lists of queue
// indices. Non-adjacent entries of the same type stay separate so a row only
// ever represents the units sitting together.
function groupQueue(queue) {
const groups = [];
queue.forEach((entry, index) => {
const last = groups[groups.length - 1];
if (last && last.protoIndex === entry.protoIndex) last.indices.push(index);
else groups.push({ protoIndex: entry.protoIndex, indices: [index] });
});
return groups;
}