1 /**********************************************************************
2 Freeciv - Copyright (C) 2002 - The Freeciv Project
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)
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 ***********************************************************************/
15 #include <fc_config.h>
20 #include <math.h> /* ceil */
24 #include "bitvector.h"
32 #include "fc_types.h" /* LINE_BREAK */
34 #include "government.h"
37 #include "traderoutes.h"
41 #include "client_main.h"
50 static int get_bulbs_per_turn(int *pours
, bool *pteam
, int *ptheirs
);
52 /****************************************************************************
53 Return a (static) string with a tile's food/prod/trade
54 ****************************************************************************/
55 const char *get_tile_output_text(const struct tile
*ptile
)
57 static struct astring str
= ASTRING_INIT
;
59 char output_text
[O_LAST
][16];
61 for (i
= 0; i
< O_LAST
; i
++) {
62 int before_penalty
= 0;
63 int x
= city_tile_output(NULL
, ptile
, FALSE
, i
);
65 if (NULL
!= client
.conn
.playing
) {
66 before_penalty
= get_player_output_bonus(client
.conn
.playing
,
68 EFT_OUTPUT_PENALTY_TILE
);
71 if (before_penalty
> 0 && x
> before_penalty
) {
72 fc_snprintf(output_text
[i
], sizeof(output_text
[i
]), "%d(-1)", x
);
74 fc_snprintf(output_text
[i
], sizeof(output_text
[i
]), "%d", x
);
78 astr_set(&str
, "%s/%s/%s", output_text
[O_FOOD
],
79 output_text
[O_SHIELD
], output_text
[O_TRADE
]);
81 return astr_str(&str
);
84 /****************************************************************************
85 For AIs, fill the buffer with their player name prefixed with "AI". For
86 humans, just fill it with their username.
87 ****************************************************************************/
88 static inline void get_full_username(char *buf
, int buflen
,
89 const struct player
*pplayer
)
91 if (!buf
|| buflen
< 1) {
100 if (pplayer
->ai_controlled
) {
101 /* TRANS: "AI <player name>" */
102 fc_snprintf(buf
, buflen
, _("AI %s"), pplayer
->name
);
104 fc_strlcpy(buf
, pplayer
->username
, buflen
);
108 /****************************************************************************
109 Fill the buffer with the player's nation name (in adjective form) and
110 optionally add the player's team name.
111 ****************************************************************************/
112 static inline void get_full_nation(char *buf
, int buflen
,
113 const struct player
*pplayer
)
115 if (!buf
|| buflen
< 1) {
125 /* TRANS: "<nation adjective>, team <team name>" */
126 fc_snprintf(buf
, buflen
, _("%s, team %s"),
127 nation_adjective_for_player(pplayer
),
128 team_name_translation(pplayer
->team
));
130 fc_strlcpy(buf
, nation_adjective_for_player(pplayer
), buflen
);
134 /****************************************************************************
135 Text to popup on a middle-click in the mapview.
136 ****************************************************************************/
137 const char *popup_info_text(struct tile
*ptile
)
139 const char *activity_text
;
140 struct city
*pcity
= tile_city(ptile
);
141 struct unit
*punit
= find_visible_unit(ptile
);
142 const char *diplo_nation_plural_adjectives
[DS_LAST
] =
143 {Q_("?nation:Neutral"), Q_("?nation:Hostile"),
144 Q_("?nation:Neutral"),
145 Q_("?nation:Peaceful"), Q_("?nation:Friendly"),
146 Q_("?nation:Mysterious"), Q_("?nation:Friendly(team)")};
147 const char *diplo_city_adjectives
[DS_LAST
] =
148 {Q_("?city:Neutral"), Q_("?city:Hostile"),
149 Q_("?nation:Neutral"),
150 Q_("?city:Peaceful"), Q_("?city:Friendly"), Q_("?city:Mysterious"),
151 Q_("?city:Friendly(team)")};
152 static struct astring str
= ASTRING_INIT
;
153 char username
[MAX_LEN_NAME
+ 32];
154 char nation
[2 * MAX_LEN_NAME
+ 32];
155 int tile_x
, tile_y
, nat_x
, nat_y
;
158 index_to_map_pos(&tile_x
, &tile_y
, tile_index(ptile
));
159 astr_add_line(&str
, _("Location: (%d, %d) [%d]"),
160 tile_x
, tile_y
, tile_continent(ptile
));
161 index_to_native_pos(&nat_x
, &nat_y
, tile_index(ptile
));
162 astr_add_line(&str
, _("Native coordinates: (%d, %d)"),
165 if (client_tile_get_known(ptile
) == TILE_UNKNOWN
) {
166 astr_add(&str
, _("Unknown"));
167 return astr_str(&str
);
169 astr_add_line(&str
, _("Terrain: %s"), tile_get_info_text(ptile
, 0));
170 astr_add_line(&str
, _("Food/Prod/Trade: %s"),
171 get_tile_output_text(ptile
));
172 if (tile_has_special(ptile
, S_HUT
)) {
173 astr_add_line(&str
, _("Minor Tribe Village"));
175 if (BORDERS_DISABLED
!= game
.info
.borders
&& !pcity
) {
176 struct player
*owner
= tile_owner(ptile
);
178 get_full_username(username
, sizeof(username
), owner
);
179 get_full_nation(nation
, sizeof(nation
), owner
);
181 if (NULL
!= client
.conn
.playing
&& owner
== client
.conn
.playing
) {
182 astr_add_line(&str
, _("Our territory"));
183 } else if (NULL
!= owner
&& NULL
== client
.conn
.playing
) {
184 /* TRANS: "Territory of <username> (<nation + team>)" */
185 astr_add_line(&str
, _("Territory of %s (%s)"), username
, nation
);
186 } else if (NULL
!= owner
) {
187 struct player_diplstate
*ds
= player_diplstate_get(client
.conn
.playing
,
190 if (ds
->type
== DS_CEASEFIRE
) {
191 int turns
= ds
->turns_left
;
194 /* TRANS: "Territory of <username> (<nation + team>)
195 * (<number> turn cease-fire)" */
196 PL_("Territory of %s (%s) (%d turn cease-fire)",
197 "Territory of %s (%s) (%d turn cease-fire)",
199 username
, nation
, turns
);
203 /* TRANS: "Territory of <username>
204 * (<nation + team> | <diplomatic state>)" */
205 astr_add_line(&str
, _("Territory of %s (%s | %s)"),
207 diplo_nation_plural_adjectives
[type
]);
210 astr_add_line(&str
, _("Unclaimed territory"));
214 /* Look at city owner, not tile owner (the two should be the same, if
215 * borders are in use). */
216 struct player
*owner
= city_owner(pcity
);
217 const char *improvements
[improvement_count()];
218 int has_improvements
= 0;
220 get_full_username(username
, sizeof(username
), owner
);
221 get_full_nation(nation
, sizeof(nation
), owner
);
223 if (NULL
== client
.conn
.playing
|| owner
== client
.conn
.playing
) {
224 /* TRANS: "City: <city name> | <username> (<nation + team>)" */
225 astr_add_line(&str
, _("City: %s | %s (%s)"),
226 city_name(pcity
), username
, nation
);
228 struct player_diplstate
*ds
229 = player_diplstate_get(client_player(), owner
);
230 if (ds
->type
== DS_CEASEFIRE
) {
231 int turns
= ds
->turns_left
;
233 /* TRANS: "City: <city name> | <username>
234 * (<nation + team>, <number> turn cease-fire)" */
235 astr_add_line(&str
, PL_("City: %s | %s (%s, %d turn cease-fire)",
236 "City: %s | %s (%s, %d turn cease-fire)",
238 city_name(pcity
), username
, nation
, turns
);
240 /* TRANS: "City: <city name> | <username>
241 * (<nation + team>, <diplomatic state>)" */
242 astr_add_line(&str
, _("City: %s | %s (%s, %s)"),
243 city_name(pcity
), username
, nation
,
244 diplo_city_adjectives
[ds
->type
]);
247 if (can_player_see_units_in_city(client_player(), pcity
)) {
248 int count
= unit_list_size(ptile
->units
);
251 astr_add(&str
, PL_(" | Occupied with %d unit.",
252 " | Occupied with %d units.", count
), count
);
254 astr_add(&str
, _(" | Not occupied."));
257 if (pcity
->client
.occupied
) {
258 astr_add(&str
, _(" | Occupied."));
260 astr_add(&str
, _(" | Not occupied."));
263 improvement_iterate(pimprove
) {
264 if (is_improvement_visible(pimprove
)
265 && city_has_building(pcity
, pimprove
)) {
266 improvements
[has_improvements
++] =
267 improvement_name_translation(pimprove
);
269 } improvement_iterate_end
;
271 if (0 < has_improvements
) {
272 struct astring list
= ASTRING_INIT
;
274 astr_build_and_list(&list
, improvements
, has_improvements
);
275 /* TRANS: %s is a list of "and"-separated improvements. */
276 astr_add_line(&str
, _(" with %s."), astr_str(&list
));
280 unit_list_iterate(get_units_in_focus(), pfocus_unit
) {
281 struct city
*hcity
= game_city_by_number(pfocus_unit
->homecity
);
283 if (unit_has_type_flag(pfocus_unit
, UTYF_TRADE_ROUTE
)
284 && can_cities_trade(hcity
, pcity
)
285 && can_establish_trade_route(hcity
, pcity
)) {
286 /* TRANS: "Trade from Warsaw: 5" */
287 astr_add_line(&str
, _("Trade from %s: %d"),
289 trade_between_cities(hcity
, pcity
));
291 } unit_list_iterate_end
;
294 const char *infratext
= get_infrastructure_text(ptile
->special
,
297 if (*infratext
!= '\0') {
298 astr_add_line(&str
, _("Infrastructure: %s"), infratext
);
301 activity_text
= concat_tile_activity_text(ptile
);
302 if (strlen(activity_text
) > 0) {
303 astr_add_line(&str
, _("Activity: %s"), activity_text
);
305 if (punit
&& !pcity
) {
306 struct player
*owner
= unit_owner(punit
);
307 struct unit_type
*ptype
= unit_type(punit
);
309 get_full_username(username
, sizeof(username
), owner
);
310 get_full_nation(nation
, sizeof(nation
), owner
);
312 if (!client_player() || owner
== client_player()) {
313 struct city
*pcity
= player_city_by_number(owner
, punit
->homecity
);
316 /* TRANS: "Unit: <unit type> | <username>
317 * (<nation + team>, <homecity>)" */
318 astr_add_line(&str
, _("Unit: %s | %s (%s, %s)"),
319 utype_name_translation(ptype
), username
,
320 nation
, city_name(pcity
));
322 /* TRANS: "Unit: <unit type> | <username> (<nation + team>)" */
323 astr_add_line(&str
, _("Unit: %s | %s (%s)"),
324 utype_name_translation(ptype
), username
, nation
);
326 } else if (NULL
!= owner
) {
327 struct player_diplstate
*ds
= player_diplstate_get(client_player(),
329 if (ds
->type
== DS_CEASEFIRE
) {
330 int turns
= ds
->turns_left
;
332 /* TRANS: "Unit: <unit type> | <username> (<nation + team>,
333 * <number> turn cease-fire)" */
334 astr_add_line(&str
, PL_("Unit: %s | %s (%s, %d turn cease-fire)",
335 "Unit: %s | %s (%s, %d turn cease-fire)",
337 utype_name_translation(ptype
),
338 username
, nation
, turns
);
340 /* TRANS: "Unit: <unit type> | <username> (<nation + team>,
341 * <diplomatic state>)" */
342 astr_add_line(&str
, _("Unit: %s | %s (%s, %s)"),
343 utype_name_translation(ptype
), username
, nation
,
344 diplo_city_adjectives
[ds
->type
]);
348 unit_list_iterate(get_units_in_focus(), pfocus_unit
) {
349 int att_chance
= FC_INFINITY
, def_chance
= FC_INFINITY
;
352 unit_list_iterate(ptile
->units
, tile_unit
) {
353 if (unit_owner(tile_unit
) != unit_owner(pfocus_unit
)) {
354 int att
= unit_win_chance(pfocus_unit
, tile_unit
) * 100;
355 int def
= (1.0 - unit_win_chance(tile_unit
, pfocus_unit
)) * 100;
359 /* Presumably the best attacker and defender will be used. */
360 att_chance
= MIN(att
, att_chance
);
361 def_chance
= MIN(def
, def_chance
);
363 } unit_list_iterate_end
;
366 /* TRANS: "Chance to win: A:95% D:46%" */
367 astr_add_line(&str
, _("Chance to win: A:%d%% D:%d%%"),
368 att_chance
, def_chance
);
370 } unit_list_iterate_end
;
372 /* TRANS: A is attack power, D is defense power, FP is firepower,
373 * HP is hitpoints (current and max). */
374 astr_add_line(&str
, _("A:%d D:%d FP:%d HP:%d/%d"),
375 ptype
->attack_strength
, ptype
->defense_strength
,
376 ptype
->firepower
, punit
->hp
, ptype
->hp
);
378 const char *veteran_name
=
379 utype_veteran_name_translation(ptype
, punit
->veteran
);
381 astr_add(&str
, " (%s)", veteran_name
);
385 if (unit_owner(punit
) == client_player()
386 || client_is_global_observer()) {
387 /* Show bribe cost for own units. */
388 astr_add_line(&str
, _("Bribe cost: %d"), unit_bribe_cost(punit
));
390 /* We can only give an (lower) boundary for units of other players. */
391 astr_add_line(&str
, _("Estimated bribe cost: > %d"),
392 unit_bribe_cost(punit
));
395 if ((NULL
== client
.conn
.playing
|| owner
== client
.conn
.playing
)
396 && unit_list_size(ptile
->units
) >= 2) {
397 /* TRANS: "5 more" units on this tile */
398 astr_add(&str
, _(" (%d more)"), unit_list_size(ptile
->units
) - 1);
402 astr_break_lines(&str
, LINE_BREAK
);
403 return astr_str(&str
);
406 /****************************************************************************
407 Creates the activity progress text for the given tile.
409 This should only be used inside popup_info_text and should eventually be
411 ****************************************************************************/
412 const char *concat_tile_activity_text(struct tile
*ptile
)
414 int activity_total
[ACTIVITY_LAST
];
415 int activity_units
[ACTIVITY_LAST
];
416 int base_total
[MAX_BASE_TYPES
];
417 int base_units
[MAX_BASE_TYPES
];
418 int road_total
[MAX_ROAD_TYPES
];
419 int road_units
[MAX_ROAD_TYPES
];
420 int num_activities
= 0;
423 static struct astring str
= ASTRING_INIT
;
427 memset(activity_total
, 0, sizeof(activity_total
));
428 memset(activity_units
, 0, sizeof(activity_units
));
429 memset(base_total
, 0, sizeof(base_total
));
430 memset(base_units
, 0, sizeof(base_units
));
431 memset(road_total
, 0, sizeof(road_total
));
432 memset(road_units
, 0, sizeof(road_units
));
434 unit_list_iterate(ptile
->units
, punit
) {
435 if (punit
->activity
== ACTIVITY_PILLAGE
) {
437 } else if (punit
->activity
== ACTIVITY_BASE
) {
438 base_total
[punit
->activity_target
.obj
.base
] += punit
->activity_count
;
439 base_total
[punit
->activity_target
.obj
.base
] += get_activity_rate_this_turn(punit
);
440 base_units
[punit
->activity_target
.obj
.base
] += get_activity_rate(punit
);
441 } else if (punit
->activity
== ACTIVITY_GEN_ROAD
) {
442 road_total
[punit
->activity_target
.obj
.road
] += punit
->activity_count
;
443 road_total
[punit
->activity_target
.obj
.road
] += get_activity_rate_this_turn(punit
);
444 road_units
[punit
->activity_target
.obj
.road
] += get_activity_rate(punit
);
446 activity_total
[punit
->activity
] += punit
->activity_count
;
447 activity_total
[punit
->activity
] += get_activity_rate_this_turn(punit
);
448 activity_units
[punit
->activity
] += get_activity_rate(punit
);
450 } unit_list_iterate_end
;
453 bv_special pillage_spe
= get_unit_tile_pillage_set(ptile
);
454 bv_bases pillage_bases
= get_unit_tile_pillage_base_set(ptile
);
455 bv_roads pillage_roads
= get_unit_tile_pillage_road_set(ptile
);
456 if (BV_ISSET_ANY(pillage_spe
)
457 || BV_ISSET_ANY(pillage_bases
)
458 || BV_ISSET_ANY(pillage_roads
)) {
459 astr_add(&str
, "%s(%s)", _("Pillage"),
460 get_infrastructure_text(pillage_spe
, pillage_bases
, pillage_roads
));
462 /* Untargeted pillaging is happening. */
463 astr_add(&str
, "%s", _("Pillage"));
468 activity_type_iterate(i
) {
469 if (i
== ACTIVITY_BASE
) {
470 base_type_iterate(bp
) {
471 Base_type_id b
= base_index(bp
);
472 if (base_units
[b
] > 0) {
473 remains
= tile_activity_base_time(ptile
, b
) - base_total
[b
];
475 turns
= 1 + (remains
+ base_units
[b
] - 1) / base_units
[b
];
477 /* base will be finished this turn */
480 if (num_activities
> 0) {
483 astr_add(&str
, "%s(%d)", base_name_translation(bp
), turns
);
486 } base_type_iterate_end
;
487 } else if (i
== ACTIVITY_GEN_ROAD
) {
488 road_type_iterate(rp
) {
489 Road_type_id r
= road_index(rp
);
490 if (road_units
[r
] > 0) {
491 remains
= tile_activity_road_time(ptile
, r
) - road_total
[r
];
493 turns
= 1 + (remains
+ road_units
[r
] - 1) / road_units
[r
];
495 /* road will be finished this turn */
498 if (num_activities
> 0) {
501 astr_add(&str
, "%s(%d)", road_name_translation(rp
), turns
);
504 } road_type_iterate_end
;
505 } else if (is_build_or_clean_activity(i
) && activity_units
[i
] > 0) {
506 if (num_activities
> 0) {
509 remains
= tile_activity_time(i
, ptile
) - activity_total
[i
];
511 turns
= 1 + (remains
+ activity_units
[i
] - 1) / activity_units
[i
];
513 /* activity will be finished this turn */
516 astr_add(&str
, "%s(%d)", get_activity_text(i
), turns
);
519 } activity_type_iterate_end
;
521 return astr_str(&str
);
524 #define FAR_CITY_SQUARE_DIST (2*(6*6))
526 /****************************************************************************
527 Returns the text describing the city and its distance.
528 ****************************************************************************/
529 const char *get_nearest_city_text(struct city
*pcity
, int sq_dist
)
531 static struct astring str
= ASTRING_INIT
;
535 /* just to be sure */
540 astr_add(&str
, (sq_dist
>= FAR_CITY_SQUARE_DIST
)
541 /* TRANS: on own line immediately following \n, ... <city> */
544 /* TRANS: on own line immediately following \n, ... <city> */
547 /* TRANS: on own line immediately following \n, ... <city> */
554 return astr_str(&str
);
557 /****************************************************************************
558 Returns the unit description.
560 FIXME: This function is not re-entrant because it returns a pointer to
562 ****************************************************************************/
563 const char *unit_description(struct unit
*punit
)
566 struct player
*owner
= unit_owner(punit
);
567 struct player
*nationality
= unit_nationality(punit
);
569 player_city_by_number(owner
, punit
->homecity
);
570 struct city
*pcity_near
= get_nearest_city(punit
, &pcity_near_dist
);
571 struct unit_type
*ptype
= unit_type(punit
);
572 static struct astring str
= ASTRING_INIT
;
573 const struct player
*pplayer
= client_player();
577 astr_add(&str
, "%s", utype_name_translation(ptype
));
580 const char *veteran_name
=
581 utype_veteran_name_translation(ptype
, punit
->veteran
);
583 astr_add(&str
, " (%s)", veteran_name
);
587 if (pplayer
== owner
) {
588 unit_upkeep_astr(punit
, &str
);
590 astr_add(&str
, "\n");
591 unit_activity_astr(punit
, &str
);
594 /* TRANS: on own line immediately following \n, ... <city> */
595 astr_add_line(&str
, _("from %s"), city_name(pcity
));
597 astr_add(&str
, "\n");
599 if (game
.info
.citizen_nationality
) {
600 if (nationality
!= NULL
&& owner
!= nationality
) {
601 /* TRANS: Nationality of the soldiers in unit, can be different from owner. */
602 astr_add(&str
, _("%s people"), nation_adjective_for_player(nationality
));
604 astr_add(&str
, "\n");
608 astr_add_line(&str
, "%s",
609 get_nearest_city_text(pcity_near
, pcity_near_dist
));
611 astr_add_line(&str
, "Unit ID: %d", punit
->id
);
614 return astr_str(&str
);
617 /****************************************************************************
618 Describe the airlift capacity of a city for the given units (from their
620 If pdest is non-NULL, describe its capacity as a destination, otherwise
621 describe the capacity of the city the unit's currently in (if any) as a
622 source. (If the units in the list are in different cities, this will
623 probably not give a useful result in this case.)
624 If not all of the listed units can be airlifted, return the description
626 Returns NULL if an airlift is not possible for any of the units.
627 ****************************************************************************/
628 const char *get_airlift_text(const struct unit_list
*punits
,
629 const struct city
*pdest
)
631 static struct astring str
= ASTRING_INIT
;
632 bool src
= (pdest
== NULL
);
633 enum texttype
{ AL_IMPOSSIBLE
, AL_UNKNOWN
, AL_FINITE
, AL_INFINITE
}
634 best
= AL_IMPOSSIBLE
;
635 int cur
= 0, max
= 0;
637 unit_list_iterate(punits
, punit
) {
638 enum texttype
this = AL_IMPOSSIBLE
;
639 enum unit_airlift_result result
;
641 /* NULL will tell us about the capability of airlifting from source */
642 result
= test_unit_can_airlift_to(client_player(), punit
, pdest
);
646 case AR_WRONG_UNITTYPE
:
649 case AR_BAD_SRC_CITY
:
650 case AR_BAD_DST_CITY
:
651 /* No chance of an airlift. */
652 this = AL_IMPOSSIBLE
;
655 case AR_OK_SRC_UNKNOWN
:
656 case AR_OK_DST_UNKNOWN
:
657 case AR_SRC_NO_FLIGHTS
:
658 case AR_DST_NO_FLIGHTS
:
659 /* May or may not be able to airlift now, but there's a chance we could
662 const struct city
*pcity
= src
? tile_city(unit_tile(punit
)) : pdest
;
663 fc_assert_ret_val(pcity
!= NULL
, fc_strdup("-"));
664 if (!src
&& (game
.info
.airlifting_style
& AIRLIFTING_UNLIMITED_DEST
)) {
665 /* No restrictions on destination (and we can infer this even for
666 * other players' cities). */
668 } else if (client_player() == city_owner(pcity
)) {
669 /* A city we know about. */
670 int this_cur
= pcity
->airlift
, this_max
= city_airlift_max(pcity
);
672 /* City known not to be airlift-capable. */
673 this = AL_IMPOSSIBLE
;
676 && (game
.info
.airlifting_style
& AIRLIFTING_UNLIMITED_SRC
)) {
677 /* Unlimited capacity. */
680 /* Limited capacity (possibly zero right now). */
682 /* Store the numbers. This whole setup assumes that numeric
683 * capacity isn't unit-dependent. */
684 if (best
== AL_FINITE
) {
685 fc_assert(cur
== this_cur
&& max
== this_max
);
692 /* Unknown capacity. */
699 /* Now take the most optimistic view. */
700 best
= MAX(best
, this);
701 } unit_list_iterate_end
;
710 astr_set(&str
, "%d/%d", cur
, max
);
713 astr_set(&str
, _("Yes"));
717 return astr_str(&str
);
720 /****************************************************************************
721 Return total expected bulbs.
722 ****************************************************************************/
723 static int get_bulbs_per_turn(int *pours
, bool *pteam
, int *ptheirs
)
725 const struct player_research
*presearch
;
726 int ours
= 0, theirs
= 0;
729 if (!client_has_player()) {
732 presearch
= player_research_get(client_player());
735 players_iterate(pplayer
) {
736 if (pplayer
== client_player()) {
737 city_list_iterate(pplayer
->cities
, pcity
) {
738 ours
+= pcity
->prod
[O_SCIENCE
];
739 } city_list_iterate_end
;
741 if (game
.info
.tech_upkeep_style
!= TECH_UPKEEP_NONE
) {
742 ours
-= player_research_get(pplayer
)->tech_upkeep
;
744 } else if (presearch
== player_research_get(pplayer
)) {
746 theirs
+= pplayer
->bulbs_last_turn
;
748 if (game
.info
.tech_upkeep_style
!= TECH_UPKEEP_NONE
) {
749 theirs
-= presearch
->tech_upkeep
;
752 } players_iterate_end
;
763 return ours
+ theirs
;
766 /****************************************************************************
767 Returns the text to display in the science dialog.
768 ****************************************************************************/
769 const char *science_dialog_text(void)
772 int ours
, theirs
, perturn
, upkeep
;
773 static struct astring str
= ASTRING_INIT
;
774 struct astring ourbuf
= ASTRING_INIT
, theirbuf
= ASTRING_INIT
;
775 struct player_research
*research
;
779 perturn
= get_bulbs_per_turn(&ours
, &team
, &theirs
);
781 research
= player_research_get(client_player());
782 upkeep
= research
->tech_upkeep
;
784 if (NULL
== client
.conn
.playing
|| (ours
== 0 && theirs
== 0
786 return _("Progress: no research");
789 if (A_UNSET
== research
->researching
) {
790 astr_add(&str
, _("Progress: no research"));
792 int done
= research
->bulbs_researched
;
793 int total
= total_bulbs_required(client_player());
796 int turns
= MAX(1, ceil((double)total
) / perturn
);
798 astr_add(&str
, PL_("Progress: %d turn/advance",
799 "Progress: %d turns/advance",
801 } else if (perturn
< 0 ) {
802 /* negative number of bulbs per turn due to tech upkeep */
803 int turns
= ceil((double) done
/ -perturn
);
805 astr_add(&str
, PL_("Progress: %d turn/advance loss",
806 "Progress: %d turns/advance loss",
810 astr_add(&str
, _("Progress: none"));
813 astr_set(&ourbuf
, PL_("%d bulb/turn", "%d bulbs/turn", ours
), ours
);
815 /* Techpool version */
817 /* TRANS: This is appended to "%d bulb/turn" text */
818 PL_(", %d bulb/turn from team",
819 ", %d bulbs/turn from team", theirs
), theirs
);
821 astr_clear(&theirbuf
);
823 astr_add(&str
, " (%s%s)", astr_str(&ourbuf
), astr_str(&theirbuf
));
825 astr_free(&theirbuf
);
827 if (game
.info
.tech_upkeep_style
!= TECH_UPKEEP_NONE
) {
828 /* perturn is defined as: (bulbs produced) - upkeep */
829 astr_add_line(&str
, _("Bulbs produced per turn: %d"), perturn
+ upkeep
);
830 /* TRANS: keep leading space; appended to "Bulbs produced per turn: %d" */
831 astr_add(&str
, _(" (needed for technology upkeep: %d)"), upkeep
);
834 return astr_str(&str
);
837 /****************************************************************************
838 Get the short science-target text. This is usually shown directly in
843 The "percent" value, if given, will be set to the completion percentage
844 of the research target (actually it's a [0,1] scale not a percent).
845 ****************************************************************************/
846 const char *get_science_target_text(double *percent
)
848 struct player_research
*research
= player_research_get(client_player());
849 static struct astring str
= ASTRING_INIT
;
856 if (research
->researching
== A_UNSET
) {
857 astr_add(&str
, _("%d/- (never)"), research
->bulbs_researched
);
862 int total
= total_bulbs_required(client
.conn
.playing
);
863 int done
= research
->bulbs_researched
;
864 int perturn
= get_bulbs_per_turn(NULL
, NULL
, NULL
);
867 int turns
= ceil( (double)(total
- done
) / perturn
);
869 astr_add(&str
, PL_("%d/%d (%d turn)", "%d/%d (%d turns)", turns
),
871 } else if (perturn
< 0 ) {
872 /* negative number of bulbs per turn due to tech upkeep */
873 int turns
= ceil( (double)done
/ -perturn
);
875 astr_add(&str
, PL_("%d/%d (%d turn)", "%d/%d (%d turns)", turns
),
876 done
, perturn
, turns
);
879 astr_add(&str
, _("%d/%d (never)"), done
, total
);
882 *percent
= (double)done
/ (double)total
;
883 *percent
= CLIP(0.0, *percent
, 1.0);
887 return astr_str(&str
);
890 /****************************************************************************
891 Set the science-goal-label text as if we're researching the given goal.
892 ****************************************************************************/
893 const char *get_science_goal_text(Tech_type_id goal
)
895 int steps
= num_unknown_techs_for_goal(client
.conn
.playing
, goal
);
896 int bulbs_needed
= total_bulbs_required_for_goal(client
.conn
.playing
, goal
);
898 int perturn
= get_bulbs_per_turn(NULL
, NULL
, NULL
);
899 struct player_research
* research
= player_research_get(client_player());
900 static struct astring str
= ASTRING_INIT
;
901 struct astring buf1
= ASTRING_INIT
,
911 if (is_tech_a_req_for_goal(client
.conn
.playing
,
912 research
->researching
, goal
)
913 || research
->researching
== goal
) {
914 bulbs_needed
-= research
->bulbs_researched
;
918 PL_("%d step", "%d steps", steps
), steps
);
920 PL_("%d bulb", "%d bulbs", bulbs_needed
), bulbs_needed
);
922 turns
= (bulbs_needed
+ perturn
- 1) / perturn
;
924 PL_("%d turn", "%d turns", turns
), turns
);
926 astr_set(&buf3
, _("never"));
928 astr_add_line(&str
, "(%s - %s - %s)",
929 astr_str(&buf1
), astr_str(&buf2
), astr_str(&buf3
));
934 return astr_str(&str
);
937 /****************************************************************************
938 Return the text for the label on the info panel. (This is traditionally
939 shown to the left of the mapview.)
941 Clicking on this text should bring up the get_info_label_text_popup text.
942 ****************************************************************************/
943 const char *get_info_label_text(bool moreinfo
)
945 static struct astring str
= ASTRING_INIT
;
949 if (NULL
!= client
.conn
.playing
) {
950 astr_add_line(&str
, _("Population: %s"),
951 population_to_text(civ_population(client
.conn
.playing
)));
953 astr_add_line(&str
, _("Year: %s (T%d)"),
954 textyear(game
.info
.year
), game
.info
.turn
);
956 if (NULL
!= client
.conn
.playing
) {
957 astr_add_line(&str
, _("Gold: %d (%+d)"),
958 client
.conn
.playing
->economic
.gold
,
959 player_get_expected_income(client
.conn
.playing
));
960 astr_add_line(&str
, _("Tax: %d Lux: %d Sci: %d"),
961 client
.conn
.playing
->economic
.tax
,
962 client
.conn
.playing
->economic
.luxury
,
963 client
.conn
.playing
->economic
.science
);
965 if (game
.info
.phase_mode
== PMT_PLAYERS_ALTERNATE
) {
966 if (game
.info
.phase
< 0 || game
.info
.phase
>= player_count()) {
967 astr_add_line(&str
, _("Moving: Nobody"));
969 astr_add_line(&str
, _("Moving: %s"),
970 player_name(player_by_number(game
.info
.phase
)));
972 } else if (game
.info
.phase_mode
== PMT_TEAMS_ALTERNATE
) {
973 if (game
.info
.phase
< 0 || game
.info
.phase
>= team_count()) {
974 astr_add_line(&str
, _("Moving: Nobody"));
976 astr_add_line(&str
, _("Moving: %s"),
977 team_name_translation(team_by_number(game
.info
.phase
)));
982 astr_add_line(&str
, _("(Click for more info)"));
985 return astr_str(&str
);
988 /****************************************************************************
989 Return the text for the popup label on the info panel. (This is
990 traditionally done as a popup whenever the regular info text is clicked
992 ****************************************************************************/
993 const char *get_info_label_text_popup(void)
995 static struct astring str
= ASTRING_INIT
;
999 if (NULL
!= client
.conn
.playing
) {
1000 astr_add_line(&str
, _("%s People"),
1001 population_to_text(civ_population(client
.conn
.playing
)));
1003 astr_add_line(&str
, _("Year: %s"), textyear(game
.info
.year
));
1004 astr_add_line(&str
, _("Turn: %d"), game
.info
.turn
);
1006 if (NULL
!= client
.conn
.playing
) {
1007 int perturn
= get_bulbs_per_turn(NULL
, NULL
, NULL
);
1008 int upkeep
= player_research_get(client_player())->tech_upkeep
;
1010 astr_add_line(&str
, _("Gold: %d"),
1011 client
.conn
.playing
->economic
.gold
);
1012 astr_add_line(&str
, _("Net Income: %d"),
1013 player_get_expected_income(client
.conn
.playing
));
1014 /* TRANS: Gold, luxury, and science rates are in percentage values. */
1015 astr_add_line(&str
, _("Tax rates: Gold:%d%% Luxury:%d%% Science:%d%%"),
1016 client
.conn
.playing
->economic
.tax
,
1017 client
.conn
.playing
->economic
.luxury
,
1018 client
.conn
.playing
->economic
.science
);
1019 astr_add_line(&str
, _("Researching %s: %s"),
1020 advance_name_researching(client
.conn
.playing
),
1021 get_science_target_text(NULL
));
1022 /* perturn is defined as: (bulbs produced) - upkeep */
1023 if (game
.info
.tech_upkeep_style
!= TECH_UPKEEP_NONE
) {
1024 astr_add_line(&str
, _("Bulbs per turn: %d - %d = %d"), perturn
+ upkeep
,
1027 fc_assert(upkeep
== 0);
1028 astr_add_line(&str
, _("Bulbs per turn: %d"), perturn
);
1032 /* See also get_global_warming_tooltip and get_nuclear_winter_tooltip. */
1034 if (game
.info
.global_warming
) {
1036 global_warming_scaled(&chance
, &rate
, 100);
1037 astr_add_line(&str
, _("Global warming chance: %d%% (%+d%%/turn)"),
1040 astr_add_line(&str
, _("Global warming deactivated."));
1043 if (game
.info
.nuclear_winter
) {
1045 nuclear_winter_scaled(&chance
, &rate
, 100);
1046 astr_add_line(&str
, _("Nuclear winter chance: %d%% (%+d%%/turn)"),
1049 astr_add_line(&str
, _("Nuclear winter deactivated."));
1052 if (NULL
!= client
.conn
.playing
) {
1053 astr_add_line(&str
, _("Government: %s"),
1054 government_name_for_player(client
.conn
.playing
));
1057 return astr_str(&str
);
1060 /****************************************************************************
1061 Return the title text for the unit info shown in the info panel.
1063 FIXME: this should be renamed.
1064 ****************************************************************************/
1065 const char *get_unit_info_label_text1(struct unit_list
*punits
)
1067 static struct astring str
= ASTRING_INIT
;
1072 int count
= unit_list_size(punits
);
1075 astr_add(&str
, "%s", unit_name_translation(unit_list_get(punits
, 0)));
1077 astr_add(&str
, PL_("%d unit", "%d units", count
), count
);
1080 return astr_str(&str
);
1083 /****************************************************************************
1084 Return the text body for the unit info shown in the info panel.
1086 FIXME: this should be renamed.
1087 ****************************************************************************/
1088 const char *get_unit_info_label_text2(struct unit_list
*punits
, int linebreaks
)
1090 static struct astring str
= ASTRING_INIT
;
1099 count
= unit_list_size(punits
);
1101 /* This text should always have the same number of lines if
1102 * 'linebreaks' has no flags at all. Otherwise the GUI widgets may be
1103 * confused and try to resize themselves. If caller asks for
1104 * conditional 'linebreaks', it should take care of these problems
1107 /* Line 1. Goto or activity text. */
1108 if (count
> 0 && hover_state
!= HOVER_NONE
) {
1111 if (!goto_get_turns(&min
, &max
)) {
1112 /* TRANS: Impossible to reach goto target tile */
1113 astr_add_line(&str
, "%s", Q_("?goto:Unreachable"));
1114 } else if (min
== max
) {
1115 astr_add_line(&str
, _("Turns to target: %d"), max
);
1117 astr_add_line(&str
, _("Turns to target: %d to %d"), min
, max
);
1119 } else if (count
== 1) {
1120 astr_add_line(&str
, "%s",
1121 unit_activity_text(unit_list_get(punits
, 0)));
1122 } else if (count
> 1) {
1123 astr_add_line(&str
, PL_("%d unit selected",
1124 "%d units selected",
1128 astr_add_line(&str
, _("No units selected."));
1131 /* Lines 2, 3, 4, and possible 5 vary. */
1133 struct unit
*punit
= unit_list_get(punits
, 0);
1134 struct player
*owner
= unit_owner(punit
);
1135 struct city
*pcity
= player_city_by_number(owner
,
1138 astr_add_line(&str
, "%s", tile_get_info_text(unit_tile(punit
),
1141 const char *infratext
1142 = get_infrastructure_text(unit_tile(punit
)->special
,
1143 unit_tile(punit
)->bases
,
1144 unit_tile(punit
)->roads
);
1145 if (*infratext
!= '\0') {
1146 astr_add_line(&str
, "%s", infratext
);
1148 astr_add_line(&str
, " ");
1152 astr_add_line(&str
, "%s", city_name(pcity
));
1154 astr_add_line(&str
, " ");
1157 if (game
.info
.citizen_nationality
) {
1158 struct player
*nationality
= unit_nationality(punit
);
1160 /* Line 5, nationality text */
1161 if (nationality
!= NULL
&& owner
!= nationality
) {
1162 astr_add(&str
, _("%s people"), nation_adjective_for_player(nationality
));
1164 astr_add(&str
, " ");
1168 } else if (count
> 1) {
1169 int mil
= 0, nonmil
= 0;
1170 int types_count
[U_LAST
], i
;
1171 struct unit_type
*top
[3];
1173 memset(types_count
, 0, sizeof(types_count
));
1174 unit_list_iterate(punits
, punit
) {
1175 if (unit_has_type_flag(punit
, UTYF_CIVILIAN
)) {
1180 types_count
[utype_index(unit_type(punit
))]++;
1181 } unit_list_iterate_end
;
1183 top
[0] = top
[1] = top
[2] = NULL
;
1184 unit_type_iterate(utype
) {
1186 || types_count
[utype_index(top
[2])] < types_count
[utype_index(utype
)]) {
1190 || types_count
[utype_index(top
[1])] < types_count
[utype_index(top
[2])]) {
1195 || types_count
[utype_index(top
[0])] < types_count
[utype_index(utype
)]) {
1201 } unit_type_iterate_end
;
1203 for (i
= 0; i
< 2; i
++) {
1204 if (top
[i
] && types_count
[utype_index(top
[i
])] > 0) {
1205 if (utype_has_flag(top
[i
], UTYF_CIVILIAN
)) {
1206 nonmil
-= types_count
[utype_index(top
[i
])];
1208 mil
-= types_count
[utype_index(top
[i
])];
1210 astr_add_line(&str
, "%d: %s",
1211 types_count
[utype_index(top
[i
])],
1212 utype_name_translation(top
[i
]));
1214 astr_add_line(&str
, " ");
1218 if (top
[2] && types_count
[utype_index(top
[2])] > 0
1219 && types_count
[utype_index(top
[2])] == nonmil
+ mil
) {
1220 astr_add_line(&str
, "%d: %s", types_count
[utype_index(top
[2])],
1221 utype_name_translation(top
[2]));
1222 } else if (nonmil
> 0 && mil
> 0) {
1223 astr_add_line(&str
, _("Others: %d civil; %d military"), nonmil
, mil
);
1224 } else if (nonmil
> 0) {
1225 astr_add_line(&str
, _("Others: %d civilian"), nonmil
);
1226 } else if (mil
> 0) {
1227 astr_add_line(&str
, _("Others: %d military"), mil
);
1229 astr_add_line(&str
, " ");
1232 if (game
.info
.citizen_nationality
) {
1233 astr_add_line(&str
, " ");
1236 astr_add_line(&str
, " ");
1237 astr_add_line(&str
, " ");
1238 astr_add_line(&str
, " ");
1240 if (game
.info
.citizen_nationality
) {
1241 astr_add_line(&str
, " ");
1245 /* Line 5/6. Debug text. */
1248 astr_add_line(&str
, "(Unit ID %d)", unit_list_get(punits
, 0)->id
);
1250 astr_add_line(&str
, " ");
1254 return astr_str(&str
);
1257 /****************************************************************************
1258 Return text about upgrading these unit lists.
1260 Returns TRUE iff any units can be upgraded.
1261 ****************************************************************************/
1262 bool get_units_upgrade_info(char *buf
, size_t bufsz
,
1263 struct unit_list
*punits
)
1265 if (unit_list_size(punits
) == 0) {
1266 fc_snprintf(buf
, bufsz
, _("No units to upgrade!"));
1268 } else if (unit_list_size(punits
) == 1) {
1269 return (UU_OK
== unit_upgrade_info(unit_list_front(punits
), buf
, bufsz
));
1271 int upgrade_cost
= 0;
1272 int num_upgraded
= 0;
1273 int min_upgrade_cost
= FC_INFINITY
;
1275 unit_list_iterate(punits
, punit
) {
1276 if (unit_owner(punit
) == client_player()
1277 && UU_OK
== unit_upgrade_test(punit
, FALSE
)) {
1278 struct unit_type
*from_unittype
= unit_type(punit
);
1279 struct unit_type
*to_unittype
= can_upgrade_unittype(client
.conn
.playing
,
1281 int cost
= unit_upgrade_price(unit_owner(punit
),
1282 from_unittype
, to_unittype
);
1285 upgrade_cost
+= cost
;
1286 min_upgrade_cost
= MIN(min_upgrade_cost
, cost
);
1288 } unit_list_iterate_end
;
1289 if (num_upgraded
== 0) {
1290 fc_snprintf(buf
, bufsz
, _("None of these units may be upgraded."));
1293 /* This may trigger sometimes if you don't have enough money for
1294 * a full upgrade. If you have enough to upgrade at least one, it
1296 /* Construct prompt in several parts to allow separate pluralisation
1297 * by localizations */
1298 char tbuf
[MAX_LEN_MSG
], ubuf
[MAX_LEN_MSG
];
1299 fc_snprintf(tbuf
, ARRAY_SIZE(tbuf
), PL_("Treasury contains %d gold.",
1300 "Treasury contains %d gold.",
1301 client_player()->economic
.gold
),
1302 client_player()->economic
.gold
);
1303 /* TRANS: this whole string is a sentence fragment that is only ever
1304 * used by including it in another string (search comments for this
1305 * string to find it) */
1306 fc_snprintf(ubuf
, ARRAY_SIZE(ubuf
), PL_("Upgrade %d unit",
1310 /* TRANS: This is complicated. The first %s is a pre-pluralised
1311 * sentence fragment "Upgrade %d unit(s)"; the second is pre-pluralised
1312 * "Treasury contains %d gold." So the whole thing reads
1313 * "Upgrade 13 units for 1000 gold?\nTreasury contains 2000 gold." */
1314 fc_snprintf(buf
, bufsz
, PL_("%s for %d gold?\n%s",
1315 "%s for %d gold?\n%s", upgrade_cost
),
1316 ubuf
, upgrade_cost
, tbuf
);
1322 /****************************************************************************
1323 Return text about disbanding these units.
1325 Returns TRUE iff any units can be disbanded.
1326 ****************************************************************************/
1327 bool get_units_disband_info(char *buf
, size_t bufsz
,
1328 struct unit_list
*punits
)
1330 if (unit_list_size(punits
) == 0) {
1331 fc_snprintf(buf
, bufsz
, _("No units to disband!"));
1333 } else if (unit_list_size(punits
) == 1) {
1334 if (unit_has_type_flag(unit_list_front(punits
), UTYF_UNDISBANDABLE
)) {
1335 fc_snprintf(buf
, bufsz
, _("%s refuses to disband!"),
1336 unit_name_translation(unit_list_front(punits
)));
1339 /* TRANS: %s is a unit type */
1340 fc_snprintf(buf
, bufsz
, _("Disband %s?"),
1341 unit_name_translation(unit_list_front(punits
)));
1346 unit_list_iterate(punits
, punit
) {
1347 if (!unit_has_type_flag(punit
, UTYF_UNDISBANDABLE
)) {
1350 } unit_list_iterate_end
;
1352 fc_snprintf(buf
, bufsz
, _("None of these units may be disbanded."));
1355 /* TRANS: %d is never 0 or 1 */
1356 fc_snprintf(buf
, bufsz
, PL_("Disband %d unit?",
1357 "Disband %d units?", count
), count
);
1363 /****************************************************************************
1364 Get a tooltip text for the info panel research indicator. See
1365 client_research_sprite().
1366 ****************************************************************************/
1367 const char *get_bulb_tooltip(void)
1369 static struct astring str
= ASTRING_INIT
;
1373 astr_add_line(&str
, _("Shows your progress in "
1374 "researching the current technology."));
1376 if (NULL
!= client
.conn
.playing
) {
1377 struct player_research
*research
= player_research_get(client_player());
1379 if (research
->researching
== A_UNSET
) {
1380 astr_add_line(&str
, _("no research target."));
1383 int perturn
= get_bulbs_per_turn(NULL
, NULL
, NULL
);
1384 int done
= research
->bulbs_researched
;
1385 int total
= total_bulbs_required(client_player());
1386 struct astring buf1
= ASTRING_INIT
, buf2
= ASTRING_INIT
;
1389 turns
= MAX(1, ceil((double) (total
- done
) / perturn
));
1390 } else if (perturn
< 0 ) {
1391 turns
= ceil((double) done
/ -perturn
);
1395 astr_set(&buf1
, _("No progress"));
1397 astr_set(&buf1
, PL_("%d turn", "%d turns", turns
), turns
);
1400 /* TRANS: <perturn> bulbs/turn */
1401 astr_set(&buf2
, PL_("%d bulb/turn", "%d bulbs/turn", perturn
), perturn
);
1403 /* TRANS: <tech>: <amount>/<total bulbs> */
1404 astr_add_line(&str
, _("%s: %d/%d (%s, %s)."),
1405 advance_name_researching(client
.conn
.playing
),
1406 research
->bulbs_researched
,
1407 total_bulbs_required(client
.conn
.playing
),
1408 astr_str(&buf1
), astr_str(&buf2
));
1414 return astr_str(&str
);
1417 /****************************************************************************
1418 Get a tooltip text for the info panel global warning indicator. See also
1419 client_warming_sprite().
1420 ****************************************************************************/
1421 const char *get_global_warming_tooltip(void)
1423 static struct astring str
= ASTRING_INIT
;
1427 if (!game
.info
.global_warming
) {
1428 astr_add_line(&str
, _("Global warming deactivated."));
1431 global_warming_scaled(&chance
, &rate
, 100);
1432 astr_add_line(&str
, _("Shows the progress of global warming:"));
1433 astr_add_line(&str
, _("Pollution rate: %d%%"), rate
);
1434 astr_add_line(&str
, _("Chance of catastrophic warming each turn: %d%%"),
1438 return astr_str(&str
);
1441 /****************************************************************************
1442 Get a tooltip text for the info panel nuclear winter indicator. See also
1443 client_cooling_sprite().
1444 ****************************************************************************/
1445 const char *get_nuclear_winter_tooltip(void)
1447 static struct astring str
= ASTRING_INIT
;
1451 if (!game
.info
.nuclear_winter
) {
1452 astr_add_line(&str
, _("Nuclear winter deactivated."));
1455 nuclear_winter_scaled(&chance
, &rate
, 100);
1456 astr_add_line(&str
, _("Shows the progress of nuclear winter:"));
1457 astr_add_line(&str
, _("Fallout rate: %d%%"), rate
);
1458 astr_add_line(&str
, _("Chance of catastrophic winter each turn: %d%%"),
1462 return astr_str(&str
);
1465 /****************************************************************************
1466 Get a tooltip text for the info panel government indicator. See also
1467 government_by_number(...)->sprite.
1468 ****************************************************************************/
1469 const char *get_government_tooltip(void)
1471 static struct astring str
= ASTRING_INIT
;
1475 astr_add_line(&str
, _("Shows your current government:"));
1477 if (NULL
!= client
.conn
.playing
) {
1478 astr_add_line(&str
, "%s",
1479 government_name_for_player(client
.conn
.playing
));
1481 return astr_str(&str
);
1484 /****************************************************************************
1485 Returns a description of the given spaceship. If there is no spaceship
1486 (pship is NULL) then text with dummy values is returned.
1487 ****************************************************************************/
1488 const char *get_spaceship_descr(struct player_spaceship
*pship
)
1490 struct player_spaceship ship
;
1491 static struct astring str
= ASTRING_INIT
;
1497 memset(&ship
, 0, sizeof(ship
));
1500 /* TRANS: spaceship text; should have constant width. */
1501 astr_add_line(&str
, _("Population: %5d"), pship
->population
);
1503 /* TRANS: spaceship text; should have constant width. */
1504 astr_add_line(&str
, _("Support: %5d %%"),
1505 (int) (pship
->support_rate
* 100.0));
1507 /* TRANS: spaceship text; should have constant width. */
1508 astr_add_line(&str
, _("Energy: %5d %%"),
1509 (int) (pship
->energy_rate
* 100.0));
1511 /* TRANS: spaceship text; should have constant width. */
1512 astr_add_line(&str
, PL_("Mass: %5d ton",
1514 pship
->mass
), pship
->mass
);
1516 if (pship
->propulsion
> 0) {
1517 /* TRANS: spaceship text; should have constant width. */
1518 astr_add_line(&str
, _("Travel time: %5.1f years"),
1519 (float) (0.1 * ((int) (pship
->travel_time
* 10.0))));
1521 /* TRANS: spaceship text; should have constant width. */
1522 astr_add_line(&str
, "%s", _("Travel time: N/A "));
1525 /* TRANS: spaceship text; should have constant width. */
1526 astr_add_line(&str
, _("Success prob.: %5d %%"),
1527 (int) (pship
->success_rate
* 100.0));
1529 /* TRANS: spaceship text; should have constant width. */
1530 astr_add_line(&str
, _("Year of arrival: %8s"),
1531 (pship
->state
== SSHIP_LAUNCHED
)
1532 ? textyear((int) (pship
->launch_year
+
1533 (int) pship
->travel_time
))
1536 return astr_str(&str
);
1539 /****************************************************************************
1540 Get the text showing the timeout. This is generally disaplyed on the info
1542 ****************************************************************************/
1543 const char *get_timeout_label_text(void)
1545 static struct astring str
= ASTRING_INIT
;
1549 if (game
.info
.timeout
<= 0) {
1550 astr_add(&str
, "%s", Q_("?timeout:off"));
1552 astr_add(&str
, "%s", format_duration(get_seconds_to_turndone()));
1555 return astr_str(&str
);
1558 /****************************************************************************
1559 Format a duration, in seconds, so it comes up in minutes or hours if
1560 that would be more meaningful.
1562 (7 characters, maximum. Enough for, e.g., "99h 59m".)
1563 ****************************************************************************/
1564 const char *format_duration(int duration
)
1566 static struct astring str
= ASTRING_INIT
;
1573 if (duration
< 60) {
1574 astr_add(&str
, Q_("?seconds:%02ds"), duration
);
1575 } else if (duration
< 3600) { /* < 60 minutes */
1576 astr_add(&str
, Q_("?mins/secs:%02dm %02ds"), duration
/ 60, duration
% 60);
1577 } else if (duration
< 360000) { /* < 100 hours */
1578 astr_add(&str
, Q_("?hrs/mns:%02dh %02dm"), duration
/ 3600, (duration
/ 60) % 60);
1579 } else if (duration
< 8640000) { /* < 100 days */
1580 astr_add(&str
, Q_("?dys/hrs:%02dd %02dh"), duration
/ 86400,
1581 (duration
/ 3600) % 24);
1583 astr_add(&str
, "%s", Q_("?duration:overflow"));
1586 return astr_str(&str
);
1589 /****************************************************************************
1590 Return text giving the ping time for the player. This is generally used
1591 used in the playerdlg. This should only be used in playerdlg_common.c.
1592 ****************************************************************************/
1593 const char *get_ping_time_text(const struct player
*pplayer
)
1595 static struct astring str
= ASTRING_INIT
;
1599 conn_list_iterate(pplayer
->connections
, pconn
) {
1600 if (!pconn
->observer
1601 /* Certainly not needed, but safer. */
1602 && 0 == strcmp(pconn
->username
, pplayer
->username
)) {
1603 if (pconn
->ping_time
!= -1) {
1604 double ping_time_in_ms
= 1000 * pconn
->ping_time
;
1606 astr_add(&str
, _("%6d.%02d ms"), (int) ping_time_in_ms
,
1607 ((int) (ping_time_in_ms
* 100.0)) % 100);
1611 } conn_list_iterate_end
;
1613 return astr_str(&str
);
1616 /****************************************************************************
1617 Return text giving the score of the player. This should only be used
1618 in playerdlg_common.c.
1619 ****************************************************************************/
1620 const char *get_score_text(const struct player
*pplayer
)
1622 static struct astring str
= ASTRING_INIT
;
1626 if (pplayer
->score
.game
> 0
1627 || NULL
== client
.conn
.playing
1628 || pplayer
== client
.conn
.playing
) {
1629 astr_add(&str
, "%d", pplayer
->score
.game
);
1631 astr_add(&str
, "?");
1634 return astr_str(&str
);
1637 /****************************************************************************
1638 Get the title for a "report". This may include the city, economy,
1639 military, trade, player, etc., reports. Some clients may generate the
1640 text themselves to get a better GUI layout.
1641 ****************************************************************************/
1642 const char *get_report_title(const char *report_name
)
1644 static struct astring str
= ASTRING_INIT
;
1645 const struct player
*pplayer
= client_player();
1649 astr_add_line(&str
, "%s", report_name
);
1651 if (pplayer
!= NULL
) {
1652 char buf
[4 * MAX_LEN_NAME
];
1654 /* TRANS: <nation adjective> <government name>.
1655 * E.g. "Polish Republic". */
1656 astr_add_line(&str
, Q_("?nationgovernment:%s %s"),
1657 nation_adjective_for_player(pplayer
),
1658 government_name_for_player(pplayer
));
1660 /* TRANS: Just appending 2 strings, using the correct localized
1662 astr_add_line(&str
, _("%s - %s"),
1663 ruler_title_for_player(pplayer
, buf
, sizeof(buf
)),
1664 textyear(game
.info
.year
));
1666 /* TRANS: "Observer - 1985 AD" */
1667 astr_add_line(&str
, _("Observer - %s"),
1668 textyear(game
.info
.year
));
1670 return astr_str(&str
);
1673 /****************************************************************************
1674 Describing buildings that affect happiness.
1675 ****************************************************************************/
1676 const char *text_happiness_buildings(const struct city
*pcity
)
1680 struct effect_list
*plist
= effect_list_new();
1681 static struct astring str
= ASTRING_INIT
;
1685 astr_add_line(&str
, _("Buildings: "));
1687 get_city_bonus_effects(plist
, pcity
, NULL
, EFT_MAKE_CONTENT
);
1689 effect_list_iterate(plist
, peffect
) {
1690 get_effect_req_text(peffect
, buf
, sizeof(buf
));
1692 /* only one comment to translators needed. */
1693 astr_add(&str
, Q_("?clistmore:, %s"), buf
);
1695 astr_add(&str
, "%s", buf
);
1697 } effect_list_iterate_end
;
1698 effect_list_destroy(plist
);
1701 astr_add(&str
, _("None. "));
1703 astr_add(&str
, "%s", Q_("?clistend:."));
1706 /* Add line breaks after 80 characters. */
1707 astr_break_lines(&str
, 80);
1709 return astr_str(&str
);
1712 /****************************************************************************
1713 Describing nationality effects that affect happiness.
1714 ****************************************************************************/
1715 const char *text_happiness_nationality(const struct city
*pcity
)
1717 static struct astring str
= ASTRING_INIT
;
1722 astr_add_line(&str
, _("Nationality: "));
1724 if (game
.info
.citizen_nationality
) {
1725 if (get_city_bonus(pcity
, EFT_ENEMY_CITIZEN_UNHAPPY_PCT
) > 0) {
1726 struct player
*owner
= city_owner(pcity
);
1728 citizens_foreign_iterate(pcity
, pslot
, nationality
) {
1729 if (pplayers_at_war(owner
, player_slot_get_player(pslot
))) {
1730 enemies
+= nationality
;
1732 } citizens_foreign_iterate_end
;
1735 astr_add(&str
, PL_("%d enemy nationalist", "%d enemy nationalists", enemies
),
1741 astr_add(&str
, _("None. "));
1744 astr_add(&str
, _("Disabled. "));
1747 return astr_str(&str
);
1750 /****************************************************************************
1751 Describing wonders that affect happiness.
1752 ****************************************************************************/
1753 const char *text_happiness_wonders(const struct city
*pcity
)
1757 struct effect_list
*plist
= effect_list_new();
1758 static struct astring str
= ASTRING_INIT
;
1762 astr_add_line(&str
, _("Wonders: "));
1763 get_city_bonus_effects(plist
, pcity
, NULL
, EFT_MAKE_HAPPY
);
1764 get_city_bonus_effects(plist
, pcity
, NULL
, EFT_FORCE_CONTENT
);
1765 get_city_bonus_effects(plist
, pcity
, NULL
, EFT_NO_UNHAPPY
);
1767 effect_list_iterate(plist
, peffect
) {
1768 get_effect_req_text(peffect
, buf
, sizeof(buf
));
1770 /* only one comment to translators needed. */
1771 astr_add(&str
, Q_("?clistmore:, %s"), buf
);
1773 astr_add(&str
, "%s", buf
);
1775 } effect_list_iterate_end
;
1777 effect_list_destroy(plist
);
1780 astr_add(&str
, _("None. "));
1782 astr_add(&str
, "%s", Q_("?clistend:."));
1785 /* Add line breaks after 80 characters. */
1786 astr_break_lines(&str
, 80);
1788 return astr_str(&str
);
1791 /****************************************************************************
1792 Describing city factors that affect happiness.
1793 ****************************************************************************/
1794 const char *text_happiness_cities(const struct city
*pcity
)
1796 struct player
*pplayer
= city_owner(pcity
);
1797 int cities
= city_list_size(pplayer
->cities
);
1798 int content
= get_player_bonus(pplayer
, EFT_CITY_UNHAPPY_SIZE
);
1799 int basis
= get_player_bonus(pplayer
, EFT_EMPIRE_SIZE_BASE
);
1800 int step
= get_player_bonus(pplayer
, EFT_EMPIRE_SIZE_STEP
);
1801 static struct astring str
= ASTRING_INIT
;
1805 if (basis
+step
<= 0) {
1806 /* Special case where penalty is disabled; see
1807 * player_content_citizens(). */
1809 _("Cities: %d total, but no penalty for empire size."),
1812 int excess
= cities
- basis
;
1817 penalty
= 1 + (excess
- 1) / step
;
1826 _("Cities: %d total, %d over threshold of %d cities."),
1827 cities
, excess
, basis
);
1829 /* TRANS: 0-21 content [citizen(s)] ... */
1830 PL_("%d content before penalty.",
1831 "%d content before penalty.",
1835 /* TRANS: 0-21 unhappy citizen(s). */
1836 PL_("%d additional unhappy citizen.",
1837 "%d additional unhappy citizens.",
1842 return astr_str(&str
);
1845 /****************************************************************************
1846 Describing units that affect happiness.
1847 ****************************************************************************/
1848 const char *text_happiness_units(const struct city
*pcity
)
1850 int mlmax
= get_city_bonus(pcity
, EFT_MARTIAL_LAW_MAX
);
1851 int uhcfac
= get_city_bonus(pcity
, EFT_UNHAPPY_FACTOR
);
1852 static struct astring str
= ASTRING_INIT
;
1857 int mleach
= get_city_bonus(pcity
, EFT_MARTIAL_LAW_EACH
);
1859 astr_add_line(&str
, "%s", _("Unlimited martial law in effect."));
1861 astr_add_line(&str
, PL_("%d military unit may impose martial law.",
1862 "Up to %d military units may impose martial "
1863 "law.", mlmax
), mlmax
);
1865 astr_add_line(&str
, PL_("Each military unit makes %d "
1866 "unhappy citizen content.",
1867 "Each military unit makes %d "
1868 "unhappy citizens content.",
1870 } else if (uhcfac
> 0) {
1872 _("Military units in the field may cause unhappiness. "));
1875 _("Military units have no happiness effect. "));
1877 return astr_str(&str
);
1880 /****************************************************************************
1881 Describing luxuries that affect happiness.
1882 ****************************************************************************/
1883 const char *text_happiness_luxuries(const struct city
*pcity
)
1885 static struct astring str
= ASTRING_INIT
;
1890 _("Luxury: %d total."),
1891 pcity
->prod
[O_LUXURY
]);
1892 return astr_str(&str
);