chore(release): 0.4.0
[risinglegends.git] / src / server / views / fight.ts
1 import { Dungeon } from "shared/dungeon";
2 import { FightRound } from "shared/fight";
3 import { LocationWithCity } from "shared/map";
4 import { Fight, MonsterForFight } from "../../shared/monsters";
5 import { Button, ButtonWithBlock } from "./components/button";
6 import { Details, Title } from "./components/city";
7
8 export function renderRoundDetails(roundData: FightRound): string {
9   let html: string[] = roundData.roundDetails.map(d => `<div>${d}</div>`);
10
11   switch(roundData.winner) {
12     case 'player':
13       html.push(`<div>You defeated the ${roundData.monster.name}!</div>`);
14       if(roundData.rewards.gold) {
15         html.push(`<div>You gained ${roundData.rewards.gold} gold</div>`);
16       }
17       if(roundData.rewards.exp) {
18         html.push(`<div>You gained ${roundData.rewards.exp} exp</div>`);
19       }
20       if(roundData.rewards.levelIncrease) {
21         html.push(`<div>You gained a level! ${roundData.player.level}</div>`);
22       }
23     break;
24     case 'monster':
25       if(roundData.player.hp === 0) {
26         html.push(`<p>You were killed...</p>`);
27       }
28       html.push('<p><button hx-get="/player/explore" hx-target="#explore">Back to Town</button></p>');
29     break;
30     case 'in-progress':
31       // fight still in progress, so we don't send any final actions back
32     break;
33   }
34
35
36   return html.join("\n");
37 }
38
39 function CastButton(blockTime?: number): string {
40   const attrs = {
41       id: 'cast-button',
42       type: 'submit', 
43       class: ['fight-action', 'red'],
44       name: 'action',
45       value: 'cast'
46   };
47
48   if(blockTime) {
49     return ButtonWithBlock(attrs, 'Cast', blockTime);
50   }
51   else {
52     return Button(attrs, 'Cast');
53   }
54
55 }
56
57 function AttackButton(blockTime?: number): string {
58   const attrs = {
59       id: 'attack-button',
60       type: 'submit', 
61       class: ['fight-action', 'red'],
62       name: 'action',
63       value: 'attack'
64   };
65
66   if(blockTime) {
67     return ButtonWithBlock(attrs, 'Attack', blockTime);
68   }
69   else {
70     return Button(attrs, 'Attack');
71   }
72 }
73
74 export function renderFight(monster: Fight, results: string = '', displayFightActions: boolean = true, blockTime: number = 0) {
75   const hpPercent = Math.floor((monster.hp / monster.maxHp) * 100);
76
77   let html = `
78   <div id="fight-container">
79     <div id="defender-info">
80       <div class="avatar-container">
81         <img id="avatar" src="https://via.placeholder.com/64x64">
82       </div>
83       <div id="defender-stat-bars">
84         <div class="monster-identifier ${monster.variant}"><span id="defender-name">${monster.name}</span>, level ${monster.level}</div>
85         <div class="progress-bar" id="defender-hp-bar" style="background: linear-gradient(to right, red, red ${hpPercent}%, transparent ${hpPercent}%, transparent)" title="${hpPercent}% - ${monster.hp}/${monster.maxHp}">${hpPercent}% - ${monster.hp} / ${monster.maxHp}</div>
86       </div>
87     </div>
88     <div id="fight-actions">
89       ${displayFightActions ? `
90       <form hx-post="/fight/turn" hx-target="#fight-container">
91         ${AttackButton(blockTime)}
92         ${CastButton(blockTime)}
93         <button type="submit" class="fight-action" name="action" value="flee">Flee</button>
94       </form>
95       `: ''}
96     </div>
97     <div id="fight-results">${results}</div>
98   </div>
99 `;
100
101   return html;
102 }
103
104 export function renderFightPreRoundDungeon(city: string, dungeon_name: string, monster: Fight): string {
105   return`
106   ${Title(city)}
107   ${Details(dungeon_name, renderFight(monster, '', true))}
108 `;
109 }
110
111 export function renderFightPreRound(monster: Fight,  displayFightActions: boolean = true, location: LocationWithCity, closestTown: number) {
112   let html = `
113 <section id="explore" class="tab active" style="background-image: url('/assets/img/map/${closestTown}.jpeg')" hx-swap-oob="true">
114   <div class="city-title-wrapper">
115     <div class="city-title">${location.city_name}</div>
116   </div>
117   <div class="city-details">
118     <h3 class="location-name"><span>${location.name}</span></h3>
119
120     ${renderFight(monster, '', displayFightActions)}
121   </div>
122 </section>
123 `;
124
125   return html;
126 }