comparison libmlk-rpg/rpg/battle-bar.c @ 243:71b3b7036de7

misc: lot of cleanups, - prefix libraries with libmlk, - move assets from source directories closes #2520, - prefix header guards closes #2519
author David Demelier <markand@malikania.fr>
date Sat, 28 Nov 2020 22:37:30 +0100
parents librpg/rpg/battle-bar.c@76afe639fd72
children bfde372bf152
comparison
equal deleted inserted replaced
242:4c24604efcab 243:71b3b7036de7
1 /*
2 * battle-bar.h -- battle status bar and menu
3 *
4 * Copyright (c) 2020 David Demelier <markand@malikania.fr>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <assert.h>
20 #include <stdio.h>
21 #include <string.h>
22
23 #include <core/event.h>
24 #include <core/font.h>
25 #include <core/window.h>
26 #include <core/util.h>
27
28 #include <ui/align.h>
29 #include <ui/theme.h>
30
31 #include "battle.h"
32 #include "battle-bar.h"
33 #include "character.h"
34 #include "spell.h"
35 #include "rpg_p.h"
36
37 static void
38 draw_status_character_stats(const struct battle *bt,
39 const struct character *ch,
40 int x,
41 int y,
42 unsigned int w,
43 unsigned int h)
44 {
45 struct theme *theme = BATTLE_THEME(bt);
46 struct label label;
47 unsigned int spacing, lw, lh;
48 char line[64];
49
50 /* Compute spacing between elements. */
51 spacing = h - (font_height(theme->fonts[THEME_FONT_INTERFACE]) * 3);
52 spacing /= 4;
53
54 /* Reuse the same label. */
55 label.theme = theme;
56 label.text = line;
57 label.flags = LABEL_FLAGS_SHADOW;
58
59 /* HP. */
60 snprintf(line, sizeof (line), "%d/%u", ch->hp, ch->hpmax);
61 label_query(&label, &lw, &lh);
62 label.x = x + w - lw - theme->padding;
63 label.y = y + spacing;
64 label_draw(&label);
65
66 /* MP. */
67 snprintf(line, sizeof (line), "%d/%u", ch->mp, ch->mpmax);
68 label_query(&label, &lw, &lh);
69 label.x = x + w - lw - theme->padding;
70 label.y = label.y + lh + spacing;
71 label_draw(&label);
72
73 /* Status. */
74 /* TODO: list all status. */
75 }
76
77 static void
78 draw_status_character(const struct battle_bar *bar,
79 const struct battle *bt,
80 const struct character *ch,
81 unsigned int index)
82 {
83 int x, y;
84 unsigned int w, h;
85
86 /* Compute bounding box for rendering. */
87 w = bar->status_frame.w / BATTLE_TEAM_MAX;
88 h = bar->status_frame.h;
89 x = bar->status_frame.x + (index * w);
90 y = bar->status_frame.y;
91
92 draw_status_character_stats(bt, ch, x, y, w, h);
93 }
94
95 static void
96 draw_status_characters(const struct battle_bar *bar, const struct battle *bt)
97 {
98 const struct battle_entity *et;
99 unsigned int index = 0;
100
101 BATTLE_TEAM_FOREACH(bt, et) {
102 if (character_ok(et->ch))
103 draw_status_character(bar, bt, et->ch, index);
104
105 ++index;
106 }
107 }
108
109 static void
110 draw_status(const struct battle_bar *bar, const struct battle *bt)
111 {
112 frame_draw(&bar->status_frame);
113 draw_status_characters(bar, bt);
114 }
115
116 static void
117 draw_menu(const struct battle_bar *bar, const struct battle *bt)
118 {
119 struct {
120 unsigned int w, h;
121 enum align align;
122 struct label label;
123 } buttons[] = {
124 {
125 .align = ALIGN_TOP,
126 .label = {
127 .text = _("Attack"),
128 .flags = LABEL_FLAGS_SHADOW
129 }
130 },
131 {
132 .align = ALIGN_RIGHT,
133 .label = {
134 .text = _("Magic"),
135 .flags = LABEL_FLAGS_SHADOW
136 }
137 },
138 {
139 .align = ALIGN_BOTTOM,
140 .label = {
141 .text = _("Objects"),
142 .flags = LABEL_FLAGS_SHADOW
143 }
144 },
145 {
146 .align = ALIGN_LEFT,
147 .label = {
148 .text = _("Special"),
149 .flags = LABEL_FLAGS_SHADOW
150 }
151 }
152 };
153
154 struct theme theme;
155 int bx, by;
156 unsigned int bw, bh;
157
158 /* Copy theme according to menu selection. */
159 theme_shallow(&theme, bt->theme);
160
161 /* Compute bounding box with margins removed. */
162 bx = bar->menu_frame.x + theme.padding;
163 by = bar->menu_frame.y + theme.padding;
164 bw = bar->menu_frame.w - theme.padding * 2;
165 bh = bar->menu_frame.h - theme.padding * 2;
166
167 /* Draw menu frame. */
168 frame_draw(&bar->menu_frame);
169
170 for (size_t i = 0; i < NELEM(buttons); ++i) {
171 buttons[i].label.theme = &theme;
172
173 label_query(&buttons[i].label, &buttons[i].w, &buttons[i].h);
174
175 /* Change theme if it's selected. */
176 if ((size_t)bar->menu == i && bar->state != BATTLE_BAR_STATE_NONE)
177 theme.colors[THEME_COLOR_NORMAL] = BATTLE_THEME(bt)->colors[THEME_COLOR_SELECTED];
178 else
179 theme.colors[THEME_COLOR_NORMAL] = BATTLE_THEME(bt)->colors[THEME_COLOR_NORMAL];
180
181 align(buttons[i].align,
182 &buttons[i].label.x, &buttons[i].label.y, buttons[i].w, buttons[i].h,
183 bx, by, bw, bh);
184 label_draw(&buttons[i].label);
185 }
186 }
187
188 static void
189 draw_sub(const struct battle_bar *bar)
190 {
191 gridmenu_draw(&bar->sub_grid);
192 }
193
194 static bool
195 handle_keydown(struct battle_bar *bar, const union event *ev)
196 {
197 assert(ev->type == EVENT_KEYDOWN);
198
199 switch (bar->state) {
200 case BATTLE_BAR_STATE_MENU:
201 /* We are selecting a main menu entry. */
202 switch (ev->key.key) {
203 case KEY_UP:
204 bar->menu = BATTLE_BAR_MENU_ATTACK;
205 break;
206 case KEY_RIGHT:
207 bar->menu = BATTLE_BAR_MENU_MAGIC;
208 break;
209 case KEY_DOWN:
210 bar->menu = BATTLE_BAR_MENU_OBJECTS;
211 break;
212 case KEY_LEFT:
213 bar->menu = BATTLE_BAR_MENU_SPECIAL;
214 break;
215 case KEY_ENTER:
216 return true;
217 default:
218 break;
219 }
220 break;
221 case BATTLE_BAR_STATE_SUB:
222 /* We are in the sub menu (objects/spells). */
223 gridmenu_handle(&bar->sub_grid, ev);
224 return bar->sub_grid.state == GRIDMENU_STATE_ACTIVATED;
225 default:
226 break;
227 }
228
229 return false;
230 }
231
232 static bool
233 handle_clickdown(struct battle_bar *bar, const union event *ev)
234 {
235 assert(ev->type == EVENT_CLICKDOWN);
236
237 switch (bar->state) {
238 case BATTLE_BAR_STATE_MENU:
239 /* We are selecting a main menu entry. */
240 /* TODO: implement click here too. */
241 break;
242 case BATTLE_BAR_STATE_SUB:
243 /* We are in the sub menu (objects/spells). */
244 gridmenu_handle(&bar->sub_grid, ev);
245 return bar->sub_grid.state == GRIDMENU_STATE_ACTIVATED;
246 default:
247 break;
248 }
249
250 return false;
251 }
252
253 void
254 battle_bar_positionate(struct battle_bar *bar, const struct battle *bt)
255 {
256 assert(bar);
257
258 /* Menu in the middle of the bar (take 20%). */
259 bar->menu_frame.w = bar->w * 0.2;
260 bar->menu_frame.h = bar->h;
261 bar->menu_frame.x = bar->x + (bar->w / 2) - (bar->menu_frame.w / 2);
262 bar->menu_frame.y = window.h - bar->h;
263 bar->menu_frame.theme = bt->theme;
264
265 /* Status bar on the right. */
266 bar->status_frame.x = bar->menu_frame.x + bar->menu_frame.w;
267 bar->status_frame.y = bar->menu_frame.y;
268 bar->status_frame.w = (bar->w - bar->menu_frame.w) / 2;
269 bar->status_frame.h = bar->h;
270 bar->status_frame.theme = bt->theme;
271 }
272
273 bool
274 battle_bar_handle(struct battle_bar *bar, const struct battle *bt, const union event *ev)
275 {
276 /* Not needed yet. */
277 (void)bt;
278
279 assert(bar);
280 assert(bt);
281 assert(ev);
282
283 if (bar->state == BATTLE_BAR_STATE_NONE)
284 return false;
285
286 switch (ev->type) {
287 case EVENT_KEYDOWN:
288 return handle_keydown(bar, ev);
289 case EVENT_CLICKDOWN:
290 return handle_clickdown(bar, ev);
291 default:
292 break;
293 }
294
295 return false;
296 }
297
298 void
299 battle_bar_reset(struct battle_bar *bar)
300 {
301 gridmenu_reset(&bar->sub_grid);
302
303 bar->menu = BATTLE_BAR_MENU_ATTACK;
304 bar->state = BATTLE_BAR_STATE_NONE;
305 }
306
307 void
308 battle_bar_open_menu(struct battle_bar *bar)
309 {
310 bar->state = BATTLE_BAR_STATE_MENU;
311 bar->menu = BATTLE_BAR_MENU_ATTACK;
312 }
313
314 void
315 battle_bar_open_spells(struct battle_bar *bar, const struct battle *bt, struct character *ch)
316 {
317 /* Sub menu bar on the left, take same space as status. */
318 bar->sub_grid.x = bar->x;
319 bar->sub_grid.y = bar->menu_frame.y;
320 bar->sub_grid.w = bar->status_frame.w;
321 bar->sub_grid.h = bar->h;
322 bar->sub_grid.theme = bt->theme;
323 bar->sub_grid.nrows = 3;
324 bar->sub_grid.ncols = 4;
325
326 memset(bar->sub_grid.menu, 0, sizeof (bar->sub_grid.menu));
327
328 for (size_t i = 0; i < CHARACTER_SPELL_MAX; ++i) {
329 if (ch->spells[i])
330 bar->sub_grid.menu[i] = ch->spells[i]->name;
331 }
332
333 gridmenu_repaint(&bar->sub_grid);
334
335 bar->state = BATTLE_BAR_STATE_SUB;
336 }
337
338 void
339 battle_bar_draw(const struct battle_bar *bar, const struct battle *bt)
340 {
341 assert(bar);
342 assert(bt);
343
344 draw_status(bar, bt);
345 draw_menu(bar, bt);
346
347 /* Sub menu is only shown if state is set to it. */
348 if (bar->state == BATTLE_BAR_STATE_SUB)
349 draw_sub(bar);
350 }
351
352 void
353 battle_bar_finish(struct battle_bar *bar)
354 {
355 assert(bar);
356
357 gridmenu_finish(&bar->sub_grid);
358
359 memset(bar, 0, sizeof (*bar));
360 }