webperimental: Mountain vision bonus.
[freeciv.git] / client / gui-sdl2 / mapview.c
blob4b9b3c275c88134b95f9e43e4b44180b1d66dc53
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 /***********************************************************************
15 mapview.c - description
16 -------------------
17 begin : Aug 10 2002
18 copyright : (C) 2002 by Rafał Bursig
19 email : Rafał Bursig <bursig@poczta.fm>
20 ***********************************************************************/
22 #ifdef HAVE_CONFIG_H
23 #include <fc_config.h>
24 #endif
26 /* SDL2 */
27 #ifdef SDL2_PLAIN_INCLUDE
28 #include <SDL.h>
29 #else /* SDL2_PLAIN_INCLUDE */
30 #include <SDL2/SDL.h>
31 #endif /* SDL2_PLAIN_INCLUDE */
33 /* utility */
34 #include "astring.h"
35 #include "fcintl.h"
36 #include "log.h"
38 /* common */
39 #include "calendar.h"
40 #include "game.h"
41 #include "goto.h"
42 #include "government.h"
43 #include "movement.h"
44 #include "research.h"
45 #include "unitlist.h"
47 /* client */
48 #include "citydlg_common.h"
49 #include "client_main.h"
50 #include "climisc.h"
51 #include "overview_common.h"
52 #include "pages_g.h"
53 #include "text.h"
55 /* gui-sdl2 */
56 #include "colors.h"
57 #include "dialogs.h"
58 #include "graphics.h"
59 #include "gui_id.h"
60 #include "gui_main.h"
61 #include "gui_mouse.h"
62 #include "gui_tilespec.h"
63 #include "mapctrl.h"
64 #include "sprite.h"
65 #include "themespec.h"
66 #include "widget.h"
68 #include "mapview.h"
70 extern SDL_Event *flush_event;
71 extern SDL_Rect *pInfo_Area;
73 int overview_start_x = 0;
74 int overview_start_y = 0;
76 static enum {
77 NORMAL = 0,
78 BORDERS = 1,
79 TEAMS
80 } overview_mode = NORMAL;
82 static struct canvas *overview_canvas;
83 static struct canvas *city_map_canvas;
84 static struct canvas *terrain_canvas;
86 /* ================================================================ */
88 void update_map_canvas_scrollbars_size(void)
90 /* No scrollbars */
93 static bool is_flush_queued = FALSE;
95 /**************************************************************************
96 Flush the mapcanvas part of the buffer(s) to the screen.
97 **************************************************************************/
98 void flush_mapcanvas(int canvas_x, int canvas_y,
99 int pixel_width, int pixel_height)
101 SDL_Rect rect = {canvas_x, canvas_y, pixel_width, pixel_height};
103 alphablit(mapview.store->surf, &rect, Main.map, &rect, 255);
106 /**************************************************************************
107 Flush the given part of the buffer(s) to the screen.
108 **************************************************************************/
109 void flush_rect(SDL_Rect *rect, bool force_flush)
111 if (is_flush_queued && !force_flush) {
112 dirty_sdl_rect(rect);
113 } else {
114 static SDL_Rect src, dst;
116 if (correct_rect_region(rect)) {
117 static int i = 0;
119 dst = *rect;
120 if (C_S_RUNNING == client_state()) {
121 flush_mapcanvas(dst.x, dst.y, dst.w, dst.h);
123 screen_blit(Main.map, rect, &dst, 255);
124 if (Main.guis) {
125 while ((i < Main.guis_count) && Main.guis[i]) {
126 src = *rect;
127 screen_rect_to_layer_rect(Main.guis[i], &src);
128 dst = *rect;
129 screen_blit(Main.guis[i++]->surface, &src, &dst, 255);
132 i = 0;
134 /* flush main buffer to framebuffer */
135 #if 0
136 SDL_UpdateRect(Main.screen, rect->x, rect->y, rect->w, rect->h);
137 #endif /* 0 */
142 /**************************************************************************
143 A callback invoked as a result of a FLUSH event, this function simply
144 flushes the mapview canvas.
145 **************************************************************************/
146 void unqueue_flush(void)
148 flush_dirty();
149 redraw_selection_rectangle();
150 is_flush_queued = FALSE;
153 /**************************************************************************
154 Called when a region is marked dirty, this function queues a flush event
155 to be handled later by SDL. The flush may end up being done
156 by freeciv before then, in which case it will be a wasted call.
157 **************************************************************************/
158 void queue_flush(void)
160 if (!is_flush_queued) {
161 if (SDL_PushEvent(flush_event) >= 0) {
162 is_flush_queued = TRUE;
163 } else {
164 /* We don't want to set is_flush_queued in this case, since then
165 * the flush code would simply stop working. But this means the
166 * below message may be repeated many times. */
167 log_error(_("Failed to add events to SDL2 event buffer: %s"),
168 SDL_GetError());
169 /* TRANS: No full stop after the URL, could cause confusion. */
170 log_error(_("Please report this message at %s"), BUG_URL);
175 /**************************************************************************
176 Save Flush area used by "end" flush.
177 **************************************************************************/
178 void dirty_rect(int canvas_x, int canvas_y,
179 int pixel_width, int pixel_height)
181 SDL_Rect Rect = {canvas_x, canvas_y, pixel_width, pixel_height};
183 dirty_sdl_rect(&Rect);
186 /**************************************************************************
187 Save Flush rect used by "end" flush.
188 **************************************************************************/
189 void dirty_sdl_rect(SDL_Rect *Rect)
191 if ((Main.rects_count < RECT_LIMIT) && correct_rect_region(Rect)) {
192 Main.rects[Main.rects_count++] = *Rect;
193 queue_flush();
197 /**************************************************************************
198 Select entire screen area to "end" flush and block "save" new areas.
199 **************************************************************************/
200 void dirty_all(void)
202 Main.rects_count = RECT_LIMIT;
203 queue_flush();
206 /**************************************************************************
207 flush entire screen.
208 **************************************************************************/
209 void flush_all(void)
211 is_flush_queued = FALSE;
212 Main.rects_count = RECT_LIMIT;
213 flush_dirty();
216 /**************************************************************************
217 Make "end" Flush "saved" parts/areas of the buffer(s) to the screen.
218 Function is called in handle_procesing_finished and handle_thaw_hint
219 **************************************************************************/
220 void flush_dirty(void)
222 static int j = 0;
224 if (!Main.rects_count) {
225 return;
228 if (Main.rects_count >= RECT_LIMIT) {
230 if ((C_S_RUNNING == client_state()) &&
231 (get_client_page() == PAGE_GAME)) {
232 flush_mapcanvas(0, 0, main_window_width(), main_window_height());
233 refresh_overview();
235 screen_blit(Main.map, NULL, NULL, 255);
236 if (Main.guis) {
237 while ((j < Main.guis_count) && Main.guis[j]) {
238 SDL_Rect dst = Main.guis[j]->dest_rect;
240 screen_blit(Main.guis[j++]->surface, NULL, &dst, 255);
243 j = 0;
245 draw_mouse_cursor();
247 /* Render to screen */
248 update_main_screen();
249 } else {
250 static int i;
251 static SDL_Rect src, dst;
253 for (i = 0; i < Main.rects_count; i++) {
255 dst = Main.rects[i];
256 if (C_S_RUNNING == client_state()) {
257 flush_mapcanvas(dst.x, dst.y, dst.w, dst.h);
259 screen_blit(Main.map, &Main.rects[i], &dst, 255);
261 if (Main.guis) {
262 while ((j < Main.guis_count) && Main.guis[j]) {
263 src = Main.rects[i];
264 screen_rect_to_layer_rect(Main.guis[j], &src);
265 dst = Main.rects[i];
266 screen_blit(Main.guis[j++]->surface, &src, &dst, 255);
269 j = 0;
271 /* restore widget info label if it overlaps with this area */
272 dst = Main.rects[i];
273 if (pInfo_Area && !(((dst.x + dst.w) < pInfo_Area->x)
274 || (dst.x > (pInfo_Area->x + pInfo_Area->w))
275 || ((dst.y + dst.h) < pInfo_Area->y)
276 || (dst.y > (pInfo_Area->y + pInfo_Area->h)))) {
277 redraw_widget_info_label(&dst);
281 draw_mouse_cursor();
283 /* Render to screen */
284 update_main_screen();
285 #if 0
286 SDL_UpdateRects(Main.screen, Main.rects_count, Main.rects);
287 #endif
289 Main.rects_count = 0;
292 /**************************************************************************
293 This function is called when the map has changed.
294 **************************************************************************/
295 void gui_flush(void)
297 if (C_S_RUNNING == client_state()) {
298 refresh_overview();
302 /* ===================================================================== */
304 /**************************************************************************
305 Set information for the indicator icons typically shown in the main
306 client window. The parameters tell which sprite to use for the
307 indicator.
308 **************************************************************************/
309 void set_indicator_icons(struct sprite *bulb, struct sprite *sol,
310 struct sprite *flake, struct sprite *gov)
312 struct widget *pBuf = NULL;
313 char cBuf[128];
315 pBuf = get_widget_pointer_form_main_list(ID_WARMING_ICON);
316 FREESURFACE(pBuf->theme);
317 pBuf->theme = adj_surf(GET_SURF(sol));
318 widget_redraw(pBuf);
320 pBuf = get_widget_pointer_form_main_list(ID_COOLING_ICON);
321 FREESURFACE(pBuf->theme);
322 pBuf->theme = adj_surf(GET_SURF(flake));
323 widget_redraw(pBuf);
325 pBuf = get_revolution_widget();
326 set_new_icon2_theme(pBuf, adj_surf(GET_SURF(gov)), TRUE);
328 if (NULL != client.conn.playing) {
329 fc_snprintf(cBuf, sizeof(cBuf), "%s (%s)\n%s", _("Revolution"), "Ctrl+Shift+R",
330 government_name_for_player(client.conn.playing));
331 } else {
332 fc_snprintf(cBuf, sizeof(cBuf), "%s (%s)\n%s", _("Revolution"), "Ctrl+Shift+R",
333 Q_("?gov:None"));
335 copy_chars_to_utf8_str(pBuf->info_label, cBuf);
337 widget_redraw(pBuf);
338 widget_mark_dirty(pBuf);
340 pBuf = get_tax_rates_widget();
341 if (!pBuf->theme) {
342 set_new_icon2_theme(pBuf, get_tax_surface(O_GOLD), TRUE);
345 pBuf = get_research_widget();
347 if (NULL == client.conn.playing) {
348 /* TRANS: Research report action */
349 fc_snprintf(cBuf, sizeof(cBuf), "%s (%s)\n%s (%d/%d)", _("Research"), "F6",
350 Q_("?tech:None"), 0, 0);
351 } else {
352 const struct research *presearch = research_get(client_player());
354 if (A_UNSET != presearch->researching) {
355 /* TRANS: Research report action */
356 fc_snprintf(cBuf, sizeof(cBuf), "%s (%s)\n%s (%d/%d)",
357 _("Research"), "F6",
358 research_advance_name_translation(presearch,
359 presearch->researching),
360 presearch->bulbs_researched,
361 presearch->client.researching_cost);
362 } else {
363 /* TRANS: Research report action */
364 fc_snprintf(cBuf, sizeof(cBuf), "%s (%s)\n%s (%d/%d)",
365 _("Research"), "F6",
366 research_advance_name_translation(presearch,
367 presearch->researching),
368 presearch->bulbs_researched,
373 copy_chars_to_utf8_str(pBuf->info_label, cBuf);
375 set_new_icon2_theme(pBuf, adj_surf(GET_SURF(bulb)), TRUE);
377 widget_redraw(pBuf);
378 widget_mark_dirty(pBuf);
381 /****************************************************************************
382 Called when the map size changes. This may be used to change the
383 size of the GUI element holding the overview canvas. The
384 overview.width and overview.height are updated if this function is
385 called.
386 ****************************************************************************/
387 void overview_size_changed(void)
389 map_canvas_resized(main_window_width(), main_window_height());
391 if (overview_canvas) {
392 canvas_free(overview_canvas);
394 overview_canvas = canvas_create(gui_options.overview.width,
395 gui_options.overview.height);
397 resize_minimap();
400 /**************************************************************************
401 Typically an info box is provided to tell the player about the state
402 of their civilization. This function is called when the label is
403 changed.
404 **************************************************************************/
405 void update_info_label(void)
407 SDL_Color bg_color = {0, 0, 0, 80};
408 SDL_Surface *pTmp = NULL;
409 char buffer[512];
410 #ifdef SMALL_SCREEN
411 SDL_Rect area = {0, 0, 0, 0};
412 #else
413 SDL_Rect area = {0, 3, 0, 0};
414 #endif
415 struct utf8_str *ptext;
417 if (get_current_client_page() != PAGE_GAME) {
418 return;
421 #ifdef SMALL_SCREEN
422 ptext = create_utf8_str(NULL, 0, 8);
423 #else
424 ptext = create_utf8_str(NULL, 0, 10);
425 #endif
427 /* set text settings */
428 ptext->style |= TTF_STYLE_BOLD;
429 ptext->fgcol = *get_theme_color(COLOR_THEME_MAPVIEW_INFO_TEXT);
430 ptext->bgcol = (SDL_Color) {0, 0, 0, 0};
432 if (NULL != client.conn.playing) {
433 #ifdef SMALL_SCREEN
434 fc_snprintf(buffer, sizeof(buffer),
435 _("%s Population: %s Year: %s "
436 "Gold %d "),
437 nation_adjective_for_player(client.conn.playing),
438 population_to_text(civ_population(client.conn.playing)),
439 calendar_text(),
440 client.conn.playing->economic.gold);
441 #else /* SMALL_SCREEN */
442 fc_snprintf(buffer, sizeof(buffer),
443 _("%s Population: %s Year: %s "
444 "Gold %d Tax: %d Lux: %d Sci: %d "),
445 nation_adjective_for_player(client.conn.playing),
446 population_to_text(civ_population(client.conn.playing)),
447 calendar_text(),
448 client.conn.playing->economic.gold,
449 client.conn.playing->economic.tax,
450 client.conn.playing->economic.luxury,
451 client.conn.playing->economic.science);
452 #endif /* SMALL_SCREEN */
453 /* convert to unistr and create text surface */
454 copy_chars_to_utf8_str(ptext, buffer);
455 pTmp = create_text_surf_from_utf8(ptext);
457 area.x = (main_window_width() - pTmp->w) / 2 - adj_size(5);
458 area.w = pTmp->w + adj_size(8);
459 area.h = pTmp->h + adj_size(4);
461 SDL_FillRect(Main.gui->surface, &area , map_rgba(Main.gui->surface->format, bg_color));
463 /* Horizontal lines */
464 create_line(Main.gui->surface,
465 area.x + 1, area.y, area.x + area.w - 2, area.y,
466 get_theme_color(COLOR_THEME_MAPVIEW_INFO_FRAME));
467 create_line(Main.gui->surface,
468 area.x + 1, area.y + area.h - 1, area.x + area.w - 2, area.y + area.h - 1,
469 get_theme_color(COLOR_THEME_MAPVIEW_INFO_FRAME));
471 /* vertical lines */
472 create_line(Main.gui->surface,
473 area.x + area.w - 1, area.y + 1, area.x + area.w - 1, area.y + area.h - 2,
474 get_theme_color(COLOR_THEME_MAPVIEW_INFO_FRAME));
475 create_line(Main.gui->surface,
476 area.x, area.y + 1, area.x, area.y + area.h - 2,
477 get_theme_color(COLOR_THEME_MAPVIEW_INFO_FRAME));
479 /* blit text to screen */
480 blit_entire_src(pTmp, Main.gui->surface, area.x + adj_size(5), area.y + adj_size(2));
482 dirty_sdl_rect(&area);
484 FREESURFACE(pTmp);
487 set_indicator_icons(client_research_sprite(),
488 client_warming_sprite(),
489 client_cooling_sprite(),
490 client_government_sprite());
492 update_timeout_label();
494 FREEUTF8STR(ptext);
496 queue_flush();
499 /**************************************************************************
500 User interacted with the focus units widget.
501 **************************************************************************/
502 static int focus_units_info_callback(struct widget *pwidget)
504 if (Main.event.button.button == SDL_BUTTON_LEFT) {
505 struct unit *punit = pwidget->data.unit;
507 if (punit) {
508 request_new_unit_activity(punit, ACTIVITY_IDLE);
509 unit_focus_set(punit);
513 return -1;
516 /**************************************************************************
517 Read Function Name :)
518 FIXME: should use same method as client/text.c popup_info_text()
519 **************************************************************************/
520 void redraw_unit_info_label(struct unit_list *punitlist)
522 struct widget *pInfo_Window;
523 SDL_Rect src, area;
524 SDL_Rect dest;
525 SDL_Surface *buf_surf;
526 utf8_str *pstr;
527 struct canvas *destcanvas;
528 struct unit *pUnit;
530 if (punitlist != NULL && unit_list_size(punitlist) > 0) {
531 pUnit = unit_list_get(punitlist, 0);
532 } else {
533 pUnit = NULL;
536 if (SDL_Client_Flags & CF_UNITINFO_SHOWN) {
537 pInfo_Window = get_unit_info_window_widget();
539 /* blit theme surface */
540 widget_redraw(pInfo_Window);
542 if (pUnit) {
543 SDL_Surface *pName, *pVet_Name = NULL, *pInfo, *pInfo_II = NULL;
544 int sy, y, width, height, n;
545 bool right;
546 char buffer[512];
547 struct tile *pTile = unit_tile(pUnit);
548 const char *vetname;
550 /* get and draw unit name (with veteran status) */
551 pstr = create_utf8_from_char(unit_name_translation(pUnit), adj_font(12));
552 pstr->style |= TTF_STYLE_BOLD;
553 pstr->bgcol = (SDL_Color) {0, 0, 0, 0};
554 pName = create_text_surf_from_utf8(pstr);
556 pstr->style &= ~TTF_STYLE_BOLD;
558 if (pInfo_Window->size.w > 1.8 *
559 ((pInfo_Window->size.w - pInfo_Window->area.w) + DEFAULT_UNITS_W)) {
560 width = pInfo_Window->area.w / 2;
561 right = TRUE;
562 } else {
563 width = pInfo_Window->area.w;
564 right = FALSE;
567 change_ptsize_utf8(pstr, adj_font(10));
568 vetname = utype_veteran_name_translation(unit_type_get(pUnit),
569 pUnit->veteran);
570 if (vetname != NULL) {
571 copy_chars_to_utf8_str(pstr, vetname);
572 pstr->fgcol = *get_theme_color(COLOR_THEME_MAPVIEW_UNITINFO_VETERAN_TEXT);
573 pVet_Name = create_text_surf_from_utf8(pstr);
574 pstr->fgcol = *get_theme_color(COLOR_THEME_MAPVIEW_UNITINFO_TEXT);
577 /* get and draw other info (MP, terrain, city, etc.) */
578 pstr->style |= SF_CENTER;
580 copy_chars_to_utf8_str(pstr,
581 get_unit_info_label_text2(punitlist,
582 TILE_LB_RESOURCE_POLL));
583 pInfo = create_text_surf_from_utf8(pstr);
585 if (pInfo_Window->size.h >
586 (DEFAULT_UNITS_H + (pInfo_Window->size.h - pInfo_Window->area.h)) || right) {
587 int h = TTF_FontHeight(pInfo_Window->string_utf8->font);
589 fc_snprintf(buffer, sizeof(buffer), "%s",
590 sdl_get_tile_defense_info_text(pTile));
592 if (pInfo_Window->size.h >
593 2 * h + (DEFAULT_UNITS_H + (pInfo_Window->size.h - pInfo_Window->area.h)) || right) {
594 struct city *pCity = tile_city(pTile);
596 if (BORDERS_DISABLED != game.info.borders && !pCity) {
597 const char *diplo_nation_plural_adjectives[DS_LAST] =
598 {"" /* unused, DS_ARMISTICE */, Q_("?nation:Hostile"),
599 "" /* unused, DS_CEASEFIRE */,
600 Q_("?nation:Peaceful"), Q_("?nation:Friendly"),
601 Q_("?nation:Mysterious")};
603 if (tile_owner(pTile) == client.conn.playing) {
604 cat_snprintf(buffer, sizeof(buffer), _("\nOur Territory"));
605 } else {
606 if (tile_owner(pTile)) {
607 struct player_diplstate *ds
608 = player_diplstate_get(client.conn.playing,
609 tile_owner(pTile));
611 if (DS_CEASEFIRE == ds->type) {
612 int turns = ds->turns_left;
614 cat_snprintf(buffer, sizeof(buffer),
615 PL_("\n%s territory (%d turn ceasefire)",
616 "\n%s territory (%d turn ceasefire)", turns),
617 nation_adjective_for_player(tile_owner(pTile)),
618 turns);
619 } else if (DS_ARMISTICE == ds->type) {
620 int turns = ds->turns_left;
622 cat_snprintf(buffer, sizeof(buffer),
623 PL_("\n%s territory (%d turn armistice)",
624 "\n%s territory (%d turn armistice)", turns),
625 nation_adjective_for_player(tile_owner(pTile)),
626 turns);
627 } else {
628 cat_snprintf(buffer, sizeof(buffer), _("\nTerritory of the %s %s"),
629 diplo_nation_plural_adjectives[ds->type],
630 nation_plural_for_player(tile_owner(pTile)));
632 } else { /* !tile_owner(pTile) */
633 cat_snprintf(buffer, sizeof(buffer), _("\nUnclaimed territory"));
636 } /* BORDERS_DISABLED != game.info.borders && !pCity */
638 if (pCity) {
639 /* Look at city owner, not tile owner (the two should be the same, if
640 * borders are in use). */
641 struct player *pOwner = city_owner(pCity);
642 const char *diplo_city_adjectives[DS_LAST] =
643 {Q_("?city:Neutral"), Q_("?city:Hostile"),
644 Q_("?city:Neutral"), Q_("?city:Peaceful"),
645 Q_("?city:Friendly"), Q_("?city:Mysterious")};
647 cat_snprintf(buffer, sizeof(buffer),
648 _("\nCity of %s"),
649 city_name_get(pCity));
651 #if 0
652 /* This has hardcoded assumption that EFT_LAND_REGEN is always
653 * provided by *building* named *Barracks*. Similar assumptions for
654 * other effects. */
655 if (pplayers_allied(client.conn.playing, pOwner)) {
656 barrack = (get_city_bonus(pCity, EFT_LAND_REGEN) > 0);
657 airport = (get_city_bonus(pCity, EFT_AIR_VETERAN) > 0);
658 port = (get_city_bonus(pCity, EFT_SEA_VETERAN) > 0);
661 if (citywall || barrack || airport || port) {
662 cat_snprintf(buffer, sizeof(buffer), Q_("?blistbegin: with "));
663 if (barrack) {
664 cat_snprintf(buffer, sizeof(buffer), _("Barracks"));
665 if (port || airport || citywall) {
666 cat_snprintf(buffer, sizeof(buffer), Q_("?blistmore:, "));
669 if (port) {
670 cat_snprintf(buffer, sizeof(buffer), _("Port"));
671 if (airport || citywall) {
672 cat_snprintf(buffer, sizeof(buffer), Q_("?blistmore:, "));
675 if (airport) {
676 cat_snprintf(buffer, sizeof(buffer), _("Airport"));
677 if (citywall) {
678 cat_snprintf(buffer, sizeof(buffer), Q_("?blistmore:, "));
681 if (citywall) {
682 cat_snprintf(buffer, sizeof(buffer), _("City Walls"));
685 cat_snprintf(buffer, sizeof(buffer), Q_("?blistend:"));
687 #endif /* 0 */
689 if (pOwner && pOwner != client.conn.playing) {
690 struct player_diplstate *ds
691 = player_diplstate_get(client.conn.playing, pOwner);
692 /* TRANS: (<nation>,<diplomatic_state>)" */
693 cat_snprintf(buffer, sizeof(buffer), _("\n(%s,%s)"),
694 nation_adjective_for_player(pOwner),
695 diplo_city_adjectives[ds->type]);
700 if (pInfo_Window->size.h >
701 4 * h + (DEFAULT_UNITS_H + (pInfo_Window->size.h - pInfo_Window->area.h)) || right) {
702 cat_snprintf(buffer, sizeof(buffer), _("\nFood/Prod/Trade: %s"),
703 get_tile_output_text(unit_tile(pUnit)));
706 copy_chars_to_utf8_str(pstr, buffer);
708 pInfo_II = create_text_surf_smaller_than_w(pstr, width - BLOCKU_W - adj_size(4));
711 FREEUTF8STR(pstr);
713 /* ------------------------------------------- */
715 n = unit_list_size(pTile->units);
716 y = 0;
718 if (n > 1 && ((!right && pInfo_II
719 && (pInfo_Window->size.h - (DEFAULT_UNITS_H + (pInfo_Window->size.h - pInfo_Window->area.h)) -
720 pInfo_II->h > 52))
721 || (right && pInfo_Window->size.h - (DEFAULT_UNITS_H + (pInfo_Window->size.h -
722 pInfo_Window->area.h)) > 52))) {
723 height = (pInfo_Window->size.h - pInfo_Window->area.h) + DEFAULT_UNITS_H;
724 } else {
725 height = pInfo_Window->size.h;
726 if (pInfo_Window->size.h > (DEFAULT_UNITS_H + (pInfo_Window->size.h - pInfo_Window->area.h))) {
727 y = (pInfo_Window->size.h - (DEFAULT_UNITS_H + (pInfo_Window->size.h - pInfo_Window->area.h)) -
728 (!right && pInfo_II ? pInfo_II->h : 0)) / 2;
732 sy = y + adj_size(3);
733 area.y = pInfo_Window->area.y + sy;
734 area.x = pInfo_Window->area.x + BLOCKU_W + (width - pName->w - BLOCKU_W) / 2;
735 dest = area;
736 alphablit(pName, NULL, pInfo_Window->dst->surface, &dest, 255);
737 sy += pName->h;
738 if (pVet_Name) {
739 area.y += pName->h - adj_size(3);
740 area.x = pInfo_Window->area.x + BLOCKU_W + (width - pVet_Name->w - BLOCKU_W) / 2;
741 alphablit(pVet_Name, NULL, pInfo_Window->dst->surface, &area, 255);
742 sy += pVet_Name->h - adj_size(3);
743 FREESURFACE(pVet_Name);
745 FREESURFACE(pName);
747 /* draw unit sprite */
748 buf_surf = ResizeSurfaceBox(get_unittype_surface(unit_type_get(pUnit),
749 pUnit->facing),
750 adj_size(80), adj_size(80), 1, TRUE, TRUE);
752 src = (SDL_Rect){0, 0, buf_surf->w, buf_surf->h};
754 area.x = pInfo_Window->area.x + BLOCKU_W - adj_size(4) +
755 ((width - BLOCKU_W + adj_size(3) - src.w) / 2);
756 area.y = pInfo_Window->size.y + sy + (DEFAULT_UNITS_H - sy - src.h) / 2;
757 alphablit(buf_surf, &src, pInfo_Window->dst->surface, &area, 32);
758 FREESURFACE(buf_surf);
760 /* blit unit info text */
761 area.x = pInfo_Window->area.x + BLOCKU_W - adj_size(4) +
762 ((width - BLOCKU_W + adj_size(4) - pInfo->w) / 2);
763 area.y = pInfo_Window->size.y + sy + (DEFAULT_UNITS_H - sy - pInfo->h) / 2;
765 alphablit(pInfo, NULL, pInfo_Window->dst->surface, &area, 255);
766 FREESURFACE(pInfo);
768 if (pInfo_II) {
769 if (right) {
770 area.x = pInfo_Window->area.x + width + (width - pInfo_II->w) / 2;
771 area.y = pInfo_Window->area.y + (height - pInfo_II->h) / 2;
772 } else {
773 area.y = pInfo_Window->area.y + DEFAULT_UNITS_H + y;
774 area.x = pInfo_Window->area.x + BLOCKU_W +
775 (width - BLOCKU_W - pInfo_II->w) / 2;
778 /* blit unit info text */
779 alphablit(pInfo_II, NULL, pInfo_Window->dst->surface, &area, 255);
781 if (right) {
782 sy = (DEFAULT_UNITS_H + (pInfo_Window->size.h - pInfo_Window->area.h));
783 } else {
784 sy = area.y - pInfo_Window->size.y + pInfo_II->h;
786 FREESURFACE(pInfo_II);
787 } else {
788 sy = (DEFAULT_UNITS_H + (pInfo_Window->size.h - pInfo_Window->area.h));
791 if (n > 1 && (pInfo_Window->size.h - sy > 52)) {
792 struct ADVANCED_DLG *pDlg = pInfo_Window->private_data.adv_dlg;
793 struct widget *pBuf = NULL, *pEnd = NULL, *pDock;
794 struct city *pHome_City;
795 struct unit_type *pUType;
796 int num_w, num_h;
798 if (pDlg->pEndActiveWidgetList && pDlg->pBeginActiveWidgetList) {
799 del_group(pDlg->pBeginActiveWidgetList, pDlg->pEndActiveWidgetList);
801 num_w = (pInfo_Window->area.w - BLOCKU_W) / 68;
802 num_h = (pInfo_Window->area.h - sy) / 52;
803 pDock = pInfo_Window;
804 n = 0;
806 unit_list_iterate(pTile->units, aunit) {
807 SDL_Surface *tmp_surf;
809 if (aunit == pUnit) {
810 continue;
813 pUType = unit_type_get(aunit);
814 vetname = utype_veteran_name_translation(pUType, aunit->veteran);
815 pHome_City = game_city_by_number(aunit->homecity);
816 fc_snprintf(buffer, sizeof(buffer), "%s (%d,%d,%s)%s%s\n%s\n(%d/%d)\n%s",
817 utype_name_translation(pUType),
818 pUType->attack_strength,
819 pUType->defense_strength,
820 move_points_text(pUType->move_rate, FALSE),
821 (vetname != NULL ? "\n" : ""),
822 (vetname != NULL ? vetname : ""),
823 unit_activity_text(aunit),
824 aunit->hp, pUType->hp,
825 pHome_City ? city_name_get(pHome_City) : Q_("?homecity:None"));
827 buf_surf = create_surf(tileset_full_tile_width(tileset),
828 tileset_full_tile_height(tileset), SDL_SWSURFACE);
830 destcanvas = canvas_create(tileset_full_tile_width(tileset),
831 tileset_full_tile_height(tileset));
833 put_unit(aunit, destcanvas, 1.0, 0, 0);
835 tmp_surf = adj_surf(destcanvas->surf);
837 alphablit(tmp_surf, NULL, buf_surf, NULL, 255);
839 FREESURFACE(tmp_surf);
840 canvas_free(destcanvas);
842 if (buf_surf->w > 64) {
843 float zoom = 64.0 / buf_surf->w;
844 SDL_Surface *zoomed = zoomSurface(buf_surf, zoom, zoom, 1);
846 FREESURFACE(buf_surf);
847 buf_surf = zoomed;
850 pstr = create_utf8_from_char(buffer, 10);
851 pstr->style |= SF_CENTER;
853 pBuf = create_icon2(buf_surf, pInfo_Window->dst,
854 WF_FREE_THEME | WF_RESTORE_BACKGROUND
855 | WF_WIDGET_HAS_INFO_LABEL);
856 pBuf->info_label = pstr;
857 pBuf->data.unit = aunit;
858 pBuf->ID = ID_ICON;
859 DownAdd(pBuf, pDock);
860 pDock = pBuf;
862 if (!pEnd) {
863 pEnd = pBuf;
866 if (++n > num_w * num_h) {
867 set_wflag(pBuf, WF_HIDDEN);
870 if (unit_owner(aunit) == client.conn.playing) {
871 set_wstate(pBuf, FC_WS_NORMAL);
874 pBuf->action = focus_units_info_callback;
876 } unit_list_iterate_end;
878 pDlg->pBeginActiveWidgetList = pBuf;
879 pDlg->pEndActiveWidgetList = pEnd;
880 pDlg->pActiveWidgetList = pDlg->pEndActiveWidgetList;
882 if (n > num_w * num_h) {
883 if (!pDlg->pScroll) {
884 create_vertical_scrollbar(pDlg, num_w, num_h, FALSE, TRUE);
885 } else {
886 pDlg->pScroll->active = num_h;
887 pDlg->pScroll->step = num_w;
888 pDlg->pScroll->count = n;
889 show_scrollbar(pDlg->pScroll);
892 /* create up button */
893 pBuf = pDlg->pScroll->pUp_Left_Button;
894 pBuf->size.x = pInfo_Window->area.x + pInfo_Window->area.w - pBuf->size.w;
895 pBuf->size.y = pInfo_Window->area.y + sy +
896 (pInfo_Window->size.h - sy - num_h * 52) / 2;
897 pBuf->size.h = (num_h * 52) / 2;
899 /* create down button */
900 pBuf = pDlg->pScroll->pDown_Right_Button;
901 pBuf->size.x = pDlg->pScroll->pUp_Left_Button->size.x;
902 pBuf->size.y = pDlg->pScroll->pUp_Left_Button->size.y +
903 pDlg->pScroll->pUp_Left_Button->size.h;
904 pBuf->size.h = pDlg->pScroll->pUp_Left_Button->size.h;
905 } else {
906 if (pDlg->pScroll) {
907 hide_scrollbar(pDlg->pScroll);
909 num_h = (n + num_w - 1) / num_w;
912 setup_vertical_widgets_position(num_w,
913 pInfo_Window->area.x + BLOCKU_W + adj_size(2),
914 pInfo_Window->size.y + sy +
915 (pInfo_Window->size.h - sy - num_h * 52) / 2,
916 0, 0, pDlg->pBeginActiveWidgetList,
917 pDlg->pEndActiveWidgetList);
918 } else {
919 if (pInfo_Window->private_data.adv_dlg->pEndActiveWidgetList) {
920 del_group(pInfo_Window->private_data.adv_dlg->pBeginActiveWidgetList,
921 pInfo_Window->private_data.adv_dlg->pEndActiveWidgetList);
923 if (pInfo_Window->private_data.adv_dlg->pScroll) {
924 hide_scrollbar(pInfo_Window->private_data.adv_dlg->pScroll);
927 } else { /* pUnit */
929 if (pInfo_Window->private_data.adv_dlg->pEndActiveWidgetList) {
930 del_group(pInfo_Window->private_data.adv_dlg->pBeginActiveWidgetList,
931 pInfo_Window->private_data.adv_dlg->pEndActiveWidgetList);
933 if (pInfo_Window->private_data.adv_dlg->pScroll) {
934 hide_scrollbar(pInfo_Window->private_data.adv_dlg->pScroll);
937 if (!client_is_observer() && !client.conn.playing->phase_done
938 && (is_human(client.conn.playing)
939 || gui_options.ai_manual_turn_done)) {
940 char buf[256];
942 fc_snprintf(buf, sizeof(buf), "%s\n%s\n%s",
943 _("End of Turn"), _("Press"), _("Shift+Return"));
944 pstr = create_utf8_from_char(buf, adj_font(14));
945 pstr->style = SF_CENTER;
946 pstr->bgcol = (SDL_Color) {0, 0, 0, 0};
947 buf_surf = create_text_surf_from_utf8(pstr);
948 area.x = pInfo_Window->size.x + BLOCKU_W +
949 (pInfo_Window->size.w - BLOCKU_W - buf_surf->w) / 2;
950 area.y = pInfo_Window->size.y + (pInfo_Window->size.h - buf_surf->h) / 2;
951 alphablit(buf_surf, NULL, pInfo_Window->dst->surface, &area, 255);
952 FREESURFACE(buf_surf);
953 FREEUTF8STR(pstr);
957 /* draw buttons */
958 redraw_group(pInfo_Window->private_data.adv_dlg->pBeginWidgetList,
959 pInfo_Window->private_data.adv_dlg->pEndWidgetList->prev, 0);
961 widget_mark_dirty(pInfo_Window);
965 /**************************************************************************
966 Is the focus animation enabled?
967 **************************************************************************/
968 static bool is_focus_anim_enabled(void)
970 return (SDL_Client_Flags & CF_FOCUS_ANIMATION) == CF_FOCUS_ANIMATION;
973 /**************************************************************************
974 Set one of the unit icons in the information area based on punit.
975 NULL will be passed to clear the icon. idx == -1 will be passed to
976 indicate this is the active unit, or idx in [0..num_units_below - 1] for
977 secondary (inactive) units on the same tile.
978 **************************************************************************/
979 void set_unit_icon(int idx, struct unit *punit)
981 /* FIXME */
982 /* update_unit_info_label(punit);*/
985 /**************************************************************************
986 Most clients use an arrow (e.g., sprites.right_arrow) to indicate when
987 the units_below will not fit. This function is called to activate and
988 deactivate the arrow.
989 **************************************************************************/
990 void set_unit_icons_more_arrow(bool onoff)
992 /* Balast */
995 /****************************************************************************
996 Called when the set of units in focus (get_units_in_focus()) changes.
997 Standard updates like update_unit_info_label() are handled in the platform-
998 independent code, so some clients will not need to do anything here.
999 ****************************************************************************/
1000 void real_focus_units_changed(void)
1002 /* Nothing to do */
1005 /**************************************************************************
1006 Update the information label which gives info on the current unit and
1007 the square under the current unit, for specified unit. Note that in
1008 practice punit is always the focus unit.
1010 Clears label if punitlist is NULL or empty.
1012 Typically also updates the cursor for the map_canvas (this is related
1013 because the info label may includes "select destination" prompt etc).
1014 And it may call update_unit_pix_label() to update the icons for units
1015 on this square.
1016 **************************************************************************/
1017 void update_unit_info_label(struct unit_list *punitlist)
1019 if (C_S_RUNNING != client_state()) {
1020 return;
1023 /* draw unit info window */
1024 redraw_unit_info_label(punitlist);
1026 if (punitlist) {
1027 if (!is_focus_anim_enabled()) {
1028 enable_focus_animation();
1030 } else {
1031 disable_focus_animation();
1035 /**************************************************************************
1036 Refresh timeout label.
1037 **************************************************************************/
1038 void update_timeout_label(void)
1040 log_debug("MAPVIEW: update_timeout_label : PORT ME");
1043 /**************************************************************************
1044 Refresh turn done button.
1045 **************************************************************************/
1046 void update_turn_done_button(bool do_restore)
1048 log_debug("MAPVIEW: update_turn_done_button : PORT ME");
1051 /* ===================================================================== */
1052 /* ========================== City Descriptions ======================== */
1053 /* ===================================================================== */
1055 /**************************************************************************
1056 Update (refresh) all of the city descriptions on the mapview.
1057 **************************************************************************/
1058 void update_city_descriptions(void)
1060 /* redraw buffer */
1061 show_city_descriptions(0, 0, mapview.store_width, mapview.store_height);
1062 dirty_all();
1065 /* ===================================================================== */
1066 /* =============================== Mini Map ============================ */
1067 /* ===================================================================== */
1069 /**************************************************************************
1070 Toggle between overview modes.
1071 **************************************************************************/
1072 void toggle_overview_mode(void)
1074 /* FIXME: has no effect anymore */
1075 if (overview_mode == BORDERS) {
1076 overview_mode = NORMAL;
1077 } else {
1078 overview_mode = BORDERS;
1082 /****************************************************************************
1083 Return a canvas that is the overview window.
1084 ****************************************************************************/
1085 struct canvas *get_overview_window(void)
1087 return overview_canvas;
1090 /****************************************************************************
1091 Return the dimensions of the area (container widget; maximum size) for
1092 the overview.
1093 ****************************************************************************/
1094 void get_overview_area_dimensions(int *width, int *height)
1096 /* calculate the dimensions in a way to always get a resulting
1097 overview with a height bigger than or equal to DEFAULT_OVERVIEW_H.
1098 First, the default dimensions are fed to the same formula that
1099 is used in overview_common.c. If the resulting height is
1100 smaller than DEFAULT_OVERVIEW_H, increase OVERVIEW_TILE_SIZE
1101 by 1 until the height condition is met.
1103 int overview_tile_size_bak = OVERVIEW_TILE_SIZE;
1104 int xfact = MAP_IS_ISOMETRIC ? 2 : 1;
1105 int shift = (MAP_IS_ISOMETRIC && !current_topo_has_flag(TF_WRAPX)) ? -1 : 0;
1107 OVERVIEW_TILE_SIZE = MIN((DEFAULT_OVERVIEW_W - 1) / (wld.map.xsize * xfact),
1108 (DEFAULT_OVERVIEW_H - 1) / wld.map.ysize) + 1;
1110 do {
1111 *height = OVERVIEW_TILE_HEIGHT * wld.map.ysize;
1112 if (*height < DEFAULT_OVERVIEW_H) {
1113 OVERVIEW_TILE_SIZE++;
1115 } while (*height < DEFAULT_OVERVIEW_H);
1117 *width = OVERVIEW_TILE_WIDTH * wld.map.xsize + shift * OVERVIEW_TILE_SIZE;
1119 OVERVIEW_TILE_SIZE = overview_tile_size_bak;
1122 /**************************************************************************
1123 Refresh (update) the viewrect on the overview. This is the rectangle
1124 showing the area covered by the mapview.
1125 **************************************************************************/
1126 void refresh_overview(void)
1128 struct widget *pMMap;
1129 SDL_Rect overview_area;
1131 if (SDL_Client_Flags & CF_OVERVIEW_SHOWN) {
1132 pMMap = get_minimap_window_widget();
1134 overview_area = (SDL_Rect) {
1135 pMMap->area.x + overview_start_x, pMMap->area.x + overview_start_y,
1136 overview_canvas->surf->w, overview_canvas->surf->h
1139 alphablit(overview_canvas->surf, NULL, pMMap->dst->surface, &overview_area,
1140 255);
1142 widget_mark_dirty(pMMap);
1146 /**************************************************************************
1147 Update (refresh) the locations of the mapview scrollbars (if it uses
1148 them).
1149 **************************************************************************/
1150 void update_map_canvas_scrollbars(void)
1152 /* No scrollbars. */
1155 /**************************************************************************
1156 Draw a cross-hair overlay on a tile.
1157 **************************************************************************/
1158 void put_cross_overlay_tile(struct tile *ptile)
1160 log_debug("MAPVIEW: put_cross_overlay_tile : PORT ME");
1163 /**************************************************************************
1164 Area Selection
1165 **************************************************************************/
1166 void draw_selection_rectangle(int canvas_x, int canvas_y, int w, int h)
1168 /* PORTME */
1169 create_frame(Main.map,
1170 canvas_x, canvas_y, w, h,
1171 get_theme_color(COLOR_THEME_SELECTIONRECTANGLE));
1174 /**************************************************************************
1175 This function is called when the tileset is changed.
1176 **************************************************************************/
1177 void tileset_changed(void)
1179 /* PORTME */
1180 /* Here you should do any necessary redraws (for instance, the city
1181 * dialogs usually need to be resized). */
1182 popdown_all_game_dialogs();
1185 /* =====================================================================
1186 City MAP
1187 ===================================================================== */
1189 /**************************************************************************
1190 Create new city map surface.
1191 **************************************************************************/
1192 SDL_Surface *create_city_map(struct city *pcity)
1194 /* city map dimensions might have changed, so create a new canvas each time */
1196 if (city_map_canvas) {
1197 canvas_free(city_map_canvas);
1200 city_map_canvas = canvas_create(get_citydlg_canvas_width(),
1201 get_citydlg_canvas_height());
1203 city_dialog_redraw_map(pcity, city_map_canvas);
1205 return city_map_canvas->surf;
1208 /**************************************************************************
1209 Return surface containing terrain of the tile.
1210 **************************************************************************/
1211 SDL_Surface *get_terrain_surface(struct tile *ptile)
1213 /* tileset dimensions might have changed, so create a new canvas each time */
1215 if (terrain_canvas) {
1216 canvas_free(terrain_canvas);
1219 terrain_canvas = canvas_create(tileset_full_tile_width(tileset),
1220 tileset_full_tile_height(tileset));
1222 put_terrain(ptile, terrain_canvas, 1.0, 0, 0);
1224 return terrain_canvas->surf;
1227 /**************************************************************************
1228 Sets the position of the overview scroll window based on mapview position.
1229 **************************************************************************/
1230 void update_overview_scroll_window_pos(int x, int y)
1232 /* TODO: PORTME. */