webperimental: Mountain vision bonus.
[freeciv.git] / client / climisc.c
blob2fdf75aa08097dbdfbce93c72ca2af926a0a8c20
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 This module contains various general - mostly highlevel - functions
16 used throughout the client.
17 ***********************************************************************/
19 #ifdef HAVE_CONFIG_H
20 #include <fc_config.h>
21 #endif
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
28 /* utility */
29 #include "bitvector.h"
30 #include "fcintl.h"
31 #include "log.h"
32 #include "support.h"
34 /* common */
35 #include "city.h"
36 #include "diptreaty.h"
37 #include "featured_text.h"
38 #include "game.h"
39 #include "government.h"
40 #include "map.h"
41 #include "mapimg.h"
42 #include "packets.h"
43 #include "research.h"
44 #include "spaceship.h"
45 #include "unitlist.h"
47 /* client/include */
48 #include "chatline_g.h"
49 #include "citydlg_g.h"
50 #include "cityrep_g.h"
51 #include "dialogs_g.h"
52 #include "gui_main_g.h"
53 #include "mapview_g.h"
55 /* client */
56 #include "client_main.h"
57 #include "climap.h"
58 #include "climisc.h"
59 #include "control.h"
60 #include "mapctrl_common.h"
61 #include "mapview_common.h"
62 #include "messagewin_common.h"
63 #include "options.h"
64 #include "packhand.h"
65 #include "repodlgs_common.h"
66 #include "tilespec.h"
69 /**************************************************************************
70 Remove unit, client end version
71 **************************************************************************/
72 void client_remove_unit(struct unit *punit)
74 struct city *pcity;
75 struct tile *ptile = unit_tile(punit);
76 int hc = punit->homecity;
77 struct unit old_unit = *punit;
78 int old = get_num_units_in_focus();
79 bool update;
81 log_debug("removing unit %d, %s %s (%d %d) hcity %d",
82 punit->id, nation_rule_name(nation_of_unit(punit)),
83 unit_rule_name(punit), TILE_XY(unit_tile(punit)), hc);
85 update = (get_focus_unit_on_tile(unit_tile(punit)) != NULL);
87 /* Check transport status. */
88 unit_transport_unload(punit);
89 if (get_transporter_occupancy(punit) > 0) {
90 unit_list_iterate(unit_transport_cargo(punit), pcargo) {
91 /* The server should take care that the unit is on the right terrain. */
92 unit_transport_unload(pcargo);
93 } unit_list_iterate_end;
96 control_unit_killed(punit);
97 game_remove_unit(punit);
98 punit = NULL;
99 if (old > 0 && get_num_units_in_focus() == 0) {
100 unit_focus_advance();
101 } else if (update) {
102 update_unit_pix_label(get_units_in_focus());
103 update_unit_info_label(get_units_in_focus());
106 pcity = tile_city(ptile);
107 if (NULL != pcity) {
108 if (can_player_see_units_in_city(client_player(), pcity)) {
109 pcity->client.occupied = (0 < unit_list_size(pcity->tile->units));
110 refresh_city_dialog(pcity);
113 log_debug("map city %s, %s, (%d %d)",
114 city_name_get(pcity), nation_rule_name(nation_of_city(pcity)),
115 TILE_XY(city_tile(pcity)));
118 if (!client_has_player() || unit_owner(&old_unit) == client_player()) {
119 pcity = game_city_by_number(hc);
120 if (NULL != pcity) {
121 refresh_city_dialog(pcity);
122 log_debug("home city %s, %s, (%d %d)",
123 city_name_get(pcity), nation_rule_name(nation_of_city(pcity)),
124 TILE_XY(city_tile(pcity)));
128 refresh_unit_mapcanvas(&old_unit, ptile, TRUE, FALSE);
131 /**************************************************************************
132 Remove city, client end version.
133 **************************************************************************/
134 void client_remove_city(struct city *pcity)
136 bool effect_update;
137 struct tile *ptile = city_tile(pcity);
138 struct city old_city = *pcity;
140 log_debug("client_remove_city() %d, %s", pcity->id, city_name_get(pcity));
142 /* Explicitly remove all improvements, to properly remove any global effects
143 and to handle the preservation of "destroyed" effects. */
144 effect_update = FALSE;
146 city_built_iterate(pcity, pimprove) {
147 effect_update = TRUE;
148 city_remove_improvement(pcity, pimprove);
149 } city_built_iterate_end;
151 if (effect_update) {
152 /* nothing yet */
155 popdown_city_dialog(pcity);
156 game_remove_city(pcity);
157 city_report_dialog_update();
158 refresh_city_mapcanvas(&old_city, ptile, TRUE, FALSE);
161 /**************************************************************************
162 Change all cities building X to building Y, if possible. X and Y
163 could be improvements or units. X and Y are compound ids.
164 **************************************************************************/
165 void client_change_all(struct universal *from, struct universal *to)
167 if (!can_client_issue_orders()) {
168 return;
171 create_event(NULL, E_CITY_PRODUCTION_CHANGED, ftc_client,
172 _("Changing production of every %s into %s."),
173 VUT_UTYPE == from->kind
174 ? utype_name_translation(from->value.utype)
175 : improvement_name_translation(from->value.building),
176 VUT_UTYPE == to->kind
177 ? utype_name_translation(to->value.utype)
178 : improvement_name_translation(to->value.building));
180 connection_do_buffer(&client.conn);
181 city_list_iterate (client.conn.playing->cities, pcity) {
182 if (are_universals_equal(&pcity->production, from)
183 && can_city_build_now(pcity, to)) {
184 city_change_production(pcity, to);
186 } city_list_iterate_end;
188 connection_do_unbuffer(&client.conn);
191 /***************************************************************************
192 Return a string indicating one nation's embassy status with another
193 ***************************************************************************/
194 const char *get_embassy_status(const struct player *me,
195 const struct player *them)
197 if (!me || !them
198 || me == them
199 || !them->is_alive
200 || !me->is_alive) {
201 return "-";
203 if (player_has_embassy(me, them)) {
204 if (player_has_embassy(them, me)) {
205 return Q_("?embassy:Both");
206 } else {
207 return Q_("?embassy:Yes");
209 } else if (player_has_embassy(them, me)) {
210 return Q_("?embassy:With Us");
211 } else if (player_diplstate_get(me, them)->contact_turns_left > 0
212 || player_diplstate_get(them, me)->contact_turns_left > 0) {
213 return Q_("?embassy:Contact");
214 } else {
215 return Q_("?embassy:No Contact");
219 /***************************************************************************
220 Return a string indicating one nation's shaed vision status with another
221 ***************************************************************************/
222 const char *get_vision_status(const struct player *me,
223 const struct player *them)
225 if (me && them && gives_shared_vision(me, them)) {
226 if (gives_shared_vision(them, me)) {
227 return Q_("?vision:Both");
228 } else {
229 return Q_("?vision:To Them");
231 } else if (me && them && gives_shared_vision(them, me)) {
232 return Q_("?vision:To Us");
233 } else {
234 return "";
238 /**************************************************************************
239 Copy a string that describes the given clause into the return buffer.
240 **************************************************************************/
241 void client_diplomacy_clause_string(char *buf, int bufsiz,
242 struct Clause *pclause)
244 struct city *pcity;
246 switch(pclause->type) {
247 case CLAUSE_ADVANCE:
248 fc_snprintf(buf, bufsiz, _("The %s give %s"),
249 nation_plural_for_player(pclause->from),
250 advance_name_translation(advance_by_number(pclause->value)));
251 break;
252 case CLAUSE_CITY:
253 pcity = game_city_by_number(pclause->value);
254 if (pcity) {
255 fc_snprintf(buf, bufsiz, _("The %s give %s"),
256 nation_plural_for_player(pclause->from),
257 city_name_get(pcity));
258 } else {
259 fc_snprintf(buf, bufsiz,_("The %s give an unknown city"),
260 nation_plural_for_player(pclause->from));
262 break;
263 case CLAUSE_GOLD:
264 fc_snprintf(buf, bufsiz, PL_("The %s give %d gold",
265 "The %s give %d gold", pclause->value),
266 nation_plural_for_player(pclause->from),
267 pclause->value);
268 break;
269 case CLAUSE_MAP:
270 fc_snprintf(buf, bufsiz, _("The %s give their worldmap"),
271 nation_plural_for_player(pclause->from));
272 break;
273 case CLAUSE_SEAMAP:
274 fc_snprintf(buf, bufsiz, _("The %s give their seamap"),
275 nation_plural_for_player(pclause->from));
276 break;
277 case CLAUSE_CEASEFIRE:
278 fc_snprintf(buf, bufsiz, _("The parties agree on a cease-fire"));
279 break;
280 case CLAUSE_PEACE:
281 fc_snprintf(buf, bufsiz, _("The parties agree on a peace"));
282 break;
283 case CLAUSE_ALLIANCE:
284 fc_snprintf(buf, bufsiz, _("The parties create an alliance"));
285 break;
286 case CLAUSE_VISION:
287 fc_snprintf(buf, bufsiz, _("The %s give shared vision"),
288 nation_plural_for_player(pclause->from));
289 break;
290 case CLAUSE_EMBASSY:
291 fc_snprintf(buf, bufsiz, _("The %s give an embassy"),
292 nation_plural_for_player(pclause->from));
293 break;
294 default:
295 fc_assert(FALSE);
296 if (bufsiz > 0) {
297 *buf = '\0';
299 break;
303 /**************************************************************************
304 Return global catastrophe chance and rate of change, scaled to some
305 maximum (e.g. 100 gives percentages).
306 This mirrors the logic in update_environmental_upset().
307 **************************************************************************/
308 static void catastrophe_scaled(int *chance, int *rate, int max,
309 int current, int accum, int level)
311 /* 20 from factor in update_environmental_upset() */
312 int numer = 20 * max;
313 int denom = map_num_tiles();
315 if (chance) {
316 *chance = CLIP(0,
317 (int)((((long)accum * numer) + (denom - 1)) / denom),
318 max);
320 if (rate) {
321 *rate = DIVIDE(((long)(current - level) * numer) + (denom - 1), denom);
325 /**************************************************************************
326 Return global warming chance and rate of change, scaled to max.
327 **************************************************************************/
328 void global_warming_scaled(int *chance, int *rate, int max)
330 return catastrophe_scaled(chance, rate, max,
331 game.info.heating, game.info.globalwarming,
332 game.info.warminglevel);
335 /**************************************************************************
336 Return nuclear winter chance and rate of change, scaled to max.
337 **************************************************************************/
338 void nuclear_winter_scaled(int *chance, int *rate, int max)
340 return catastrophe_scaled(chance, rate, max,
341 game.info.cooling, game.info.nuclearwinter,
342 game.info.coolinglevel);
345 /**************************************************************************
346 Return the sprite for the research indicator.
347 **************************************************************************/
348 struct sprite *client_research_sprite(void)
350 if (NULL != client.conn.playing && can_client_change_view()) {
351 const struct research *presearch = research_get(client_player());
352 int idx = 0;
354 if (A_UNSET != presearch->researching) {
355 idx = (NUM_TILES_PROGRESS * presearch->bulbs_researched
356 / (presearch->client.researching_cost + 1));
359 /* This clipping can be necessary since we can end up with excess
360 * research */
361 idx = CLIP(0, idx, NUM_TILES_PROGRESS - 1);
362 return get_indicator_sprite(tileset, INDICATOR_BULB, idx);
363 } else {
364 return get_indicator_sprite(tileset, INDICATOR_BULB, 0);
368 /**************************************************************************
369 Return the sprite for the global-warming indicator.
370 **************************************************************************/
371 struct sprite *client_warming_sprite(void)
373 int idx;
375 if (can_client_change_view()) {
376 /* Highest sprite kicks in at about 25% risk */
377 global_warming_scaled(&idx, NULL, (NUM_TILES_PROGRESS-1)*4);
378 idx = CLIP(0, idx, NUM_TILES_PROGRESS-1);
379 } else {
380 idx = 0;
382 return get_indicator_sprite(tileset, INDICATOR_WARMING, idx);
385 /**************************************************************************
386 Return the sprite for the global-cooling indicator.
387 **************************************************************************/
388 struct sprite *client_cooling_sprite(void)
390 int idx;
392 if (can_client_change_view()) {
393 /* Highest sprite kicks in at about 25% risk */
394 nuclear_winter_scaled(&idx, NULL, (NUM_TILES_PROGRESS-1)*4);
395 idx = CLIP(0, idx, NUM_TILES_PROGRESS-1);
396 } else {
397 idx = 0;
399 return get_indicator_sprite(tileset, INDICATOR_COOLING, idx);
402 /**************************************************************************
403 Return the sprite for the government indicator.
404 **************************************************************************/
405 struct sprite *client_government_sprite(void)
407 if (NULL != client.conn.playing && can_client_change_view()
408 && government_count() > 0) {
409 struct government *gov = government_of_player(client.conn.playing);
411 return get_government_sprite(tileset, gov);
412 } else {
413 /* HACK: the UNHAPPY citizen is used for the government
414 * when we don't know any better. */
415 return get_citizen_sprite(tileset, CITIZEN_UNHAPPY, 0, NULL);
419 /**************************************************************************
420 Find something sensible to display. This is used to overwrite the
421 intro gfx.
422 **************************************************************************/
423 void center_on_something(void)
425 struct city *pcity;
426 struct unit *punit;
428 if (!can_client_change_view()) {
429 return;
432 can_slide = FALSE;
433 if (get_num_units_in_focus() > 0) {
434 center_tile_mapcanvas(unit_tile(head_of_units_in_focus()));
435 } else if (client_has_player()
436 && NULL != (pcity = player_capital(client_player()))) {
437 /* Else focus on the capital. */
438 center_tile_mapcanvas(pcity->tile);
439 } else if (NULL != client.conn.playing
440 && 0 < city_list_size(client.conn.playing->cities)) {
441 /* Just focus on any city. */
442 pcity = city_list_get(client.conn.playing->cities, 0);
443 fc_assert_ret(pcity != NULL);
444 center_tile_mapcanvas(pcity->tile);
445 } else if (NULL != client.conn.playing
446 && 0 < unit_list_size(client.conn.playing->units)) {
447 /* Just focus on any unit. */
448 punit = unit_list_get(client.conn.playing->units, 0);
449 fc_assert_ret(punit != NULL);
450 center_tile_mapcanvas(unit_tile(punit));
451 } else {
452 struct tile *ctile = native_pos_to_tile(wld.map.xsize / 2, wld.map.ysize / 2);
454 /* Just any known tile will do; search near the middle first. */
455 /* Iterate outward from the center tile. We have to give a radius that
456 * is guaranteed to be larger than the map will be. Although this is
457 * a misuse of map.xsize and map.ysize (which are native dimensions),
458 * it should give a sufficiently large radius. */
459 iterate_outward(ctile, wld.map.xsize + wld.map.ysize, ptile) {
460 if (client_tile_get_known(ptile) != TILE_UNKNOWN) {
461 ctile = ptile;
462 break;
464 } iterate_outward_end;
466 center_tile_mapcanvas(ctile);
468 can_slide = TRUE;
471 /****************************************************************************
472 Encode a CID for the target production.
473 ****************************************************************************/
474 cid cid_encode(struct universal target)
476 return VUT_UTYPE == target.kind
477 ? B_LAST + utype_number(target.value.utype)
478 : improvement_number(target.value.building);
481 /****************************************************************************
482 Encode a CID for the target unit type.
483 ****************************************************************************/
484 cid cid_encode_unit(struct unit_type *punittype)
486 struct universal target = {
487 .kind = VUT_UTYPE,
488 .value = {.utype = punittype}};
490 return cid_encode(target);
493 /****************************************************************************
494 Encode a CID for the target building.
495 ****************************************************************************/
496 cid cid_encode_building(struct impr_type *pimprove)
498 struct universal target = {
499 .kind = VUT_IMPROVEMENT,
500 .value = {.building = pimprove}
503 return cid_encode(target);
506 /****************************************************************************
507 Encode a CID for the target city's production.
508 ****************************************************************************/
509 cid cid_encode_from_city(const struct city *pcity)
511 return cid_encode(pcity->production);
514 /**************************************************************************
515 Decode the CID into a city_production structure.
516 **************************************************************************/
517 struct universal cid_decode(cid id)
519 struct universal target;
521 if (id >= B_LAST) {
522 target.kind = VUT_UTYPE;
523 target.value.utype = utype_by_number(id - B_LAST);
524 } else {
525 target.kind = VUT_IMPROVEMENT;
526 target.value.building = improvement_by_number(id);
529 return target;
532 /****************************************************************************
533 Return TRUE if the city supports at least one unit of the given
534 production type (returns FALSE if the production is a building).
535 ****************************************************************************/
536 bool city_unit_supported(const struct city *pcity,
537 const struct universal *target)
539 if (VUT_UTYPE == target->kind) {
540 struct unit_type *tvtype = target->value.utype;
542 unit_list_iterate(pcity->units_supported, punit) {
543 if (unit_type_get(punit) == tvtype) {
544 return TRUE;
546 } unit_list_iterate_end;
548 return FALSE;
551 /****************************************************************************
552 Return TRUE if the city has present at least one unit of the given
553 production type (returns FALSE if the production is a building).
554 ****************************************************************************/
555 bool city_unit_present(const struct city *pcity,
556 const struct universal *target)
558 if (VUT_UTYPE == target->kind) {
559 struct unit_type *tvtype = target->value.utype;
561 unit_list_iterate(pcity->tile->units, punit) {
562 if (unit_type_get(punit) == tvtype) {
563 return TRUE;
566 unit_list_iterate_end;
568 return FALSE;
571 /****************************************************************************
572 A TestCityFunc to tell whether the item is a building and is present.
573 ****************************************************************************/
574 bool city_building_present(const struct city *pcity,
575 const struct universal *target)
577 return VUT_IMPROVEMENT == target->kind
578 && city_has_building(pcity, target->value.building);
581 /**************************************************************************
582 Return the numerical "section" of an item. This is used for sorting.
583 **************************************************************************/
584 static int target_get_section(struct universal target)
586 if (VUT_UTYPE == target.kind) {
587 if (utype_has_flag(target.value.utype, UTYF_CIVILIAN)) {
588 return 2;
589 } else {
590 return 3;
592 } else {
593 if (improvement_has_flag(target.value.building, IF_GOLD)) {
594 return 1;
595 } else if (is_small_wonder(target.value.building)) {
596 return 4;
597 } else if (is_great_wonder(target.value.building)) {
598 return 5;
599 } else {
600 return 0;
605 /**************************************************************************
606 Helper for name_and_sort_items.
607 **************************************************************************/
608 static int fc_cmp(const void *p1, const void *p2)
610 const struct item *i1 = p1, *i2 = p2;
611 int s1 = target_get_section(i1->item);
612 int s2 = target_get_section(i2->item);
614 if (s1 == s2) {
615 return fc_strcasecmp(i1->descr, i2->descr);
617 return s1 - s2;
620 /**************************************************************************
621 Takes an array of compound ids (cids). It will fill out an array of
622 struct items and also sort it.
624 section 0: normal buildings
625 section 1: Capitalization
626 section 2: UTYF_CIVILIAN units
627 section 3: other units
628 section 4: small wonders
629 section 5: great wonders
630 **************************************************************************/
631 void name_and_sort_items(struct universal *targets, int num_targets,
632 struct item *items,
633 bool show_cost, struct city *pcity)
635 int i;
637 for (i = 0; i < num_targets; i++) {
638 struct universal target = targets[i];
639 int cost;
640 struct item *pitem = &items[i];
641 const char *name;
643 pitem->item = target;
645 if (VUT_UTYPE == target.kind) {
646 name = utype_values_translation(target.value.utype);
647 cost = utype_build_shield_cost(target.value.utype);
648 } else {
649 name = city_improvement_name_translation(pcity, target.value.building);
650 if (improvement_has_flag(target.value.building, IF_GOLD)) {
651 cost = -1;
652 } else {
653 cost = impr_build_shield_cost(target.value.building);
657 if (show_cost) {
658 if (cost < 0) {
659 fc_snprintf(pitem->descr, sizeof(pitem->descr), "%s (XX)", name);
660 } else {
661 fc_snprintf(pitem->descr, sizeof(pitem->descr),
662 "%s (%d)", name, cost);
664 } else {
665 (void) fc_strlcpy(pitem->descr, name, sizeof(pitem->descr));
669 qsort(items, num_targets, sizeof(struct item), fc_cmp);
672 /**************************************************************************
673 Return possible production targets for the current player's cities.
675 FIXME: this should probably take a pplayer argument.
676 **************************************************************************/
677 int collect_production_targets(struct universal *targets,
678 struct city **selected_cities,
679 int num_selected_cities, bool append_units,
680 bool append_wonders, bool change_prod,
681 TestCityFunc test_func)
683 cid first = append_units ? B_LAST : 0;
684 cid last = (append_units
685 ? utype_count() + B_LAST
686 : improvement_count());
687 cid id;
688 int items_used = 0;
690 for (id = first; id < last; id++) {
691 bool append = FALSE;
692 struct universal target = cid_decode(id);
694 if (!append_units && (append_wonders != is_wonder(target.value.building))) {
695 continue;
698 if (!change_prod) {
699 if (client_has_player()) {
700 city_list_iterate(client_player()->cities, pcity) {
701 append |= test_func(pcity, &target);
702 } city_list_iterate_end;
703 } else {
704 cities_iterate(pcity) {
705 append |= test_func(pcity, &target);
706 } cities_iterate_end;
708 } else {
709 int i;
711 for (i = 0; i < num_selected_cities; i++) {
712 append |= test_func(selected_cities[i], &target);
716 if (!append)
717 continue;
719 targets[items_used] = target;
720 items_used++;
722 return items_used;
725 /**************************************************************************
726 Collect the cids of all targets (improvements and units) which are
727 currently built in a city.
729 FIXME: this should probably take a pplayer argument.
730 **************************************************************************/
731 int collect_currently_building_targets(struct universal *targets)
733 bool mapping[MAX_NUM_PRODUCTION_TARGETS];
734 int cids_used = 0;
735 cid id;
737 if (NULL == client.conn.playing) {
738 return 0;
741 memset(mapping, 0, sizeof(mapping));
742 city_list_iterate(client.conn.playing->cities, pcity) {
743 mapping[cid_encode_from_city(pcity)] = TRUE;
745 city_list_iterate_end;
747 for (id = 0; id < ARRAY_SIZE(mapping); id++) {
748 if (mapping[id]) {
749 targets[cids_used] = cid_decode(id);
750 cids_used++;
754 return cids_used;
757 /**************************************************************************
758 Collect the cids of all targets (improvements and units) which can
759 be build in a city.
761 FIXME: this should probably take a pplayer argument.
762 **************************************************************************/
763 int collect_buildable_targets(struct universal *targets)
765 int cids_used = 0;
767 if (NULL == client.conn.playing) {
768 return 0;
771 improvement_iterate(pimprove) {
772 if (can_player_build_improvement_now(client.conn.playing, pimprove)) {
773 targets[cids_used].kind = VUT_IMPROVEMENT;
774 targets[cids_used].value.building = pimprove;
775 cids_used++;
777 } improvement_iterate_end;
779 unit_type_iterate(punittype) {
780 if (can_player_build_unit_now(client.conn.playing, punittype)) {
781 targets[cids_used].kind = VUT_UTYPE;
782 targets[cids_used].value.utype = punittype;
783 cids_used++;
785 } unit_type_iterate_end
787 return cids_used;
790 /****************************************************************************
791 Collect the cids of all targets which can be build by this city or
792 in general.
793 ****************************************************************************/
794 int collect_eventually_buildable_targets(struct universal *targets,
795 struct city *pcity,
796 bool advanced_tech)
798 struct player *pplayer = client_player();
799 int cids_used = 0;
801 improvement_iterate(pimprove) {
802 bool can_build;
803 bool can_eventually_build;
805 if (NULL != pcity) {
806 /* Can the city build? */
807 can_build = can_city_build_improvement_now(pcity, pimprove);
808 can_eventually_build = can_city_build_improvement_later(pcity,
809 pimprove);
810 } else if (NULL != pplayer) {
811 /* Can our player build? */
812 can_build = can_player_build_improvement_now(pplayer, pimprove);
813 can_eventually_build = can_player_build_improvement_later(pplayer,
814 pimprove);
815 } else {
816 /* Global observer case: can any player build? */
817 can_build = FALSE;
818 players_iterate(aplayer) {
819 if (can_player_build_improvement_now(aplayer, pimprove)) {
820 can_build = TRUE;
821 break;
823 } players_iterate_end;
825 can_eventually_build = FALSE;
826 players_iterate(aplayer) {
827 if (can_player_build_improvement_later(aplayer, pimprove)) {
828 can_eventually_build = TRUE;
829 break;
831 } players_iterate_end;
834 if ((advanced_tech && can_eventually_build)
835 || (!advanced_tech && can_build)) {
836 targets[cids_used].kind = VUT_IMPROVEMENT;
837 targets[cids_used].value.building = pimprove;
838 cids_used++;
840 } improvement_iterate_end;
842 unit_type_iterate(punittype) {
843 bool can_build;
844 bool can_eventually_build;
846 if (NULL != pcity) {
847 /* Can the city build? */
848 can_build = can_city_build_unit_now(pcity, punittype);
849 can_eventually_build = can_city_build_unit_later(pcity, punittype);
850 } else if (NULL != pplayer) {
851 /* Can our player build? */
852 can_build = can_player_build_unit_now(pplayer, punittype);
853 can_eventually_build = can_player_build_unit_later(pplayer, punittype);
854 } else {
855 /* Global observer case: can any player build? */
856 can_build = FALSE;
857 players_iterate(aplayer) {
858 if (can_player_build_unit_now(aplayer, punittype)) {
859 can_build = TRUE;
860 break;
862 } players_iterate_end;
864 can_eventually_build = FALSE;
865 players_iterate(aplayer) {
866 if (can_player_build_unit_later(aplayer, punittype)) {
867 can_eventually_build = TRUE;
868 break;
870 } players_iterate_end;
873 if ((advanced_tech && can_eventually_build)
874 || (!advanced_tech && can_build)) {
875 targets[cids_used].kind = VUT_UTYPE;
876 targets[cids_used].value.utype = punittype;
877 cids_used++;
879 } unit_type_iterate_end;
881 return cids_used;
884 /**************************************************************************
885 Collect the cids of all improvements which are built in the given city.
886 **************************************************************************/
887 int collect_already_built_targets(struct universal *targets,
888 struct city *pcity)
890 int cids_used = 0;
892 fc_assert_ret_val(pcity != NULL, 0);
894 city_built_iterate(pcity, pimprove) {
895 targets[cids_used].kind = VUT_IMPROVEMENT;
896 targets[cids_used].value.building = pimprove;
897 cids_used++;
898 } city_built_iterate_end;
900 return cids_used;
903 /**************************************************************************
904 Returns number of units known to be supported by city. This might not real
905 number of units in case of enemy city.
906 **************************************************************************/
907 int num_supported_units_in_city(struct city *pcity)
909 struct unit_list *plist;
911 if (can_player_see_city_internals(client.conn.playing, pcity)) {
912 /* Other players don't see inside the city (but observers do). */
913 plist = pcity->client.info_units_supported;
914 } else {
915 plist = pcity->units_supported;
918 return unit_list_size(plist);
921 /**************************************************************************
922 Returns number of units known to be in city. This might not real
923 number of units in case of enemy city.
924 **************************************************************************/
925 int num_present_units_in_city(struct city *pcity)
927 struct unit_list *plist;
929 if (can_player_see_units_in_city(client.conn.playing, pcity)) {
930 /* Other players don't see inside the city (but observers do). */
931 plist = pcity->client.info_units_present;
932 } else {
933 plist = pcity->tile->units;
936 return unit_list_size(plist);
939 /**************************************************************************
940 Handles a chat or event message.
941 **************************************************************************/
942 void handle_event(const char *featured_text, struct tile *ptile,
943 enum event_type event, int turn, int phase, int conn_id)
945 char plain_text[MAX_LEN_MSG];
946 struct text_tag_list *tags;
947 int where = MW_OUTPUT; /* where to display the message */
948 bool fallback_needed = FALSE; /* we want fallback if actual 'where' is not
949 * usable */
950 bool shown = FALSE; /* Message displayed somewhere at least */
952 if (!event_type_is_valid(event)) {
953 /* Server may have added a new event; leave as MW_OUTPUT */
954 log_verbose("Unknown event type %d!", event);
955 } else if (event >= 0) {
956 where = messages_where[event];
959 /* Get the original text. */
960 featured_text_to_plain_text(featured_text, plain_text,
961 sizeof(plain_text), &tags, conn_id != -1);
963 /* Display link marks when an user is pointed us something. */
964 if (conn_id != -1) {
965 text_tag_list_iterate(tags, ptag) {
966 if (text_tag_type(ptag) == TTT_LINK) {
967 link_mark_add_new(text_tag_link_type(ptag), text_tag_link_id(ptag));
969 } text_tag_list_iterate_end;
972 /* Maybe highlight our player and user names if someone is talking
973 * about us. */
974 if (-1 != conn_id
975 && client.conn.id != conn_id
976 && ft_color_requested(gui_options.highlight_our_names)) {
977 const char *username = client.conn.username;
978 size_t userlen = strlen(username);
979 const char *playername = ((client_player() && !client_is_observer())
980 ? player_name(client_player()) : NULL);
981 size_t playerlen = playername ? strlen(playername) : 0;
982 const char *p;
984 if (playername && playername[0] == '\0') {
985 playername = NULL;
988 if (username && username[0] == '\0') {
989 username = NULL;
992 for (p = plain_text; *p != '\0'; p++) {
993 if (NULL != username
994 && 0 == fc_strncasecmp(p, username, userlen)) {
995 struct text_tag *ptag = text_tag_new(TTT_COLOR, p - plain_text,
996 p - plain_text + userlen,
997 gui_options.highlight_our_names);
999 fc_assert(ptag != NULL);
1001 if (ptag != NULL) {
1002 /* Appends to be sure it will be applied at last. */
1003 text_tag_list_append(tags, ptag);
1005 } else if (NULL != playername
1006 && 0 == fc_strncasecmp(p, playername, playerlen)) {
1007 struct text_tag *ptag = text_tag_new(TTT_COLOR, p - plain_text,
1008 p - plain_text + playerlen,
1009 gui_options.highlight_our_names);
1011 fc_assert(ptag != NULL);
1013 if (ptag != NULL) {
1014 /* Appends to be sure it will be applied at last. */
1015 text_tag_list_append(tags, ptag);
1021 /* Popup */
1022 if (BOOL_VAL(where & MW_POPUP)) {
1023 /* Popups are usually not shown if player is under AI control.
1024 * Server operator messages are shown always. */
1025 if (NULL == client.conn.playing
1026 || is_human(client.conn.playing)
1027 || event == E_MESSAGE_WALL) {
1028 popup_notify_goto_dialog(_("Popup Request"), plain_text, tags, ptile);
1029 shown = TRUE;
1030 } else {
1031 /* Force to chatline so it will be visible somewhere at least.
1032 * Messages window may still handle this so chatline is not needed
1033 * after all. */
1034 fallback_needed = TRUE;
1038 /* Message window */
1039 if (BOOL_VAL(where & MW_MESSAGES)) {
1040 /* When the game isn't running, the messages dialog isn't present. */
1041 if (C_S_RUNNING <= client_state()) {
1042 meswin_add(plain_text, tags, ptile, event, turn, phase);
1043 shown = TRUE;
1044 } else {
1045 /* Force to chatline instead. */
1046 fallback_needed = TRUE;
1050 /* Chatline */
1051 if (BOOL_VAL(where & MW_OUTPUT) || (fallback_needed && !shown)) {
1052 output_window_event(plain_text, tags, conn_id);
1055 if (turn == game.info.turn) {
1056 play_sound_for_event(event);
1059 /* Free tags */
1060 text_tag_list_destroy(tags);
1063 /**************************************************************************
1064 Creates a struct packet_generic_message packet and injects it via
1065 handle_chat_msg.
1066 **************************************************************************/
1067 void create_event(struct tile *ptile, enum event_type event,
1068 const struct ft_color color, const char *format, ...)
1070 va_list ap;
1071 char message[MAX_LEN_MSG];
1073 va_start(ap, format);
1074 fc_vsnprintf(message, sizeof(message), format, ap);
1075 va_end(ap);
1077 if (ft_color_requested(color)) {
1078 char colored_text[MAX_LEN_MSG];
1080 featured_text_apply_tag(message, colored_text, sizeof(colored_text),
1081 TTT_COLOR, 0, FT_OFFSET_UNSET, color);
1082 handle_event(colored_text, ptile, event, game.info.turn, game.info.phase, -1);
1083 } else {
1084 handle_event(message, ptile, event, game.info.turn, game.info.phase, -1);
1088 /**************************************************************************
1089 Find city nearest to given unit and optionally return squared city
1090 distance Parameter sq_dist may be NULL. Returns NULL only if no city is
1091 known. Favors punit owner's cities over other cities if equally distant.
1092 **************************************************************************/
1093 struct city *get_nearest_city(const struct unit *punit, int *sq_dist)
1095 struct city *pcity_near;
1096 int pcity_near_dist;
1098 if ((pcity_near = tile_city(unit_tile(punit)))) {
1099 pcity_near_dist = 0;
1100 } else {
1101 pcity_near = NULL;
1102 pcity_near_dist = -1;
1103 players_iterate(pplayer) {
1104 city_list_iterate(pplayer->cities, pcity_current) {
1105 int dist = sq_map_distance(pcity_current->tile, unit_tile(punit));
1106 if (pcity_near_dist == -1 || dist < pcity_near_dist
1107 || (dist == pcity_near_dist
1108 && unit_owner(punit) == city_owner(pcity_current))) {
1109 pcity_near = pcity_current;
1110 pcity_near_dist = dist;
1112 } city_list_iterate_end;
1113 } players_iterate_end;
1116 if (sq_dist) {
1117 *sq_dist = pcity_near_dist;
1120 return pcity_near;
1123 /**************************************************************************
1124 Called when the "Buy" button is pressed in the city report for every
1125 selected city. Checks for coinage and sufficient funds or request the
1126 purchase if everything is ok.
1127 **************************************************************************/
1128 void cityrep_buy(struct city *pcity)
1130 int value;
1132 if (city_production_has_flag(pcity, IF_GOLD)) {
1133 create_event(pcity->tile, E_BAD_COMMAND, ftc_client,
1134 _("You can't buy %s in %s!"),
1135 improvement_name_translation(pcity->production.value.building),
1136 city_link(pcity));
1137 return;
1139 value = city_production_buy_gold_cost(pcity);
1141 if (city_owner(pcity)->economic.gold >= value) {
1142 city_buy_production(pcity);
1143 } else {
1144 /* Split into two to allow localization of two pluralisations. */
1145 char buf[MAX_LEN_MSG];
1146 /* TRANS: %s is a production type; this whole string is a sentence
1147 * fragment that is only ever included in one other string
1148 * (search comments for this string to find it) */
1149 fc_snprintf(buf, ARRAY_SIZE(buf), PL_("%s costs %d gold",
1150 "%s costs %d gold", value),
1151 city_production_name_translation(pcity),
1152 value);
1153 create_event(NULL, E_BAD_COMMAND, ftc_client,
1154 /* TRANS: %s is a pre-pluralised sentence fragment:
1155 * "%s costs %d gold" */
1156 PL_("%s and you only have %d gold.",
1157 "%s and you only have %d gold.",
1158 city_owner(pcity)->economic.gold),
1159 buf, city_owner(pcity)->economic.gold);
1163 /**************************************************************************
1164 Switch between tax/sci/lux at given slot.
1165 **************************************************************************/
1166 void common_taxrates_callback(int i)
1168 int lux_end, sci_end, tax, lux, sci;
1169 int delta = 10;
1171 if (!can_client_issue_orders()) {
1172 return;
1175 lux_end = client.conn.playing->economic.luxury;
1176 sci_end = lux_end + client.conn.playing->economic.science;
1178 lux = client.conn.playing->economic.luxury;
1179 sci = client.conn.playing->economic.science;
1180 tax = client.conn.playing->economic.tax;
1182 i *= 10;
1183 if (i < lux_end) {
1184 lux -= delta;
1185 sci += delta;
1186 } else if (i < sci_end) {
1187 sci -= delta;
1188 tax += delta;
1189 } else {
1190 tax -= delta;
1191 lux += delta;
1193 dsend_packet_player_rates(&client.conn, tax, lux, sci);
1196 /****************************************************************************
1197 Returns TRUE if any of the units can do the connect activity.
1198 ****************************************************************************/
1199 bool can_units_do_connect(struct unit_list *punits,
1200 enum unit_activity activity,
1201 struct extra_type *tgt)
1203 unit_list_iterate(punits, punit) {
1204 if (can_unit_do_connect(punit, activity, tgt)) {
1205 return TRUE;
1207 } unit_list_iterate_end;
1209 return FALSE;
1212 /**************************************************************************
1213 Initialize the action probability cache. Shouldn't be kept around
1214 permanently. Its data is quickly outdated.
1215 **************************************************************************/
1216 void client_unit_init_act_prob_cache(struct unit *punit)
1218 /* A double init would cause a leak. */
1219 fc_assert_ret(punit->client.act_prob_cache == NULL);
1221 punit->client.act_prob_cache = (struct act_prob*)fc_malloc(
1222 NUM_ACTIONS * sizeof(*punit->client.act_prob_cache));
1225 /****************************************************************************
1226 Determines which color type should be used for unit background.
1227 This is only guesswork based on unit properties. One should not
1228 take UNIT_BG_FLYING seriously meaning that unit can fly - custom
1229 ruleset might have units with similar properties but explains these
1230 properties by some other means than by flying.
1231 ****************************************************************************/
1232 enum unit_bg_color_type unit_color_type(const struct unit_type *punittype)
1234 struct unit_class *pclass = utype_class(punittype);
1236 if (pclass->hp_loss_pct > 0) {
1237 return UNIT_BG_HP_LOSS;
1240 if (pclass->move_type == UMT_LAND) {
1241 return UNIT_BG_LAND;
1243 if (pclass->move_type == UMT_SEA) {
1244 return UNIT_BG_SEA;
1247 fc_assert(pclass->move_type == UMT_BOTH);
1249 if (uclass_has_flag(pclass, UCF_TERRAIN_SPEED)) {
1250 /* Unit moves on both sea and land by speed determined by terrain */
1251 return UNIT_BG_AMPHIBIOUS;
1254 return UNIT_BG_FLYING;
1257 /****************************************************************************
1258 Comparison function used by qsort in buy_production_in_selected_cities().
1259 ****************************************************************************/
1260 static int city_buy_cost_compare(const void *a, const void *b)
1262 const struct city *ca, *cb;
1263 ca = *((const struct city **) a);
1264 cb = *((const struct city **) b);
1265 return (city_production_buy_gold_cost(ca)
1266 - city_production_buy_gold_cost(cb));
1269 /****************************************************************************
1270 For each selected city, buy the current production. The selected cities
1271 are sorted so production is bought in the cities with lowest cost first.
1272 ****************************************************************************/
1273 void buy_production_in_selected_cities(void)
1275 const struct player *pplayer = client_player();
1276 if (!pplayer || !pplayer->cities
1277 || city_list_size(pplayer->cities) < 1) {
1278 return;
1281 int gold = pplayer->economic.gold;
1282 if (gold < 1) {
1283 return;
1286 const int n = city_list_size(pplayer->cities);
1287 struct city *cities[n];
1288 int i, count = 0;
1290 city_list_iterate(pplayer->cities, pcity) {
1291 if (!is_city_hilited(pcity) || !city_can_buy(pcity)) {
1292 continue;
1294 cities[count++] = pcity;
1295 } city_list_iterate_end;
1297 if (count < 1) {
1298 return;
1301 qsort(cities, count, sizeof(*cities), city_buy_cost_compare);
1303 struct connection *pconn = &client.conn;
1304 connection_do_buffer(pconn);
1306 for (i = 0; i < count && gold > 0; i++) {
1307 gold -= city_production_buy_gold_cost(cities[i]);
1308 city_buy_production(cities[i]);
1311 connection_do_unbuffer(pconn);
1314 /***************************************************************
1315 Set focus status of all player units to FOCUS_AVAIL.
1316 ***************************************************************/
1317 void unit_focus_set_status(struct player *pplayer)
1319 unit_list_iterate(pplayer->units, punit) {
1320 punit->client.focus_status = FOCUS_AVAIL;
1321 } unit_list_iterate_end;
1324 /***************************************************************
1325 Initialize a player on the client side.
1326 ***************************************************************/
1327 void client_player_init(struct player *pplayer)
1329 vision_layer_iterate(v) {
1330 pplayer->client.tile_vision[v].vec = NULL;
1331 pplayer->client.tile_vision[v].bits = 0;
1332 } vision_layer_iterate_end;
1335 /***************************************************************
1336 Reset the private maps of all players.
1337 ***************************************************************/
1338 void client_player_maps_reset(void)
1340 players_iterate(pplayer) {
1341 int new_size;
1343 if (pplayer == client.conn.playing) {
1344 new_size = MAP_INDEX_SIZE;
1345 } else {
1346 /* We don't need (or have) information about players other
1347 * than user of the client. Allocate just one bit as that's
1348 * the minimum bitvector size (cannot allocate 0 bits)*/
1349 new_size = 1;
1352 vision_layer_iterate(v) {
1353 dbv_resize(&pplayer->client.tile_vision[v], new_size);
1354 } vision_layer_iterate_end;
1356 dbv_resize(&pplayer->tile_known, new_size);
1357 } players_iterate_end;
1360 /***************************************************************
1361 Create a map image definition on the client.
1362 ***************************************************************/
1363 bool mapimg_client_define(void)
1365 char str[MAX_LEN_MAPDEF];
1366 char mi_map[MAPIMG_LAYER_COUNT + 1];
1367 enum mapimg_layer layer;
1368 int map_pos = 0;
1370 /* Only one definition allowed. */
1371 while (mapimg_count() != 0) {
1372 mapimg_delete(0);
1375 /* Map image definition: zoom, turns */
1376 fc_snprintf(str, sizeof(str), "zoom=%d:turns=0:format=%s",
1377 gui_options.mapimg_zoom, gui_options.mapimg_format);
1379 /* Map image definition: show */
1380 if (client_is_global_observer()) {
1381 cat_snprintf(str, sizeof(str), ":show=all");
1382 /* use all available knowledge */
1383 gui_options.mapimg_layer[MAPIMG_LAYER_KNOWLEDGE] = FALSE;
1384 } else {
1385 cat_snprintf(str, sizeof(str), ":show=plrid:plrid=%d",
1386 player_index(client.conn.playing));
1387 /* use only player knowledge */
1388 gui_options.mapimg_layer[MAPIMG_LAYER_KNOWLEDGE] = TRUE;
1391 /* Map image definition: map */
1392 for (layer = mapimg_layer_begin(); layer != mapimg_layer_end();
1393 layer = mapimg_layer_next(layer)) {
1394 if (gui_options.mapimg_layer[layer]) {
1395 cat_snprintf(mi_map, sizeof(mi_map), "%s",
1396 mapimg_layer_name(layer));
1397 mi_map[map_pos++] = mapimg_layer_name(layer)[0];
1400 mi_map[map_pos] = '\0';
1402 if (map_pos == 0) {
1403 /* no value set - use dummy setting */
1404 sz_strlcpy(mi_map, "-");
1406 cat_snprintf(str, sizeof(str), ":map=%s", mi_map);
1408 log_debug("client map image definition: %s", str);
1410 if (!mapimg_define(str, FALSE) || !mapimg_isvalid(0)) {
1411 /* An error in the definition string or an error validation the string.
1412 * The error message is available via mapimg_error(). */
1413 return FALSE;
1416 return TRUE;
1419 /****************************************************************************
1420 Save map image.
1421 ****************************************************************************/
1422 bool mapimg_client_createmap(const char *filename)
1424 struct mapdef *pmapdef;
1425 char mapimgfile[512];
1427 if (NULL == filename || '\0' == filename[0]) {
1428 sz_strlcpy(mapimgfile, gui_options.mapimg_filename);
1429 } else {
1430 sz_strlcpy(mapimgfile, filename);
1433 if (!mapimg_client_define()) {
1434 return FALSE;
1437 pmapdef = mapimg_isvalid(0);
1438 if (!pmapdef) {
1439 return FALSE;
1442 return mapimg_create(pmapdef, TRUE, mapimgfile, NULL);
1445 /****************************************************************************
1446 Returns the nation set in use.
1447 ****************************************************************************/
1448 struct nation_set *client_current_nation_set(void)
1450 struct option *poption = optset_option_by_name(server_optset, "nationset");
1451 const char *setting_str;
1453 if (poption == NULL
1454 || option_type(poption) != OT_STRING
1455 || (setting_str = option_str_get(poption)) == NULL) {
1456 setting_str = "";
1458 return nation_set_by_setting_value(setting_str);
1461 /****************************************************************************
1462 Returns Whether 'pnation' is in the current nation set.
1463 ****************************************************************************/
1464 bool client_nation_is_in_current_set(const struct nation_type *pnation)
1466 return nation_is_in_set(pnation, client_current_nation_set());