Split "Establish Embassy".
[freeciv.git] / tools / ruleutil / rulesave.c
blob74bc4ed47134b32d0fe0373696f0dba5524180c4
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 /* utility */
19 #include "registry.h"
20 #include "string_vector.h"
22 /* common */
23 #include "achievements.h"
24 #include "game.h"
25 #include "government.h"
26 #include "map.h"
27 #include "movement.h"
28 #include "multipliers.h"
29 #include "specialist.h"
30 #include "style.h"
31 #include "unittype.h"
32 #include "version.h"
34 /* server */
35 #include "ruleset.h"
36 #include "settings.h"
38 /* tools/ruledit */
39 #include "comments.h"
41 #include "rulesave.h"
43 /* Ruleset format version */
45 * 1 - Freeciv-2.6
46 * 10 - Freeciv-3.0
47 * 20 - Freeciv-3.1
49 #define FORMAT_VERSION 20
51 /**************************************************************************
52 Create new ruleset section file with common header.
53 **************************************************************************/
54 static struct section_file *create_ruleset_file(const char *rsname,
55 const char *rstype)
57 struct section_file *sfile = secfile_new(TRUE);
58 char buf[500];
60 comment_file_header(sfile);
62 if (rsname != NULL && rsname[0] != '\0') {
63 fc_snprintf(buf, sizeof(buf), "%s %s data for Freeciv", rsname, rstype);
64 } else {
65 fc_snprintf(buf, sizeof(buf), "Template %s data for Freeciv", rstype);
68 secfile_insert_str(sfile, buf, "datafile.description");
69 secfile_insert_str(sfile, freeciv_datafile_version(), "datafile.ruledit");
70 secfile_insert_str(sfile, RULESET_CAPABILITIES, "datafile.options");
71 secfile_insert_int(sfile, FORMAT_VERSION, "datafile.format_version");
73 return sfile;
76 /**************************************************************************
77 Save int value that has default applied upon loading.
78 **************************************************************************/
79 static bool save_default_int(struct section_file *sfile, int value,
80 int default_value, const char *path, const char *entry)
82 if (value != default_value) {
83 if (entry != NULL) {
84 secfile_insert_int(sfile, value,
85 "%s.%s", path, entry);
86 } else {
87 secfile_insert_int(sfile, value,
88 "%s", path);
92 return TRUE;
95 /**************************************************************************
96 Save bool value that has default applied upon loading.
97 **************************************************************************/
98 static bool save_default_bool(struct section_file *sfile, bool value,
99 bool default_value, const char *path, const char *entry)
101 if ((value && !default_value)
102 || (!value && default_value)) {
103 if (entry != NULL) {
104 secfile_insert_bool(sfile, value,
105 "%s.%s", path, entry);
106 } else {
107 secfile_insert_bool(sfile, value,
108 "%s", path);
112 return TRUE;
115 /**************************************************************************
116 Save name of the object.
117 **************************************************************************/
118 static bool save_name_translation(struct section_file *sfile,
119 struct name_translation *name,
120 const char *path)
122 struct entry *mod_entry;
124 mod_entry = secfile_insert_str(sfile,
125 untranslated_name(name),
126 "%s.name", path);
127 entry_str_set_gt_marking(mod_entry, TRUE);
128 if (strcmp(skip_intl_qualifier_prefix(untranslated_name(name)),
129 rule_name_get(name))) {
130 secfile_insert_str(sfile,
131 rule_name_get(name),
132 "%s.rule_name", path);
135 return TRUE;
138 /**************************************************************************
139 Save vector of requirements
140 **************************************************************************/
141 static bool save_reqs_vector(struct section_file *sfile,
142 const struct requirement_vector *reqs,
143 const char *path, const char *entry)
145 int i;
146 bool includes_negated = FALSE;
147 bool includes_surviving = FALSE;
148 bool includes_quiet = FALSE;
150 requirement_vector_iterate(reqs, preq) {
151 if (!preq->present) {
152 includes_negated = TRUE;
154 if (preq->survives) {
155 includes_surviving = TRUE;
157 if (preq->quiet) {
158 includes_quiet = TRUE;
160 } requirement_vector_iterate_end;
162 i = 0;
163 requirement_vector_iterate(reqs, preq) {
164 secfile_insert_str(sfile,
165 universals_n_name(preq->source.kind),
166 "%s.%s%d.type", path, entry, i);
167 secfile_insert_str(sfile,
168 universal_rule_name(&(preq->source)),
169 "%s.%s%d.name", path, entry, i);
170 secfile_insert_str(sfile,
171 req_range_name(preq->range),
172 "%s.%s%d.range", path, entry, i);
174 if (includes_surviving) {
175 secfile_insert_bool(sfile,
176 preq->survives,
177 "%s.%s%d.survives", path, entry, i);
180 if (includes_negated) {
181 secfile_insert_bool(sfile,
182 preq->present,
183 "%s.%s%d.present", path, entry, i);
186 if (includes_quiet) {
187 secfile_insert_bool(sfile,
188 preq->quiet,
189 "%s.%s%d.quiet", path, entry, i);
192 i++;
193 } requirement_vector_iterate_end;
195 return TRUE;
198 /**************************************************************************
199 Save techs vector. Input is A_LAST terminated array of techs to save.
200 **************************************************************************/
201 static bool save_tech_list(struct section_file *sfile, int *input,
202 const char *path, const char *entry)
204 const char *tech_names[MAX_NUM_TECH_LIST];
205 int set_count;
206 int i;
208 set_count = 0;
209 for (i = 0; input[i] != A_LAST && i < MAX_NUM_TECH_LIST; i++) {
210 tech_names[set_count++] = advance_rule_name(advance_by_number(input[i]));
213 if (set_count > 0) {
214 secfile_insert_str_vec(sfile, tech_names, set_count,
215 "%s.%s", path, entry);
218 return TRUE;
221 /**************************************************************************
222 Save tech reference
223 **************************************************************************/
224 static bool save_tech_ref(struct section_file *sfile,
225 const struct advance *padv,
226 const char *path, const char *entry)
228 if (padv == A_NEVER) {
229 secfile_insert_str(sfile, "Never", "%s.%s", path, entry);
230 } else {
231 secfile_insert_str(sfile, advance_rule_name(padv),
232 "%s.%s", path, entry);
235 return TRUE;
238 /**************************************************************************
239 Save terrain reference
240 **************************************************************************/
241 static bool save_terrain_ref(struct section_file *sfile,
242 const struct terrain *save,
243 const struct terrain *pthis,
244 const char *path, const char *entry)
246 if (save == NULL) {
247 secfile_insert_str(sfile, "none", "%s.%s", path, entry);
248 } else if (save == pthis) {
249 secfile_insert_str(sfile, "yes", "%s.%s", path, entry);
250 } else {
251 secfile_insert_str(sfile, terrain_rule_name(save),
252 "%s.%s", path, entry);
255 return TRUE;
258 /**************************************************************************
259 Save government reference
260 **************************************************************************/
261 static bool save_gov_ref(struct section_file *sfile,
262 const struct government *gov,
263 const char *path, const char *entry)
265 secfile_insert_str(sfile, government_rule_name(gov), "%s.%s", path, entry);
267 return TRUE;
270 /**************************************************************************
271 Save buildings vector. Input is B_LAST terminated array of buildings
272 to save.
273 **************************************************************************/
274 static bool save_building_list(struct section_file *sfile, int *input,
275 const char *path, const char *entry)
277 const char *building_names[MAX_NUM_BUILDING_LIST];
278 int set_count;
279 int i;
281 set_count = 0;
282 for (i = 0; input[i] != B_LAST && i < MAX_NUM_BUILDING_LIST; i++) {
283 building_names[set_count++] = improvement_rule_name(improvement_by_number(input[i]));
286 if (set_count > 0) {
287 secfile_insert_str_vec(sfile, building_names, set_count,
288 "%s.%s", path, entry);
291 return TRUE;
294 /**************************************************************************
295 Save units vector. Input is NULL terminated array of units
296 to save.
297 **************************************************************************/
298 static bool save_unit_list(struct section_file *sfile, struct unit_type **input,
299 const char *path, const char *entry)
301 const char *unit_names[MAX_NUM_UNIT_LIST];
302 int set_count;
303 int i;
305 set_count = 0;
306 for (i = 0; input[i] != NULL && i < MAX_NUM_UNIT_LIST; i++) {
307 unit_names[set_count++] = utype_rule_name(input[i]);
310 if (set_count > 0) {
311 secfile_insert_str_vec(sfile, unit_names, set_count,
312 "%s.%s", path, entry);
315 return TRUE;
318 /**************************************************************************
319 Save vector of unit class names based on bitvector bits
320 **************************************************************************/
321 static bool save_uclass_vec(struct section_file *sfile,
322 bv_unit_classes *bits,
323 const char *path, const char *entry,
324 bool unreachable_only)
326 const char *class_names[UCL_LAST];
327 int classes = 0;
329 unit_class_iterate(pcargo) {
330 if (BV_ISSET(*(bits), uclass_index(pcargo))
331 && (uclass_has_flag(pcargo, UCF_UNREACHABLE)
332 || !unreachable_only)) {
333 class_names[classes++] = uclass_rule_name(pcargo);
335 } unit_class_iterate_end;
337 if (classes > 0) {
338 secfile_insert_str_vec(sfile, class_names, classes,
339 "%s.%s", path, entry);
342 return TRUE;
345 /**************************************************************************
346 Save strvec as ruleset vector of strings
347 **************************************************************************/
348 static bool save_strvec(struct section_file *sfile,
349 struct strvec *to_save,
350 const char *path, const char *entry)
352 if (to_save != NULL) {
353 int sect_count = strvec_size(to_save);
354 const char *sections[sect_count];
355 int i;
357 for (i = 0; i < sect_count; i++) {
358 sections[i] = strvec_get(to_save, i);
361 secfile_insert_str_vec(sfile, sections, sect_count, "%s.%s", path, entry);
364 return TRUE;
367 /**************************************************************************
368 Save ruleset file.
369 **************************************************************************/
370 static bool save_ruleset_file(struct section_file *sfile, const char *filename)
372 return secfile_save(sfile, filename, 0, FZ_PLAIN);
375 /**************************************************************************
376 Save buildings.ruleset
377 **************************************************************************/
378 static bool save_buildings_ruleset(const char *filename, const char *name)
380 struct section_file *sfile = create_ruleset_file(name, "building");
381 int sect_idx;
383 if (sfile == NULL) {
384 return FALSE;
387 comment_buildings(sfile);
389 sect_idx = 0;
390 improvement_active_iterate(pb) {
391 if (!pb->disabled) {
392 char path[512];
393 const char *flag_names[IF_COUNT];
394 int set_count;
395 int flagi;
397 fc_snprintf(path, sizeof(path), "building_%d", sect_idx++);
399 save_name_translation(sfile, &(pb->name), path);
401 secfile_insert_str(sfile, impr_genus_id_name(pb->genus),
402 "%s.genus", path);
404 if (strcmp(pb->graphic_str, "-")) {
405 secfile_insert_str(sfile, pb->graphic_str, "%s.graphic", path);
407 if (strcmp(pb->graphic_alt, "-")) {
408 secfile_insert_str(sfile, pb->graphic_alt, "%s.graphic_alt", path);
410 if (strcmp(pb->soundtag, "-")) {
411 secfile_insert_str(sfile, pb->soundtag, "%s.sound", path);
413 if (strcmp(pb->soundtag_alt, "-")) {
414 secfile_insert_str(sfile, pb->soundtag_alt, "%s.sound_alt", path);
417 save_reqs_vector(sfile, &(pb->reqs), path, "reqs");
418 save_reqs_vector(sfile, &(pb->obsolete_by), path, "obsolete_by");
420 secfile_insert_int(sfile, pb->build_cost, "%s.build_cost", path);
421 secfile_insert_int(sfile, pb->upkeep, "%s.upkeep", path);
422 secfile_insert_int(sfile, pb->sabotage, "%s.sabotage", path);
424 set_count = 0;
425 for (flagi = 0; flagi < IF_COUNT; flagi++) {
426 if (improvement_has_flag(pb, flagi)) {
427 flag_names[set_count++] = impr_flag_id_name(flagi);
431 if (set_count > 0) {
432 secfile_insert_str_vec(sfile, flag_names, set_count,
433 "%s.flags", path);
436 save_strvec(sfile, pb->helptext, path, "helptext");
438 } improvement_active_iterate_end;
440 return save_ruleset_file(sfile, filename);
443 /**************************************************************************
444 Save styles.ruleset
445 **************************************************************************/
446 static bool save_styles_ruleset(const char *filename, const char *name)
448 struct section_file *sfile = create_ruleset_file(name, "styles");
449 int sect_idx;
450 int i;
452 if (sfile == NULL) {
453 return FALSE;
456 comment_styles(sfile);
458 sect_idx = 0;
459 styles_active_iterate(pstyle) {
460 char path[512];
462 fc_snprintf(path, sizeof(path), "style_%d", sect_idx++);
464 save_name_translation(sfile, &(pstyle->name), path);
465 } styles_active_iterate_end;
467 comment_citystyles(sfile);
469 sect_idx = 0;
470 for (i = 0; i < game.control.styles_count; i++) {
471 char path[512];
473 fc_snprintf(path, sizeof(path), "citystyle_%d", sect_idx++);
475 save_name_translation(sfile, &(city_styles[i].name), path);
477 secfile_insert_str(sfile, city_styles[i].graphic, "%s.graphic", path);
478 secfile_insert_str(sfile, city_styles[i].graphic_alt, "%s.graphic_alt", path);
479 if (strcmp(city_styles[i].citizens_graphic, "-")) {
480 secfile_insert_str(sfile, city_styles[i].citizens_graphic,
481 "%s.citizens_graphic", path);
483 if (strcmp(city_styles[i].citizens_graphic_alt, "generic")) {
484 secfile_insert_str(sfile, city_styles[i].citizens_graphic_alt,
485 "%s.citizens_graphic_alt", path);
488 save_reqs_vector(sfile, &(city_styles[i].reqs), path, "reqs");
491 comment_musicstyles(sfile);
493 sect_idx = 0;
494 music_styles_iterate(pmus) {
495 char path[512];
497 fc_snprintf(path, sizeof(path), "musicstyle_%d", sect_idx++);
499 secfile_insert_str(sfile, pmus->music_peaceful, "%s.music_peaceful", path);
500 secfile_insert_str(sfile, pmus->music_combat, "%s.music_combat", path);
502 save_reqs_vector(sfile, &(pmus->reqs), path, "reqs");
503 } music_styles_iterate_end;
505 return save_ruleset_file(sfile, filename);
508 /**************************************************************************
509 Save an action auto performer's !present utype reqs as a regular setting.
510 This is done because the Action Auto Perform rules system isn't ready to
511 be exposed to the ruleset yet. The setting is a list of utype flags that
512 prevents the auto action performer.
513 **************************************************************************/
514 static bool save_action_auto_uflag_block(struct section_file *sfile,
515 const int aap,
516 const char *uflags_path,
517 bool (*unexpected_req)(
518 const struct requirement *preq))
520 enum unit_type_flag_id protecor_flag[MAX_NUM_USER_UNIT_FLAGS];
521 size_t i;
522 size_t ret;
524 const struct action_auto_perf *auto_perf =
525 action_auto_perf_by_number(aap);
527 i = 0;
528 requirement_vector_iterate(&auto_perf->reqs, req) {
529 fc_assert(req->range == REQ_RANGE_LOCAL);
531 if (req->source.kind == VUT_UTFLAG) {
532 fc_assert(!req->present);
534 protecor_flag[i++] = req->source.value.unitflag;
535 } else if (unexpected_req(req)) {
536 log_error("Can't handle action auto performer requirement %s",
537 req_to_fstring(req));
539 return FALSE;
541 } requirement_vector_iterate_end;
543 ret = secfile_insert_enum_vec(sfile, &protecor_flag, i,
544 unit_type_flag_id,
545 "%s", uflags_path);
547 if (ret != i) {
548 log_error("%s: didn't save all unit type flags.", uflags_path);
550 return FALSE;
553 return TRUE;
556 /**************************************************************************
557 Save an action auto performer's action list as a regular setting. This
558 is done because the Action Auto Perform rules system isn't ready to be
559 exposed to the ruleset yet. The setting is a list of actions in the
560 order they should be tried.
561 **************************************************************************/
562 static bool save_action_auto_actions(struct section_file *sfile,
563 const int aap,
564 const char *actions_path)
566 enum gen_action unit_acts[MAX_NUM_ACTIONS];
567 size_t i;
568 size_t ret;
570 const struct action_auto_perf *auto_perf =
571 action_auto_perf_by_number(aap);
573 i = 0;
574 for (i = 0;
575 i < NUM_ACTIONS && auto_perf->alternatives[i] != ACTION_NONE;
576 i++) {
577 unit_acts[i] = auto_perf->alternatives[i];
580 ret = secfile_insert_enum_vec(sfile, &unit_acts, i, gen_action,
581 "%s", actions_path);
583 if (ret != i) {
584 log_error("%s: didn't save all actions.", actions_path);
586 return FALSE;
589 return TRUE;
592 /**************************************************************************
593 Missing unit upkeep should only contain output type and absence of
594 blocking unit type flag requirements.
595 **************************************************************************/
596 static bool unexpected_non_otype(const struct requirement *req)
598 return !(req->source.kind == VUT_OTYPE && req->present);
601 /**************************************************************************
602 Save the action a unit should perform when its missing food, gold or
603 shield upkeep. Save as regular settings since the Action Auto Perform
604 rules system isn't ready to be exposed to the ruleset yet.
605 **************************************************************************/
606 static bool save_muuk_action_auto(struct section_file *sfile,
607 const int aap,
608 const char *item)
610 char uflags_path[100];
611 char action_path[100];
613 fc_snprintf(uflags_path, sizeof(uflags_path),
614 "missing_unit_upkeep.%s_protected", item);
615 fc_snprintf(action_path, sizeof(action_path),
616 "missing_unit_upkeep.%s_unit_act", item);
618 return (save_action_auto_uflag_block(sfile, aap, uflags_path,
619 unexpected_non_otype)
620 && save_action_auto_actions(sfile, aap, action_path));
623 /**************************************************************************
624 Save cities.ruleset
625 **************************************************************************/
626 static bool save_cities_ruleset(const char *filename, const char *name)
628 struct section_file *sfile = create_ruleset_file(name, "cities");
629 int sect_idx;
631 if (sfile == NULL) {
632 return FALSE;
635 comment_specialists(sfile);
637 sect_idx = 0;
638 specialist_type_iterate(sp) {
639 struct specialist *s = specialist_by_number(sp);
640 char path[512];
642 fc_snprintf(path, sizeof(path), "specialist_%d", sect_idx++);
644 save_name_translation(sfile, &(s->name), path);
646 if (strcmp(rule_name_get(&s->name), rule_name_get(&s->abbreviation))) {
647 secfile_insert_str(sfile, rule_name_get(&s->abbreviation),
648 "%s.short_name", path);
651 save_reqs_vector(sfile, &(s->reqs), path, "reqs");
653 if (strcmp(s->graphic_alt, "-")) {
654 secfile_insert_str(sfile, s->graphic_alt, "%s.graphic_alt", path);
657 save_strvec(sfile, s->helptext, path, "helptext");
659 } specialist_type_iterate_end;
661 if (game.info.celebratesize != GAME_DEFAULT_CELEBRATESIZE) {
662 secfile_insert_int(sfile, game.info.celebratesize,
663 "parameters.celebrate_size_limit");
665 if (game.info.add_to_size_limit != GAME_DEFAULT_ADDTOSIZE) {
666 secfile_insert_int(sfile, game.info.add_to_size_limit,
667 "parameters.add_to_size_limit");
669 if (game.info.angrycitizen != GAME_DEFAULT_ANGRYCITIZEN) {
670 secfile_insert_bool(sfile, game.info.angrycitizen,
671 "parameters.angry_citizens");
673 if (game.info.changable_tax != GAME_DEFAULT_CHANGABLE_TAX) {
674 secfile_insert_bool(sfile, game.info.changable_tax,
675 "parameters.changable_tax");
677 if (game.info.forced_science != 0) {
678 secfile_insert_int(sfile, game.info.forced_science,
679 "parameters.forced_science");
681 if (game.info.forced_luxury != 100) {
682 secfile_insert_int(sfile, game.info.forced_luxury,
683 "parameters.forced_luxury");
685 if (game.info.forced_gold != 0) {
686 secfile_insert_int(sfile, game.info.forced_gold,
687 "parameters.forced_gold");
689 if (game.server.vision_reveal_tiles != GAME_DEFAULT_VISION_REVEAL_TILES) {
690 secfile_insert_bool(sfile, game.server.vision_reveal_tiles,
691 "parameters.vision_reveal_tiles");
693 if (game.info.pop_report_zeroes != 1) {
694 secfile_insert_int(sfile, game.info.pop_report_zeroes,
695 "parameters.pop_report_zeroes");
697 if (game.info.citizen_nationality != GAME_DEFAULT_NATIONALITY) {
698 secfile_insert_bool(sfile, game.info.citizen_nationality,
699 "citizen.nationality");
701 if (game.info.citizen_convert_speed != GAME_DEFAULT_CONVERT_SPEED) {
702 secfile_insert_int(sfile, game.info.citizen_convert_speed,
703 "citizen.convert_speed");
705 if (game.info.citizen_partisans_pct != 0) {
706 secfile_insert_int(sfile, game.info.citizen_partisans_pct,
707 "citizen.partisans_pct");
710 save_muuk_action_auto(sfile, ACTION_AUTO_UPKEEP_FOOD,
711 "food");
712 if (game.info.muuk_food_wipe != RS_DEFAULT_MUUK_FOOD_WIPE) {
713 secfile_insert_bool(sfile, game.info.muuk_food_wipe,
714 "missing_unit_upkeep.food_wipe");
717 save_muuk_action_auto(sfile, ACTION_AUTO_UPKEEP_GOLD,
718 "gold");
719 if (game.info.muuk_gold_wipe != RS_DEFAULT_MUUK_GOLD_WIPE) {
720 secfile_insert_bool(sfile, game.info.muuk_gold_wipe,
721 "missing_unit_upkeep.gold_wipe");
724 save_muuk_action_auto(sfile, ACTION_AUTO_UPKEEP_SHIELD,
725 "shield");
726 if (game.info.muuk_shield_wipe != RS_DEFAULT_MUUK_SHIELD_WIPE) {
727 secfile_insert_bool(sfile, game.info.muuk_shield_wipe,
728 "missing_unit_upkeep.shield_wipe");
731 return save_ruleset_file(sfile, filename);
734 /**************************************************************************
735 Effect saving callback data structure.
736 **************************************************************************/
737 typedef struct {
738 int idx;
739 struct section_file *sfile;
740 } effect_cb_data;
742 /**************************************************************************
743 Save one effect. Callback called for each effect in cache.
744 **************************************************************************/
745 static bool effect_save(struct effect *peffect, void *data)
747 effect_cb_data *cbdata = (effect_cb_data *)data;
748 char path[512];
750 fc_snprintf(path, sizeof(path), "effect_%d", cbdata->idx++);
752 secfile_insert_str(cbdata->sfile,
753 effect_type_name(peffect->type),
754 "%s.type", path);
755 secfile_insert_int(cbdata->sfile, peffect->value, "%s.value", path);
757 save_reqs_vector(cbdata->sfile, &peffect->reqs, path, "reqs");
759 return TRUE;
762 /**************************************************************************
763 Save effects.ruleset
764 **************************************************************************/
765 static bool save_effects_ruleset(const char *filename, const char *name)
767 struct section_file *sfile = create_ruleset_file(name, "effect");
768 effect_cb_data data;
770 if (sfile == NULL) {
771 return FALSE;
774 data.idx = 0;
775 data.sfile = sfile;
777 comment_effects(sfile);
779 if (!iterate_effect_cache(effect_save, &data)) {
780 return FALSE;
783 return save_ruleset_file(sfile, filename);
786 /**************************************************************************
787 Auto attack should only require war, remaining movement and the absence
788 of blocking utype flags.
789 **************************************************************************/
790 static bool unexpected_auto_attack(const struct requirement *req)
792 return !((req->source.kind == VUT_DIPLREL
793 && req->source.value.diplrel == DS_WAR
794 && req->present)
795 || (req->source.kind == VUT_MINMOVES
796 && req->source.value.minmoves == 1
797 && req->present));
800 /**************************************************************************
801 Save game.ruleset
802 **************************************************************************/
803 static bool save_game_ruleset(const char *filename, const char *name)
805 struct section_file *sfile = create_ruleset_file(name, "game");
806 int sect_idx;
807 int col_idx;
808 int set_count;
809 enum gameloss_style gs;
810 const char *style_names[32]; /* FIXME: Should determine max length automatically.
811 * currently it's 3 (bits 0,1, and 2) so there's plenty of
812 * safety margin here. */
813 const char *tnames[game.server.ruledit.named_teams];
814 enum trade_route_type trt;
815 int i;
816 enum gen_action quiet_actions[MAX_NUM_ACTIONS];
817 bool locks;
818 int force_capture_units, force_bombard, force_explode_nuclear;
820 if (sfile == NULL) {
821 return FALSE;
824 if (game.server.ruledit.description_file != NULL) {
825 secfile_insert_str(sfile, game.server.ruledit.description_file,
826 "ruledit.description_file");
829 if (game.control.preferred_tileset[0] != '\0') {
830 secfile_insert_str(sfile, game.control.preferred_tileset,
831 "tileset.preferred");
833 if (game.control.preferred_soundset[0] != '\0') {
834 secfile_insert_str(sfile, game.control.preferred_soundset,
835 "soundset.preferred");
837 if (game.control.preferred_musicset[0] != '\0') {
838 secfile_insert_str(sfile, game.control.preferred_musicset,
839 "musicset.preferred");
842 secfile_insert_str(sfile, game.control.name, "about.name");
843 secfile_insert_str(sfile, game.control.version, "about.version");
845 if (game.ruleset_summary != NULL) {
846 struct entry *mod_entry;
848 mod_entry = secfile_insert_str(sfile, game.ruleset_summary,
849 "about.summary");
850 entry_str_set_gt_marking(mod_entry, TRUE);
853 if (game.ruleset_description != NULL) {
854 if (game.server.ruledit.description_file == NULL) {
855 secfile_insert_str(sfile, game.ruleset_description,
856 "about.description");
857 } else {
858 secfile_insert_filereference(sfile, game.server.ruledit.description_file,
859 "about.description");
863 save_tech_list(sfile, game.rgame.global_init_techs,
864 "options", "global_init_techs");
865 save_building_list(sfile, game.rgame.global_init_buildings,
866 "options", "global_init_buildings");
868 save_default_bool(sfile, game.control.popup_tech_help,
869 FALSE,
870 "options.popup_tech_help", NULL);
871 save_default_int(sfile, game.info.base_pollution,
872 RS_DEFAULT_BASE_POLLUTION,
873 "civstyle.base_pollution", NULL);
875 set_count = 0;
876 for (gs = gameloss_style_begin(); gs != gameloss_style_end(); gs = gameloss_style_next(gs)) {
877 if (game.info.gameloss_style & gs) {
878 style_names[set_count++] = gameloss_style_name(gs);
882 if (set_count > 0) {
883 secfile_insert_str_vec(sfile, style_names, set_count,
884 "civstyle.gameloss_style");
887 save_default_int(sfile, game.info.happy_cost,
888 RS_DEFAULT_HAPPY_COST,
889 "civstyle.happy_cost", NULL);
890 save_default_int(sfile, game.info.food_cost,
891 RS_DEFAULT_FOOD_COST,
892 "civstyle.food_cost", NULL);
893 save_default_bool(sfile, game.info.civil_war_enabled,
894 TRUE,
895 "civstyle.civil_war_enabled", NULL);
896 save_default_bool(sfile, game.info.paradrop_to_transport,
897 FALSE,
898 "civstyle.paradrop_to_transport", NULL);
899 save_default_int(sfile, game.info.base_bribe_cost,
900 RS_DEFAULT_BASE_BRIBE_COST,
901 "civstyle.base_bribe_cost", NULL);
902 save_default_int(sfile, game.server.ransom_gold,
903 RS_DEFAULT_RANSOM_GOLD,
904 "civstyle.ransom_gold", NULL);
905 save_default_bool(sfile, game.info.pillage_select,
906 RS_DEFAULT_PILLAGE_SELECT,
907 "civstyle.pillage_select", NULL);
908 save_default_bool(sfile, game.info.tech_steal_allow_holes,
909 RS_DEFAULT_TECH_STEAL_HOLES,
910 "civstyle.tech_steal_allow_holes", NULL);
911 save_default_bool(sfile, game.info.tech_trade_allow_holes,
912 RS_DEFAULT_TECH_TRADE_HOLES,
913 "civstyle.tech_trade_allow_holes", NULL);
914 save_default_bool(sfile, game.info.tech_trade_loss_allow_holes,
915 RS_DEFAULT_TECH_TRADE_LOSS_HOLES,
916 "civstyle.tech_trade_loss_allow_holes", NULL);
917 save_default_bool(sfile, game.info.tech_parasite_allow_holes,
918 RS_DEFAULT_TECH_PARASITE_HOLES,
919 "civstyle.tech_parasite_allow_holes", NULL);
920 save_default_bool(sfile, game.info.tech_loss_allow_holes,
921 RS_DEFAULT_TECH_LOSS_HOLES,
922 "civstyle.tech_loss_allow_holes", NULL);
923 save_default_int(sfile, game.server.upgrade_veteran_loss,
924 RS_DEFAULT_UPGRADE_VETERAN_LOSS,
925 "civstyle.upgrade_veteran_loss", NULL);
926 save_default_int(sfile, game.server.autoupgrade_veteran_loss,
927 RS_DEFAULT_UPGRADE_VETERAN_LOSS,
928 "civstyle.autoupgrade_veteran_loss", NULL);
930 secfile_insert_int_vec(sfile, game.info.granary_food_ini,
931 game.info.granary_num_inis,
932 "civstyle.granary_food_ini");
934 save_default_int(sfile, game.info.granary_food_inc,
935 RS_DEFAULT_GRANARY_FOOD_INC,
936 "civstyle.granary_food_inc", NULL);
938 output_type_iterate(o) {
939 char buffer[256];
941 fc_snprintf(buffer, sizeof(buffer),
942 "civstyle.min_city_center_%s",
943 get_output_identifier(o));
945 save_default_int(sfile, game.info.min_city_center_output[o],
946 RS_DEFAULT_CITY_CENTER_OUTPUT,
947 buffer, NULL);
948 } output_type_iterate_end;
950 save_default_int(sfile, game.server.init_vis_radius_sq,
951 RS_DEFAULT_VIS_RADIUS_SQ,
952 "civstyle.init_vis_radius_sq", NULL);
953 save_default_int(sfile, game.info.init_city_radius_sq,
954 RS_DEFAULT_CITY_RADIUS_SQ,
955 "civstyle.init_city_radius_sq", NULL);
956 if (0 != fc_strcasecmp(gold_upkeep_style_name(game.info.gold_upkeep_style),
957 RS_DEFAULT_GOLD_UPKEEP_STYLE)) {
958 secfile_insert_str(sfile,
959 gold_upkeep_style_name(game.info.gold_upkeep_style),
960 "civstyle.gold_upkeep_style");
962 save_default_bool(sfile, game.info.illness_on,
963 RS_DEFAULT_ILLNESS_ON,
964 "illness.illness_on", NULL);
965 save_default_int(sfile, game.info.illness_base_factor,
966 RS_DEFAULT_ILLNESS_BASE_FACTOR,
967 "illness.illness_base_factor", NULL);
968 save_default_int(sfile, game.info.illness_min_size,
969 RS_DEFAULT_ILLNESS_MIN_SIZE,
970 "illness.illness_min_size", NULL);
971 save_default_int(sfile, game.info.illness_trade_infection,
972 RS_DEFAULT_ILLNESS_TRADE_INFECTION_PCT,
973 "illness.illness_trade_infection", NULL);
974 save_default_int(sfile, game.info.illness_pollution_factor,
975 RS_DEFAULT_ILLNESS_POLLUTION_PCT,
976 "illness.illness_pollution_factor", NULL);
977 save_default_int(sfile, game.server.base_incite_cost,
978 RS_DEFAULT_INCITE_BASE_COST,
979 "incite_cost.base_incite_cost", NULL);
980 save_default_int(sfile, game.server.incite_improvement_factor,
981 RS_DEFAULT_INCITE_IMPROVEMENT_FCT,
982 "incite_cost.improvement_factor", NULL);
983 save_default_int(sfile, game.server.incite_unit_factor,
984 RS_DEFAULT_INCITE_UNIT_FCT,
985 "incite_cost.unit_factor", NULL);
986 save_default_int(sfile, game.server.incite_total_factor,
987 RS_DEFAULT_INCITE_TOTAL_FCT,
988 "incite_cost.total_factor", NULL);
989 save_default_bool(sfile, game.info.slow_invasions,
990 RS_DEFAULT_SLOW_INVASIONS,
991 "global_unit_options.slow_invasions", NULL);
993 save_action_auto_uflag_block(sfile, ACTION_AUTO_MOVED_ADJ,
994 "auto_attack.will_never",
995 unexpected_auto_attack);
997 save_default_bool(sfile,
998 action_id_would_be_blocked_by(ACTION_MARKETPLACE,
999 ACTION_TRADE_ROUTE),
1000 RS_DEFAULT_FORCE_TRADE_ROUTE,
1001 "actions.force_trade_route", NULL);
1003 /* The ruleset options force_capture_units, force_bombard and
1004 * force_explode_nuclear sets their respective action to block many other
1005 * actions' blocked_by. Checking one is therefore enough. */
1006 force_capture_units =
1007 BV_ISSET(action_by_number(ACTION_ATTACK)->blocked_by,
1008 ACTION_CAPTURE_UNITS);
1009 save_default_bool(sfile, force_capture_units,
1010 RS_DEFAULT_FORCE_CAPTURE_UNITS,
1011 "actions.force_capture_units", NULL);
1012 force_bombard =
1013 BV_ISSET(action_by_number(ACTION_ATTACK)->blocked_by, ACTION_BOMBARD);
1014 save_default_bool(sfile, force_bombard,
1015 RS_DEFAULT_FORCE_BOMBARD,
1016 "actions.force_bombard", NULL);
1017 force_explode_nuclear =
1018 BV_ISSET(action_by_number(ACTION_ATTACK)->blocked_by, ACTION_NUKE);
1019 save_default_bool(sfile, force_explode_nuclear,
1020 RS_DEFAULT_FORCE_EXPLODE_NUCLEAR,
1021 "actions.force_explode_nuclear", NULL);
1023 save_default_bool(sfile, game.info.poison_empties_food_stock,
1024 RS_DEFAULT_POISON_EMPTIES_FOOD_STOCK,
1025 "actions.poison_empties_food_stock", NULL);
1027 if (action_by_number(ACTION_BOMBARD)->max_distance
1028 == ACTION_DISTANCE_UNLIMITED) {
1029 secfile_insert_str(sfile, RS_ACTION_NO_MAX_DISTANCE,
1030 "actions.bombard_max_range");
1031 } else {
1032 save_default_int(sfile, action_by_number(ACTION_BOMBARD)->max_distance,
1033 RS_DEFAULT_BOMBARD_MAX_RANGE,
1034 "actions.bombard_max_range", NULL);
1037 secfile_insert_str(sfile,
1038 action_by_number(ACTION_SPY_POISON)->ui_name,
1039 "actions.ui_name_poison_city");
1040 secfile_insert_str(sfile,
1041 action_by_number(ACTION_SPY_SABOTAGE_UNIT)->ui_name,
1042 "actions.ui_name_sabotage_unit");
1043 secfile_insert_str(sfile,
1044 action_by_number(ACTION_SPY_BRIBE_UNIT)->ui_name,
1045 "actions.ui_name_bribe_unit");
1046 secfile_insert_str(sfile,
1047 action_by_number(ACTION_SPY_SABOTAGE_CITY)->ui_name,
1048 "actions.ui_name_sabotage_city");
1049 secfile_insert_str(sfile,
1050 action_by_number(ACTION_SPY_TARGETED_SABOTAGE_CITY)->ui_name,
1051 "actions.ui_name_targeted_sabotage_city");
1052 secfile_insert_str(sfile,
1053 action_by_number(ACTION_SPY_INCITE_CITY)->ui_name,
1054 "actions.ui_name_incite_city");
1055 secfile_insert_str(sfile,
1056 action_by_number(ACTION_ESTABLISH_EMBASSY)->ui_name,
1057 "actions.ui_name_establish_embassy");
1058 secfile_insert_str(sfile,
1059 action_by_number(ACTION_ESTABLISH_EMBASSY_STAY)->ui_name,
1060 "actions.ui_name_establish_embassy_stay");
1061 secfile_insert_str(sfile,
1062 action_by_number(ACTION_SPY_STEAL_TECH)->ui_name,
1063 "actions.ui_name_steal_tech");
1064 secfile_insert_str(sfile,
1065 action_by_number(ACTION_SPY_TARGETED_STEAL_TECH)->ui_name,
1066 "actions.ui_name_targeted_steal_tech");
1067 secfile_insert_str(sfile,
1068 action_by_number(ACTION_SPY_INVESTIGATE_CITY)->ui_name,
1069 "actions.ui_name_investigate_city");
1070 secfile_insert_str(sfile,
1071 action_by_number(ACTION_INV_CITY_SPEND)->ui_name,
1072 "actions.ui_name_investigate_city_spend_unit");
1073 secfile_insert_str(sfile,
1074 action_by_number(ACTION_SPY_STEAL_GOLD)->ui_name,
1075 "actions.ui_name_steal_gold");
1076 secfile_insert_str(sfile,
1077 action_by_number(ACTION_STEAL_MAPS)->ui_name,
1078 "actions.ui_name_steal_maps");
1079 secfile_insert_str(sfile,
1080 action_by_number(ACTION_TRADE_ROUTE)->ui_name,
1081 "actions.ui_name_establish_trade_route");
1082 secfile_insert_str(sfile,
1083 action_by_number(ACTION_MARKETPLACE)->ui_name,
1084 "actions.ui_name_enter_marketplace");
1085 secfile_insert_str(sfile,
1086 action_by_number(ACTION_HELP_WONDER)->ui_name,
1087 "actions.ui_name_help_wonder");
1088 secfile_insert_str(sfile,
1089 action_by_number(ACTION_CAPTURE_UNITS)->ui_name,
1090 "actions.ui_name_capture_units");
1091 secfile_insert_str(sfile,
1092 action_by_number(ACTION_EXPEL_UNIT)->ui_name,
1093 "actions.ui_name_expel_unit");
1094 secfile_insert_str(sfile,
1095 action_by_number(ACTION_FOUND_CITY)->ui_name,
1096 "actions.ui_name_found_city");
1097 secfile_insert_str(sfile,
1098 action_by_number(ACTION_JOIN_CITY)->ui_name,
1099 "actions.ui_name_join_city");
1100 secfile_insert_str(sfile,
1101 action_by_number(ACTION_BOMBARD)->ui_name,
1102 "actions.ui_name_bombard");
1103 secfile_insert_str(sfile,
1104 action_by_number(ACTION_SPY_NUKE)->ui_name,
1105 "actions.ui_name_suitcase_nuke");
1106 secfile_insert_str(sfile,
1107 action_by_number(ACTION_NUKE)->ui_name,
1108 "actions.ui_name_explode_nuclear");
1109 secfile_insert_str(sfile,
1110 action_by_number(ACTION_DESTROY_CITY)->ui_name,
1111 "actions.ui_name_destroy_city");
1112 secfile_insert_str(sfile,
1113 action_by_number(ACTION_RECYCLE_UNIT)->ui_name,
1114 "actions.ui_name_recycle_unit");
1115 secfile_insert_str(sfile,
1116 action_by_number(ACTION_DISBAND_UNIT)->ui_name,
1117 "actions.ui_name_disband_unit");
1118 secfile_insert_str(sfile,
1119 action_by_number(ACTION_HOME_CITY)->ui_name,
1120 "actions.ui_name_home_city");
1121 secfile_insert_str(sfile,
1122 action_by_number(ACTION_UPGRADE_UNIT)->ui_name,
1123 "actions.ui_upgrade_unit");
1124 secfile_insert_str(sfile,
1125 action_by_number(ACTION_PARADROP)->ui_name,
1126 "actions.ui_paradrop_unit");
1127 secfile_insert_str(sfile,
1128 action_by_number(ACTION_AIRLIFT)->ui_name,
1129 "actions.ui_airlift_unit");
1130 secfile_insert_str(sfile,
1131 action_by_number(ACTION_ATTACK)->ui_name,
1132 "actions.ui_name_attack");
1133 secfile_insert_str(sfile,
1134 action_by_number(ACTION_CONQUER_CITY)->ui_name,
1135 "actions.ui_name_conquer_city");
1136 secfile_insert_str(sfile,
1137 action_by_number(ACTION_HEAL_UNIT)->ui_name,
1138 "actions.ui_name_heal_unit");
1140 i = 0;
1141 action_iterate(act) {
1142 if (action_by_number(act)->quiet) {
1143 quiet_actions[i] = act;
1144 i++;
1146 } action_iterate_end;
1148 if (secfile_insert_enum_vec(sfile, &quiet_actions, i, gen_action,
1149 "actions.quiet_actions") != i) {
1150 log_error("Didn't save all quiet actions.");
1152 return FALSE;
1155 comment_enablers(sfile);
1156 sect_idx = 0;
1157 action_enablers_iterate(pae) {
1158 char path[512];
1160 fc_snprintf(path, sizeof(path), "actionenabler_%d", sect_idx++);
1162 secfile_insert_str(sfile, gen_action_name(pae->action),
1163 "%s.action", path);
1165 save_reqs_vector(sfile, &(pae->actor_reqs), path, "actor_reqs");
1166 save_reqs_vector(sfile, &(pae->target_reqs), path, "target_reqs");
1167 } action_enablers_iterate_end;
1169 save_default_bool(sfile, game.info.tired_attack,
1170 RS_DEFAULT_TIRED_ATTACK,
1171 "combat_rules.tired_attack", NULL);
1172 save_default_int(sfile, game.info.border_city_radius_sq,
1173 RS_DEFAULT_BORDER_RADIUS_SQ_CITY,
1174 "borders.radius_sq_city", NULL);
1175 save_default_int(sfile, game.info.border_size_effect,
1176 RS_DEFAULT_BORDER_SIZE_EFFECT,
1177 "borders.size_effect", NULL);
1178 save_default_int(sfile, game.info.border_city_permanent_radius_sq,
1179 RS_DEFAULT_BORDER_RADIUS_SQ_CITY_PERMANENT,
1180 "borders.radius_sq_city_permanent", NULL);
1181 secfile_insert_str(sfile, tech_cost_style_name(game.info.tech_cost_style),
1182 "research.tech_cost_style");
1183 save_default_int(sfile, game.info.base_tech_cost,
1184 RS_DEFAULT_BASE_TECH_COST,
1185 "research.base_tech_cost", NULL);
1186 secfile_insert_str(sfile, tech_leakage_style_name(game.info.tech_leakage),
1187 "research.tech_leakage");
1188 secfile_insert_str(sfile, tech_upkeep_style_name(game.info.tech_upkeep_style),
1189 "research.tech_upkeep_style");
1190 save_default_int(sfile, game.info.tech_upkeep_divider,
1191 RS_DEFAULT_TECH_UPKEEP_DIVIDER,
1192 "research.tech_upkeep_divider", NULL);
1193 secfile_insert_str(sfile, free_tech_method_name(game.info.free_tech_method),
1194 "research.free_tech_method");
1196 save_default_int(sfile, game.info.culture_vic_points,
1197 RS_DEFAULT_CULTURE_VIC_POINTS,
1198 "culture.victory_min_points", NULL);
1199 save_default_int(sfile, game.info.culture_vic_lead,
1200 RS_DEFAULT_CULTURE_VIC_LEAD,
1201 "culture.victory_lead_pct", NULL);
1202 save_default_int(sfile, game.info.culture_migration_pml,
1203 RS_DEFAULT_CULTURE_MIGRATION_PML,
1204 "culture.migration_pml", NULL);
1206 save_default_bool(sfile, game.calendar.calendar_skip_0,
1207 RS_DEFAULT_CALENDAR_SKIP_0,
1208 "calendar.skip_year_0", NULL);
1209 save_default_int(sfile, game.server.start_year,
1210 GAME_START_YEAR,
1211 "calendar.start_year", NULL);
1212 save_default_int(sfile, game.calendar.calendar_fragments,
1213 0, "calendar.fragments", NULL);
1215 for (i = 0; i < MAX_CALENDAR_FRAGMENTS; i++) {
1216 if (game.calendar.calendar_fragment_name[i][0] != '\0') {
1217 secfile_insert_str(sfile, game.calendar.calendar_fragment_name[i],
1218 "calendar.fragment_name%d", i);
1222 if (strcmp(game.calendar.positive_year_label, RS_DEFAULT_POS_YEAR_LABEL)) {
1223 secfile_insert_str(sfile, game.calendar.positive_year_label,
1224 "calendar.positive_label");
1226 if (strcmp(game.calendar.negative_year_label, RS_DEFAULT_NEG_YEAR_LABEL)) {
1227 secfile_insert_str(sfile, game.calendar.negative_year_label,
1228 "calendar.negative_label");
1231 if (game.plr_bg_color != NULL) {
1232 rgbcolor_save(sfile, game.plr_bg_color, "playercolors.background");
1235 col_idx = 0;
1236 rgbcolor_list_iterate(game.server.plr_colors, pcol) {
1237 rgbcolor_save(sfile, pcol, "playercolors.colorlist%d", col_idx++);
1238 } rgbcolor_list_iterate_end;
1241 if (game.server.ruledit.named_teams > 0) {
1242 for (i = 0; i < game.server.ruledit.named_teams; i++) {
1243 tnames[i] = team_slot_rule_name(team_slot_by_number(i));
1246 secfile_insert_str_vec(sfile, tnames,
1247 game.server.ruledit.named_teams,
1248 "teams.names");
1251 comment_disasters(sfile);
1253 sect_idx = 0;
1254 disaster_type_iterate(pd) {
1255 char path[512];
1256 enum disaster_effect_id de;
1257 const char *effect_names[DE_COUNT];
1259 fc_snprintf(path, sizeof(path), "disaster_%d", sect_idx++);
1261 save_name_translation(sfile, &(pd->name), path);
1262 save_reqs_vector(sfile, &(pd->reqs), path, "reqs");
1263 if (pd->frequency != GAME_DEFAULT_DISASTER_FREQ) {
1264 secfile_insert_int(sfile, pd->frequency,
1265 "%s.frequency", path);
1268 set_count = 0;
1269 for (de = disaster_effect_id_begin();
1270 de != disaster_effect_id_end();
1271 de = disaster_effect_id_next(de)) {
1272 if (BV_ISSET(pd->effects, de)) {
1273 effect_names[set_count++] = disaster_effect_id_name(de);
1277 if (set_count > 0) {
1278 secfile_insert_str_vec(sfile, effect_names, set_count,
1279 "%s.effects", path);
1281 } disaster_type_iterate_end;
1283 comment_achievements(sfile);
1285 sect_idx = 0;
1286 achievements_iterate(pach) {
1287 char path[512];
1289 fc_snprintf(path, sizeof(path), "achievement_%d", sect_idx++);
1291 save_name_translation(sfile, &(pach->name), path);
1293 secfile_insert_str(sfile, achievement_type_name(pach->type),
1294 "%s.type", path);
1296 save_default_bool(sfile, pach->unique,
1297 GAME_DEFAULT_ACH_UNIQUE,
1298 path, "unique");
1299 save_default_int(sfile, pach->value,
1300 GAME_DEFAULT_ACH_VALUE,
1301 path, "value");
1302 save_default_int(sfile, pach->culture,
1303 0, path, "culture");
1305 secfile_insert_str(sfile, pach->first_msg, "%s.first_msg", path);
1306 if (pach->cons_msg != NULL) {
1307 secfile_insert_str(sfile, pach->cons_msg, "%s.cons_msg", path);
1310 } achievements_iterate_end;
1312 set_count = 0;
1313 for (trt = 0; trt < TRT_LAST; trt++) {
1314 struct trade_route_settings *set = trade_route_settings_by_type(trt);
1315 const char *cancelling = traderoute_cancelling_type_name(set->cancelling);
1317 if (set->trade_pct != 100 || strcmp(cancelling, "Active")) {
1318 char path[256];
1320 fc_snprintf(path, sizeof(path),
1321 "trade.settings%d", set_count++);
1323 secfile_insert_str(sfile, trade_route_type_name(trt),
1324 "%s.type", path);
1325 secfile_insert_int(sfile, set->trade_pct,
1326 "%s.pct", path);
1327 secfile_insert_str(sfile, cancelling,
1328 "%s.cancelling", path);
1329 secfile_insert_str(sfile, traderoute_bonus_type_name(set->bonus_type),
1330 "%s.bonus", path);
1334 /* Goods */
1335 comment_goods(sfile);
1337 sect_idx = 0;
1338 goods_active_type_iterate(pgood) {
1339 char path[512];
1340 const char *flag_names[GF_COUNT];
1341 int flagi;
1343 fc_snprintf(path, sizeof(path), "goods_%d", sect_idx++);
1345 save_name_translation(sfile, &(pgood->name), path);
1347 save_reqs_vector(sfile, &(pgood->reqs), path, "reqs");
1349 save_default_int(sfile, pgood->from_pct, 100, path, "from_pct");
1350 save_default_int(sfile, pgood->to_pct, 100, path, "to_pct");
1352 set_count = 0;
1353 for (flagi = 0; flagi < GF_COUNT; flagi++) {
1354 if (goods_has_flag(pgood, flagi)) {
1355 flag_names[set_count++] = goods_flag_id_name(flagi);
1359 if (set_count > 0) {
1360 secfile_insert_str_vec(sfile, flag_names, set_count,
1361 "%s.flags", path);
1364 save_strvec(sfile, pgood->helptext, path, "helptext");
1365 } goods_active_type_iterate_end;
1367 locks = FALSE;
1368 settings_iterate(SSET_ALL, pset) {
1369 if (setting_locked(pset)) {
1370 locks = TRUE;
1371 break;
1373 } settings_iterate_end;
1375 set_count = 0;
1376 settings_iterate(SSET_ALL, pset) {
1377 if (setting_get_setdef(pset) == SETDEF_RULESET || setting_locked(pset)) {
1378 secfile_insert_str(sfile, setting_name(pset),
1379 "settings.set%d.name", set_count);
1380 switch (setting_type(pset)) {
1381 case SST_BOOL:
1382 secfile_insert_bool(sfile, setting_bool_get(pset),
1383 "settings.set%d.value", set_count);
1384 break;
1385 case SST_INT:
1386 secfile_insert_int(sfile, setting_int_get(pset),
1387 "settings.set%d.value", set_count);
1388 break;
1389 case SST_STRING:
1390 secfile_insert_str(sfile, setting_str_get(pset),
1391 "settings.set%d.value", set_count);
1392 break;
1393 case SST_ENUM:
1394 secfile_insert_enum_data(sfile, read_enum_value(pset), FALSE,
1395 setting_enum_secfile_str, pset,
1396 "settings.set%d.value", set_count);
1397 break;
1398 case SST_BITWISE:
1399 secfile_insert_enum_data(sfile, setting_bitwise_get(pset), TRUE,
1400 setting_bitwise_secfile_str, pset,
1401 "settings.set%d.value", set_count);
1402 break;
1403 case SST_COUNT:
1404 fc_assert(setting_type(pset) != SST_COUNT);
1405 secfile_insert_str(sfile, "Unknown setting type",
1406 "settings.set%d.value", set_count);
1407 break;
1410 if (locks) {
1411 secfile_insert_bool(sfile, setting_locked(pset),
1412 "settings.set%d.lock", set_count);
1415 set_count++;
1417 } settings_iterate_end;
1419 return save_ruleset_file(sfile, filename);
1422 /**************************************************************************
1423 Save governments.ruleset
1424 **************************************************************************/
1425 static bool save_governments_ruleset(const char *filename, const char *name)
1427 struct section_file *sfile = create_ruleset_file(name, "government");
1428 int sect_idx;
1430 if (sfile == NULL) {
1431 return FALSE;
1434 save_gov_ref(sfile, game.government_during_revolution, "governments",
1435 "during_revolution");
1437 comment_govs(sfile);
1439 sect_idx = 0;
1440 governments_active_iterate(pg) {
1441 char path[512];
1442 struct ruler_title *prtitle;
1444 fc_snprintf(path, sizeof(path), "government_%d", sect_idx++);
1446 save_name_translation(sfile, &(pg->name), path);
1448 secfile_insert_str(sfile, pg->graphic_str, "%s.graphic", path);
1449 secfile_insert_str(sfile, pg->graphic_alt, "%s.graphic_alt", path);
1451 save_reqs_vector(sfile, &(pg->reqs), path, "reqs");
1453 if (pg->ai.better != NULL) {
1454 save_gov_ref(sfile, pg->ai.better, path,
1455 "ai_better");
1458 ruler_title_hash_lookup(pg->ruler_titles, NULL,
1459 &prtitle);
1460 if (prtitle != NULL) {
1461 const char *title;
1463 title = ruler_title_male_untranslated_name(prtitle);
1464 if (title != NULL) {
1465 secfile_insert_str(sfile, title,
1466 "%s.ruler_male_title", path);
1469 title = ruler_title_female_untranslated_name(prtitle);
1470 if (title != NULL) {
1471 secfile_insert_str(sfile, title,
1472 "%s.ruler_female_title", path);
1476 save_strvec(sfile, pg->helptext, path, "helptext");
1478 } governments_active_iterate_end;
1480 comment_policies(sfile);
1482 sect_idx = 0;
1483 multipliers_iterate(pmul) {
1484 char path[512];
1486 fc_snprintf(path, sizeof(path), "multiplier_%d", sect_idx++);
1488 save_name_translation(sfile, &(pmul->name), path);
1490 secfile_insert_int(sfile, pmul->start, "%s.start", path);
1491 secfile_insert_int(sfile, pmul->stop, "%s.stop", path);
1492 secfile_insert_int(sfile, pmul->step, "%s.step", path);
1493 secfile_insert_int(sfile, pmul->def, "%s.default", path);
1495 save_strvec(sfile, pmul->helptext, path, "helptext");
1496 } multipliers_iterate_end;
1498 return save_ruleset_file(sfile, filename);
1501 /**************************************************************************
1502 Save list of AI traits
1503 **************************************************************************/
1504 static bool save_traits(struct trait_limits *traits,
1505 struct trait_limits *default_traits,
1506 struct section_file *sfile,
1507 const char *secname, const char *field_prefix)
1509 enum trait tr;
1511 /* FIXME: Use specenum trait names without duplicating them here.
1512 * Just needs to take care of case. */
1513 const char *trait_names[] = {
1514 "expansionist",
1515 "trader",
1516 "aggressive",
1517 NULL
1520 for (tr = trait_begin(); tr != trait_end() && trait_names[tr] != NULL;
1521 tr = trait_next(tr)) {
1522 int default_default;
1524 default_default = (traits[tr].min + traits[tr].max) / 2;
1526 if ((default_traits == NULL && traits[tr].min != TRAIT_DEFAULT_VALUE)
1527 || (default_traits != NULL && traits[tr].min != default_traits[tr].min)) {
1528 secfile_insert_int(sfile, traits[tr].min, "%s.%s%s_min", secname, field_prefix,
1529 trait_names[tr]);
1531 if ((default_traits == NULL && traits[tr].max != TRAIT_DEFAULT_VALUE)
1532 || (default_traits != NULL && traits[tr].max != default_traits[tr].max)) {
1533 secfile_insert_int(sfile, traits[tr].max, "%s.%s%s_max", secname, field_prefix,
1534 trait_names[tr]);
1536 if (default_default != traits[tr].fixed) {
1537 secfile_insert_int(sfile, traits[tr].fixed, "%s.%s%s_default", secname, field_prefix,
1538 trait_names[tr]);
1542 return TRUE;
1545 /**************************************************************************
1546 Save a single nation.
1547 **************************************************************************/
1548 static bool save_nation(struct section_file *sfile, struct nation_type *pnat,
1549 int sect_idx)
1551 char path[512];
1552 int max_items = nation_city_list_size(pnat->server.default_cities);
1553 char *city_str[max_items];
1554 max_items = MAX(max_items, MAX_NUM_NATION_SETS + MAX_NUM_NATION_GROUPS);
1555 max_items = MAX(max_items, game.control.nation_count);
1556 const char *list_items[max_items];
1557 int set_count;
1558 int subsect_idx;
1560 fc_snprintf(path, sizeof(path), "nation_%d", sect_idx++);
1562 if (pnat->translation_domain == NULL) {
1563 secfile_insert_str(sfile, "freeciv", "%s.translation_domain", path);
1564 } else {
1565 secfile_insert_str(sfile, pnat->translation_domain, "%s.translation_domain", path);
1568 save_name_translation(sfile, &(pnat->adjective), path);
1569 secfile_insert_str(sfile, untranslated_name(&(pnat->noun_plural)), "%s.plural", path);
1571 set_count = 0;
1572 nation_sets_iterate(pset) {
1573 if (nation_is_in_set(pnat, pset)) {
1574 list_items[set_count++] = nation_set_rule_name(pset);
1576 } nation_sets_iterate_end;
1577 nation_groups_iterate(pgroup) {
1578 if (nation_is_in_group(pnat, pgroup)) {
1579 list_items[set_count++] = nation_group_rule_name(pgroup);
1581 } nation_groups_iterate_end;
1583 if (set_count > 0) {
1584 secfile_insert_str_vec(sfile, list_items, set_count, "%s.groups", path);
1587 set_count = 0;
1588 nation_list_iterate(pnat->server.conflicts_with, pconfl) {
1589 list_items[set_count++] = nation_rule_name(pconfl);
1590 } nation_list_iterate_end;
1591 if (set_count > 0) {
1592 secfile_insert_str_vec(sfile, list_items, set_count, "%s.conflicts_with", path);
1595 subsect_idx = 0;
1596 nation_leader_list_iterate(pnat->leaders, pleader) {
1597 secfile_insert_str(sfile, nation_leader_name(pleader), "%s.leaders%d.name",
1598 path, subsect_idx);
1599 secfile_insert_str(sfile, nation_leader_is_male(pleader) ? "Male" : "Female",
1600 "%s.leaders%d.sex", path, subsect_idx++);
1601 } nation_leader_list_iterate_end;
1603 if (pnat->server.rgb != NULL) {
1604 rgbcolor_save(sfile, pnat->server.rgb, "%s.color", path);
1607 save_traits(pnat->server.traits, game.server.default_traits,
1608 sfile, path, "trait_");
1610 if (!pnat->is_playable) {
1611 secfile_insert_bool(sfile, pnat->is_playable, "%s.is_playable", path);
1614 if (pnat->barb_type != NOT_A_BARBARIAN) {
1615 secfile_insert_str(sfile, barbarian_type_name(pnat->barb_type),
1616 "%s.barbarian_type", path);
1619 if (strcmp(pnat->flag_graphic_str, "-")) {
1620 secfile_insert_str(sfile, pnat->flag_graphic_str, "%s.flag", path);
1622 if (strcmp(pnat->flag_graphic_alt, "-")) {
1623 secfile_insert_str(sfile, pnat->flag_graphic_alt, "%s.flag_alt", path);
1626 subsect_idx = 0;
1627 governments_iterate(pgov) {
1628 struct ruler_title *prtitle;
1630 if (ruler_title_hash_lookup(pgov->ruler_titles, pnat, &prtitle)) {
1631 secfile_insert_str(sfile, government_rule_name(pgov),
1632 "%s.ruler_titles%d.government", path, subsect_idx);
1633 secfile_insert_str(sfile, ruler_title_male_untranslated_name(prtitle),
1634 "%s.ruler_titles%d.male_title", path, subsect_idx);
1635 secfile_insert_str(sfile, ruler_title_female_untranslated_name(prtitle),
1636 "%s.ruler_titles%d.female_title", path, subsect_idx++);
1638 } governments_iterate_end;
1640 secfile_insert_str(sfile, style_rule_name(pnat->style), "%s.style", path);
1642 set_count = 0;
1643 nation_list_iterate(pnat->server.civilwar_nations, pconfl) {
1644 list_items[set_count++] = nation_rule_name(pconfl);
1645 } nation_list_iterate_end;
1646 if (set_count > 0) {
1647 secfile_insert_str_vec(sfile, list_items, set_count, "%s.civilwar_nations", path);
1650 save_tech_list(sfile, pnat->init_techs, path, "init_techs");
1651 save_building_list(sfile, pnat->init_buildings, path, "init_buildings");
1652 save_unit_list(sfile, pnat->init_units, path, "init_units");
1654 if (pnat->init_government) {
1655 secfile_insert_str(sfile, government_rule_name(pnat->init_government),
1656 "%s.init_government", path);
1659 set_count = 0;
1660 nation_city_list_iterate(pnat->server.default_cities, pncity) {
1661 bool list_started = FALSE;
1663 city_str[set_count] = fc_malloc(strlen(nation_city_name(pncity)) + strlen(" (!river")
1664 + strlen(")")
1665 + MAX_NUM_TERRAINS * (strlen(", ") + MAX_LEN_NAME));
1667 strcpy(city_str[set_count], nation_city_name(pncity));
1668 switch(nation_city_river_preference(pncity)) {
1669 case NCP_DISLIKE:
1670 strcat(city_str[set_count], " (!river");
1671 list_started = TRUE;
1672 break;
1673 case NCP_LIKE:
1674 strcat(city_str[set_count], " (river");
1675 list_started = TRUE;
1676 break;
1677 case NCP_NONE:
1678 break;
1681 terrain_type_iterate(pterr) {
1682 const char *pref = NULL;
1684 switch(nation_city_terrain_preference(pncity, pterr)) {
1685 case NCP_DISLIKE:
1686 pref = "!";
1687 break;
1688 case NCP_LIKE:
1689 pref = "";
1690 break;
1691 case NCP_NONE:
1692 pref = NULL;
1693 break;
1696 if (pref != NULL) {
1697 if (list_started) {
1698 strcat(city_str[set_count], ", ");
1699 } else {
1700 strcat(city_str[set_count], " (");
1701 list_started = TRUE;
1703 strcat(city_str[set_count], pref);
1704 strcat(city_str[set_count], terrain_rule_name(pterr));
1707 } terrain_type_iterate_end;
1709 if (list_started) {
1710 strcat(city_str[set_count], ")");
1713 list_items[set_count] = city_str[set_count];
1714 set_count++;
1715 } nation_city_list_iterate_end;
1716 if (set_count > 0) {
1717 int i;
1719 secfile_insert_str_vec(sfile, list_items, set_count, "%s.cities", path);
1721 for (i = 0; i < set_count; i++) {
1722 FC_FREE(city_str[i]);
1726 secfile_insert_str(sfile, pnat->legend, "%s.legend", path);
1728 return TRUE;
1731 /**************************************************************************
1732 Save nations.ruleset
1733 **************************************************************************/
1734 static bool save_nations_ruleset(const char *filename, const char *name,
1735 struct rule_data *data)
1737 struct section_file *sfile = create_ruleset_file(name, "nation");
1739 if (sfile == NULL) {
1740 return FALSE;
1743 if (data->nationlist != NULL) {
1744 secfile_insert_str(sfile, data->nationlist, "ruledit.nationlist");
1746 if (game.server.ruledit.embedded_nations != NULL) {
1747 int i;
1748 const char **tmp = fc_malloc(game.server.ruledit.embedded_nations_count * sizeof(char *));
1750 /* Dance around the secfile_insert_str_vec() parameter type (requires extra const)
1751 * resrictions */
1752 for (i = 0; i < game.server.ruledit.embedded_nations_count; i++) {
1753 tmp[i] = game.server.ruledit.embedded_nations[i];
1756 secfile_insert_str_vec(sfile, tmp,
1757 game.server.ruledit.embedded_nations_count,
1758 "ruledit.embedded_nations");
1759 free(tmp);
1762 save_traits(game.server.default_traits, NULL, sfile,
1763 "default_traits", "");
1765 if (data->nationlist == NULL) {
1766 if (game.server.ruledit.allowed_govs != NULL) {
1767 secfile_insert_str_vec(sfile, game.server.ruledit.allowed_govs,
1768 game.server.ruledit.ag_count,
1769 "compatibility.allowed_govs");
1771 if (game.server.ruledit.allowed_terrains != NULL) {
1772 secfile_insert_str_vec(sfile, game.server.ruledit.allowed_terrains,
1773 game.server.ruledit.at_count,
1774 "compatibility.allowed_terrains");
1776 if (game.server.ruledit.allowed_styles != NULL) {
1777 secfile_insert_str_vec(sfile, game.server.ruledit.allowed_styles,
1778 game.server.ruledit.as_count,
1779 "compatibility.allowed_styles");
1783 if (game.default_government != NULL) {
1784 secfile_insert_str(sfile, government_rule_name(game.default_government),
1785 "compatibility.default_government");
1788 if (data->nationlist != NULL) {
1789 secfile_insert_include(sfile, data->nationlist);
1791 if (game.server.ruledit.embedded_nations != NULL) {
1792 int sect_idx;
1794 comment_nations(sfile);
1796 for (sect_idx = 0; sect_idx < game.server.ruledit.embedded_nations_count;
1797 sect_idx++) {
1798 struct nation_type *pnat
1799 = nation_by_rule_name(game.server.ruledit.embedded_nations[sect_idx]);
1801 if (pnat == NULL) {
1802 log_error("Embedded nation \"%s\" not found!",
1803 game.server.ruledit.embedded_nations[sect_idx]);
1804 } else {
1805 save_nation(sfile, pnat, sect_idx);
1809 } else {
1810 int sect_idx = 0;
1812 comment_nationsets(sfile);
1814 nation_sets_iterate(pset) {
1815 char path[512];
1817 fc_snprintf(path, sizeof(path), "nset_%d", sect_idx++);
1819 /* We don't use save_name_translation() for this as name and rule_name must
1820 * always be saved separately */
1821 secfile_insert_str(sfile, nation_set_untranslated_name(pset), "%s.name", path);
1822 secfile_insert_str(sfile, nation_set_rule_name(pset), "%s.rule_name", path);
1823 secfile_insert_str(sfile, nation_set_description(pset), "%s.description", path);
1824 } nation_sets_iterate_end;
1826 comment_nationgroups(sfile);
1828 sect_idx = 0;
1829 nation_groups_iterate(pgroup) {
1830 char path[512];
1832 fc_snprintf(path, sizeof(path), "ngroup_%d", sect_idx++);
1834 save_name_translation(sfile, &(pgroup->name), path);
1836 secfile_insert_int(sfile, pgroup->server.match, "%s.match", path);
1837 if (pgroup->hidden) {
1838 secfile_insert_bool(sfile, pgroup->hidden, "%s.hidden", path);
1840 } nation_groups_iterate_end;
1842 comment_nations(sfile);
1844 sect_idx = 0;
1845 nations_iterate(pnat) {
1846 save_nation(sfile, pnat, sect_idx++);
1847 } nations_iterate_end;
1850 return save_ruleset_file(sfile, filename);
1853 /**************************************************************************
1854 Save techs.ruleset
1855 **************************************************************************/
1856 static bool save_techs_ruleset(const char *filename, const char *name)
1858 struct section_file *sfile = create_ruleset_file(name, "tech");
1859 int i;
1860 int sect_idx;
1861 struct advance *a_none = advance_by_number(A_NONE);
1864 if (sfile == NULL) {
1865 return FALSE;
1868 for (i = 0; i < MAX_NUM_USER_TECH_FLAGS; i++) {
1869 const char *flagname = tech_flag_id_name_cb(i + TECH_USER_1);
1870 const char *helptxt = tech_flag_helptxt(i + TECH_USER_1);
1872 if (flagname != NULL) {
1873 secfile_insert_str(sfile, flagname, "control.flags%d.name", i);
1875 /* Save the user flag help text even when it is undefined. That makes
1876 * the formatting code happy. The resulting "" is ignored when the
1877 * ruleset is loaded. */
1878 secfile_insert_str(sfile, helptxt, "control.flags%d.helptxt", i);
1882 comment_tech_classes(sfile);
1884 sect_idx = 0;
1885 tech_class_iterate(ptclass) {
1886 char path[512];
1888 fc_snprintf(path, sizeof(path), "techclass_%d", sect_idx++);
1890 save_name_translation(sfile, &(ptclass->name), path);
1891 } tech_class_iterate_end;
1893 comment_techs(sfile);
1895 sect_idx = 0;
1896 advance_active_iterate(pa) {
1897 if (pa->require[AR_ONE] != A_NEVER) {
1898 char path[512];
1899 const char *flag_names[TF_COUNT];
1900 int set_count;
1901 int flagi;
1903 fc_snprintf(path, sizeof(path), "advance_%d", sect_idx++);
1905 save_name_translation(sfile, &(pa->name), path);
1907 if (game.control.num_tech_classes > 0) {
1908 if (pa->tclass != NULL) {
1909 secfile_insert_str(sfile, tech_class_rule_name(pa->tclass),
1910 "%s.class", path);
1914 save_tech_ref(sfile, pa->require[AR_ONE], path, "req1");
1915 save_tech_ref(sfile, pa->require[AR_TWO], path, "req2");
1916 if (pa->require[AR_ROOT] != a_none) {
1917 save_tech_ref(sfile, pa->require[AR_ROOT], path, "root_req");
1920 save_reqs_vector(sfile, &(pa->research_reqs), path,
1921 "research_reqs");
1923 secfile_insert_str(sfile, pa->graphic_str, "%s.graphic", path);
1924 if (strcmp("-", pa->graphic_alt)) {
1925 secfile_insert_str(sfile, pa->graphic_alt, "%s.graphic_alt", path);
1927 if (pa->bonus_message != NULL) {
1928 secfile_insert_str(sfile, pa->bonus_message, "%s.bonus_message", path);
1931 set_count = 0;
1932 for (flagi = 0; flagi < TF_COUNT; flagi++) {
1933 if (advance_has_flag(advance_index(pa), flagi)) {
1934 flag_names[set_count++] = tech_flag_id_name(flagi);
1938 if (set_count > 0) {
1939 secfile_insert_str_vec(sfile, flag_names, set_count,
1940 "%s.flags", path);
1942 if (pa->cost >= 0) {
1943 secfile_insert_int(sfile, pa->cost, "%s.cost", path);
1946 save_strvec(sfile, pa->helptext, path, "helptext");
1949 } advance_active_iterate_end;
1951 return save_ruleset_file(sfile, filename);
1954 /**************************************************************************
1955 Save terrain.ruleset
1956 **************************************************************************/
1957 static bool save_terrain_ruleset(const char *filename, const char *name)
1959 struct section_file *sfile = create_ruleset_file(name, "terrain");
1960 int sect_idx;
1961 int i;
1963 if (sfile == NULL) {
1964 return FALSE;
1967 for (i = 0; i < MAX_NUM_USER_TER_FLAGS; i++) {
1968 const char *flagname = terrain_flag_id_name_cb(i + TER_USER_1);
1969 const char *helptxt = terrain_flag_helptxt(i + TER_USER_1);
1971 if (flagname != NULL) {
1972 secfile_insert_str(sfile, flagname, "control.flags%d.name", i);
1974 /* Save the user flag help text even when it is undefined. That makes
1975 * the formatting code happy. The resulting "" is ignored when the
1976 * ruleset is loaded. */
1977 secfile_insert_str(sfile, helptxt, "control.flags%d.helptxt", i);
1981 for (i = 0; i < MAX_NUM_USER_EXTRA_FLAGS; i++) {
1982 const char *flagname = extra_flag_id_name_cb(i + EF_USER_FLAG_1);
1983 const char *helptxt = extra_flag_helptxt(i + EF_USER_FLAG_1);
1985 if (flagname != NULL) {
1986 secfile_insert_str(sfile, flagname, "control.extra_flags%d.name", i);
1988 /* Save the user flag help text even when it is undefined. That makes
1989 * the formatting code happy. The resulting "" is ignored when the
1990 * ruleset is loaded. */
1991 secfile_insert_str(sfile, helptxt,
1992 "control.extra_flags%d.helptxt", i);
1996 if (terrain_control.ocean_reclaim_requirement_pct <= 100) {
1997 secfile_insert_int(sfile, terrain_control.ocean_reclaim_requirement_pct,
1998 "parameters.ocean_reclaim_requirement");
2000 if (terrain_control.land_channel_requirement_pct <= 100) {
2001 secfile_insert_int(sfile, terrain_control.land_channel_requirement_pct,
2002 "parameters.land_channel_requirement");
2004 if (terrain_control.terrain_thaw_requirement_pct <= 100) {
2005 secfile_insert_int(sfile, terrain_control.terrain_thaw_requirement_pct,
2006 "parameters.thaw_requirement");
2008 if (terrain_control.terrain_freeze_requirement_pct <= 100) {
2009 secfile_insert_int(sfile, terrain_control.terrain_freeze_requirement_pct,
2010 "parameters.freeze_requirement");
2012 if (terrain_control.lake_max_size != 0) {
2013 secfile_insert_int(sfile, terrain_control.lake_max_size,
2014 "parameters.lake_max_size");
2016 if (terrain_control.min_start_native_area != 0) {
2017 secfile_insert_int(sfile, terrain_control.min_start_native_area,
2018 "parameters.min_start_native_area");
2020 if (terrain_control.move_fragments != 3) {
2021 secfile_insert_int(sfile, terrain_control.move_fragments,
2022 "parameters.move_fragments");
2024 if (terrain_control.igter_cost != 1) {
2025 secfile_insert_int(sfile, terrain_control.igter_cost,
2026 "parameters.igter_cost");
2028 if (terrain_control.pythagorean_diagonal != RS_DEFAULT_PYTHAGOREAN_DIAGONAL) {
2029 secfile_insert_bool(sfile, TRUE,
2030 "parameters.pythagorean_diagonal");
2032 if (wld.map.server.ocean_resources) {
2033 secfile_insert_bool(sfile, TRUE,
2034 "parameters.ocean_resources");
2037 comment_terrains(sfile);
2039 sect_idx = 0;
2040 terrain_type_iterate(pterr) {
2041 char path[512];
2042 char identifier[2];
2043 int r;
2044 const char *flag_names[TER_USER_LAST];
2045 const char *puc_names[UCL_LAST];
2046 int flagi;
2047 int set_count;
2049 fc_snprintf(path, sizeof(path), "terrain_%d", sect_idx++);
2051 save_name_translation(sfile, &(pterr->name), path);
2053 secfile_insert_str(sfile, pterr->graphic_str, "%s.graphic", path);
2054 secfile_insert_str(sfile, pterr->graphic_alt, "%s.graphic_alt", path);
2055 identifier[0] = pterr->identifier;
2056 identifier[1] = '\0';
2057 secfile_insert_str(sfile, identifier, "%s.identifier", path);
2059 secfile_insert_str(sfile, terrain_class_name(pterr->tclass),
2060 "%s.class", path);
2062 secfile_insert_int(sfile, pterr->movement_cost, "%s.movement_cost", path);
2063 secfile_insert_int(sfile, pterr->defense_bonus, "%s.defense_bonus", path);
2065 output_type_iterate(o) {
2066 if (pterr->output[o] != 0) {
2067 secfile_insert_int(sfile, pterr->output[o], "%s.%s", path,
2068 get_output_identifier(o));
2070 } output_type_iterate_end;
2072 /* Check resource count */
2073 for (r = 0; pterr->resources[r] != NULL; r++) {
2074 /* Just increasing r as long as there is resources */
2078 const char *resource_names[r];
2080 for (r = 0; pterr->resources[r] != NULL; r++) {
2081 resource_names[r] = extra_rule_name(pterr->resources[r]);
2084 secfile_insert_str_vec(sfile, resource_names, r,
2085 "%s.resources", path);
2088 output_type_iterate(o) {
2089 if (pterr->road_output_incr_pct[o] != 0) {
2090 secfile_insert_int(sfile, pterr->road_output_incr_pct[o],
2091 "%s.road_%s_incr_pct", path,
2092 get_output_identifier(o));
2094 } output_type_iterate_end;
2096 secfile_insert_int(sfile, pterr->base_time, "%s.base_time", path);
2097 secfile_insert_int(sfile, pterr->road_time, "%s.road_time", path);
2099 save_terrain_ref(sfile, pterr->irrigation_result, pterr, path,
2100 "irrigation_result");
2101 secfile_insert_int(sfile, pterr->irrigation_food_incr,
2102 "%s.irrigation_food_incr", path);
2103 secfile_insert_int(sfile, pterr->irrigation_time,
2104 "%s.irrigation_time", path);
2106 save_terrain_ref(sfile, pterr->mining_result, pterr, path,
2107 "mining_result");
2108 secfile_insert_int(sfile, pterr->mining_shield_incr,
2109 "%s.mining_shield_incr", path);
2110 secfile_insert_int(sfile, pterr->mining_time,
2111 "%s.mining_time", path);
2113 save_terrain_ref(sfile, pterr->transform_result, pterr, path,
2114 "transform_result");
2115 secfile_insert_int(sfile, pterr->transform_time,
2116 "%s.transform_time", path);
2118 if (pterr->animal != NULL) {
2119 secfile_insert_str(sfile, utype_rule_name(pterr->animal),
2120 "%s.animal", path);
2121 } else {
2122 secfile_insert_str(sfile, "None",
2123 "%s.animal", path);
2126 secfile_insert_int(sfile, pterr->pillage_time,
2127 "%s.pillage_time", path);
2128 secfile_insert_int(sfile, pterr->clean_pollution_time,
2129 "%s.clean_pollution_time", path);
2130 secfile_insert_int(sfile, pterr->clean_fallout_time,
2131 "%s.clean_fallout_time", path);
2133 save_terrain_ref(sfile, pterr->warmer_wetter_result, pterr, path,
2134 "warmer_wetter_result");
2135 save_terrain_ref(sfile, pterr->warmer_drier_result, pterr, path,
2136 "warmer_drier_result");
2137 save_terrain_ref(sfile, pterr->cooler_wetter_result, pterr, path,
2138 "cooler_wetter_result");
2139 save_terrain_ref(sfile, pterr->cooler_drier_result, pterr, path,
2140 "cooler_drier_result");
2142 set_count = 0;
2143 for (flagi = 0; flagi < TER_USER_LAST; flagi++) {
2144 if (terrain_has_flag(pterr, flagi)) {
2145 flag_names[set_count++] = terrain_flag_id_name(flagi);
2149 if (set_count > 0) {
2150 secfile_insert_str_vec(sfile, flag_names, set_count,
2151 "%s.flags", path);
2155 enum mapgen_terrain_property mtp;
2157 for (mtp = mapgen_terrain_property_begin();
2158 mtp != mapgen_terrain_property_end();
2159 mtp = mapgen_terrain_property_next(mtp)) {
2160 if (pterr->property[mtp] != 0) {
2161 secfile_insert_int(sfile, pterr->property[mtp],
2162 "%s.property_%s", path,
2163 mapgen_terrain_property_name(mtp));
2168 set_count = 0;
2169 unit_class_iterate(puc) {
2170 if (BV_ISSET(pterr->native_to, uclass_index(puc))) {
2171 puc_names[set_count++] = uclass_rule_name(puc);
2173 } unit_class_iterate_end;
2175 if (set_count > 0) {
2176 secfile_insert_str_vec(sfile, puc_names, set_count,
2177 "%s.native_to", path);
2180 rgbcolor_save(sfile, pterr->rgb, "%s.color", path);
2182 save_strvec(sfile, pterr->helptext, path, "helptext");
2184 } terrain_type_iterate_end;
2186 comment_resources(sfile);
2188 sect_idx = 0;
2189 extra_type_by_cause_iterate(EC_RESOURCE, pres) {
2190 if (!pres->disabled) {
2191 char path[512];
2192 char identifier[2];
2194 fc_snprintf(path, sizeof(path), "resource_%d", sect_idx++);
2196 secfile_insert_str(sfile, extra_rule_name(pres),
2197 "%s.extra", path);
2199 output_type_iterate(o) {
2200 if (pres->data.resource->output[o] != 0) {
2201 secfile_insert_int(sfile, pres->data.resource->output[o], "%s.%s",
2202 path, get_output_identifier(o));
2204 } output_type_iterate_end;
2206 identifier[0] = pres->data.resource->id_old_save;
2207 identifier[1] = '\0';
2208 secfile_insert_str(sfile, identifier, "%s.identifier", path);
2210 } extra_type_by_cause_iterate_end;
2212 secfile_insert_str(sfile, terrain_control.gui_type_base0,
2213 "extraui.ui_name_base_fortress");
2214 secfile_insert_str(sfile, terrain_control.gui_type_base1,
2215 "extraui.ui_name_base_airbase");
2217 comment_extras(sfile);
2219 sect_idx = 0;
2220 extra_active_type_iterate(pextra) {
2221 char path[512];
2222 const char *flag_names[EF_COUNT];
2223 const char *cause_names[EC_COUNT];
2224 const char *puc_names[UCL_LAST];
2225 const char *extra_names[MAX_EXTRA_TYPES];
2226 int flagi;
2227 int causei;
2228 int set_count;
2230 fc_snprintf(path, sizeof(path), "extra_%d", sect_idx++);
2232 save_name_translation(sfile, &(pextra->name), path);
2234 secfile_insert_str(sfile, extra_category_name(pextra->category),
2235 "%s.category", path);
2237 set_count = 0;
2238 for (causei = 0; causei < EC_COUNT; causei++) {
2239 if (is_extra_caused_by(pextra, causei)) {
2240 cause_names[set_count++] = extra_cause_name(causei);
2244 if (set_count > 0) {
2245 secfile_insert_str_vec(sfile, cause_names, set_count,
2246 "%s.causes", path);
2249 set_count = 0;
2250 for (causei = 0; causei < ERM_COUNT; causei++) {
2251 if (is_extra_removed_by(pextra, causei)) {
2252 cause_names[set_count++] = extra_rmcause_name(causei);
2256 if (set_count > 0) {
2257 secfile_insert_str_vec(sfile, cause_names, set_count,
2258 "%s.rmcauses", path);
2261 if (strcmp(pextra->graphic_str, "-")) {
2262 secfile_insert_str(sfile, pextra->graphic_str, "%s.graphic", path);
2264 if (strcmp(pextra->graphic_alt, "-")) {
2265 secfile_insert_str(sfile, pextra->graphic_alt, "%s.graphic_alt", path);
2267 if (strcmp(pextra->activity_gfx, "-")) {
2268 secfile_insert_str(sfile, pextra->activity_gfx, "%s.activity_gfx", path);
2270 if (strcmp(pextra->act_gfx_alt, "-")) {
2271 secfile_insert_str(sfile, pextra->act_gfx_alt, "%s.act_gfx_alt", path);
2273 if (strcmp(pextra->act_gfx_alt2, "-")) {
2274 secfile_insert_str(sfile, pextra->act_gfx_alt2, "%s.act_gfx_alt2", path);
2276 if (strcmp(pextra->rmact_gfx, "-")) {
2277 secfile_insert_str(sfile, pextra->rmact_gfx, "%s.rmact_gfx", path);
2279 if (strcmp(pextra->rmact_gfx_alt, "-")) {
2280 secfile_insert_str(sfile, pextra->rmact_gfx_alt, "%s.rmact_gfx_alt", path);
2283 save_reqs_vector(sfile, &(pextra->reqs), path, "reqs");
2284 save_reqs_vector(sfile, &(pextra->rmreqs), path, "rmreqs");
2285 save_reqs_vector(sfile, &(pextra->appearance_reqs), path, "appearance_reqs");
2286 save_reqs_vector(sfile, &(pextra->disappearance_reqs), path, "disappearance_reqs");
2288 if (!pextra->buildable) {
2289 secfile_insert_bool(sfile, pextra->buildable, "%s.buildable", path);
2291 secfile_insert_int(sfile, pextra->build_time, "%s.build_time", path);
2292 secfile_insert_int(sfile, pextra->removal_time, "%s.removal_time", path);
2293 if (pextra->build_time_factor != 1) {
2294 secfile_insert_int(sfile, pextra->build_time_factor, "%s.build_time_factor", path);
2296 if (pextra->removal_time_factor != 1) {
2297 secfile_insert_int(sfile, pextra->removal_time_factor, "%s.removal_time_factor", path);
2299 if (pextra->defense_bonus != 0) {
2300 secfile_insert_int(sfile, pextra->defense_bonus, "%s.defense_bonus", path);
2302 if (pextra->eus != EUS_NORMAL) {
2303 secfile_insert_str(sfile, extra_unit_seen_type_name(pextra->eus),
2304 "%s.unit_seen", path);
2306 if (is_extra_caused_by(pextra, EC_APPEARANCE)
2307 && pextra->appearance_chance != RS_DEFAULT_EXTRA_APPEARANCE) {
2308 secfile_insert_int(sfile, pextra->appearance_chance, "%s.appearance_chance", path);
2310 if (is_extra_removed_by(pextra, ERM_DISAPPEARANCE)
2311 && pextra->disappearance_chance != RS_DEFAULT_EXTRA_DISAPPEARANCE) {
2312 secfile_insert_int(sfile, pextra->disappearance_chance, "%s.disappearance_chance",
2313 path);
2316 set_count = 0;
2317 unit_class_iterate(puc) {
2318 if (BV_ISSET(pextra->native_to, uclass_index(puc))) {
2319 puc_names[set_count++] = uclass_rule_name(puc);
2321 } unit_class_iterate_end;
2323 if (set_count > 0) {
2324 secfile_insert_str_vec(sfile, puc_names, set_count,
2325 "%s.native_to", path);
2328 set_count = 0;
2329 for (flagi = 0; flagi < EF_COUNT; flagi++) {
2330 if (extra_has_flag(pextra, flagi)) {
2331 flag_names[set_count++] = extra_flag_id_name(flagi);
2335 if (set_count > 0) {
2336 secfile_insert_str_vec(sfile, flag_names, set_count,
2337 "%s.flags", path);
2340 set_count = 0;
2341 extra_type_iterate(confl) {
2342 if (!can_extras_coexist(pextra, confl)) {
2343 extra_names[set_count++] = extra_rule_name(confl);
2345 } extra_type_iterate_end;
2347 if (set_count > 0) {
2348 secfile_insert_str_vec(sfile, extra_names, set_count,
2349 "%s.conflicts", path);
2352 set_count = 0;
2353 extra_type_iterate(top) {
2354 if (BV_ISSET(pextra->hidden_by, extra_index(top))) {
2355 extra_names[set_count++] = extra_rule_name(top);
2357 } extra_type_iterate_end;
2359 if (set_count > 0) {
2360 secfile_insert_str_vec(sfile, extra_names, set_count,
2361 "%s.hidden_by", path);
2364 save_strvec(sfile, pextra->helptext, path, "helptext");
2366 } extra_active_type_iterate_end;
2368 comment_bases(sfile);
2370 sect_idx = 0;
2371 extra_type_by_cause_iterate(EC_BASE, pextra) {
2372 if (!pextra->disabled) {
2373 char path[512];
2374 struct base_type *pbase = extra_base_get(pextra);
2375 const char *flag_names[BF_COUNT];
2376 int flagi;
2377 int set_count;
2379 fc_snprintf(path, sizeof(path), "base_%d", sect_idx++);
2381 secfile_insert_str(sfile, extra_rule_name(pextra),
2382 "%s.extra", path);
2384 secfile_insert_str(sfile, base_gui_type_name(pbase->gui_type),
2385 "%s.gui_type", path);
2387 if (pbase->border_sq >= 0) {
2388 secfile_insert_int(sfile, pbase->border_sq, "%s.border_sq", path);
2390 if (pbase->vision_main_sq >= 0) {
2391 secfile_insert_int(sfile, pbase->vision_main_sq, "%s.vision_main_sq", path);
2394 set_count = 0;
2395 for (flagi = 0; flagi < BF_COUNT; flagi++) {
2396 if (base_has_flag(pbase, flagi)) {
2397 flag_names[set_count++] = base_flag_id_name(flagi);
2401 if (set_count > 0) {
2402 secfile_insert_str_vec(sfile, flag_names, set_count,
2403 "%s.flags", path);
2406 } extra_type_by_cause_iterate_end;
2408 comment_roads(sfile);
2410 sect_idx = 0;
2411 extra_type_by_cause_iterate(EC_ROAD, pextra) {
2412 if (!pextra->disabled) {
2413 struct road_type *proad = extra_road_get(pextra);
2414 char path[512];
2415 const char *flag_names[RF_COUNT];
2416 int flagi;
2417 int set_count;
2419 fc_snprintf(path, sizeof(path), "road_%d", sect_idx++);
2421 secfile_insert_str(sfile, extra_rule_name(pextra),
2422 "%s.extra", path);
2424 secfile_insert_int(sfile, proad->move_cost, "%s.move_cost", path);
2426 if (proad->move_mode != RMM_FAST_ALWAYS) {
2427 secfile_insert_str(sfile, road_move_mode_name(proad->move_mode),
2428 "%s.move_mode", path);
2431 output_type_iterate(o) {
2432 if (proad->tile_incr_const[o] != 0) {
2433 secfile_insert_int(sfile, proad->tile_incr_const[o],
2434 "%s.%s_incr_const", path, get_output_identifier(o));
2436 if (proad->tile_incr[o] != 0) {
2437 secfile_insert_int(sfile, proad->tile_incr[o],
2438 "%s.%s_incr", path, get_output_identifier(o));
2440 if (proad->tile_bonus[o] != 0) {
2441 secfile_insert_int(sfile, proad->tile_bonus[o],
2442 "%s.%s_bonus", path, get_output_identifier(o));
2444 } output_type_iterate_end;
2446 switch (proad->compat) {
2447 case ROCO_ROAD:
2448 secfile_insert_str(sfile, "Road", "%s.compat_special", path);
2449 break;
2450 case ROCO_RAILROAD:
2451 secfile_insert_str(sfile, "Railroad", "%s.compat_special", path);
2452 break;
2453 case ROCO_RIVER:
2454 secfile_insert_str(sfile, "River", "%s.compat_special", path);
2455 break;
2456 case ROCO_NONE:
2457 secfile_insert_str(sfile, "None", "%s.compat_special", path);
2458 break;
2461 set_count = 0;
2462 for (flagi = 0; flagi < RF_COUNT; flagi++) {
2463 if (road_has_flag(proad, flagi)) {
2464 flag_names[set_count++] = road_flag_id_name(flagi);
2468 if (set_count > 0) {
2469 secfile_insert_str_vec(sfile, flag_names, set_count,
2470 "%s.flags", path);
2473 } extra_type_by_cause_iterate_end;
2475 return save_ruleset_file(sfile, filename);
2478 /**************************************************************************
2479 Save one veteran system.
2480 **************************************************************************/
2481 static bool save_veteran_system(struct section_file *sfile, const char *path,
2482 struct veteran_system *vsystem)
2484 const char *vlist_name[vsystem->levels];
2485 int vlist_power[vsystem->levels];
2486 int vlist_raise[vsystem->levels];
2487 int vlist_wraise[vsystem->levels];
2488 int vlist_move[vsystem->levels];
2489 int i;
2491 for (i = 0; i < vsystem->levels; i++) {
2492 vlist_name[i] = rule_name_get(&(vsystem->definitions[i].name));
2493 vlist_power[i] = vsystem->definitions[i].power_fact;
2494 vlist_raise[i] = vsystem->definitions[i].raise_chance;
2495 vlist_wraise[i] = vsystem->definitions[i].work_raise_chance;
2496 vlist_move[i] = vsystem->definitions[i].move_bonus;
2499 secfile_insert_str_vec(sfile, vlist_name, vsystem->levels,
2500 "%s.veteran_names", path);
2501 secfile_insert_int_vec(sfile, vlist_power, vsystem->levels,
2502 "%s.veteran_power_fact", path);
2503 secfile_insert_int_vec(sfile, vlist_raise, vsystem->levels,
2504 "%s.veteran_raise_chance", path);
2505 secfile_insert_int_vec(sfile, vlist_wraise, vsystem->levels,
2506 "%s.veteran_work_raise_chance", path);
2507 secfile_insert_int_vec(sfile, vlist_move, vsystem->levels,
2508 "%s.veteran_move_bonus", path);
2510 return TRUE;
2513 /**************************************************************************
2514 Save unit combat bonuses list.
2515 **************************************************************************/
2516 static bool save_combat_bonuses(struct section_file *sfile,
2517 struct unit_type *put,
2518 char *path)
2520 int i;
2521 bool has_quiet = FALSE;
2523 combat_bonus_list_iterate(put->bonuses, pbonus) {
2524 if (pbonus->quiet) {
2525 has_quiet = TRUE;
2527 } combat_bonus_list_iterate_end;
2529 i = 0;
2531 combat_bonus_list_iterate(put->bonuses, pbonus) {
2532 secfile_insert_str(sfile, unit_type_flag_id_name(pbonus->flag),
2533 "%s.bonuses%d.flag", path, i);
2534 secfile_insert_str(sfile, combat_bonus_type_name(pbonus->type),
2535 "%s.bonuses%d.type", path, i);
2536 secfile_insert_int(sfile, pbonus->value,
2537 "%s.bonuses%d.value", path, i);
2539 if (has_quiet) {
2540 secfile_insert_bool(sfile, pbonus->quiet,
2541 "%s.bonuses%d.quiet", path, i);
2544 i++;
2545 } combat_bonus_list_iterate_end;
2547 return TRUE;
2550 /**************************************************************************
2551 Save units.ruleset
2552 **************************************************************************/
2553 static bool save_units_ruleset(const char *filename, const char *name)
2555 struct section_file *sfile = create_ruleset_file(name, "unit");
2556 int i;
2557 int sect_idx;
2559 if (sfile == NULL) {
2560 return FALSE;
2563 for (i = 0; i < MAX_NUM_USER_UNIT_FLAGS; i++) {
2564 const char *flagname = unit_type_flag_id_name_cb(i + UTYF_USER_FLAG_1);
2565 const char *helptxt = unit_type_flag_helptxt(i + UTYF_USER_FLAG_1);
2567 if (flagname != NULL) {
2568 secfile_insert_str(sfile, flagname, "control.flags%d.name", i);
2570 /* Save the user flag help text even when it is undefined. That makes
2571 * the formatting code happy. The resulting "" is ignored when the
2572 * ruleset is loaded. */
2573 secfile_insert_str(sfile, helptxt, "control.flags%d.helptxt", i);
2577 for (i = 0; i < MAX_NUM_USER_UCLASS_FLAGS; i++) {
2578 const char *flagname = unit_class_flag_id_name_cb(i + UCF_USER_FLAG_1);
2579 const char *helptxt = unit_class_flag_helptxt(i + UCF_USER_FLAG_1);
2581 if (flagname != NULL) {
2582 secfile_insert_str(sfile, flagname, "control.class_flags%d.name", i);
2584 /* Save the user flag help text even when it is undefined. That makes
2585 * the formatting code happy. The resulting "" is ignored when the
2586 * ruleset is loaded. */
2587 secfile_insert_str(sfile, helptxt,
2588 "control.class_flags%d.helptxt", i);
2592 save_veteran_system(sfile, "veteran_system", game.veteran);
2594 comment_uclasses(sfile);
2596 sect_idx = 0;
2597 unit_active_class_iterate(puc) {
2598 char path[512];
2599 char *hut_str = NULL;
2600 const char *flag_names[UCF_COUNT];
2601 int flagi;
2602 int set_count;
2604 fc_snprintf(path, sizeof(path), "unitclass_%d", sect_idx++);
2606 save_name_translation(sfile, &(puc->name), path);
2608 secfile_insert_int(sfile, puc->min_speed / SINGLE_MOVE,
2609 "%s.min_speed", path);
2610 secfile_insert_int(sfile, puc->hp_loss_pct, "%s.hp_loss_pct", path);
2611 if (puc->non_native_def_pct != 100) {
2612 secfile_insert_int(sfile, puc->non_native_def_pct,
2613 "%s.non_native_def_pct", path);
2615 if (puc->hut_behavior != HUT_NORMAL) {
2616 switch (puc->hut_behavior) {
2617 case HUT_NORMAL:
2618 hut_str = "Normal";
2619 break;
2620 case HUT_NOTHING:
2621 hut_str = "Nothing";
2622 break;
2623 case HUT_FRIGHTEN:
2624 hut_str = "Frighten";
2625 break;
2627 fc_assert(hut_str != NULL);
2628 secfile_insert_str(sfile, hut_str, "%s.hut_behavior", path);
2631 set_count = 0;
2632 for (flagi = 0; flagi < UCF_COUNT; flagi++) {
2633 if (uclass_has_flag(puc, flagi)) {
2634 flag_names[set_count++] = unit_class_flag_id_name(flagi);
2638 if (set_count > 0) {
2639 secfile_insert_str_vec(sfile, flag_names, set_count,
2640 "%s.flags", path);
2643 save_strvec(sfile, puc->helptext, path, "helptext");
2645 } unit_active_class_iterate_end;
2647 comment_utypes(sfile);
2649 sect_idx = 0;
2650 unit_active_type_iterate(put) {
2651 if (!put->disabled) {
2652 char path[512];
2653 const char *flag_names[UTYF_LAST_USER_FLAG + 1];
2654 int flagi;
2655 int set_count;
2657 fc_snprintf(path, sizeof(path), "unit_%d", sect_idx++);
2659 save_name_translation(sfile, &(put->name), path);
2661 secfile_insert_str(sfile, uclass_rule_name(put->uclass),
2662 "%s.class", path);
2664 save_tech_ref(sfile, put->require_advance, path, "tech_req");
2666 if (put->need_government != NULL) {
2667 secfile_insert_str(sfile, government_rule_name(put->need_government),
2668 "%s.gov_req", path);
2671 if (put->need_improvement != NULL) {
2672 secfile_insert_str(sfile, improvement_rule_name(put->need_improvement),
2673 "%s.impr_req", path);
2676 if (put->obsoleted_by != NULL) {
2677 secfile_insert_str(sfile, utype_rule_name(put->obsoleted_by),
2678 "%s.obsolete_by", path);
2681 secfile_insert_str(sfile, put->graphic_str, "%s.graphic", path);
2682 if (strcmp("-", put->graphic_alt)) {
2683 secfile_insert_str(sfile, put->graphic_alt, "%s.graphic_alt", path);
2685 if (strcmp("-", put->sound_move)) {
2686 secfile_insert_str(sfile, put->sound_move, "%s.sound_move", path);
2688 if (strcmp("-", put->sound_move_alt)) {
2689 secfile_insert_str(sfile, put->sound_move_alt, "%s.sound_move_alt", path);
2691 if (strcmp("-", put->sound_fight)) {
2692 secfile_insert_str(sfile, put->sound_fight, "%s.sound_fight", path);
2694 if (strcmp("-", put->sound_fight_alt)) {
2695 secfile_insert_str(sfile, put->sound_fight_alt, "%s.sound_fight_alt", path);
2698 secfile_insert_int(sfile, put->build_cost, "%s.build_cost", path);
2699 secfile_insert_int(sfile, put->pop_cost, "%s.pop_cost", path);
2700 secfile_insert_int(sfile, put->attack_strength, "%s.attack", path);
2701 secfile_insert_int(sfile, put->defense_strength, "%s.defense", path);
2702 secfile_insert_int(sfile, put->move_rate / SINGLE_MOVE, "%s.move_rate", path);
2703 secfile_insert_int(sfile, put->vision_radius_sq, "%s.vision_radius_sq", path);
2704 secfile_insert_int(sfile, put->transport_capacity, "%s.transport_cap", path);
2706 save_uclass_vec(sfile, &(put->cargo), path, "cargo", FALSE);
2707 save_uclass_vec(sfile, &(put->embarks), path, "embarks", TRUE);
2708 save_uclass_vec(sfile, &(put->disembarks), path, "disembarks", TRUE);
2710 secfile_insert_int(sfile, put->hp, "%s.hitpoints", path);
2711 secfile_insert_int(sfile, put->firepower, "%s.firepower", path);
2712 secfile_insert_int(sfile, put->fuel, "%s.fuel", path);
2713 secfile_insert_int(sfile, put->happy_cost, "%s.uk_happy", path);
2715 output_type_iterate(o) {
2716 if (put->upkeep[o] != 0) {
2717 secfile_insert_int(sfile, put->upkeep[o], "%s.uk_%s",
2718 path, get_output_identifier(o));
2720 } output_type_iterate_end;
2722 if (put->converted_to != NULL) {
2723 secfile_insert_str(sfile, utype_rule_name(put->converted_to),
2724 "%s.convert_to", path);
2726 if (put->convert_time != 1) {
2727 secfile_insert_int(sfile, put->convert_time, "%s.convert_time", path);
2730 save_combat_bonuses(sfile, put, path);
2731 save_uclass_vec(sfile, &(put->targets), path, "targets", TRUE);
2733 if (put->veteran != NULL) {
2734 save_veteran_system(sfile, path, put->veteran);
2737 if (put->paratroopers_range != 0) {
2738 secfile_insert_int(sfile, put->paratroopers_range,
2739 "%s.paratroopers_range", path);
2740 secfile_insert_int(sfile, put->paratroopers_mr_req / SINGLE_MOVE,
2741 "%s.paratroopers_mr_req", path);
2742 secfile_insert_int(sfile, put->paratroopers_mr_sub / SINGLE_MOVE,
2743 "%s.paratroopers_mr_sub", path);
2745 if (put->bombard_rate != 0) {
2746 secfile_insert_int(sfile, put->bombard_rate,
2747 "%s.bombard_rate", path);
2749 if (put->city_slots != 0) {
2750 secfile_insert_int(sfile, put->city_slots,
2751 "%s.city_slots", path);
2753 if (put->city_size != 1) {
2754 secfile_insert_int(sfile, put->city_size,
2755 "%s.city_size", path);
2758 set_count = 0;
2759 for (flagi = 0; flagi <= UTYF_LAST_USER_FLAG; flagi++) {
2760 if (utype_has_flag(put, flagi)) {
2761 flag_names[set_count++] = unit_type_flag_id_name(flagi);
2765 if (set_count > 0) {
2766 secfile_insert_str_vec(sfile, flag_names, set_count,
2767 "%s.flags", path);
2770 set_count = 0;
2771 for (flagi = L_FIRST; flagi < L_LAST; flagi++) {
2772 if (utype_has_role(put, flagi)) {
2773 flag_names[set_count++] = unit_role_id_name(flagi);
2777 if (set_count > 0) {
2778 secfile_insert_str_vec(sfile, flag_names, set_count,
2779 "%s.roles", path);
2782 save_strvec(sfile, put->helptext, path, "helptext");
2784 } unit_active_type_iterate_end;
2786 return save_ruleset_file(sfile, filename);
2789 /**************************************************************************
2790 Save script.lua
2791 **************************************************************************/
2792 static bool save_script_lua(const char *filename, const char *name)
2794 char *buffer = get_script_buffer();
2796 if (buffer != NULL) {
2797 FILE *ffile = fc_fopen(filename, "w");
2798 int full_len = strlen(buffer);
2799 int len;
2801 if (ffile != NULL) {
2802 len = fwrite(buffer, 1, full_len, ffile);
2804 if (len != full_len) {
2805 return FALSE;
2808 fclose(ffile);
2809 } else {
2810 return FALSE;
2814 return TRUE;
2817 /**************************************************************************
2818 Save ruleset to directory given.
2819 **************************************************************************/
2820 bool save_ruleset(const char *path, const char *name, struct rule_data *data)
2822 if (make_dir(path)) {
2823 bool success = TRUE;
2824 char filename[500];
2826 if (success) {
2827 fc_snprintf(filename, sizeof(filename), "%s/buildings.ruleset", path);
2828 success = save_buildings_ruleset(filename, name);
2831 if (success) {
2832 fc_snprintf(filename, sizeof(filename), "%s/styles.ruleset", path);
2833 success = save_styles_ruleset(filename, name);
2836 if (success) {
2837 fc_snprintf(filename, sizeof(filename), "%s/cities.ruleset", path);
2838 success = save_cities_ruleset(filename, name);
2841 if (success) {
2842 fc_snprintf(filename, sizeof(filename), "%s/effects.ruleset", path);
2843 success = save_effects_ruleset(filename, name);
2846 if (success) {
2847 fc_snprintf(filename, sizeof(filename), "%s/game.ruleset", path);
2848 success = save_game_ruleset(filename, name);
2851 if (success) {
2852 fc_snprintf(filename, sizeof(filename), "%s/governments.ruleset", path);
2853 success = save_governments_ruleset(filename, name);
2856 if (success) {
2857 fc_snprintf(filename, sizeof(filename), "%s/nations.ruleset", path);
2858 success = save_nations_ruleset(filename, name, data);
2861 if (success) {
2862 fc_snprintf(filename, sizeof(filename), "%s/techs.ruleset", path);
2863 success = save_techs_ruleset(filename, name);
2866 if (success) {
2867 fc_snprintf(filename, sizeof(filename), "%s/terrain.ruleset", path);
2868 success = save_terrain_ruleset(filename, name);
2871 if (success) {
2872 fc_snprintf(filename, sizeof(filename), "%s/units.ruleset", path);
2873 success = save_units_ruleset(filename, name);
2876 if (success) {
2877 fc_snprintf(filename, sizeof(filename), "%s/script.lua", path);
2878 success = save_script_lua(filename, name);
2881 return success;
2882 } else {
2883 log_error("Failed to create directory %s", path);
2884 return FALSE;
2887 return TRUE;