1 import _ from "lodash";
2 import { DateTime } from "luxon";
3 import { Account } from "../repository/accounts";
4 import { ArmyQueueWithAttackedOwner } from "../repository/army";
5 import { City, CityRepository, CityWithLocation } from "../repository/city";
6 import { topbar } from "./topbar";
7 import { AVAILABLE_SECTORS } from '../config';
8 import {initArray} from "../lib/util";
10 const cityRepo = new CityRepository();
12 export function renderOverworldMap(cities: CityWithLocation[], yourCity: CityWithLocation, activeSector: number): string {
13 let map: CityWithLocation[][] = new Array(25);
14 for(let i = 0; i < cities.length; ++i) {
15 if(!map[cities[i].location_y]) {
16 map[cities[i].location_y] = new Array(25);
19 map[cities[i].location_y][cities[i].location_x] = cities[i];
22 let rows: string[] = [];
23 let headerRow: string[] = [];
24 for(let y = 0; y <= 25; ++y) {
27 // first row! add the X coors
28 for(let x = 0; x <= 25; ++x) {
29 headerRow.push(`<td class="grid-numbers">${x}</td>`);
33 for(let x = 0; x <= 25; ++x) {
35 rows[y] += `<td class="grid-numbers">${y}</td>`;
38 if(!map[y] || !map[y][x]) {
39 rows[y] += `<td title="(${x},${y})"><div class="empty"></div></td>` ;
42 if(map[y][x].id === yourCity.id)
43 rows[y] += `<td title="You (${x},${y})"><div class="city yours"><img src="/assets/${map[y][x].icon}"></div></td>`;
45 rows[y] += `<td title="(${x},${y})"><div class="city others" hx-trigger="click" hx-get="/city/${map[y][x].id}" hx-target="#city-offence">
46 <img src="/assets/${map[y][x].icon}">
50 rows[y] += '<td><div class="empty"></div></td>';
57 let sectorSwitch: string[] = [];
58 for(let i = 1; i <= AVAILABLE_SECTORS; ++i) {
60 `<option value="${i}" ${activeSector === i ? 'selected': ''}>Sector ${i}</option>`
64 <h2 data-augmented-ui="tl-clip bl-clip none">Map</h2>
65 <div id="city-offence"></div>
66 <div id="outgoing-attacks" hx-trigger="reload-outgoing-attacks, every 600s, load" hx-get="/attacks/outgoing"></div>
67 <div id="sector-selector" class="text-right">
68 Switch Scanned Sectors:
69 <select name="sector" hx-trigger="change" hx-post="/poll/map" hx-target="#main">
70 ${sectorSwitch.join("\n")}
73 <table id="overworld-map" style="background-image:url('/assets/bg/mapoverview_${activeSector}.png');">
75 ${headerRow.join("\n")}
87 export function listOperations(outgoingOps: ArmyQueueWithAttackedOwner[]): string {
89 <h4>Outgoing Attacks</h4>
101 ${outgoingOps.map(op => {
104 <td>${op.attacked_account} - (${op.location_x},${op.location_y})</td>
105 <td>${op.soldiers.toLocaleString()}</td>
106 <td>${op.attackers.toLocaleString()}</td>
107 <td>${op.defenders.toLocaleString()}</td>
108 <td>${op.sp_attackers.toLocaleString()}</td>
109 <td>${op.sp_defenders.toLocaleString()}</td>
110 <td>${ _.round(DateTime.fromMillis(op.due).diffNow().as('hours'), 2)} hours</td>
120 export async function launchOffensive(city: CityWithLocation, owner: Account, yourCity: CityWithLocation, you: Account): Promise<string> {
121 const distance = cityRepo.distanceInHours(city, yourCity);
122 const power = await cityRepo.power({
123 soldiers: yourCity.soldiers,
124 attackers: yourCity.attackers,
125 defenders: yourCity.defenders,
126 sp_attackers: yourCity.sp_attackers,
127 sp_defenders: yourCity.sp_defenders
130 <h3 data-augmented-ui="tl-clip bl-clip none">Viewing (${city.location_x},${city.location_y}) owned by ${owner.username}</h3>
132 <p>This city is ${distance} hours away.</p>
133 <form hx-post="/attack">
134 <input type="hidden" name="city" value="${city.id}">
139 <input type="number" class="potential-attack" name="soldiers" max="${yourCity.soldiers}" value="${yourCity.soldiers}" hx-target="#total-attack-power" hx-post="/attack-power" hx-include=".potential-attack">
140 (${yourCity.soldiers})
146 <input type="number" class="potential-attack" name="attackers" max="${yourCity.attackers}" value="${yourCity.attackers}" hx-target="#total-attack-power" hx-post="/attack-power" hx-include=".potential-attack">
147 (${yourCity.attackers})
153 <input type="number" class="potential-attack" name="defenders" max="${yourCity.defenders}" value="${yourCity.defenders}" hx-target="#total-attack-power" hx-post="/attack-power" hx-include=".potential-attack">
154 (${yourCity.defenders})
160 <input type="number" class="potential-attack" name="sp_attackers" max="${yourCity.sp_attackers}" value="${yourCity.sp_attackers}" hx-target="#total-attack-power" hx-post="/attack-power" hx-include=".potential-attack">
161 (${yourCity.sp_attackers})
165 <th>Sp. Defenders</th>
167 <input type="number" class="potential-attack" name="sp_defenders" max="${yourCity.sp_defenders}" value="${yourCity.sp_defenders}" hx-target="#total-attack-power" hx-post="/attack-power" hx-include=".potential-attack">
168 (${yourCity.sp_defenders})
173 <td id="total-attack-power">${power}</td>
176 <td colspan="2" style="text-align: right">
177 <button type="submit">Attack (${city.location_x},${city.location_y})</button>
184 return html + topbar(yourCity);