219103d5cfb5f76b5051d7e3a0a51baaad503482
[browser-rts.git] / src / render / map.ts
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";
9
10 const cityRepo = new CityRepository();
11
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);
17         }
18
19         map[cities[i].location_y][cities[i].location_x] = cities[i];
20     }
21
22     let rows: string[] = [];
23     let headerRow: string[] = [];
24     for(let y = 0; y <= 25; ++y) {
25         rows[y] = '<tr>';
26         if(y === 0) {
27           // first row! add the X coors
28           for(let x = 0; x <= 25; ++x) {
29             headerRow.push(`<td class="grid-numbers">${x}</td>`);
30           }
31         }
32         else {
33           for(let x = 0; x <= 25; ++x) {
34             if(x === 0) {
35               rows[y] += `<td class="grid-numbers">${y}</td>`;
36             }
37             else {
38               if(!map[y] || !map[y][x]) {
39                   rows[y] += `<td title="(${x},${y})"><div class="empty"></div></td>` ;
40               }
41               else if(map[y][x]) {
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>`;
44                   else
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}">
47                     </div></td>`;
48               }
49               else {
50                   rows[y] += '<td><div class="empty"></div></td>';
51               }
52             }
53           }
54         }
55         rows[y] += '</tr>';
56     }
57     let sectorSwitch: string[] = [];
58     for(let i = 1; i <= AVAILABLE_SECTORS; ++i) {
59       sectorSwitch.push(
60         `<option value="${i}" ${activeSector === i ? 'selected': ''}>Sector ${i}</option>`
61       );
62     }
63     let html = `
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")}
71       </select>
72     </div>
73     <table id="overworld-map" style="background-image:url('/assets/bg/mapoverview_${activeSector}.png');">
74     <tr>
75     ${headerRow.join("\n")}
76     </tr>
77     ${rows.join("\n")}
78     </table>
79     <br>
80     `;
81
82
83     return html;
84
85 }
86
87 export function listOperations(outgoingOps: ArmyQueueWithAttackedOwner[]): string {
88     let html = `
89     <h4>Outgoing Attacks</h4>
90     <table>
91     <tr>
92         <th>Destination</th>
93         <th>Sol.</th>
94         <th>Atk.</th>
95         <th>Def.</th>
96         <th>Sp. Atk.</th>
97         <th>Sp. Def.</th>
98         <th>Est. Arrival</th>
99     </tr>
100
101     ${outgoingOps.map(op => {
102         return `
103         <tr>
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>
111         </tr>
112         `;
113     }).join("\n")}
114     </table>
115     `;
116
117     return html;
118 }
119
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
128     })
129     let html = `
130     <h3 data-augmented-ui="tl-clip bl-clip none">Viewing (${city.location_x},${city.location_y}) owned by ${owner.username}</h3>
131     <h4>Report</h4>
132     <p>This city is ${distance} hours away.</p>
133     <form hx-post="/attack">
134     <input type="hidden" name="city" value="${city.id}">
135     <table>
136     <tr>
137         <th>Soldiers</th>
138         <td>
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})
141         </td>
142     </tr>
143     <tr>
144         <th>Attackers</th>
145         <td>
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})
148         </td>
149     </tr>
150     <tr>
151         <th>Defenders</th>
152         <td>
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})
155         </td>
156     </tr>
157     <tr>
158         <th>Sp. Attack</th>
159         <td>
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})
162         </td>
163     </tr>
164     <tr>
165         <th>Sp. Defenders</th>
166         <td>
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})
169         </td>
170     </tr>
171     <tr>
172         <th>Total Power</th>
173         <td id="total-attack-power">${power}</td>
174     </tr>
175     <tr>
176         <td colspan="2" style="text-align: right">
177             <button type="submit">Attack (${city.location_x},${city.location_y})</button>
178         </td>
179     </tr>
180     </table>
181     </form>
182     `;
183
184     return html + topbar(yourCity);
185 }