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