Added translator comment about government overthrown message.
[freeciv.git] / server / report.c
blob1c58aa66e1d7b7cbe222b10013a584c58d8d1f89
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 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <stdio.h>
19 #include <string.h>
21 /* utility */
22 #include "bitvector.h"
23 #include "fciconv.h"
24 #include "fcintl.h"
25 #include "log.h"
26 #include "mem.h"
27 #include "rand.h"
28 #include "support.h"
30 /* common */
31 #include "achievements.h"
32 #include "calendar.h"
33 #include "connection.h"
34 #include "events.h"
35 #include "game.h"
36 #include "government.h"
37 #include "packets.h"
38 #include "player.h"
39 #include "research.h"
40 #include "specialist.h"
41 #include "unitlist.h"
42 #include "version.h"
44 /* server */
45 #include "citytools.h"
46 #include "plrhand.h"
47 #include "score.h"
48 #include "srv_main.h"
50 #include "report.h"
53 /* data needed for logging civ score */
54 struct plrdata_slot {
55 char *name;
58 struct logging_civ_score {
59 FILE *fp;
60 int last_turn;
61 struct plrdata_slot *plrdata;
64 /* Have to be initialized to value less than -1 so it doesn't seem like report was created at
65 * the end of previous turn in the beginning to turn 0. */
66 struct history_report latest_history_report = { -2 };
68 static struct logging_civ_score *score_log = NULL;
70 static void plrdata_slot_init(struct plrdata_slot *plrdata,
71 const char *name);
72 static void plrdata_slot_replace(struct plrdata_slot *plrdata,
73 const char *name);
74 static void plrdata_slot_free(struct plrdata_slot *plrdata);
76 static void page_conn_etype(struct conn_list *dest, const char *caption,
77 const char *headline, const char *lines,
78 enum event_type event);
79 enum historian_type {
80 HISTORIAN_RICHEST=0,
81 HISTORIAN_ADVANCED=1,
82 HISTORIAN_MILITARY=2,
83 HISTORIAN_HAPPIEST=3,
84 HISTORIAN_LARGEST=4};
86 #define HISTORIAN_FIRST HISTORIAN_RICHEST
87 #define HISTORIAN_LAST HISTORIAN_LARGEST
89 static const char *historian_message[]={
90 /* TRANS: year <name> reports ... */
91 N_("%s %s reports on the RICHEST Civilizations in the World."),
92 /* TRANS: year <name> reports ... */
93 N_("%s %s reports on the most ADVANCED Civilizations in the World."),
94 /* TRANS: year <name> reports ... */
95 N_("%s %s reports on the most MILITARIZED Civilizations in the World."),
96 /* TRANS: year <name> reports ... */
97 N_("%s %s reports on the HAPPIEST Civilizations in the World."),
98 /* TRANS: year <name> reports ... */
99 N_("%s %s reports on the LARGEST Civilizations in the World.")
102 static const char *historian_name[]={
103 /* TRANS: [year] <name> [reports ...] */
104 N_("Herodotus"),
105 /* TRANS: [year] <name> [reports ...] */
106 N_("Thucydides"),
107 /* TRANS: [year] <name> [reports ...] */
108 N_("Pliny the Elder"),
109 /* TRANS: [year] <name> [reports ...] */
110 N_("Livy"),
111 /* TRANS: [year] <name> [reports ...] */
112 N_("Toynbee"),
113 /* TRANS: [year] <name> [reports ...] */
114 N_("Gibbon"),
115 /* TRANS: [year] <name> [reports ...] */
116 N_("Ssu-ma Ch'ien"),
117 /* TRANS: [year] <name> [reports ...] */
118 N_("Pan Ku")
121 static const char scorelog_magic[] = "#FREECIV SCORELOG2 ";
123 struct player_score_entry {
124 const struct player *player;
125 int value;
128 struct city_score_entry {
129 struct city *city;
130 int value;
133 static int get_population(const struct player *pplayer);
134 static int get_landarea(const struct player *pplayer);
135 static int get_settledarea(const struct player *pplayer);
136 static int get_research(const struct player *pplayer);
137 static int get_literacy(const struct player *pplayer);
138 static int get_production(const struct player *pplayer);
139 static int get_economics(const struct player *pplayer);
140 static int get_pollution(const struct player *pplayer);
141 static int get_mil_service(const struct player *pplayer);
142 static int get_culture(const struct player *pplayer);
144 static const char *area_to_text(int value);
145 static const char *percent_to_text(int value);
146 static const char *production_to_text(int value);
147 static const char *economics_to_text(int value);
148 static const char *science_to_text(int value);
149 static const char *mil_service_to_text(int value);
150 static const char *pollution_to_text(int value);
151 static const char *culture_to_text(int value);
153 #define GOOD_PLAYER(p) ((p)->is_alive && !is_barbarian(p))
154 #define AI_PLAYER(p) ((p)->ai_controlled)
157 * Describes a row.
159 static struct dem_row {
160 const char key;
161 const char *name;
162 int (*get_value) (const struct player *);
163 const char *(*to_text) (int);
164 bool greater_values_are_better;
165 } rowtable[] = {
166 {'N', N_("Population"), get_population, population_to_text, TRUE },
167 {'A', N_("Land Area"), get_landarea, area_to_text, TRUE },
168 {'S', N_("Settled Area"), get_settledarea, area_to_text, TRUE },
169 {'R', N_("Research Speed"), get_research, science_to_text, TRUE },
170 /* TRANS: How literate people are. */
171 {'L', N_("?ability:Literacy"), get_literacy, percent_to_text, TRUE },
172 {'P', N_("Production"), get_production, production_to_text, TRUE },
173 {'E', N_("Economics"), get_economics, economics_to_text, TRUE },
174 {'M', N_("Military Service"), get_mil_service, mil_service_to_text, FALSE },
175 {'O', N_("Pollution"), get_pollution, pollution_to_text, FALSE },
176 {'C', N_("Culture"), get_culture, culture_to_text, TRUE }
179 /* Demographics columns. */
180 enum dem_flag {
181 DEM_COL_QUANTITY,
182 DEM_COL_RANK,
183 DEM_COL_BEST,
184 DEM_COL_LAST
186 BV_DEFINE(bv_cols, DEM_COL_LAST);
187 static struct dem_col {
188 char key;
189 } coltable[] = {{'q'}, {'r'}, {'b'}}; /* Corresponds to dem_flag enum */
191 /* prime number of entries makes for better scaling */
192 static const char *ranking[] = {
193 /* TRANS: <#>: The <ranking> Poles */
194 N_("%2d: The Supreme %s"),
195 /* TRANS: <#>: The <ranking> Poles */
196 N_("%2d: The Magnificent %s"),
197 /* TRANS: <#>: The <ranking> Poles */
198 N_("%2d: The Great %s"),
199 /* TRANS: <#>: The <ranking> Poles */
200 N_("%2d: The Glorious %s"),
201 /* TRANS: <#>: The <ranking> Poles */
202 N_("%2d: The Excellent %s"),
203 /* TRANS: <#>: The <ranking> Poles */
204 N_("%2d: The Eminent %s"),
205 /* TRANS: <#>: The <ranking> Poles */
206 N_("%2d: The Distinguished %s"),
207 /* TRANS: <#>: The <ranking> Poles */
208 N_("%2d: The Average %s"),
209 /* TRANS: <#>: The <ranking> Poles */
210 N_("%2d: The Mediocre %s"),
211 /* TRANS: <#>: The <ranking> Poles */
212 N_("%2d: The Ordinary %s"),
213 /* TRANS: <#>: The <ranking> Poles */
214 N_("%2d: The Pathetic %s"),
215 /* TRANS: <#>: The <ranking> Poles */
216 N_("%2d: The Useless %s"),
217 /* TRANS: <#>: The <ranking> Poles */
218 N_("%2d: The Valueless %s"),
219 /* TRANS: <#>: The <ranking> Poles */
220 N_("%2d: The Worthless %s"),
221 /* TRANS: <#>: The <ranking> Poles */
222 N_("%2d: The Wretched %s"),
225 /**************************************************************************
226 Compare two player score entries. Used as callback for qsort.
227 **************************************************************************/
228 static int secompare(const void *a, const void *b)
230 return (((const struct player_score_entry *)b)->value -
231 ((const struct player_score_entry *)a)->value);
234 /**************************************************************************
235 Construct Historian Report
236 **************************************************************************/
237 static void historian_generic(struct history_report *report,
238 enum historian_type which_news)
240 int i, j = 0, rank = 0;
241 struct player_score_entry size[player_count()];
243 report->turn = game.info.turn;
244 players_iterate(pplayer) {
245 if (GOOD_PLAYER(pplayer)) {
246 switch(which_news) {
247 case HISTORIAN_RICHEST:
248 size[j].value = pplayer->economic.gold;
249 break;
250 case HISTORIAN_ADVANCED:
251 size[j].value
252 = pplayer->score.techs + research_get(pplayer)->future_tech;
253 break;
254 case HISTORIAN_MILITARY:
255 size[j].value = pplayer->score.units;
256 break;
257 case HISTORIAN_HAPPIEST:
258 size[j].value =
259 (((pplayer->score.happy - pplayer->score.unhappy) * 1000) /
260 (1 + total_player_citizens(pplayer)));
261 break;
262 case HISTORIAN_LARGEST:
263 size[j].value = total_player_citizens(pplayer);
264 break;
266 size[j].player = pplayer;
267 j++;
268 } /* else the player is dead or barbarian or observer */
269 } players_iterate_end;
271 qsort(size, j, sizeof(size[0]), secompare);
272 report->body[0] = '\0';
273 for (i = 0; i < j; i++) {
274 if (i > 0 && size[i].value < size[i - 1].value) {
275 /* since i < j, only top entry reigns Supreme */
276 rank = ((i * ARRAY_SIZE(ranking)) / j) + 1;
278 if (rank >= ARRAY_SIZE(ranking)) {
279 /* clamp to final entry */
280 rank = ARRAY_SIZE(ranking) - 1;
282 cat_snprintf(report->body, REPORT_BODYSIZE,
283 _(ranking[rank]),
284 i + 1,
285 nation_plural_for_player(size[i].player));
286 fc_strlcat(report->body, "\n", REPORT_BODYSIZE);
288 fc_snprintf(report->title, REPORT_TITLESIZE, _(historian_message[which_news]),
289 calendar_text(),
290 _(historian_name[fc_rand(ARRAY_SIZE(historian_name))]));
293 /**************************************************************************
294 Send history report of this turn.
295 **************************************************************************/
296 void send_current_history_report(struct conn_list *dest)
298 /* History report is actually constructed at the end of previous turn. */
299 if (latest_history_report.turn >= game.info.turn - 1) {
300 page_conn_etype(dest, _("Historian Publishes!"),
301 latest_history_report.title, latest_history_report.body,
302 E_BROADCAST_REPORT);
306 /**************************************************************************
307 Returns the number of wonders the given city has.
308 **************************************************************************/
309 static int nr_wonders(struct city *pcity)
311 int result = 0;
313 city_built_iterate(pcity, i) {
314 if (is_great_wonder(i)) {
315 result++;
317 } city_built_iterate_end;
319 return result;
322 /**************************************************************************
323 Send report listing the "best" 5 cities in the world.
324 **************************************************************************/
325 void report_top_five_cities(struct conn_list *dest)
327 const int NUM_BEST_CITIES = 5;
328 /* a wonder equals WONDER_FACTOR citizen */
329 const int WONDER_FACTOR = 5;
330 struct city_score_entry size[NUM_BEST_CITIES];
331 int i;
332 char buffer[4096];
334 for (i = 0; i < NUM_BEST_CITIES; i++) {
335 size[i].value = 0;
336 size[i].city = NULL;
339 shuffled_players_iterate(pplayer) {
340 city_list_iterate(pplayer->cities, pcity) {
341 int value_of_pcity = city_size_get(pcity)
342 + nr_wonders(pcity) * WONDER_FACTOR;
344 if (value_of_pcity > size[NUM_BEST_CITIES - 1].value) {
345 size[NUM_BEST_CITIES - 1].value = value_of_pcity;
346 size[NUM_BEST_CITIES - 1].city = pcity;
347 qsort(size, NUM_BEST_CITIES, sizeof(size[0]), secompare);
349 } city_list_iterate_end;
350 } shuffled_players_iterate_end;
352 buffer[0] = '\0';
353 for (i = 0; i < NUM_BEST_CITIES; i++) {
354 int wonders;
356 if (!size[i].city) {
358 * pcity may be NULL if there are less then NUM_BEST_CITIES in
359 * the whole game.
361 break;
364 if (player_count() > team_count()) {
365 /* There exists a team with more than one member. */
366 char team_name[2 * MAX_LEN_NAME];
368 team_pretty_name(city_owner(size[i].city)->team, team_name,
369 sizeof(team_name));
370 cat_snprintf(buffer, sizeof(buffer),
371 /* TRANS:"The French City of Lyon (team 3) of size 18". */
372 _("%2d: The %s City of %s (%s) of size %d, "), i + 1,
373 nation_adjective_for_player(city_owner(size[i].city)),
374 city_name_get(size[i].city), team_name,
375 city_size_get(size[i].city));
376 } else {
377 cat_snprintf(buffer, sizeof(buffer),
378 _("%2d: The %s City of %s of size %d, "), i + 1,
379 nation_adjective_for_player(city_owner(size[i].city)),
380 city_name_get(size[i].city), city_size_get(size[i].city));
383 wonders = nr_wonders(size[i].city);
384 if (wonders == 0) {
385 cat_snprintf(buffer, sizeof(buffer), _("with no wonders\n"));
386 } else {
387 cat_snprintf(buffer, sizeof(buffer),
388 PL_("with %d wonder\n", "with %d wonders\n", wonders),
389 wonders);}
391 page_conn(dest, _("Traveler's Report:"),
392 _("The Five Greatest Cities in the World!"), buffer);
395 /**************************************************************************
396 Send report listing all built and destroyed wonders, and wonders
397 currently being built.
398 **************************************************************************/
399 void report_wonders_of_the_world(struct conn_list *dest)
401 char buffer[4096];
403 buffer[0] = '\0';
405 improvement_iterate(i) {
406 if (is_great_wonder(i)) {
407 struct city *pcity = city_from_great_wonder(i);
409 if (pcity) {
410 if (player_count() > team_count()) {
411 /* There exists a team with more than one member. */
412 char team_name[2 * MAX_LEN_NAME];
414 team_pretty_name(city_owner(pcity)->team, team_name,
415 sizeof(team_name));
416 cat_snprintf(buffer, sizeof(buffer),
417 /* TRANS: "Colossus in Rhodes (Greek, team 2)". */
418 _("%s in %s (%s, %s)\n"),
419 city_improvement_name_translation(pcity, i),
420 city_name_get(pcity),
421 nation_adjective_for_player(city_owner(pcity)),
422 team_name);
423 } else {
424 cat_snprintf(buffer, sizeof(buffer), _("%s in %s (%s)\n"),
425 city_improvement_name_translation(pcity, i),
426 city_name_get(pcity),
427 nation_adjective_for_player(city_owner(pcity)));
429 } else if (great_wonder_is_destroyed(i)) {
430 cat_snprintf(buffer, sizeof(buffer), _("%s has been DESTROYED\n"),
431 improvement_name_translation(i));
434 } improvement_iterate_end;
436 improvement_iterate(i) {
437 if (is_great_wonder(i)) {
438 players_iterate(pplayer) {
439 city_list_iterate(pplayer->cities, pcity) {
440 if (VUT_IMPROVEMENT == pcity->production.kind
441 && pcity->production.value.building == i) {
442 if (player_count() > team_count()) {
443 /* There exists a team with more than one member. */
444 char team_name[2 * MAX_LEN_NAME];
446 team_pretty_name(city_owner(pcity)->team, team_name,
447 sizeof(team_name));
448 cat_snprintf(buffer, sizeof(buffer),
449 /* TRANS: "([...] (Roman, team 4))". */
450 _("(building %s in %s (%s, %s))\n"),
451 improvement_name_translation(i), city_name_get(pcity),
452 nation_adjective_for_player(pplayer), team_name);
453 } else {
454 cat_snprintf(buffer, sizeof(buffer),
455 _("(building %s in %s (%s))\n"),
456 improvement_name_translation(i), city_name_get(pcity),
457 nation_adjective_for_player(pplayer));
460 } city_list_iterate_end;
461 } players_iterate_end;
463 } improvement_iterate_end;
465 page_conn(dest, _("Traveler's Report:"),
466 _("Wonders of the World"), buffer);
469 /****************************************************************************
470 Helper functions which return the value for the given player.
471 ****************************************************************************/
473 /****************************************************************************
474 Population of player
475 ****************************************************************************/
476 static int get_population(const struct player *pplayer)
478 return pplayer->score.population;
481 /****************************************************************************
482 Number of citizen units of player
483 ****************************************************************************/
484 static int get_pop(const struct player *pplayer)
486 return total_player_citizens(pplayer);
489 /****************************************************************************
490 Number of citizens of player
491 ****************************************************************************/
492 static int get_real_pop(const struct player *pplayer)
494 return 1000 * get_pop(pplayer);
497 /****************************************************************************
498 Land area controlled by player
499 ****************************************************************************/
500 static int get_landarea(const struct player *pplayer)
502 return pplayer->score.landarea;
505 /****************************************************************************
506 Area settled.
507 ****************************************************************************/
508 static int get_settledarea(const struct player *pplayer)
510 return pplayer->score.settledarea;
513 /****************************************************************************
514 Research speed
515 ****************************************************************************/
516 static int get_research(const struct player *pplayer)
518 return pplayer->score.techout;
521 /****************************************************************************
522 Literacy score calculated one way. See also get_literacy2() for
523 alternative way.
524 ****************************************************************************/
525 static int get_literacy(const struct player *pplayer)
527 int pop = civ_population(pplayer);
529 if (pop <= 0) {
530 return 0;
531 } else if (pop >= 10000) {
532 return pplayer->score.literacy / (pop / 100);
533 } else {
534 return (pplayer->score.literacy * 100) / pop;
538 /****************************************************************************
539 Production of player
540 ****************************************************************************/
541 static int get_production(const struct player *pplayer)
543 return pplayer->score.mfg;
546 /****************************************************************************
547 BNP of player
548 ****************************************************************************/
549 static int get_economics(const struct player *pplayer)
551 return pplayer->score.bnp;
554 /****************************************************************************
555 Pollution of player
556 ****************************************************************************/
557 static int get_pollution(const struct player *pplayer)
559 return pplayer->score.pollution;
562 /****************************************************************************
563 Military service length
564 ****************************************************************************/
565 static int get_mil_service(const struct player *pplayer)
567 return (pplayer->score.units * 5000) / (10 + civ_population(pplayer));
570 /****************************************************************************
571 Number of cities
572 ****************************************************************************/
573 static int get_cities(const struct player *pplayer)
575 return pplayer->score.cities;
578 /****************************************************************************
579 Number of techs
580 ****************************************************************************/
581 static int get_techs(const struct player *pplayer)
583 return pplayer->score.techs;
586 /****************************************************************************
587 Number of military units
588 ****************************************************************************/
589 static int get_munits(const struct player *pplayer)
591 int result = 0;
593 /* count up military units */
594 unit_list_iterate(pplayer->units, punit) {
595 if (is_military_unit(punit)) {
596 result++;
598 } unit_list_iterate_end;
600 return result;
603 /****************************************************************************
604 Number of city building units.
605 ****************************************************************************/
606 static int get_settlers(const struct player *pplayer)
608 int result = 0;
610 if (!game.scenario.prevent_new_cities) {
611 /* count up settlers */
612 unit_list_iterate(pplayer->units, punit) {
613 if (unit_has_type_flag(punit, UTYF_CITIES)) {
614 result++;
616 } unit_list_iterate_end;
619 return result;
622 /****************************************************************************
623 Wonder scpre
624 ****************************************************************************/
625 static int get_wonders(const struct player *pplayer)
627 return pplayer->score.wonders;
630 /****************************************************************************
631 Technology output
632 ****************************************************************************/
633 static int get_techout(const struct player *pplayer)
635 return pplayer->score.techout;
638 /****************************************************************************
639 Literacy score calculated one way. See also get_literacy() to see
640 alternative way.
641 ****************************************************************************/
642 static int get_literacy2(const struct player *pplayer)
644 return pplayer->score.literacy;
647 /****************************************************************************
648 Spaceship score
649 ****************************************************************************/
650 static int get_spaceship(const struct player *pplayer)
652 return pplayer->score.spaceship;
655 /****************************************************************************
656 Number of units built
657 ****************************************************************************/
658 static int get_units_built(const struct player *pplayer)
660 return pplayer->score.units_built;
663 /****************************************************************************
664 Number of units killed
665 ****************************************************************************/
666 static int get_units_killed(const struct player *pplayer)
668 return pplayer->score.units_killed;
671 /****************************************************************************
672 Number of units lost
673 ****************************************************************************/
674 static int get_units_lost(const struct player *pplayer)
676 return pplayer->score.units_lost;
679 /****************************************************************************
680 Amount of gold.
681 ****************************************************************************/
682 static int get_gold(const struct player *pplayer)
684 return pplayer->economic.gold;
687 /****************************************************************************
688 Tax rate
689 ****************************************************************************/
690 static int get_taxrate(const struct player *pplayer)
692 return pplayer->economic.tax;
695 /****************************************************************************
696 Science rate
697 ****************************************************************************/
698 static int get_scirate(const struct player *pplayer)
700 return pplayer->economic.science;
703 /****************************************************************************
704 Luxury rate
705 ****************************************************************************/
706 static int get_luxrate(const struct player *pplayer)
708 return pplayer->economic.luxury;
711 /****************************************************************************
712 Number of rioting cities
713 ****************************************************************************/
714 static int get_riots(const struct player *pplayer)
716 int result = 0;
718 city_list_iterate(pplayer->cities, pcity) {
719 if (pcity->anarchy > 0) {
720 result++;
722 } city_list_iterate_end;
724 return result;
727 /****************************************************************************
728 Number of happy citizens
729 ****************************************************************************/
730 static int get_happypop(const struct player *pplayer)
732 return pplayer->score.happy;
735 /****************************************************************************
736 Number of content citizens
737 ****************************************************************************/
738 static int get_contentpop(const struct player *pplayer)
740 return pplayer->score.content;
743 /****************************************************************************
744 Number of unhappy citizens
745 ****************************************************************************/
746 static int get_unhappypop(const struct player *pplayer)
748 return pplayer->score.unhappy;
751 /****************************************************************************
752 Number of specialists.
753 ****************************************************************************/
754 static int get_specialists(const struct player *pplayer)
756 int count = 0;
758 specialist_type_iterate(sp) {
759 count += pplayer->score.specialists[sp];
760 } specialist_type_iterate_end;
762 return count;
765 /****************************************************************************
766 Current government
767 ****************************************************************************/
768 static int get_gov(const struct player *pplayer)
770 return (int) government_number(government_of_player(pplayer));
773 /****************************************************************************
774 Total corruption
775 ****************************************************************************/
776 static int get_corruption(const struct player *pplayer)
778 int result = 0;
780 city_list_iterate(pplayer->cities, pcity) {
781 result += pcity->waste[O_TRADE];
782 } city_list_iterate_end;
784 return result;
787 /****************************************************************************
788 Total score
789 ****************************************************************************/
790 static int get_total_score(const struct player *pplayer)
792 return pplayer->score.game;
795 /****************************************************************************
796 Culture score
797 ****************************************************************************/
798 static int get_culture(const struct player *pplayer)
800 return pplayer->score.culture;
803 /**************************************************************************
804 Construct string containing value and its unit.
805 **************************************************************************/
806 static const char *value_units(int val, const char *uni)
808 static char buf[64];
810 if (fc_snprintf(buf, sizeof(buf), "%s%s", int_to_text(val), uni) == -1) {
811 log_error("String truncated in value_units()!");
814 return buf;
817 /**************************************************************************
818 Helper functions which transform the given value to a string
819 depending on the unit.
820 **************************************************************************/
821 static const char *area_to_text(int value)
823 /* TRANS: abbreviation of "square miles" */
824 return value_units(value, PL_(" sq. mi.", " sq. mi.", value));
827 /**************************************************************************
828 Construct string containing value followed by '%'. So value is already
829 considered to be in units of 1/100.
830 **************************************************************************/
831 static const char *percent_to_text(int value)
833 return value_units(value, "%");
836 /**************************************************************************
837 Construct string containing value followed by unit suitable for
838 production stats.
839 **************************************************************************/
840 static const char *production_to_text(int value)
842 int clip = MAX(0, value);
843 /* TRANS: "M tons" = million tons, so always plural */
844 return value_units(clip, PL_(" M tons", " M tons", clip));
847 /**************************************************************************
848 Construct string containing value followed by unit suitable for
849 economics stats.
850 **************************************************************************/
851 static const char *economics_to_text(int value)
853 /* TRANS: "M goods" = million goods, so always plural */
854 return value_units(value, PL_(" M goods", " M goods", value));
857 /**************************************************************************
858 Construct string containing value followed by unit suitable for
859 science stats.
860 **************************************************************************/
861 static const char *science_to_text(int value)
863 return value_units(value, PL_(" bulb", " bulbs", value));
866 /**************************************************************************
867 Construct string containing value followed by unit suitable for
868 military service stats.
869 **************************************************************************/
870 static const char *mil_service_to_text(int value)
872 return value_units(value, PL_(" month", " months", value));
875 /**************************************************************************
876 Construct string containing value followed by unit suitable for
877 pollution stats.
878 **************************************************************************/
879 static const char *pollution_to_text(int value)
881 return value_units(value, PL_(" ton", " tons", value));
884 /**************************************************************************
885 Construct string containing value followed by unit suitable for
886 culture stats.
887 **************************************************************************/
888 static const char *culture_to_text(int value)
890 /* TRANS: Unit(s) of culture */
891 return value_units(value, PL_(" act", " acts", value));
894 /**************************************************************************
895 Construct one demographics line.
896 **************************************************************************/
897 static void dem_line_item(char *outptr, size_t out_size,
898 struct player *pplayer, struct dem_row *prow,
899 bv_cols selcols)
901 if (NULL != pplayer && BV_ISSET(selcols, DEM_COL_QUANTITY)) {
902 const char *text = prow->to_text(prow->get_value(pplayer));
904 cat_snprintf(outptr, out_size, " %s", text);
905 cat_snprintf(outptr, out_size, "%*s",
906 18 - (int) get_internal_string_length(text), "");
909 if (NULL != pplayer && BV_ISSET(selcols, DEM_COL_RANK)) {
910 int basis = prow->get_value(pplayer);
911 int place = 1;
913 players_iterate(other) {
914 if (GOOD_PLAYER(other)
915 && ((prow->greater_values_are_better
916 && prow->get_value(other) > basis)
917 || (!prow->greater_values_are_better
918 && prow->get_value(other) < basis))) {
919 place++;
921 } players_iterate_end;
923 cat_snprintf(outptr, out_size, _("(ranked %d)"), place);
926 if (NULL == pplayer || BV_ISSET(selcols, DEM_COL_BEST)) {
927 struct player *best_player = pplayer;
928 int best_value = NULL != pplayer ? prow->get_value(pplayer) : 0;
930 players_iterate(other) {
931 if (GOOD_PLAYER(other)) {
932 int value = prow->get_value(other);
934 if (!best_player
935 || (prow->greater_values_are_better && value > best_value)
936 || (!prow->greater_values_are_better && value < best_value)) {
937 best_player = other;
938 best_value = value;
941 } players_iterate_end;
943 if (NULL == pplayer
944 || (player_has_embassy(pplayer, best_player)
945 && (pplayer != best_player))) {
946 cat_snprintf(outptr, out_size, " %s: %s",
947 nation_plural_for_player(best_player),
948 prow->to_text(prow->get_value(best_player)));
953 /*************************************************************************
954 Verify that a given demography string is valid. See
955 game.demography. If the string is not valid the index of the _first_
956 invalid character is return as 'error'.
958 Other settings callback functions are in settings.c, but this one uses
959 static values from this file so it's done separately.
960 *************************************************************************/
961 bool is_valid_demography(const char *demography, int *error)
963 int len = strlen(demography), i;
965 /* We check each character individually to see if it's valid. This
966 * does not check for duplicate entries. */
967 for (i = 0; i < len; i++) {
968 bool found = FALSE;
969 int j;
971 /* See if the character is a valid column label. */
972 for (j = 0; j < DEM_COL_LAST; j++) {
973 if (demography[i] == coltable[j].key) {
974 found = TRUE;
975 break;
979 if (found) {
980 continue;
983 /* See if the character is a valid row label. */
984 for (j = 0; j < ARRAY_SIZE(rowtable); j++) {
985 if (demography[i] == rowtable[j].key) {
986 found = TRUE;
987 break;
991 if (!found) {
992 if (error != NULL) {
993 (*error) = i;
995 /* The character is invalid. */
996 return FALSE;
1000 /* Looks like all characters were valid. */
1001 return TRUE;
1004 /*************************************************************************
1005 Send demographics report; what gets reported depends on value of
1006 demographics server option.
1007 *************************************************************************/
1008 void report_demographics(struct connection *pconn)
1010 char civbuf[1024];
1011 char buffer[4096];
1012 unsigned int i;
1013 bool anyrows;
1014 bv_cols selcols;
1015 int numcols = 0;
1016 struct player *pplayer = pconn->playing;
1018 BV_CLR_ALL(selcols);
1019 fc_assert_ret(ARRAY_SIZE(coltable) == DEM_COL_LAST);
1020 for (i = 0; i < DEM_COL_LAST; i++) {
1021 if (strchr(game.server.demography, coltable[i].key)) {
1022 BV_SET(selcols, i);
1023 numcols++;
1027 anyrows = FALSE;
1028 for (i = 0; i < ARRAY_SIZE(rowtable); i++) {
1029 if (strchr(game.server.demography, rowtable[i].key)) {
1030 anyrows = TRUE;
1031 break;
1035 if ((!pconn->observer && !pplayer)
1036 || (pplayer && !pplayer->is_alive)
1037 || !anyrows
1038 || numcols == 0) {
1039 page_conn(pconn->self, _("Demographics Report:"),
1040 _("Sorry, the Demographics report is unavailable."), "");
1041 return;
1044 if (pplayer) {
1045 fc_snprintf(civbuf, sizeof(civbuf), _("%s %s (%s)"),
1046 nation_adjective_for_player(pplayer),
1047 government_name_for_player(pplayer),
1048 calendar_text());
1049 } else {
1050 civbuf[0] = '\0';
1053 buffer[0] = '\0';
1054 for (i = 0; i < ARRAY_SIZE(rowtable); i++) {
1055 if (strchr(game.server.demography, rowtable[i].key)) {
1056 const char *name = Q_(rowtable[i].name);
1058 cat_snprintf(buffer, sizeof(buffer), "%s", name);
1059 cat_snprintf(buffer, sizeof(buffer), "%*s",
1060 18 - (int) get_internal_string_length(name), "");
1061 dem_line_item(buffer, sizeof(buffer), pplayer, &rowtable[i], selcols);
1062 sz_strlcat(buffer, "\n");
1066 page_conn(pconn->self, _("Demographics Report:"), civbuf, buffer);
1069 /*************************************************************************
1070 Send achievements list
1071 *************************************************************************/
1072 void report_achievements(struct connection *pconn)
1074 char civbuf[1024];
1075 char buffer[4096];
1076 struct player *pplayer = pconn->playing;
1078 if (pplayer == NULL) {
1079 return;
1082 fc_snprintf(civbuf, sizeof(civbuf), _("%s %s (%s)"),
1083 nation_adjective_for_player(pplayer),
1084 government_name_for_player(pplayer),
1085 calendar_text());
1087 buffer[0] = '\0';
1089 achievements_iterate(pach) {
1090 if (achievement_player_has(pach, pplayer)) {
1091 cat_snprintf(buffer, sizeof(buffer), "%s\n",
1092 achievement_name_translation(pach));
1094 } achievements_iterate_end;
1096 page_conn(pconn->self, _("Achievements List:"), civbuf, buffer);
1099 /**************************************************************************
1100 Allocate and initialize plrdata slot.
1101 **************************************************************************/
1102 static void plrdata_slot_init(struct plrdata_slot *plrdata,
1103 const char *name)
1105 fc_assert_ret(plrdata->name == NULL);
1107 plrdata->name = fc_calloc(MAX_LEN_NAME, sizeof(plrdata->name));
1108 plrdata_slot_replace(plrdata, name);
1111 /**************************************************************************
1112 Replace plrdata slot with new one named according to input parameter.
1113 **************************************************************************/
1114 static void plrdata_slot_replace(struct plrdata_slot *plrdata,
1115 const char *name)
1117 fc_assert_ret(plrdata->name != NULL);
1119 fc_strlcpy(plrdata->name, name, MAX_LEN_NAME);
1122 /**************************************************************************
1123 Free resources allocated for plrdata slot.
1124 **************************************************************************/
1125 static void plrdata_slot_free(struct plrdata_slot *plrdata)
1127 if (plrdata->name != NULL) {
1128 free(plrdata->name);
1129 plrdata->name = NULL;
1133 /**************************************************************************
1134 Reads the whole file denoted by fp. Sets last_turn and id to the
1135 values contained in the file. Returns the player_names indexed by
1136 player_no at the end of the log file.
1138 Returns TRUE iff the file had read successfully.
1139 **************************************************************************/
1140 static bool scan_score_log(char *id)
1142 int line_nr, turn, plr_no, spaces;
1143 struct plrdata_slot *plrdata;
1144 char plr_name[MAX_LEN_NAME], line[80], *ptr;
1146 fc_assert_ret_val(score_log != NULL, FALSE);
1147 fc_assert_ret_val(score_log->fp != NULL, FALSE);
1149 score_log->last_turn = -1;
1150 id[0] = '\0';
1152 for (line_nr = 1;; line_nr++) {
1153 if (!fgets(line, sizeof(line), score_log->fp)) {
1154 if (feof(score_log->fp) != 0) {
1155 break;
1157 log_error("[%s:-] Can't read scorelog file header!",
1158 game.server.scorefile);
1159 return FALSE;
1162 ptr = strchr(line, '\n');
1163 if (!ptr) {
1164 log_error("[%s:%d] Line too long!", game.server.scorefile, line_nr);
1165 return FALSE;
1167 *ptr = '\0';
1169 if (line_nr == 1) {
1170 if (strncmp(line, scorelog_magic, strlen(scorelog_magic)) != 0) {
1171 log_error("[%s:%d] Bad file magic!", game.server.scorefile, line_nr);
1172 return FALSE;
1176 if (strncmp(line, "id ", strlen("id ")) == 0) {
1177 if (strlen(id) > 0) {
1178 log_error("[%s:%d] Multiple ID entries!", game.server.scorefile,
1179 line_nr);
1180 return FALSE;
1182 fc_strlcpy(id, line + strlen("id "), MAX_LEN_GAME_IDENTIFIER);
1183 if (strcmp(id, server.game_identifier) != 0) {
1184 log_error("[%s:%d] IDs don't match! game='%s' scorelog='%s'",
1185 game.server.scorefile, line_nr, server.game_identifier,
1186 id);
1187 return FALSE;
1191 if (strncmp(line, "turn ", strlen("turn ")) == 0) {
1192 if (sscanf(line + strlen("turn "), "%d", &turn) != 1) {
1193 log_error("[%s:%d] Bad line (turn)!", game.server.scorefile,
1194 line_nr);
1195 return FALSE;
1198 fc_assert_ret_val(turn > score_log->last_turn, FALSE);
1199 score_log->last_turn = turn;
1202 if (strncmp(line, "addplayer ", strlen("addplayer ")) == 0) {
1203 if (3 != sscanf(line + strlen("addplayer "), "%d %d %s",
1204 &turn, &plr_no, plr_name)) {
1205 log_error("[%s:%d] Bad line (addplayer)!",
1206 game.server.scorefile, line_nr);
1207 return FALSE;
1210 /* Now get the complete player name if there are several parts. */
1211 ptr = line + strlen("addplayer ");
1212 spaces = 0;
1213 while (*ptr != '\0' && spaces < 2) {
1214 if (*ptr == ' ') {
1215 spaces++;
1217 ptr++;
1219 fc_snprintf(plr_name, sizeof(plr_name), "%s", ptr);
1220 log_debug("add player '%s' (from line %d: '%s')", plr_name, line_nr,
1221 line);
1223 if (0 > plr_no || plr_no >= player_slot_count()) {
1224 log_error("[%s:%d] Invalid player number: %d!",
1225 game.server.scorefile, line_nr, plr_no);
1226 return FALSE;
1229 plrdata = score_log->plrdata + plr_no;
1230 if (plrdata->name != NULL) {
1231 log_error("[%s:%d] Two names for one player (id %d)!",
1232 game.server.scorefile, line_nr, plr_no);
1233 return FALSE;
1236 plrdata_slot_init(plrdata, plr_name);
1239 if (strncmp(line, "delplayer ", strlen("delplayer ")) == 0) {
1240 if (2 != sscanf(line + strlen("delplayer "), "%d %d",
1241 &turn, &plr_no)) {
1242 log_error("[%s:%d] Bad line (delplayer)!",
1243 game.server.scorefile, line_nr);
1244 return FALSE;
1247 if (!(plr_no >= 0 && plr_no < player_slot_count())) {
1248 log_error("[%s:%d] Invalid player number: %d!",
1249 game.server.scorefile, line_nr, plr_no);
1250 return FALSE;
1253 plrdata = score_log->plrdata + plr_no;
1254 if (plrdata->name == NULL) {
1255 log_error("[%s:%d] Trying to remove undefined player (id %d)!",
1256 game.server.scorefile, line_nr, plr_no);
1257 return FALSE;
1260 plrdata_slot_free(plrdata);
1264 if (score_log->last_turn == -1) {
1265 log_error("[%s:-] Scorelog contains no turn!", game.server.scorefile);
1266 return FALSE;
1269 if (strlen(id) == 0) {
1270 log_error("[%s:-] Scorelog contains no ID!", game.server.scorefile);
1271 return FALSE;
1274 if (score_log->last_turn + 1 != game.info.turn) {
1275 log_error("[%s:-] Scorelog doesn't match savegame!",
1276 game.server.scorefile);
1277 return FALSE;
1280 return TRUE;
1283 /**************************************************************************
1284 Initialize score logging system
1285 **************************************************************************/
1286 void log_civ_score_init(void)
1288 if (score_log != NULL) {
1289 return;
1292 score_log = fc_calloc(1, sizeof(*score_log));
1293 score_log->fp = NULL;
1294 score_log->last_turn = -1;
1295 score_log->plrdata = fc_calloc(player_slot_count(),
1296 sizeof(*score_log->plrdata));
1297 player_slots_iterate(pslot) {
1298 struct plrdata_slot *plrdata = score_log->plrdata
1299 + player_slot_index(pslot);
1300 plrdata->name = NULL;
1301 } player_slots_iterate_end;
1303 latest_history_report.turn = -2;
1306 /**************************************************************************
1307 Free resources allocated for score logging system
1308 **************************************************************************/
1309 void log_civ_score_free(void)
1311 if (!score_log) {
1312 /* nothing to do */
1313 return;
1316 if (score_log->fp) {
1317 fclose(score_log->fp);
1318 score_log->fp = NULL;
1321 if (score_log->plrdata) {
1322 player_slots_iterate(pslot) {
1323 struct plrdata_slot *plrdata = score_log->plrdata
1324 + player_slot_index(pslot);
1325 if (plrdata->name != NULL) {
1326 free(plrdata->name);
1328 } player_slots_iterate_end;
1329 free(score_log->plrdata);
1332 free(score_log);
1333 score_log = NULL;
1336 /**************************************************************************
1337 Create a log file of the civilizations so you can see what was happening.
1338 **************************************************************************/
1339 void log_civ_score_now(void)
1341 enum { SL_CREATE, SL_APPEND, SL_UNSPEC } oper = SL_UNSPEC;
1342 char id[MAX_LEN_GAME_IDENTIFIER];
1343 int i = 0;
1345 /* Add new tags only at end of this list. Maintaining the order of
1346 * old tags is critical. */
1347 static const struct {
1348 char *name;
1349 int (*get_value) (const struct player *);
1350 } score_tags[] = {
1351 {"pop", get_pop},
1352 {"bnp", get_economics},
1353 {"mfg", get_production},
1354 {"cities", get_cities},
1355 {"techs", get_techs},
1356 {"munits", get_munits},
1357 {"settlers", get_settlers}, /* "original" tags end here */
1359 {"wonders", get_wonders},
1360 {"techout", get_techout},
1361 {"landarea", get_landarea},
1362 {"settledarea", get_settledarea},
1363 {"pollution", get_pollution},
1364 {"literacy", get_literacy2},
1365 {"spaceship", get_spaceship}, /* new 1.8.2 tags end here */
1367 {"gold", get_gold},
1368 {"taxrate", get_taxrate},
1369 {"scirate", get_scirate},
1370 {"luxrate", get_luxrate},
1371 {"riots", get_riots},
1372 {"happypop", get_happypop},
1373 {"contentpop", get_contentpop},
1374 {"unhappypop", get_unhappypop},
1375 {"specialists", get_specialists},
1376 {"gov", get_gov},
1377 {"corruption", get_corruption}, /* new 1.11.5 tags end here */
1379 {"score", get_total_score}, /* New 2.1.10 tag end here. */
1381 {"unitsbuilt", get_units_built}, /* New tags since 2.3.0. */
1382 {"unitskilled", get_units_killed},
1383 {"unitslost", get_units_lost},
1385 {"culture", get_culture} /* New tag in 2.6.0. */
1388 if (!game.server.scorelog) {
1389 return;
1392 if (!score_log) {
1393 return;
1396 if (!score_log->fp) {
1397 if (game.info.year == GAME_START_YEAR) {
1398 oper = SL_CREATE;
1399 } else {
1400 score_log->fp = fc_fopen(game.server.scorefile, "r");
1401 if (!score_log->fp) {
1402 oper = SL_CREATE;
1403 } else {
1404 if (!scan_score_log(id)) {
1405 goto log_civ_score_disable;
1407 oper = SL_APPEND;
1409 fclose(score_log->fp);
1410 score_log->fp = NULL;
1414 switch (oper) {
1415 case SL_CREATE:
1416 score_log->fp = fc_fopen(game.server.scorefile, "w");
1417 if (!score_log->fp) {
1418 log_error("Can't open scorelog file '%s' for creation!",
1419 game.server.scorefile);
1420 goto log_civ_score_disable;
1422 fprintf(score_log->fp, "%s%s\n", scorelog_magic, VERSION_STRING);
1423 fprintf(score_log->fp,
1424 "\n"
1425 "# For a specification of the format of this see doc/README.scorelog or \n"
1426 "# <http://svn.gna.org/viewcvs/freeciv/trunk/doc/README.scorelog?view=auto>.\n"
1427 "\n");
1429 fprintf(score_log->fp, "id %s\n", server.game_identifier);
1430 for (i = 0; i < ARRAY_SIZE(score_tags); i++) {
1431 fprintf(score_log->fp, "tag %d %s\n", i, score_tags[i].name);
1433 break;
1434 case SL_APPEND:
1435 score_log->fp = fc_fopen(game.server.scorefile, "a");
1436 if (!score_log->fp) {
1437 log_error("Can't open scorelog file '%s' for appending!",
1438 game.server.scorefile);
1439 goto log_civ_score_disable;
1441 break;
1442 default:
1443 log_error("[%s] bad operation %d", __FUNCTION__, (int) oper);
1444 goto log_civ_score_disable;
1448 if (game.info.turn > score_log->last_turn) {
1449 fprintf(score_log->fp, "turn %d %d %s\n", game.info.turn, game.info.year,
1450 calendar_text());
1451 score_log->last_turn = game.info.turn;
1454 player_slots_iterate(pslot) {
1455 struct plrdata_slot *plrdata = score_log->plrdata
1456 + player_slot_index(pslot);
1457 if (plrdata->name != NULL
1458 && player_slot_is_used(pslot)) {
1459 struct player *pplayer = player_slot_get_player(pslot);
1461 if (!GOOD_PLAYER(pplayer)) {
1462 fprintf(score_log->fp, "delplayer %d %d\n", game.info.turn - 1,
1463 player_number(pplayer));
1464 plrdata_slot_free(plrdata);
1467 } player_slots_iterate_end;
1469 players_iterate(pplayer) {
1470 struct plrdata_slot *plrdata = score_log->plrdata + player_index(pplayer);
1471 if (plrdata->name == NULL && GOOD_PLAYER(pplayer)) {
1472 switch (game.server.scoreloglevel) {
1473 case SL_HUMANS:
1474 if (AI_PLAYER(pplayer)) {
1475 break;
1477 case SL_ALL:
1478 fprintf(score_log->fp, "addplayer %d %d %s\n", game.info.turn,
1479 player_number(pplayer), player_name(pplayer));
1480 plrdata_slot_init(plrdata, player_name(pplayer));
1483 } players_iterate_end;
1485 players_iterate(pplayer) {
1486 struct plrdata_slot *plrdata = score_log->plrdata + player_index(pplayer);
1488 if (GOOD_PLAYER(pplayer)) {
1489 switch (game.server.scoreloglevel) {
1490 case SL_HUMANS:
1491 if (AI_PLAYER(pplayer) && plrdata->name == NULL) {
1492 /* If a human player toggled into AI mode, don't break. */
1493 break;
1495 case SL_ALL:
1496 if (strcmp(plrdata->name, player_name(pplayer)) != 0) {
1497 log_debug("player names does not match '%s' != '%s'", plrdata->name,
1498 player_name(pplayer));
1499 fprintf(score_log->fp, "delplayer %d %d\n", game.info.turn - 1,
1500 player_number(pplayer));
1501 fprintf(score_log->fp, "addplayer %d %d %s\n", game.info.turn,
1502 player_number(pplayer), player_name(pplayer));
1503 plrdata_slot_replace(plrdata, player_name(pplayer));
1507 } players_iterate_end;
1509 for (i = 0; i < ARRAY_SIZE(score_tags); i++) {
1510 players_iterate(pplayer) {
1511 if (!GOOD_PLAYER(pplayer) ||
1512 (game.server.scoreloglevel == SL_HUMANS && AI_PLAYER(pplayer))) {
1513 continue;
1516 fprintf(score_log->fp, "data %d %d %d %d\n", game.info.turn, i,
1517 player_number(pplayer), score_tags[i].get_value(pplayer));
1518 } players_iterate_end;
1521 fflush(score_log->fp);
1523 return;
1525 log_civ_score_disable:
1527 log_civ_score_free();
1530 /**************************************************************************
1531 Produce random history report if it's time for one.
1532 **************************************************************************/
1533 void make_history_report(void)
1535 if (player_count() == 1) {
1536 return;
1539 if (game.server.scoreturn > game.info.turn) {
1540 return;
1543 game.server.scoreturn = (game.info.turn + GAME_DEFAULT_SCORETURN
1544 + fc_rand(GAME_DEFAULT_SCORETURN));
1546 historian_generic(&latest_history_report, game.server.scoreturn % HISTORIAN_LAST);
1547 send_current_history_report(game.est_connections);
1550 /**************************************************************************
1551 Inform clients about player scores and statistics when the game ends.
1552 Called only from server/srv_main.c srv_scores()
1553 **************************************************************************/
1554 void report_final_scores(struct conn_list *dest)
1556 static const struct {
1557 const char *name;
1558 int (*score) (const struct player *);
1559 } score_categories[] = {
1560 { N_("Population\n"), get_real_pop },
1561 /* TRANS: "M goods" = million goods */
1562 { N_("Trade\n(M goods)"), get_economics },
1563 /* TRANS: "M tons" = million tons */
1564 { N_("Production\n(M tons)"), get_production },
1565 { N_("Cities\n"), get_cities },
1566 { N_("Technologies\n"), get_techs },
1567 { N_("Military Service\n(months)"), get_mil_service },
1568 { N_("Wonders\n"), get_wonders },
1569 { N_("Research Speed\n(bulbs)"), get_research },
1570 /* TRANS: "sq. mi." is abbreviation for "square miles" */
1571 { N_("Land Area\n(sq. mi.)"), get_landarea },
1572 /* TRANS: "sq. mi." is abbreviation for "square miles" */
1573 { N_("Settled Area\n(sq. mi.)"), get_settledarea },
1574 { N_("Literacy\n(%)"), get_literacy },
1575 { N_("Culture\n"), get_culture },
1576 { N_("Spaceship\n"), get_spaceship },
1577 { N_("Built Units\n"), get_units_built },
1578 { N_("Killed Units\n"), get_units_killed },
1579 { N_("Unit Losses\n"), get_units_lost },
1581 const size_t score_categories_num = ARRAY_SIZE(score_categories);
1583 int i, j;
1584 struct player_score_entry size[player_count()];
1585 struct packet_endgame_report packet;
1587 fc_assert(score_categories_num <= ARRAY_SIZE(packet.category_name));
1589 if (!dest) {
1590 dest = game.est_connections;
1593 packet.category_num = score_categories_num;
1594 for (j = 0; j < score_categories_num; j++) {
1595 sz_strlcpy(packet.category_name[j], score_categories[j].name);
1598 i = 0;
1599 players_iterate(pplayer) {
1600 if (GOOD_PLAYER(pplayer)) {
1601 size[i].value = pplayer->score.game;
1602 size[i].player = pplayer;
1603 i++;
1605 } players_iterate_end;
1607 qsort(size, i, sizeof(size[0]), secompare);
1609 packet.player_num = i;
1611 lsend_packet_endgame_report(dest, &packet);
1613 for (i = 0; i < packet.player_num; i++) {
1614 struct packet_endgame_player ppacket;
1615 const struct player *pplayer = size[i].player;
1617 ppacket.category_num = score_categories_num;
1618 ppacket.player_id = player_number(pplayer);
1619 ppacket.score = size[i].value;
1620 for (j = 0; j < score_categories_num; j++) {
1621 ppacket.category_score[j] = score_categories[j].score(pplayer);
1624 ppacket.winner = pplayer->is_winner;
1626 lsend_packet_endgame_player(dest, &ppacket);
1630 /**************************************************************************
1631 This function pops up a non-modal message dialog on the player's desktop
1632 **************************************************************************/
1633 void page_conn(struct conn_list *dest, const char *caption,
1634 const char *headline, const char *lines) {
1635 page_conn_etype(dest, caption, headline, lines, E_REPORT);
1639 /****************************************************************************
1640 This function pops up a non-modal message dialog on the player's desktop
1642 event == E_REPORT: message should not be ignored by clients watching
1643 AI players with ai_popup_windows off. Example:
1644 Server Options, Demographics Report, etc.
1646 event == E_BROADCAST_REPORT: message can safely be ignored by clients
1647 watching AI players with ai_popup_windows off. For
1648 example: Herodot's report... and similar messages.
1649 ****************************************************************************/
1650 static void page_conn_etype(struct conn_list *dest, const char *caption,
1651 const char *headline, const char *lines,
1652 enum event_type event)
1654 struct packet_page_msg packet;
1655 int i;
1656 int len;
1658 sz_strlcpy(packet.caption, caption);
1659 sz_strlcpy(packet.headline, headline);
1660 packet.event = event;
1661 len = strlen(lines);
1662 if ((len % (MAX_LEN_CONTENT - 1)) == 0) {
1663 packet.parts = len / (MAX_LEN_CONTENT - 1);
1664 } else {
1665 packet.parts = len / (MAX_LEN_CONTENT - 1) + 1;
1667 packet.len = len;
1669 lsend_packet_page_msg(dest, &packet);
1671 for (i = 0; i < packet.parts; i++) {
1672 struct packet_page_msg_part part;
1673 int plen;
1675 plen = MIN(len, (MAX_LEN_CONTENT - 1));
1676 strncpy(part.lines, &(lines[(MAX_LEN_CONTENT - 1) * i]), plen);
1677 part.lines[plen] = '\0';
1679 lsend_packet_page_msg_part(dest, &part);
1681 len -= plen;
1685 /**************************************************************************
1686 Return current history report
1687 **************************************************************************/
1688 struct history_report *history_report_get(void)
1690 return &latest_history_report;