Resource fixes!
[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 { CityRepository, CityWithLocation } from "../repository/city";
6 import { AVAILABLE_SECTORS } from '../config';
7
8 const cityRepo = new CityRepository();
9
10 export function renderOverworldMap(cities: CityWithLocation[], yourCity: CityWithLocation, activeSector: number): string {
11     let map: CityWithLocation[][] = new Array(25);
12     for(let i = 0; i < cities.length; ++i) {
13         if(!map[cities[i].location_y]) {
14             map[cities[i].location_y] = new Array(25);
15         }
16
17         map[cities[i].location_y][cities[i].location_x] = cities[i];
18     }
19
20     let rows: string[] = [];
21     let headerRow: string[] = [];
22     for(let y = 0; y <= 25; ++y) {
23         rows[y] = '<tr>';
24         if(y === 0) {
25           // first row! add the X coors
26           for(let x = 0; x <= 25; ++x) {
27             headerRow.push(`<td class="grid-numbers">${x}</td>`);
28           }
29         }
30         else {
31           for(let x = 0; x <= 25; ++x) {
32             if(x === 0) {
33               rows[y] += `<td class="grid-numbers">${y}</td>`;
34             }
35             else {
36               if(!map[y] || !map[y][x]) {
37                   rows[y] += `<td title="(${x},${y})"><div class="empty"></div></td>` ;
38               }
39               else if(map[y][x]) {
40                   if(map[y][x].id === yourCity.id)
41                       rows[y] += `<td title="You (${x},${y})"><div class="city yours"><img src="/assets/${map[y][x].icon}"></div></td>`;
42                   else
43                       rows[y] += `<td title="(${x},${y})"><div class="city others" hx-trigger="click" hx-get="/city/${map[y][x].id}" hx-target="#city-offence">
44                     <img src="/assets/${map[y][x].icon}">
45                     </div></td>`;
46               }
47               else {
48                   rows[y] += '<td><div class="empty"></div></td>';
49               }
50             }
51           }
52         }
53         rows[y] += '</tr>';
54     }
55     let sectorSwitch: string[] = [];
56     for(let i = 1; i <= AVAILABLE_SECTORS; ++i) {
57       sectorSwitch.push(
58         `<option value="${i}" ${activeSector === i ? 'selected': ''}>Sector ${i}</option>`
59       );
60     }
61     let html = `
62     <h2 data-augmented-ui="tl-clip bl-clip none">Map</h2>
63     <div id="city-offence"></div>
64     <div id="outgoing-attacks" hx-trigger="reload-outgoing-attacks, every 600s, load" hx-get="/attacks/outgoing"></div>
65     <div id="sector-selector" class="text-right">
66       Switch Scanned Sectors: 
67       <select name="sector" hx-trigger="change" hx-post="/poll/map" hx-target="#main">
68         ${sectorSwitch.join("\n")}
69       </select>
70     </div>
71     <table id="overworld-map" style="background-image:url('/assets/bg/mapoverview_${activeSector}.png');">
72     <tr>
73     ${headerRow.join("\n")}
74     </tr>
75     ${rows.join("\n")}
76     </table>
77     <br>
78     `;
79
80
81     return html;
82
83 }
84
85 export function listOperations(outgoingOps: ArmyQueueWithAttackedOwner[]): string {
86     let html = `
87     <h4>Outgoing Attacks</h4>
88     <table>
89     <tr>
90         <th>Destination</th>
91         <th>Sol.</th>
92         <th>Atk.</th>
93         <th>Def.</th>
94         <th>Sp. Atk.</th>
95         <th>Sp. Def.</th>
96         <th>Est. Arrival</th>
97     </tr>
98
99     ${outgoingOps.map(op => {
100         return `
101         <tr>
102             <td>${op.attacked_account} - (${op.location_x},${op.location_y})</td>
103             <td>${op.soldiers.toLocaleString()}</td>
104             <td>${op.attackers.toLocaleString()}</td>
105             <td>${op.defenders.toLocaleString()}</td>
106             <td>${op.sp_attackers.toLocaleString()}</td>
107             <td>${op.sp_defenders.toLocaleString()}</td>
108             <td>${ _.round(DateTime.fromMillis(op.due).diffNow().as('hours'), 2)} hours</td>
109         </tr>
110         `;
111     }).join("\n")}
112     </table>
113     `;
114
115     return html;
116 }
117
118 export async function launchOffensive(city: CityWithLocation, owner: Account, yourCity: CityWithLocation, you: Account): Promise<string> {
119     const distance = cityRepo.distanceInHours(city, yourCity);
120     const power = await cityRepo.power({
121         soldiers: yourCity.soldiers,
122         attackers: yourCity.attackers,
123         defenders: yourCity.defenders,
124         sp_attackers: yourCity.sp_attackers,
125         sp_defenders: yourCity.sp_defenders
126     })
127     let html = `
128     <h3 data-augmented-ui="tl-clip bl-clip none">Viewing (${city.location_x},${city.location_y}) owned by ${owner.username}</h3>
129     <h4>Report</h4>
130     <p>This city is ${distance} hours away.</p>
131     <form hx-post="/attack">
132     <input type="hidden" name="city" value="${city.id}">
133     <table>
134     <tr>
135         <th>Soldiers</th>
136         <td>
137         <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">
138         (${yourCity.soldiers})
139         </td>
140     </tr>
141     <tr>
142         <th>Attackers</th>
143         <td>
144         <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">
145         (${yourCity.attackers})
146         </td>
147     </tr>
148     <tr>
149         <th>Defenders</th>
150         <td>
151         <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">
152         (${yourCity.defenders})
153         </td>
154     </tr>
155     <tr>
156         <th>Sp. Attack</th>
157         <td>
158         <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">
159         (${yourCity.sp_attackers})
160         </td>
161     </tr>
162     <tr>
163         <th>Sp. Defenders</th>
164         <td>
165         <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">
166         (${yourCity.sp_defenders})
167         </td>
168     </tr>
169     <tr>
170         <th>Total Power</th>
171         <td id="total-attack-power">${power}</td>
172     </tr>
173     <tr>
174         <td colspan="2" style="text-align: right">
175             <button type="submit">Attack (${city.location_x},${city.location_y})</button>
176         </td>
177     </tr>
178     </table>
179     </form>
180     `;
181
182     return html;
183 }