Mercurial > molko
comparison src/libmlk-rpg/rpg/battle-bar-default.c @ 385:3f13dc6c0e37
rpg: separate battle and the bar, closes #2522
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 15 Feb 2022 14:45:11 +0100 |
parents | |
children | 7d5032755b7d |
comparison
equal
deleted
inserted
replaced
384:c458441ff472 | 385:3f13dc6c0e37 |
---|---|
1 /* | |
2 * battle-bar-default.c -- default battle status bar and menu implementation | |
3 * | |
4 * Copyright (c) 2020-2022 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 <stdlib.h> | |
22 #include <string.h> | |
23 | |
24 #include <core/alloc.h> | |
25 #include <core/event.h> | |
26 #include <core/font.h> | |
27 #include <core/sprite.h> | |
28 #include <core/trace.h> | |
29 #include <core/util.h> | |
30 #include <core/window.h> | |
31 | |
32 #include <ui/align.h> | |
33 #include <ui/theme.h> | |
34 | |
35 #include "battle-bar-default.h" | |
36 #include "battle-bar.h" | |
37 #include "battle-state-item.h" | |
38 #include "battle-state-selection.h" | |
39 #include "battle.h" | |
40 #include "character.h" | |
41 #include "inventory.h" | |
42 #include "item.h" | |
43 #include "rpg_p.h" | |
44 #include "spell.h" | |
45 | |
46 struct self { | |
47 struct battle_bar_default data; | |
48 struct battle_bar bar; | |
49 }; | |
50 | |
51 /* | |
52 * The following validate_* functions are called when the user has validated a | |
53 * selection depending on the current menu (e.g. Magic, Items). | |
54 * | |
55 * They change the battle state to the appropriate one. | |
56 */ | |
57 | |
58 static void | |
59 validate_attack(struct battle_bar_default *bar, struct battle *bt, const struct selection *sel) | |
60 { | |
61 (void)bar; | |
62 | |
63 struct character *target; | |
64 | |
65 if (sel->index_side == 0) | |
66 target = bt->enemies[sel->index_character].ch; | |
67 else | |
68 target = bt->team[sel->index_character].ch; | |
69 | |
70 battle_attack(bt, bt->order_cur->ch, target); | |
71 } | |
72 | |
73 static void | |
74 validate_magic(struct battle_bar_default *bar, struct battle *bt, const struct selection *sel) | |
75 { | |
76 struct character *source = bt->order_cur->ch; | |
77 const struct spell *spell = source->spells[bar->sub_grid.selected]; | |
78 | |
79 battle_cast(bt, source, spell, sel); | |
80 } | |
81 | |
82 static void | |
83 validate_item(struct battle_bar_default *bar, struct battle *bt, const struct selection *sel) | |
84 { | |
85 struct inventory_slot *slot; | |
86 struct battle_entity *source, *target; | |
87 | |
88 if (bar->sub_grid.selected >= INVENTORY_ITEM_MAX) | |
89 return; | |
90 if (!(slot = &bt->inventory->items[bar->sub_grid.selected])) | |
91 return; | |
92 | |
93 source = bt->order_cur; | |
94 target = sel->index_side == 0 | |
95 ? &bt->enemies[sel->index_character] | |
96 : &bt->team[sel->index_character]; | |
97 | |
98 /* TODO: battle_use? */ | |
99 battle_state_item(bt, source, target, slot); | |
100 } | |
101 | |
102 /* | |
103 * The following functions are used to switch to the battle selection state | |
104 * using the appropriate selector algorithm. For example, an item can only be | |
105 * used on a unique target while a spell can have multiple choices. | |
106 */ | |
107 | |
108 static void | |
109 switch_selection_attack(struct battle *bt) | |
110 { | |
111 struct selection sel = { | |
112 .allowed_kinds = SELECTION_KIND_ONE, | |
113 .allowed_sides = SELECTION_SIDE_ENEMY, | |
114 .index_side = 0 | |
115 }; | |
116 | |
117 /* Just make sure */ | |
118 | |
119 selection_first(&sel, bt); | |
120 battle_state_selection(bt, &sel); | |
121 } | |
122 | |
123 static void | |
124 switch_selection_spell(struct battle_bar_default *bar, struct battle *bt) | |
125 { | |
126 const struct character *ch = bt->order_cur->ch; | |
127 const struct spell *sp = ch->spells[bar->sub_grid.selected]; | |
128 struct selection sel = {0}; | |
129 | |
130 /* Don't forget to reset the gridmenu state. */ | |
131 gridmenu_reset(&bar->sub_grid); | |
132 | |
133 if (bar->sub_grid.selected > CHARACTER_SPELL_MAX) | |
134 return; | |
135 if (!(sp = ch->spells[bar->sub_grid.selected]) || sp->mp > (unsigned int)(ch->mp)) | |
136 return; | |
137 | |
138 /* Use the spell selection algorithm to fill default values. */ | |
139 spell_select(sp, bt, &sel); | |
140 battle_state_selection(bt, &sel); | |
141 | |
142 /* A cursor should be present. */ | |
143 if (!sprite_ok(BATTLE_THEME(bt)->sprites[THEME_SPRITE_CURSOR])) | |
144 tracef("battle: no cursor sprite in theme"); | |
145 } | |
146 | |
147 static void | |
148 switch_selection_item(struct battle *bt) | |
149 { | |
150 const struct selection slt = { | |
151 .allowed_kinds = SELECTION_KIND_ONE, | |
152 .allowed_sides = SELECTION_SIDE_TEAM | SELECTION_SIDE_ENEMY, | |
153 .index_side = 1, | |
154 .index_character = bt->order_curindex | |
155 }; | |
156 | |
157 battle_state_selection(bt, &slt); | |
158 } | |
159 | |
160 /* | |
161 * The following functions actually draw the bar and their components depending | |
162 * on the current selected menu. | |
163 */ | |
164 | |
165 static void | |
166 draw_help(const struct battle_bar_default *bar, const struct battle *bt, const char *what) | |
167 { | |
168 struct label label = {0}; | |
169 unsigned int lw = 0, lh = 0; | |
170 | |
171 label.flags = LABEL_FLAGS_SHADOW; | |
172 label.text = what; | |
173 label_query(&label, &lw, &lh); | |
174 label.x = bar->sub_grid.x + (bar->sub_grid.w / 2) - (lw / 2); | |
175 label.y = bar->sub_grid.y - lh - BATTLE_THEME(bt)->padding; | |
176 label_draw(&label); | |
177 } | |
178 | |
179 static void | |
180 draw_spell_help(const struct battle_bar_default *bar, const struct battle *bt) | |
181 { | |
182 const struct character *ch = bt->order_cur->ch; | |
183 const struct spell *sp; | |
184 | |
185 if (bar->sub_grid.selected >= CHARACTER_SPELL_MAX) | |
186 return; | |
187 if (!(sp = ch->spells[bar->sub_grid.selected])) | |
188 return; | |
189 | |
190 draw_help(bar, bt, sp->description); | |
191 } | |
192 | |
193 static void | |
194 draw_item_help(const struct battle_bar_default *bar, const struct battle *bt) | |
195 { | |
196 const struct inventory_slot *slot; | |
197 | |
198 if (bar->sub_grid.selected >= INVENTORY_ITEM_MAX) | |
199 return; | |
200 | |
201 slot = &bt->inventory->items[bar->sub_grid.selected]; | |
202 | |
203 if (!slot->item) | |
204 return; | |
205 | |
206 draw_help(bar, bt, slot->item->description); | |
207 } | |
208 | |
209 static void | |
210 draw_status_character_stats(const struct battle *bt, | |
211 const struct character *ch, | |
212 int x, | |
213 int y, | |
214 unsigned int w, | |
215 unsigned int h) | |
216 { | |
217 struct theme *theme = BATTLE_THEME(bt); | |
218 struct label label; | |
219 unsigned int spacing, lw, lh; | |
220 char line[64]; | |
221 | |
222 /* Compute spacing between elements. */ | |
223 spacing = h - (font_height(theme->fonts[THEME_FONT_INTERFACE]) * 3); | |
224 spacing /= 4; | |
225 | |
226 /* Reuse the same label. */ | |
227 label.theme = theme; | |
228 label.text = line; | |
229 label.flags = LABEL_FLAGS_SHADOW; | |
230 | |
231 /* HP. */ | |
232 snprintf(line, sizeof (line), "%d/%u", ch->hp, ch->hpmax); | |
233 label_query(&label, &lw, &lh); | |
234 label.x = x + w - lw - theme->padding; | |
235 label.y = y + spacing; | |
236 label_draw(&label); | |
237 | |
238 /* MP. */ | |
239 snprintf(line, sizeof (line), "%d/%u", ch->mp, ch->mpmax); | |
240 label_query(&label, &lw, &lh); | |
241 label.x = x + w - lw - theme->padding; | |
242 label.y = label.y + lh + spacing; | |
243 label_draw(&label); | |
244 | |
245 /* Status. */ | |
246 /* TODO: list all status. */ | |
247 } | |
248 | |
249 static void | |
250 draw_status_character(const struct battle_bar_default *bar, | |
251 const struct battle *bt, | |
252 const struct character *ch, | |
253 unsigned int index) | |
254 { | |
255 int x, y; | |
256 unsigned int w, h; | |
257 | |
258 /* Compute bounding box for rendering. */ | |
259 w = bar->status_frame.w / BATTLE_TEAM_MAX; | |
260 h = bar->status_frame.h; | |
261 x = bar->status_frame.x + (index * w); | |
262 y = bar->status_frame.y; | |
263 | |
264 draw_status_character_stats(bt, ch, x, y, w, h); | |
265 } | |
266 | |
267 static void | |
268 draw_status_characters(const struct battle_bar_default *bar, const struct battle *bt) | |
269 { | |
270 const struct battle_entity *et; | |
271 unsigned int index = 0; | |
272 | |
273 BATTLE_TEAM_FOREACH(bt, et) { | |
274 if (character_ok(et->ch)) | |
275 draw_status_character(bar, bt, et->ch, index); | |
276 | |
277 ++index; | |
278 } | |
279 } | |
280 | |
281 static void | |
282 draw_status(const struct battle_bar_default *bar, const struct battle *bt) | |
283 { | |
284 frame_draw(&bar->status_frame); | |
285 draw_status_characters(bar, bt); | |
286 } | |
287 | |
288 static void | |
289 draw_menu(const struct battle_bar_default *bar, const struct battle *bt) | |
290 { | |
291 struct { | |
292 unsigned int w, h; | |
293 enum align align; | |
294 struct label label; | |
295 } buttons[] = { | |
296 { | |
297 .align = ALIGN_TOP, | |
298 .label = { | |
299 .text = _("Attack"), | |
300 .flags = LABEL_FLAGS_SHADOW | |
301 } | |
302 }, | |
303 { | |
304 .align = ALIGN_RIGHT, | |
305 .label = { | |
306 .text = _("Magic"), | |
307 .flags = LABEL_FLAGS_SHADOW | |
308 } | |
309 }, | |
310 { | |
311 .align = ALIGN_BOTTOM, | |
312 .label = { | |
313 .text = _("Objects"), | |
314 .flags = LABEL_FLAGS_SHADOW | |
315 } | |
316 }, | |
317 { | |
318 .align = ALIGN_LEFT, | |
319 .label = { | |
320 .text = _("Special"), | |
321 .flags = LABEL_FLAGS_SHADOW | |
322 } | |
323 } | |
324 }; | |
325 | |
326 struct theme theme; | |
327 int bx, by; | |
328 unsigned int bw, bh; | |
329 | |
330 /* Copy theme according to menu selection. */ | |
331 theme_shallow(&theme, bt->theme); | |
332 | |
333 /* Compute bounding box with margins removed. */ | |
334 bx = bar->menu_frame.x + theme.padding; | |
335 by = bar->menu_frame.y + theme.padding; | |
336 bw = bar->menu_frame.w - theme.padding * 2; | |
337 bh = bar->menu_frame.h - theme.padding * 2; | |
338 | |
339 /* Draw menu frame. */ | |
340 frame_draw(&bar->menu_frame); | |
341 | |
342 for (size_t i = 0; i < UTIL_SIZE(buttons); ++i) { | |
343 buttons[i].label.theme = &theme; | |
344 | |
345 label_query(&buttons[i].label, &buttons[i].w, &buttons[i].h); | |
346 | |
347 /* Change theme if it's selected. */ | |
348 if ((size_t)bar->menu == i /*&& bar->state != BATTLE_BAR_DEFAULT_STATE_NONE*/) | |
349 theme.colors[THEME_COLOR_NORMAL] = BATTLE_THEME(bt)->colors[THEME_COLOR_SELECTED]; | |
350 else | |
351 theme.colors[THEME_COLOR_NORMAL] = BATTLE_THEME(bt)->colors[THEME_COLOR_NORMAL]; | |
352 | |
353 align(buttons[i].align, | |
354 &buttons[i].label.x, &buttons[i].label.y, buttons[i].w, buttons[i].h, | |
355 bx, by, bw, bh); | |
356 label_draw(&buttons[i].label); | |
357 } | |
358 } | |
359 | |
360 /* | |
361 * This function is called only in the first level of the bar menu: selecting | |
362 * one of the Attack, Magic, Item and Special items. | |
363 */ | |
364 static void | |
365 handle_keydown_menu(struct battle_bar_default *bar, struct battle *bt, const union event *ev) | |
366 { | |
367 (void)bt; | |
368 | |
369 switch (ev->key.key) { | |
370 case KEY_UP: | |
371 bar->menu = BATTLE_BAR_DEFAULT_MENU_ATTACK; | |
372 break; | |
373 case KEY_RIGHT: | |
374 bar->menu = BATTLE_BAR_DEFAULT_MENU_MAGIC; | |
375 break; | |
376 case KEY_DOWN: | |
377 bar->menu = BATTLE_BAR_DEFAULT_MENU_ITEM; | |
378 break; | |
379 case KEY_LEFT: | |
380 bar->menu = BATTLE_BAR_DEFAULT_MENU_SPECIAL; | |
381 break; | |
382 case KEY_ENTER: | |
383 /* | |
384 * At this step, attack does not require opening the sub menu so | |
385 * we change selection state immediately if needed. | |
386 */ | |
387 switch (bar->menu) { | |
388 case BATTLE_BAR_DEFAULT_MENU_ATTACK: | |
389 switch_selection_attack(bt); | |
390 break; | |
391 case BATTLE_BAR_DEFAULT_MENU_ITEM: | |
392 battle_bar_default_open_item(bar, bt); | |
393 break; | |
394 case BATTLE_BAR_DEFAULT_MENU_MAGIC: | |
395 battle_bar_default_open_magic(bar, bt, bt->order_cur->ch); | |
396 break; | |
397 default: | |
398 break; | |
399 } | |
400 break; | |
401 default: | |
402 break; | |
403 } | |
404 } | |
405 | |
406 /* | |
407 * This function is called when we're selecting a submenu entry from Items | |
408 * and Magic. | |
409 */ | |
410 static void | |
411 handle_keydown_grid(struct battle_bar_default *bar, struct battle *bt, const union event *ev) | |
412 { | |
413 /* Go back to main menu if I press escape. */ | |
414 if (ev->key.key == KEY_ESCAPE) { | |
415 gridmenu_reset(&bar->sub_grid); | |
416 bar->state = BATTLE_BAR_DEFAULT_STATE_MENU; | |
417 return; | |
418 } | |
419 | |
420 gridmenu_handle(&bar->sub_grid, ev); | |
421 | |
422 if (bar->sub_grid.state == GRIDMENU_STATE_ACTIVATED) { | |
423 gridmenu_reset(&bar->sub_grid); | |
424 | |
425 switch (bar->menu) { | |
426 case BATTLE_BAR_DEFAULT_MENU_MAGIC: | |
427 switch_selection_spell(bar, bt); | |
428 break; | |
429 case BATTLE_BAR_DEFAULT_MENU_ITEM: | |
430 switch_selection_item(bt); | |
431 break; | |
432 default: | |
433 break; | |
434 } | |
435 } | |
436 } | |
437 | |
438 static void | |
439 handle_keydown(struct battle_bar_default *bar, struct battle *bt, const union event *ev) | |
440 { | |
441 assert(ev->type == EVENT_KEYDOWN); | |
442 | |
443 static void (*handlers[])(struct battle_bar_default *, struct battle *, const union event *) = { | |
444 [BATTLE_BAR_DEFAULT_STATE_MENU] = handle_keydown_menu, | |
445 [BATTLE_BAR_DEFAULT_STATE_GRID] = handle_keydown_grid | |
446 }; | |
447 | |
448 handlers[bar->state](bar, bt, ev); | |
449 } | |
450 | |
451 #if 0 | |
452 | |
453 static void | |
454 handle_clickdown(struct battle_bar_default *bar, struct battle *bt, const union event *ev) | |
455 { | |
456 (void)bar; | |
457 (void)bt; | |
458 (void)ev; | |
459 assert(ev->type == EVENT_CLICKDOWN); | |
460 | |
461 switch (bar->state) { | |
462 case BATTLE_BAR_DEFAULT_STATE_MENU: | |
463 /* We are selecting a main menu entry. */ | |
464 /* TODO: implement click here too. */ | |
465 break; | |
466 case BATTLE_BAR_DEFAULT_STATE_SUB: | |
467 /* We are in the sub menu (objects/spells). */ | |
468 gridmenu_handle(&bar->sub_grid, ev); | |
469 | |
470 if (bar->sub_grid.state == GRIDMENU_STATE_ACTIVATED) | |
471 default: | |
472 break; | |
473 } | |
474 | |
475 return 0; | |
476 } | |
477 | |
478 #endif | |
479 | |
480 static void | |
481 init_gridmenu(struct battle_bar_default *bar, const struct battle *bt) | |
482 { | |
483 bar->sub_grid.x = bar->x; | |
484 bar->sub_grid.y = bar->menu_frame.y; | |
485 bar->sub_grid.w = bar->status_frame.w; | |
486 bar->sub_grid.h = bar->h; | |
487 bar->sub_grid.theme = bt->theme; | |
488 bar->sub_grid.nrows = 3; | |
489 bar->sub_grid.ncols = 4; | |
490 | |
491 memset(bar->sub_grid.menu, 0, sizeof (bar->sub_grid.menu)); | |
492 } | |
493 | |
494 static void | |
495 start(struct battle_bar *bar, struct battle *bt) | |
496 { | |
497 (void)bt; | |
498 | |
499 battle_bar_default_start(bar->data); | |
500 } | |
501 | |
502 static void | |
503 select(struct battle_bar *bar, struct battle *bt, const struct selection *sel) | |
504 { | |
505 battle_bar_default_select(bar->data, bt, sel); | |
506 } | |
507 | |
508 static void | |
509 handle(struct battle_bar *bar, struct battle *bt, const union event *ev) | |
510 { | |
511 battle_bar_default_handle(bar->data, bt, ev); | |
512 } | |
513 | |
514 static void | |
515 draw(const struct battle_bar *bar, const struct battle *bt) | |
516 { | |
517 battle_bar_default_draw(bar->data, bt); | |
518 } | |
519 | |
520 static void | |
521 finish(struct battle_bar *bar, struct battle *bt) | |
522 { | |
523 (void)bt; | |
524 | |
525 battle_bar_default_finish(bar->data); | |
526 free(bar->data); | |
527 } | |
528 | |
529 void | |
530 battle_bar_default_positionate(struct battle_bar_default *bar, const struct battle *bt) | |
531 { | |
532 assert(bar); | |
533 assert(bt); | |
534 | |
535 bar->w = window.w; | |
536 bar->h = window.h * 0.12; | |
537 bar->x = 0; | |
538 bar->y = window.h - bar->h; | |
539 | |
540 /* Menu in the middle of the bar (take 20%). */ | |
541 bar->menu_frame.w = bar->w * 0.2; | |
542 bar->menu_frame.h = bar->h; | |
543 bar->menu_frame.x = bar->x + (bar->w / 2) - (bar->menu_frame.w / 2); | |
544 bar->menu_frame.y = window.h - bar->h; | |
545 bar->menu_frame.theme = bt->theme; | |
546 | |
547 /* Status bar on the right. */ | |
548 bar->status_frame.x = bar->menu_frame.x + bar->menu_frame.w; | |
549 bar->status_frame.y = bar->menu_frame.y; | |
550 bar->status_frame.w = (bar->w - bar->menu_frame.w) / 2; | |
551 bar->status_frame.h = bar->h; | |
552 bar->status_frame.theme = bt->theme; | |
553 } | |
554 | |
555 void | |
556 battle_bar_default_open_magic(struct battle_bar_default *bar, const struct battle *bt, struct character *ch) | |
557 { | |
558 assert(bar); | |
559 assert(bt); | |
560 assert(ch); | |
561 | |
562 init_gridmenu(bar, bt); | |
563 | |
564 for (size_t i = 0; i < CHARACTER_SPELL_MAX; ++i) | |
565 if (ch->spells[i]) | |
566 bar->sub_grid.menu[i] = ch->spells[i]->name; | |
567 | |
568 gridmenu_repaint(&bar->sub_grid); | |
569 | |
570 bar->state = BATTLE_BAR_DEFAULT_STATE_GRID; | |
571 } | |
572 | |
573 void | |
574 battle_bar_default_open_item(struct battle_bar_default *bar, const struct battle *bt) | |
575 { | |
576 asssert(bar); | |
577 assert(bt); | |
578 | |
579 init_gridmenu(bar, bt); | |
580 | |
581 for (size_t i = 0; i < INVENTORY_ITEM_MAX; ++i) { | |
582 if (bt->inventory->items[i].item) { | |
583 snprintf(bar->sub_items[i], sizeof (bar->sub_items[i]), "%-16s %u", | |
584 bt->inventory->items[i].item->name, bt->inventory->items[i].amount); | |
585 bar->sub_grid.menu[i] = bar->sub_items[i]; | |
586 } | |
587 } | |
588 | |
589 gridmenu_repaint(&bar->sub_grid); | |
590 | |
591 bar->state = BATTLE_BAR_DEFAULT_STATE_GRID; | |
592 } | |
593 | |
594 void | |
595 battle_bar_default_start(struct battle_bar_default *bar) | |
596 { | |
597 assert(bar); | |
598 | |
599 gridmenu_reset(&bar->sub_grid); | |
600 | |
601 bar->menu = BATTLE_BAR_DEFAULT_MENU_ATTACK; | |
602 bar->state = BATTLE_BAR_DEFAULT_STATE_MENU; | |
603 } | |
604 | |
605 /* | |
606 * Apply the battle selection for the current menu item. This function is called | |
607 * from the battle-state-selection state when the user validated the selection. | |
608 */ | |
609 void | |
610 battle_bar_default_select(struct battle_bar_default *bar, struct battle *bt, const struct selection *sel) | |
611 { | |
612 assert(bar); | |
613 assert(bt); | |
614 assert(sel); | |
615 | |
616 static void (*validate[])(struct battle_bar_default *, struct battle *, const struct selection *) = { | |
617 [BATTLE_BAR_DEFAULT_MENU_ATTACK] = validate_attack, | |
618 [BATTLE_BAR_DEFAULT_MENU_ITEM] = validate_item, | |
619 [BATTLE_BAR_DEFAULT_MENU_MAGIC] = validate_magic, | |
620 [BATTLE_BAR_DEFAULT_MENU_SPECIAL] = NULL | |
621 }; | |
622 | |
623 if (validate[bar->menu]) | |
624 validate[bar->menu](bar, bt, sel); | |
625 } | |
626 | |
627 void | |
628 battle_bar_default_handle(struct battle_bar_default *bar, struct battle *bt, const union event *ev) | |
629 { | |
630 assert(bar); | |
631 assert(bt); | |
632 assert(ev); | |
633 | |
634 static void (*handlers[])(struct battle_bar_default *, struct battle *, const union event *) = { | |
635 [EVENT_KEYDOWN] = handle_keydown, | |
636 [EVENT_NUM] = NULL | |
637 }; | |
638 | |
639 if (handlers[ev->type]) | |
640 handlers[ev->type](bar, bt, ev); | |
641 } | |
642 | |
643 void | |
644 battle_bar_default_draw(const struct battle_bar_default *bar, const struct battle *bt) | |
645 { | |
646 assert(bar); | |
647 assert(bt); | |
648 | |
649 draw_status(bar, bt); | |
650 draw_menu(bar, bt); | |
651 | |
652 if (bar->state == BATTLE_BAR_DEFAULT_STATE_GRID) { | |
653 switch (bar->menu) { | |
654 case BATTLE_BAR_DEFAULT_MENU_MAGIC: | |
655 draw_spell_help(bar, bt); | |
656 break; | |
657 case BATTLE_BAR_DEFAULT_MENU_ITEM: | |
658 draw_item_help(bar, bt); | |
659 break; | |
660 default: | |
661 break; | |
662 } | |
663 } | |
664 | |
665 /* Sub menu is only shown if state is set to it. */ | |
666 if (bar->state == BATTLE_BAR_DEFAULT_STATE_GRID) | |
667 gridmenu_draw(&bar->sub_grid); | |
668 } | |
669 | |
670 void | |
671 battle_bar_default_finish(struct battle_bar_default *bar) | |
672 { | |
673 assert(bar); | |
674 | |
675 gridmenu_finish(&bar->sub_grid); | |
676 | |
677 memset(bar, 0, sizeof (*bar)); | |
678 } | |
679 | |
680 void | |
681 battle_bar_default(struct battle *bt) | |
682 { | |
683 assert(bt); | |
684 | |
685 struct self *self; | |
686 | |
687 self = alloc_new0(sizeof (*self)); | |
688 self->bar.data = self; | |
689 self->bar.start = start; | |
690 self->bar.select = select; | |
691 self->bar.handle = handle; | |
692 self->bar.draw = draw; | |
693 self->bar.finish = finish; | |
694 | |
695 battle_bar_default_positionate(&self->data, bt); | |
696 | |
697 bt->bar = &self->bar; | |
698 } |