UI updates and bug fixes
[browser-rts.git] / src / render / fight.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
8 const cityRepo = new CityRepository();
9
10 export function renderOverworldMap(cities: CityWithLocation[], yourCity: CityWithLocation): 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     for(let y = 0; y < 25; ++y) {
22         rows[y] = '<tr>';
23         for(let x = 0; x < 25; ++x) {
24             if(!map[y] || !map[y][x]) {
25                 rows[y] += '<td><div class="empty"></div></td>' ;
26             }
27             else if(map[y][x]) {
28                 if(map[y][x].id === yourCity.id)
29                     rows[y] += '<td><div class="city yours"></div></td>';
30                 else
31                     rows[y] += `<td><div class="city others" hx-trigger="click" hx-get="/city/${map[y][x].id}" hx-target="#city-offence"></div></td>`;
32             }
33             else {
34                 rows[y] += '<td><div class="empty"></div></td>';
35             }
36         }
37         rows[y] += '</tr>';
38     }
39     let html = `
40     <h2 data-augmented-ui="tl-clip bl-clip none">Map</h2>
41     <div id="city-offence"></div>
42     <div id="outgoing-attacks" hx-trigger="reload-outgoing-attacks, every 600s, load" hx-get="/attacks/outgoing"></div>
43     <table id="overworld-map">
44     ${rows.join("\n")}
45     </table>
46     
47     `;
48
49     return html;
50
51 }
52
53 export function listOperations(outgoingOps: ArmyQueueWithAttackedOwner[]): string {
54     let html = `
55     <h4>Outgoing Attacks</h4>
56     <table>
57     <tr>
58         <th>Destination</th>
59         <th>Sol.</th>
60         <th>Atk.</th>
61         <th>Def.</th>
62         <th>Sp. Atk.</th>
63         <th>Sp. Def.</th>
64         <th>Est. Arrival</th>
65     </tr>
66
67     ${outgoingOps.map(op => {
68         return `
69         <tr>
70             <td>${op.attacked_account} - (${op.location_x},${op.location_y})</td>
71             <td>${op.soldiers.toLocaleString()}</td>
72             <td>${op.attackers.toLocaleString()}</td>
73             <td>${op.defenders.toLocaleString()}</td>
74             <td>${op.sp_attackers.toLocaleString()}</td>
75             <td>${op.sp_defenders.toLocaleString()}</td>
76             <td>${ _.round(DateTime.fromMillis(op.due).diffNow().as('hours'), 2)} hours</td>
77         </tr>
78         `;
79     }).join("\n")}
80     </table>
81     `;
82
83     return html;
84 }
85
86 export async function launchOffensive(city: CityWithLocation, owner: Account, yourCity: CityWithLocation, you: Account): Promise<string> {
87     const distance = cityRepo.distanceInHours(city, yourCity);
88     const power = await cityRepo.power({
89         soldiers: yourCity.soldiers,
90         attackers: yourCity.attackers,
91         defenders: yourCity.defenders,
92         sp_attackers: yourCity.sp_attackers,
93         sp_defenders: yourCity.sp_defenders
94     })
95     let html = `
96     <h3 data-augmented-ui="tl-clip bl-clip none">Viewing (${city.location_x},${city.location_y}) owned by ${owner.username}</h3>
97     <h4>Report</h4>
98     <p>This city is ${distance} hours away.</p>
99     <form hx-post="/attack">
100     <input type="hidden" name="city" value="${city.id}">
101     <table>
102     <tr>
103         <th>Soldiers</th>
104         <td>
105         <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">
106         (${yourCity.soldiers})
107         </td>
108     </tr>
109     <tr>
110         <th>Attackers</th>
111         <td>
112         <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">
113         (${yourCity.attackers})
114         </td>
115     </tr>
116     <tr>
117         <th>Defenders</th>
118         <td>
119         <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">
120         (${yourCity.defenders})
121         </td>
122     </tr>
123     <tr>
124         <th>Sp. Attack</th>
125         <td>
126         <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">
127         (${yourCity.sp_attackers})
128         </td>
129     </tr>
130     <tr>
131         <th>Sp. Defenders</th>
132         <td>
133         <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">
134         (${yourCity.sp_defenders})
135         </td>
136     </tr>
137     <tr>
138         <th>Total Power</th>
139         <td id="total-attack-power">${power}</td>
140     </tr>
141     <tr>
142         <td colspan="2" style="text-align: right">
143             <button type="submit">Attack (${city.location_x},${city.location_y})</button>
144         </td>
145     </tr>
146     </table>
147     </form>
148     `;
149
150     return html + topbar(yourCity);
151 }