Add 'parser.lua' to rulesets
[freeciv.git] / tools / ruleutil / rulesave.c
blobd95c4e4936f56437bb0db1efc521784c4c813aae
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/ruleutil */
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 if (game.ruleset_capabilities != NULL) {
864 secfile_insert_str(sfile, game.ruleset_capabilities,
865 "about.capabilities");
866 } else {
867 secfile_insert_str(sfile, "", "about.capabilities");
870 save_tech_list(sfile, game.rgame.global_init_techs,
871 "options", "global_init_techs");
872 save_building_list(sfile, game.rgame.global_init_buildings,
873 "options", "global_init_buildings");
875 save_default_bool(sfile, game.control.popup_tech_help,
876 FALSE,
877 "options.popup_tech_help", NULL);
878 save_default_int(sfile, game.info.base_pollution,
879 RS_DEFAULT_BASE_POLLUTION,
880 "civstyle.base_pollution", NULL);
882 set_count = 0;
883 for (gs = gameloss_style_begin(); gs != gameloss_style_end(); gs = gameloss_style_next(gs)) {
884 if (game.info.gameloss_style & gs) {
885 style_names[set_count++] = gameloss_style_name(gs);
889 if (set_count > 0) {
890 secfile_insert_str_vec(sfile, style_names, set_count,
891 "civstyle.gameloss_style");
894 save_default_int(sfile, game.info.happy_cost,
895 RS_DEFAULT_HAPPY_COST,
896 "civstyle.happy_cost", NULL);
897 save_default_int(sfile, game.info.food_cost,
898 RS_DEFAULT_FOOD_COST,
899 "civstyle.food_cost", NULL);
900 save_default_bool(sfile, game.info.civil_war_enabled,
901 TRUE,
902 "civstyle.civil_war_enabled", NULL);
903 save_default_bool(sfile, game.info.paradrop_to_transport,
904 FALSE,
905 "civstyle.paradrop_to_transport", NULL);
906 save_default_int(sfile, game.info.base_bribe_cost,
907 RS_DEFAULT_BASE_BRIBE_COST,
908 "civstyle.base_bribe_cost", NULL);
909 save_default_int(sfile, game.server.ransom_gold,
910 RS_DEFAULT_RANSOM_GOLD,
911 "civstyle.ransom_gold", NULL);
912 save_default_bool(sfile, game.info.pillage_select,
913 RS_DEFAULT_PILLAGE_SELECT,
914 "civstyle.pillage_select", NULL);
915 save_default_bool(sfile, game.info.tech_steal_allow_holes,
916 RS_DEFAULT_TECH_STEAL_HOLES,
917 "civstyle.tech_steal_allow_holes", NULL);
918 save_default_bool(sfile, game.info.tech_trade_allow_holes,
919 RS_DEFAULT_TECH_TRADE_HOLES,
920 "civstyle.tech_trade_allow_holes", NULL);
921 save_default_bool(sfile, game.info.tech_trade_loss_allow_holes,
922 RS_DEFAULT_TECH_TRADE_LOSS_HOLES,
923 "civstyle.tech_trade_loss_allow_holes", NULL);
924 save_default_bool(sfile, game.info.tech_parasite_allow_holes,
925 RS_DEFAULT_TECH_PARASITE_HOLES,
926 "civstyle.tech_parasite_allow_holes", NULL);
927 save_default_bool(sfile, game.info.tech_loss_allow_holes,
928 RS_DEFAULT_TECH_LOSS_HOLES,
929 "civstyle.tech_loss_allow_holes", NULL);
930 save_default_int(sfile, game.server.upgrade_veteran_loss,
931 RS_DEFAULT_UPGRADE_VETERAN_LOSS,
932 "civstyle.upgrade_veteran_loss", NULL);
933 save_default_int(sfile, game.server.autoupgrade_veteran_loss,
934 RS_DEFAULT_UPGRADE_VETERAN_LOSS,
935 "civstyle.autoupgrade_veteran_loss", NULL);
937 secfile_insert_int_vec(sfile, game.info.granary_food_ini,
938 game.info.granary_num_inis,
939 "civstyle.granary_food_ini");
941 save_default_int(sfile, game.info.granary_food_inc,
942 RS_DEFAULT_GRANARY_FOOD_INC,
943 "civstyle.granary_food_inc", NULL);
945 output_type_iterate(o) {
946 char buffer[256];
948 fc_snprintf(buffer, sizeof(buffer),
949 "civstyle.min_city_center_%s",
950 get_output_identifier(o));
952 save_default_int(sfile, game.info.min_city_center_output[o],
953 RS_DEFAULT_CITY_CENTER_OUTPUT,
954 buffer, NULL);
955 } output_type_iterate_end;
957 save_default_int(sfile, game.server.init_vis_radius_sq,
958 RS_DEFAULT_VIS_RADIUS_SQ,
959 "civstyle.init_vis_radius_sq", NULL);
960 save_default_int(sfile, game.info.init_city_radius_sq,
961 RS_DEFAULT_CITY_RADIUS_SQ,
962 "civstyle.init_city_radius_sq", NULL);
963 if (0 != fc_strcasecmp(gold_upkeep_style_name(game.info.gold_upkeep_style),
964 RS_DEFAULT_GOLD_UPKEEP_STYLE)) {
965 secfile_insert_str(sfile,
966 gold_upkeep_style_name(game.info.gold_upkeep_style),
967 "civstyle.gold_upkeep_style");
969 save_default_bool(sfile, game.info.illness_on,
970 RS_DEFAULT_ILLNESS_ON,
971 "illness.illness_on", NULL);
972 save_default_int(sfile, game.info.illness_base_factor,
973 RS_DEFAULT_ILLNESS_BASE_FACTOR,
974 "illness.illness_base_factor", NULL);
975 save_default_int(sfile, game.info.illness_min_size,
976 RS_DEFAULT_ILLNESS_MIN_SIZE,
977 "illness.illness_min_size", NULL);
978 save_default_int(sfile, game.info.illness_trade_infection,
979 RS_DEFAULT_ILLNESS_TRADE_INFECTION_PCT,
980 "illness.illness_trade_infection", NULL);
981 save_default_int(sfile, game.info.illness_pollution_factor,
982 RS_DEFAULT_ILLNESS_POLLUTION_PCT,
983 "illness.illness_pollution_factor", NULL);
984 save_default_int(sfile, game.server.base_incite_cost,
985 RS_DEFAULT_INCITE_BASE_COST,
986 "incite_cost.base_incite_cost", NULL);
987 save_default_int(sfile, game.server.incite_improvement_factor,
988 RS_DEFAULT_INCITE_IMPROVEMENT_FCT,
989 "incite_cost.improvement_factor", NULL);
990 save_default_int(sfile, game.server.incite_unit_factor,
991 RS_DEFAULT_INCITE_UNIT_FCT,
992 "incite_cost.unit_factor", NULL);
993 save_default_int(sfile, game.server.incite_total_factor,
994 RS_DEFAULT_INCITE_TOTAL_FCT,
995 "incite_cost.total_factor", NULL);
996 save_default_bool(sfile, game.info.slow_invasions,
997 RS_DEFAULT_SLOW_INVASIONS,
998 "global_unit_options.slow_invasions", NULL);
1000 save_action_auto_uflag_block(sfile, ACTION_AUTO_MOVED_ADJ,
1001 "auto_attack.will_never",
1002 unexpected_auto_attack);
1004 save_default_bool(sfile,
1005 action_id_would_be_blocked_by(ACTION_MARKETPLACE,
1006 ACTION_TRADE_ROUTE),
1007 RS_DEFAULT_FORCE_TRADE_ROUTE,
1008 "actions.force_trade_route", NULL);
1010 /* The ruleset options force_capture_units, force_bombard and
1011 * force_explode_nuclear sets their respective action to block many other
1012 * actions' blocked_by. Checking one is therefore enough. */
1013 force_capture_units =
1014 BV_ISSET(action_by_number(ACTION_ATTACK)->blocked_by,
1015 ACTION_CAPTURE_UNITS);
1016 save_default_bool(sfile, force_capture_units,
1017 RS_DEFAULT_FORCE_CAPTURE_UNITS,
1018 "actions.force_capture_units", NULL);
1019 force_bombard =
1020 BV_ISSET(action_by_number(ACTION_ATTACK)->blocked_by, ACTION_BOMBARD);
1021 save_default_bool(sfile, force_bombard,
1022 RS_DEFAULT_FORCE_BOMBARD,
1023 "actions.force_bombard", NULL);
1024 force_explode_nuclear =
1025 BV_ISSET(action_by_number(ACTION_ATTACK)->blocked_by, ACTION_NUKE);
1026 save_default_bool(sfile, force_explode_nuclear,
1027 RS_DEFAULT_FORCE_EXPLODE_NUCLEAR,
1028 "actions.force_explode_nuclear", NULL);
1030 save_default_bool(sfile, game.info.poison_empties_food_stock,
1031 RS_DEFAULT_POISON_EMPTIES_FOOD_STOCK,
1032 "actions.poison_empties_food_stock", NULL);
1034 if (action_by_number(ACTION_BOMBARD)->max_distance
1035 == ACTION_DISTANCE_UNLIMITED) {
1036 secfile_insert_str(sfile, RS_ACTION_NO_MAX_DISTANCE,
1037 "actions.bombard_max_range");
1038 } else {
1039 save_default_int(sfile, action_by_number(ACTION_BOMBARD)->max_distance,
1040 RS_DEFAULT_BOMBARD_MAX_RANGE,
1041 "actions.bombard_max_range", NULL);
1044 secfile_insert_str(sfile,
1045 action_by_number(ACTION_SPY_POISON)->ui_name,
1046 "actions.ui_name_poison_city");
1047 secfile_insert_str(sfile,
1048 action_by_number(ACTION_SPY_SABOTAGE_UNIT)->ui_name,
1049 "actions.ui_name_sabotage_unit");
1050 secfile_insert_str(sfile,
1051 action_by_number(ACTION_SPY_BRIBE_UNIT)->ui_name,
1052 "actions.ui_name_bribe_unit");
1053 secfile_insert_str(sfile,
1054 action_by_number(ACTION_SPY_SABOTAGE_CITY)->ui_name,
1055 "actions.ui_name_sabotage_city");
1056 secfile_insert_str(sfile,
1057 action_by_number(ACTION_SPY_TARGETED_SABOTAGE_CITY)->ui_name,
1058 "actions.ui_name_targeted_sabotage_city");
1059 secfile_insert_str(sfile,
1060 action_by_number(ACTION_SPY_INCITE_CITY)->ui_name,
1061 "actions.ui_name_incite_city");
1062 secfile_insert_str(sfile,
1063 action_by_number(ACTION_SPY_INCITE_CITY_ESC)->ui_name,
1064 "actions.ui_name_incite_city_escape");
1065 secfile_insert_str(sfile,
1066 action_by_number(ACTION_ESTABLISH_EMBASSY)->ui_name,
1067 "actions.ui_name_establish_embassy");
1068 secfile_insert_str(sfile,
1069 action_by_number(ACTION_ESTABLISH_EMBASSY_STAY)->ui_name,
1070 "actions.ui_name_establish_embassy_stay");
1071 secfile_insert_str(sfile,
1072 action_by_number(ACTION_SPY_STEAL_TECH)->ui_name,
1073 "actions.ui_name_steal_tech");
1074 secfile_insert_str(sfile,
1075 action_by_number(ACTION_SPY_TARGETED_STEAL_TECH)->ui_name,
1076 "actions.ui_name_targeted_steal_tech");
1077 secfile_insert_str(sfile,
1078 action_by_number(ACTION_SPY_INVESTIGATE_CITY)->ui_name,
1079 "actions.ui_name_investigate_city");
1080 secfile_insert_str(sfile,
1081 action_by_number(ACTION_INV_CITY_SPEND)->ui_name,
1082 "actions.ui_name_investigate_city_spend_unit");
1083 secfile_insert_str(sfile,
1084 action_by_number(ACTION_SPY_STEAL_GOLD)->ui_name,
1085 "actions.ui_name_steal_gold");
1086 secfile_insert_str(sfile,
1087 action_by_number(ACTION_STEAL_MAPS)->ui_name,
1088 "actions.ui_name_steal_maps");
1089 secfile_insert_str(sfile,
1090 action_by_number(ACTION_TRADE_ROUTE)->ui_name,
1091 "actions.ui_name_establish_trade_route");
1092 secfile_insert_str(sfile,
1093 action_by_number(ACTION_MARKETPLACE)->ui_name,
1094 "actions.ui_name_enter_marketplace");
1095 secfile_insert_str(sfile,
1096 action_by_number(ACTION_HELP_WONDER)->ui_name,
1097 "actions.ui_name_help_wonder");
1098 secfile_insert_str(sfile,
1099 action_by_number(ACTION_CAPTURE_UNITS)->ui_name,
1100 "actions.ui_name_capture_units");
1101 secfile_insert_str(sfile,
1102 action_by_number(ACTION_EXPEL_UNIT)->ui_name,
1103 "actions.ui_name_expel_unit");
1104 secfile_insert_str(sfile,
1105 action_by_number(ACTION_FOUND_CITY)->ui_name,
1106 "actions.ui_name_found_city");
1107 secfile_insert_str(sfile,
1108 action_by_number(ACTION_JOIN_CITY)->ui_name,
1109 "actions.ui_name_join_city");
1110 secfile_insert_str(sfile,
1111 action_by_number(ACTION_BOMBARD)->ui_name,
1112 "actions.ui_name_bombard");
1113 secfile_insert_str(sfile,
1114 action_by_number(ACTION_SPY_NUKE)->ui_name,
1115 "actions.ui_name_suitcase_nuke");
1116 secfile_insert_str(sfile,
1117 action_by_number(ACTION_SPY_NUKE_ESC)->ui_name,
1118 "actions.ui_name_suitcase_nuke_escape");
1119 secfile_insert_str(sfile,
1120 action_by_number(ACTION_NUKE)->ui_name,
1121 "actions.ui_name_explode_nuclear");
1122 secfile_insert_str(sfile,
1123 action_by_number(ACTION_DESTROY_CITY)->ui_name,
1124 "actions.ui_name_destroy_city");
1125 secfile_insert_str(sfile,
1126 action_by_number(ACTION_RECYCLE_UNIT)->ui_name,
1127 "actions.ui_name_recycle_unit");
1128 secfile_insert_str(sfile,
1129 action_by_number(ACTION_DISBAND_UNIT)->ui_name,
1130 "actions.ui_name_disband_unit");
1131 secfile_insert_str(sfile,
1132 action_by_number(ACTION_HOME_CITY)->ui_name,
1133 "actions.ui_name_home_city");
1134 secfile_insert_str(sfile,
1135 action_by_number(ACTION_UPGRADE_UNIT)->ui_name,
1136 "actions.ui_upgrade_unit");
1137 secfile_insert_str(sfile,
1138 action_by_number(ACTION_PARADROP)->ui_name,
1139 "actions.ui_paradrop_unit");
1140 secfile_insert_str(sfile,
1141 action_by_number(ACTION_AIRLIFT)->ui_name,
1142 "actions.ui_airlift_unit");
1143 secfile_insert_str(sfile,
1144 action_by_number(ACTION_ATTACK)->ui_name,
1145 "actions.ui_name_attack");
1146 secfile_insert_str(sfile,
1147 action_by_number(ACTION_CONQUER_CITY)->ui_name,
1148 "actions.ui_name_conquer_city");
1149 secfile_insert_str(sfile,
1150 action_by_number(ACTION_HEAL_UNIT)->ui_name,
1151 "actions.ui_name_heal_unit");
1153 i = 0;
1154 action_iterate(act) {
1155 if (action_by_number(act)->quiet) {
1156 quiet_actions[i] = act;
1157 i++;
1159 } action_iterate_end;
1161 if (secfile_insert_enum_vec(sfile, &quiet_actions, i, gen_action,
1162 "actions.quiet_actions") != i) {
1163 log_error("Didn't save all quiet actions.");
1165 return FALSE;
1168 comment_enablers(sfile);
1169 sect_idx = 0;
1170 action_enablers_iterate(pae) {
1171 char path[512];
1173 fc_snprintf(path, sizeof(path), "actionenabler_%d", sect_idx++);
1175 secfile_insert_str(sfile, gen_action_name(pae->action),
1176 "%s.action", path);
1178 save_reqs_vector(sfile, &(pae->actor_reqs), path, "actor_reqs");
1179 save_reqs_vector(sfile, &(pae->target_reqs), path, "target_reqs");
1180 } action_enablers_iterate_end;
1182 save_default_bool(sfile, game.info.tired_attack,
1183 RS_DEFAULT_TIRED_ATTACK,
1184 "combat_rules.tired_attack", NULL);
1185 save_default_int(sfile, game.info.border_city_radius_sq,
1186 RS_DEFAULT_BORDER_RADIUS_SQ_CITY,
1187 "borders.radius_sq_city", NULL);
1188 save_default_int(sfile, game.info.border_size_effect,
1189 RS_DEFAULT_BORDER_SIZE_EFFECT,
1190 "borders.size_effect", NULL);
1191 save_default_int(sfile, game.info.border_city_permanent_radius_sq,
1192 RS_DEFAULT_BORDER_RADIUS_SQ_CITY_PERMANENT,
1193 "borders.radius_sq_city_permanent", NULL);
1194 secfile_insert_str(sfile, tech_cost_style_name(game.info.tech_cost_style),
1195 "research.tech_cost_style");
1196 save_default_int(sfile, game.info.base_tech_cost,
1197 RS_DEFAULT_BASE_TECH_COST,
1198 "research.base_tech_cost", NULL);
1199 secfile_insert_str(sfile, tech_leakage_style_name(game.info.tech_leakage),
1200 "research.tech_leakage");
1201 secfile_insert_str(sfile, tech_upkeep_style_name(game.info.tech_upkeep_style),
1202 "research.tech_upkeep_style");
1203 save_default_int(sfile, game.info.tech_upkeep_divider,
1204 RS_DEFAULT_TECH_UPKEEP_DIVIDER,
1205 "research.tech_upkeep_divider", NULL);
1206 secfile_insert_str(sfile, free_tech_method_name(game.info.free_tech_method),
1207 "research.free_tech_method");
1209 save_default_int(sfile, game.info.culture_vic_points,
1210 RS_DEFAULT_CULTURE_VIC_POINTS,
1211 "culture.victory_min_points", NULL);
1212 save_default_int(sfile, game.info.culture_vic_lead,
1213 RS_DEFAULT_CULTURE_VIC_LEAD,
1214 "culture.victory_lead_pct", NULL);
1215 save_default_int(sfile, game.info.culture_migration_pml,
1216 RS_DEFAULT_CULTURE_MIGRATION_PML,
1217 "culture.migration_pml", NULL);
1219 save_default_bool(sfile, game.calendar.calendar_skip_0,
1220 RS_DEFAULT_CALENDAR_SKIP_0,
1221 "calendar.skip_year_0", NULL);
1222 save_default_int(sfile, game.server.start_year,
1223 GAME_START_YEAR,
1224 "calendar.start_year", NULL);
1225 save_default_int(sfile, game.calendar.calendar_fragments,
1226 0, "calendar.fragments", NULL);
1228 for (i = 0; i < MAX_CALENDAR_FRAGMENTS; i++) {
1229 if (game.calendar.calendar_fragment_name[i][0] != '\0') {
1230 secfile_insert_str(sfile, game.calendar.calendar_fragment_name[i],
1231 "calendar.fragment_name%d", i);
1235 if (strcmp(game.calendar.positive_year_label, RS_DEFAULT_POS_YEAR_LABEL)) {
1236 secfile_insert_str(sfile, game.calendar.positive_year_label,
1237 "calendar.positive_label");
1239 if (strcmp(game.calendar.negative_year_label, RS_DEFAULT_NEG_YEAR_LABEL)) {
1240 secfile_insert_str(sfile, game.calendar.negative_year_label,
1241 "calendar.negative_label");
1244 if (game.plr_bg_color != NULL) {
1245 rgbcolor_save(sfile, game.plr_bg_color, "playercolors.background");
1248 col_idx = 0;
1249 rgbcolor_list_iterate(game.server.plr_colors, pcol) {
1250 rgbcolor_save(sfile, pcol, "playercolors.colorlist%d", col_idx++);
1251 } rgbcolor_list_iterate_end;
1254 if (game.server.ruledit.named_teams > 0) {
1255 for (i = 0; i < game.server.ruledit.named_teams; i++) {
1256 tnames[i] = team_slot_rule_name(team_slot_by_number(i));
1259 secfile_insert_str_vec(sfile, tnames,
1260 game.server.ruledit.named_teams,
1261 "teams.names");
1264 comment_disasters(sfile);
1266 sect_idx = 0;
1267 disaster_type_iterate(pd) {
1268 char path[512];
1269 enum disaster_effect_id de;
1270 const char *effect_names[DE_COUNT];
1272 fc_snprintf(path, sizeof(path), "disaster_%d", sect_idx++);
1274 save_name_translation(sfile, &(pd->name), path);
1275 save_reqs_vector(sfile, &(pd->reqs), path, "reqs");
1276 if (pd->frequency != GAME_DEFAULT_DISASTER_FREQ) {
1277 secfile_insert_int(sfile, pd->frequency,
1278 "%s.frequency", path);
1281 set_count = 0;
1282 for (de = disaster_effect_id_begin();
1283 de != disaster_effect_id_end();
1284 de = disaster_effect_id_next(de)) {
1285 if (BV_ISSET(pd->effects, de)) {
1286 effect_names[set_count++] = disaster_effect_id_name(de);
1290 if (set_count > 0) {
1291 secfile_insert_str_vec(sfile, effect_names, set_count,
1292 "%s.effects", path);
1294 } disaster_type_iterate_end;
1296 comment_achievements(sfile);
1298 sect_idx = 0;
1299 achievements_iterate(pach) {
1300 char path[512];
1302 fc_snprintf(path, sizeof(path), "achievement_%d", sect_idx++);
1304 save_name_translation(sfile, &(pach->name), path);
1306 secfile_insert_str(sfile, achievement_type_name(pach->type),
1307 "%s.type", path);
1309 save_default_bool(sfile, pach->unique,
1310 GAME_DEFAULT_ACH_UNIQUE,
1311 path, "unique");
1312 save_default_int(sfile, pach->value,
1313 GAME_DEFAULT_ACH_VALUE,
1314 path, "value");
1315 save_default_int(sfile, pach->culture,
1316 0, path, "culture");
1318 secfile_insert_str(sfile, pach->first_msg, "%s.first_msg", path);
1319 if (pach->cons_msg != NULL) {
1320 secfile_insert_str(sfile, pach->cons_msg, "%s.cons_msg", path);
1323 } achievements_iterate_end;
1325 set_count = 0;
1326 for (trt = 0; trt < TRT_LAST; trt++) {
1327 struct trade_route_settings *set = trade_route_settings_by_type(trt);
1328 const char *cancelling = traderoute_cancelling_type_name(set->cancelling);
1330 if (set->trade_pct != 100 || strcmp(cancelling, "Active")) {
1331 char path[256];
1333 fc_snprintf(path, sizeof(path),
1334 "trade.settings%d", set_count++);
1336 secfile_insert_str(sfile, trade_route_type_name(trt),
1337 "%s.type", path);
1338 secfile_insert_int(sfile, set->trade_pct,
1339 "%s.pct", path);
1340 secfile_insert_str(sfile, cancelling,
1341 "%s.cancelling", path);
1342 secfile_insert_str(sfile, traderoute_bonus_type_name(set->bonus_type),
1343 "%s.bonus", path);
1347 /* Goods */
1348 comment_goods(sfile);
1350 sect_idx = 0;
1351 goods_active_type_iterate(pgood) {
1352 char path[512];
1353 const char *flag_names[GF_COUNT];
1354 int flagi;
1356 fc_snprintf(path, sizeof(path), "goods_%d", sect_idx++);
1358 save_name_translation(sfile, &(pgood->name), path);
1360 save_reqs_vector(sfile, &(pgood->reqs), path, "reqs");
1362 save_default_int(sfile, pgood->from_pct, 100, path, "from_pct");
1363 save_default_int(sfile, pgood->to_pct, 100, path, "to_pct");
1365 set_count = 0;
1366 for (flagi = 0; flagi < GF_COUNT; flagi++) {
1367 if (goods_has_flag(pgood, flagi)) {
1368 flag_names[set_count++] = goods_flag_id_name(flagi);
1372 if (set_count > 0) {
1373 secfile_insert_str_vec(sfile, flag_names, set_count,
1374 "%s.flags", path);
1377 save_strvec(sfile, pgood->helptext, path, "helptext");
1378 } goods_active_type_iterate_end;
1380 locks = FALSE;
1381 settings_iterate(SSET_ALL, pset) {
1382 if (setting_locked(pset)) {
1383 locks = TRUE;
1384 break;
1386 } settings_iterate_end;
1388 set_count = 0;
1389 settings_iterate(SSET_ALL, pset) {
1390 if (setting_get_setdef(pset) == SETDEF_RULESET || setting_locked(pset)) {
1391 secfile_insert_str(sfile, setting_name(pset),
1392 "settings.set%d.name", set_count);
1393 switch (setting_type(pset)) {
1394 case SST_BOOL:
1395 secfile_insert_bool(sfile, setting_bool_get(pset),
1396 "settings.set%d.value", set_count);
1397 break;
1398 case SST_INT:
1399 secfile_insert_int(sfile, setting_int_get(pset),
1400 "settings.set%d.value", set_count);
1401 break;
1402 case SST_STRING:
1403 secfile_insert_str(sfile, setting_str_get(pset),
1404 "settings.set%d.value", set_count);
1405 break;
1406 case SST_ENUM:
1407 secfile_insert_enum_data(sfile, read_enum_value(pset), FALSE,
1408 setting_enum_secfile_str, pset,
1409 "settings.set%d.value", set_count);
1410 break;
1411 case SST_BITWISE:
1412 secfile_insert_enum_data(sfile, setting_bitwise_get(pset), TRUE,
1413 setting_bitwise_secfile_str, pset,
1414 "settings.set%d.value", set_count);
1415 break;
1416 case SST_COUNT:
1417 fc_assert(setting_type(pset) != SST_COUNT);
1418 secfile_insert_str(sfile, "Unknown setting type",
1419 "settings.set%d.value", set_count);
1420 break;
1423 if (locks) {
1424 secfile_insert_bool(sfile, setting_locked(pset),
1425 "settings.set%d.lock", set_count);
1428 set_count++;
1430 } settings_iterate_end;
1432 return save_ruleset_file(sfile, filename);
1435 /**************************************************************************
1436 Save governments.ruleset
1437 **************************************************************************/
1438 static bool save_governments_ruleset(const char *filename, const char *name)
1440 struct section_file *sfile = create_ruleset_file(name, "government");
1441 int sect_idx;
1443 if (sfile == NULL) {
1444 return FALSE;
1447 save_gov_ref(sfile, game.government_during_revolution, "governments",
1448 "during_revolution");
1450 comment_govs(sfile);
1452 sect_idx = 0;
1453 governments_active_iterate(pg) {
1454 char path[512];
1455 struct ruler_title *prtitle;
1457 fc_snprintf(path, sizeof(path), "government_%d", sect_idx++);
1459 save_name_translation(sfile, &(pg->name), path);
1461 secfile_insert_str(sfile, pg->graphic_str, "%s.graphic", path);
1462 secfile_insert_str(sfile, pg->graphic_alt, "%s.graphic_alt", path);
1464 save_reqs_vector(sfile, &(pg->reqs), path, "reqs");
1466 if (pg->ai.better != NULL) {
1467 save_gov_ref(sfile, pg->ai.better, path,
1468 "ai_better");
1471 ruler_title_hash_lookup(pg->ruler_titles, NULL,
1472 &prtitle);
1473 if (prtitle != NULL) {
1474 const char *title;
1476 title = ruler_title_male_untranslated_name(prtitle);
1477 if (title != NULL) {
1478 secfile_insert_str(sfile, title,
1479 "%s.ruler_male_title", path);
1482 title = ruler_title_female_untranslated_name(prtitle);
1483 if (title != NULL) {
1484 secfile_insert_str(sfile, title,
1485 "%s.ruler_female_title", path);
1489 save_strvec(sfile, pg->helptext, path, "helptext");
1491 } governments_active_iterate_end;
1493 comment_policies(sfile);
1495 sect_idx = 0;
1496 multipliers_iterate(pmul) {
1497 char path[512];
1499 fc_snprintf(path, sizeof(path), "multiplier_%d", sect_idx++);
1501 save_name_translation(sfile, &(pmul->name), path);
1503 secfile_insert_int(sfile, pmul->start, "%s.start", path);
1504 secfile_insert_int(sfile, pmul->stop, "%s.stop", path);
1505 secfile_insert_int(sfile, pmul->step, "%s.step", path);
1506 secfile_insert_int(sfile, pmul->def, "%s.default", path);
1508 save_strvec(sfile, pmul->helptext, path, "helptext");
1509 } multipliers_iterate_end;
1511 return save_ruleset_file(sfile, filename);
1514 /**************************************************************************
1515 Save list of AI traits
1516 **************************************************************************/
1517 static bool save_traits(struct trait_limits *traits,
1518 struct trait_limits *default_traits,
1519 struct section_file *sfile,
1520 const char *secname, const char *field_prefix)
1522 enum trait tr;
1524 /* FIXME: Use specenum trait names without duplicating them here.
1525 * Just needs to take care of case. */
1526 const char *trait_names[] = {
1527 "expansionist",
1528 "trader",
1529 "aggressive",
1530 NULL
1533 for (tr = trait_begin(); tr != trait_end() && trait_names[tr] != NULL;
1534 tr = trait_next(tr)) {
1535 int default_default;
1537 default_default = (traits[tr].min + traits[tr].max) / 2;
1539 if ((default_traits == NULL && traits[tr].min != TRAIT_DEFAULT_VALUE)
1540 || (default_traits != NULL && traits[tr].min != default_traits[tr].min)) {
1541 secfile_insert_int(sfile, traits[tr].min, "%s.%s%s_min", secname, field_prefix,
1542 trait_names[tr]);
1544 if ((default_traits == NULL && traits[tr].max != TRAIT_DEFAULT_VALUE)
1545 || (default_traits != NULL && traits[tr].max != default_traits[tr].max)) {
1546 secfile_insert_int(sfile, traits[tr].max, "%s.%s%s_max", secname, field_prefix,
1547 trait_names[tr]);
1549 if (default_default != traits[tr].fixed) {
1550 secfile_insert_int(sfile, traits[tr].fixed, "%s.%s%s_default", secname, field_prefix,
1551 trait_names[tr]);
1555 return TRUE;
1558 /**************************************************************************
1559 Save a single nation.
1560 **************************************************************************/
1561 static bool save_nation(struct section_file *sfile, struct nation_type *pnat,
1562 int sect_idx)
1564 char path[512];
1565 int max_items = nation_city_list_size(pnat->server.default_cities);
1566 char *city_str[max_items];
1567 max_items = MAX(max_items, MAX_NUM_NATION_SETS + MAX_NUM_NATION_GROUPS);
1568 max_items = MAX(max_items, game.control.nation_count);
1569 const char *list_items[max_items];
1570 int set_count;
1571 int subsect_idx;
1573 fc_snprintf(path, sizeof(path), "nation_%d", sect_idx++);
1575 if (pnat->translation_domain == NULL) {
1576 secfile_insert_str(sfile, "freeciv", "%s.translation_domain", path);
1577 } else {
1578 secfile_insert_str(sfile, pnat->translation_domain, "%s.translation_domain", path);
1581 save_name_translation(sfile, &(pnat->adjective), path);
1582 secfile_insert_str(sfile, untranslated_name(&(pnat->noun_plural)), "%s.plural", path);
1584 set_count = 0;
1585 nation_sets_iterate(pset) {
1586 if (nation_is_in_set(pnat, pset)) {
1587 list_items[set_count++] = nation_set_rule_name(pset);
1589 } nation_sets_iterate_end;
1590 nation_groups_iterate(pgroup) {
1591 if (nation_is_in_group(pnat, pgroup)) {
1592 list_items[set_count++] = nation_group_rule_name(pgroup);
1594 } nation_groups_iterate_end;
1596 if (set_count > 0) {
1597 secfile_insert_str_vec(sfile, list_items, set_count, "%s.groups", path);
1600 set_count = 0;
1601 nation_list_iterate(pnat->server.conflicts_with, pconfl) {
1602 list_items[set_count++] = nation_rule_name(pconfl);
1603 } nation_list_iterate_end;
1604 if (set_count > 0) {
1605 secfile_insert_str_vec(sfile, list_items, set_count, "%s.conflicts_with", path);
1608 subsect_idx = 0;
1609 nation_leader_list_iterate(pnat->leaders, pleader) {
1610 secfile_insert_str(sfile, nation_leader_name(pleader), "%s.leaders%d.name",
1611 path, subsect_idx);
1612 secfile_insert_str(sfile, nation_leader_is_male(pleader) ? "Male" : "Female",
1613 "%s.leaders%d.sex", path, subsect_idx++);
1614 } nation_leader_list_iterate_end;
1616 if (pnat->server.rgb != NULL) {
1617 rgbcolor_save(sfile, pnat->server.rgb, "%s.color", path);
1620 save_traits(pnat->server.traits, game.server.default_traits,
1621 sfile, path, "trait_");
1623 if (!pnat->is_playable) {
1624 secfile_insert_bool(sfile, pnat->is_playable, "%s.is_playable", path);
1627 if (pnat->barb_type != NOT_A_BARBARIAN) {
1628 secfile_insert_str(sfile, barbarian_type_name(pnat->barb_type),
1629 "%s.barbarian_type", path);
1632 if (strcmp(pnat->flag_graphic_str, "-")) {
1633 secfile_insert_str(sfile, pnat->flag_graphic_str, "%s.flag", path);
1635 if (strcmp(pnat->flag_graphic_alt, "-")) {
1636 secfile_insert_str(sfile, pnat->flag_graphic_alt, "%s.flag_alt", path);
1639 subsect_idx = 0;
1640 governments_iterate(pgov) {
1641 struct ruler_title *prtitle;
1643 if (ruler_title_hash_lookup(pgov->ruler_titles, pnat, &prtitle)) {
1644 secfile_insert_str(sfile, government_rule_name(pgov),
1645 "%s.ruler_titles%d.government", path, subsect_idx);
1646 secfile_insert_str(sfile, ruler_title_male_untranslated_name(prtitle),
1647 "%s.ruler_titles%d.male_title", path, subsect_idx);
1648 secfile_insert_str(sfile, ruler_title_female_untranslated_name(prtitle),
1649 "%s.ruler_titles%d.female_title", path, subsect_idx++);
1651 } governments_iterate_end;
1653 secfile_insert_str(sfile, style_rule_name(pnat->style), "%s.style", path);
1655 set_count = 0;
1656 nation_list_iterate(pnat->server.civilwar_nations, pconfl) {
1657 list_items[set_count++] = nation_rule_name(pconfl);
1658 } nation_list_iterate_end;
1659 if (set_count > 0) {
1660 secfile_insert_str_vec(sfile, list_items, set_count, "%s.civilwar_nations", path);
1663 save_tech_list(sfile, pnat->init_techs, path, "init_techs");
1664 save_building_list(sfile, pnat->init_buildings, path, "init_buildings");
1665 save_unit_list(sfile, pnat->init_units, path, "init_units");
1667 if (pnat->init_government) {
1668 secfile_insert_str(sfile, government_rule_name(pnat->init_government),
1669 "%s.init_government", path);
1672 set_count = 0;
1673 nation_city_list_iterate(pnat->server.default_cities, pncity) {
1674 bool list_started = FALSE;
1676 city_str[set_count] = fc_malloc(strlen(nation_city_name(pncity)) + strlen(" (!river")
1677 + strlen(")")
1678 + MAX_NUM_TERRAINS * (strlen(", ") + MAX_LEN_NAME));
1680 strcpy(city_str[set_count], nation_city_name(pncity));
1681 switch(nation_city_river_preference(pncity)) {
1682 case NCP_DISLIKE:
1683 strcat(city_str[set_count], " (!river");
1684 list_started = TRUE;
1685 break;
1686 case NCP_LIKE:
1687 strcat(city_str[set_count], " (river");
1688 list_started = TRUE;
1689 break;
1690 case NCP_NONE:
1691 break;
1694 terrain_type_iterate(pterr) {
1695 const char *pref = NULL;
1697 switch(nation_city_terrain_preference(pncity, pterr)) {
1698 case NCP_DISLIKE:
1699 pref = "!";
1700 break;
1701 case NCP_LIKE:
1702 pref = "";
1703 break;
1704 case NCP_NONE:
1705 pref = NULL;
1706 break;
1709 if (pref != NULL) {
1710 if (list_started) {
1711 strcat(city_str[set_count], ", ");
1712 } else {
1713 strcat(city_str[set_count], " (");
1714 list_started = TRUE;
1716 strcat(city_str[set_count], pref);
1717 strcat(city_str[set_count], terrain_rule_name(pterr));
1720 } terrain_type_iterate_end;
1722 if (list_started) {
1723 strcat(city_str[set_count], ")");
1726 list_items[set_count] = city_str[set_count];
1727 set_count++;
1728 } nation_city_list_iterate_end;
1729 if (set_count > 0) {
1730 int i;
1732 secfile_insert_str_vec(sfile, list_items, set_count, "%s.cities", path);
1734 for (i = 0; i < set_count; i++) {
1735 FC_FREE(city_str[i]);
1739 secfile_insert_str(sfile, pnat->legend, "%s.legend", path);
1741 return TRUE;
1744 /**************************************************************************
1745 Save nations.ruleset
1746 **************************************************************************/
1747 static bool save_nations_ruleset(const char *filename, const char *name,
1748 struct rule_data *data)
1750 struct section_file *sfile = create_ruleset_file(name, "nation");
1752 if (sfile == NULL) {
1753 return FALSE;
1756 if (data->nationlist != NULL) {
1757 secfile_insert_str(sfile, data->nationlist, "ruledit.nationlist");
1759 if (game.server.ruledit.embedded_nations != NULL) {
1760 int i;
1761 const char **tmp = fc_malloc(game.server.ruledit.embedded_nations_count * sizeof(char *));
1763 /* Dance around the secfile_insert_str_vec() parameter type (requires extra const)
1764 * resrictions */
1765 for (i = 0; i < game.server.ruledit.embedded_nations_count; i++) {
1766 tmp[i] = game.server.ruledit.embedded_nations[i];
1769 secfile_insert_str_vec(sfile, tmp,
1770 game.server.ruledit.embedded_nations_count,
1771 "ruledit.embedded_nations");
1772 free(tmp);
1775 save_traits(game.server.default_traits, NULL, sfile,
1776 "default_traits", "");
1778 if (data->nationlist == NULL) {
1779 if (game.server.ruledit.allowed_govs != NULL) {
1780 secfile_insert_str_vec(sfile, game.server.ruledit.allowed_govs,
1781 game.server.ruledit.ag_count,
1782 "compatibility.allowed_govs");
1784 if (game.server.ruledit.allowed_terrains != NULL) {
1785 secfile_insert_str_vec(sfile, game.server.ruledit.allowed_terrains,
1786 game.server.ruledit.at_count,
1787 "compatibility.allowed_terrains");
1789 if (game.server.ruledit.allowed_styles != NULL) {
1790 secfile_insert_str_vec(sfile, game.server.ruledit.allowed_styles,
1791 game.server.ruledit.as_count,
1792 "compatibility.allowed_styles");
1796 if (game.default_government != NULL) {
1797 secfile_insert_str(sfile, government_rule_name(game.default_government),
1798 "compatibility.default_government");
1801 if (data->nationlist != NULL) {
1802 secfile_insert_include(sfile, data->nationlist);
1804 if (game.server.ruledit.embedded_nations != NULL) {
1805 int sect_idx;
1807 comment_nations(sfile);
1809 for (sect_idx = 0; sect_idx < game.server.ruledit.embedded_nations_count;
1810 sect_idx++) {
1811 struct nation_type *pnat
1812 = nation_by_rule_name(game.server.ruledit.embedded_nations[sect_idx]);
1814 if (pnat == NULL) {
1815 log_error("Embedded nation \"%s\" not found!",
1816 game.server.ruledit.embedded_nations[sect_idx]);
1817 } else {
1818 save_nation(sfile, pnat, sect_idx);
1822 } else {
1823 int sect_idx = 0;
1825 comment_nationsets(sfile);
1827 nation_sets_iterate(pset) {
1828 char path[512];
1830 fc_snprintf(path, sizeof(path), "nset_%d", sect_idx++);
1832 /* We don't use save_name_translation() for this as name and rule_name must
1833 * always be saved separately */
1834 secfile_insert_str(sfile, nation_set_untranslated_name(pset), "%s.name", path);
1835 secfile_insert_str(sfile, nation_set_rule_name(pset), "%s.rule_name", path);
1836 secfile_insert_str(sfile, nation_set_description(pset), "%s.description", path);
1837 } nation_sets_iterate_end;
1839 comment_nationgroups(sfile);
1841 sect_idx = 0;
1842 nation_groups_iterate(pgroup) {
1843 char path[512];
1845 fc_snprintf(path, sizeof(path), "ngroup_%d", sect_idx++);
1847 save_name_translation(sfile, &(pgroup->name), path);
1849 secfile_insert_int(sfile, pgroup->server.match, "%s.match", path);
1850 if (pgroup->hidden) {
1851 secfile_insert_bool(sfile, pgroup->hidden, "%s.hidden", path);
1853 } nation_groups_iterate_end;
1855 comment_nations(sfile);
1857 sect_idx = 0;
1858 nations_iterate(pnat) {
1859 save_nation(sfile, pnat, sect_idx++);
1860 } nations_iterate_end;
1863 return save_ruleset_file(sfile, filename);
1866 /**************************************************************************
1867 Save techs.ruleset
1868 **************************************************************************/
1869 static bool save_techs_ruleset(const char *filename, const char *name)
1871 struct section_file *sfile = create_ruleset_file(name, "tech");
1872 int i;
1873 int sect_idx;
1874 struct advance *a_none = advance_by_number(A_NONE);
1877 if (sfile == NULL) {
1878 return FALSE;
1881 for (i = 0; i < MAX_NUM_USER_TECH_FLAGS; i++) {
1882 const char *flagname = tech_flag_id_name_cb(i + TECH_USER_1);
1883 const char *helptxt = tech_flag_helptxt(i + TECH_USER_1);
1885 if (flagname != NULL) {
1886 secfile_insert_str(sfile, flagname, "control.flags%d.name", i);
1888 /* Save the user flag help text even when it is undefined. That makes
1889 * the formatting code happy. The resulting "" is ignored when the
1890 * ruleset is loaded. */
1891 secfile_insert_str(sfile, helptxt, "control.flags%d.helptxt", i);
1895 comment_tech_classes(sfile);
1897 sect_idx = 0;
1898 tech_class_iterate(ptclass) {
1899 char path[512];
1901 fc_snprintf(path, sizeof(path), "techclass_%d", sect_idx++);
1903 save_name_translation(sfile, &(ptclass->name), path);
1904 } tech_class_iterate_end;
1906 comment_techs(sfile);
1908 sect_idx = 0;
1909 advance_active_iterate(pa) {
1910 if (pa->require[AR_ONE] != A_NEVER) {
1911 char path[512];
1912 const char *flag_names[TF_COUNT];
1913 int set_count;
1914 int flagi;
1916 fc_snprintf(path, sizeof(path), "advance_%d", sect_idx++);
1918 save_name_translation(sfile, &(pa->name), path);
1920 if (game.control.num_tech_classes > 0) {
1921 if (pa->tclass != NULL) {
1922 secfile_insert_str(sfile, tech_class_rule_name(pa->tclass),
1923 "%s.class", path);
1927 save_tech_ref(sfile, pa->require[AR_ONE], path, "req1");
1928 save_tech_ref(sfile, pa->require[AR_TWO], path, "req2");
1929 if (pa->require[AR_ROOT] != a_none) {
1930 save_tech_ref(sfile, pa->require[AR_ROOT], path, "root_req");
1933 save_reqs_vector(sfile, &(pa->research_reqs), path,
1934 "research_reqs");
1936 secfile_insert_str(sfile, pa->graphic_str, "%s.graphic", path);
1937 if (strcmp("-", pa->graphic_alt)) {
1938 secfile_insert_str(sfile, pa->graphic_alt, "%s.graphic_alt", path);
1940 if (pa->bonus_message != NULL) {
1941 secfile_insert_str(sfile, pa->bonus_message, "%s.bonus_message", path);
1944 set_count = 0;
1945 for (flagi = 0; flagi < TF_COUNT; flagi++) {
1946 if (advance_has_flag(advance_index(pa), flagi)) {
1947 flag_names[set_count++] = tech_flag_id_name(flagi);
1951 if (set_count > 0) {
1952 secfile_insert_str_vec(sfile, flag_names, set_count,
1953 "%s.flags", path);
1955 if (pa->cost >= 0) {
1956 secfile_insert_int(sfile, pa->cost, "%s.cost", path);
1959 save_strvec(sfile, pa->helptext, path, "helptext");
1962 } advance_active_iterate_end;
1964 return save_ruleset_file(sfile, filename);
1967 /**************************************************************************
1968 Save terrain.ruleset
1969 **************************************************************************/
1970 static bool save_terrain_ruleset(const char *filename, const char *name)
1972 struct section_file *sfile = create_ruleset_file(name, "terrain");
1973 int sect_idx;
1974 int i;
1976 if (sfile == NULL) {
1977 return FALSE;
1980 for (i = 0; i < MAX_NUM_USER_TER_FLAGS; i++) {
1981 const char *flagname = terrain_flag_id_name_cb(i + TER_USER_1);
1982 const char *helptxt = terrain_flag_helptxt(i + TER_USER_1);
1984 if (flagname != NULL) {
1985 secfile_insert_str(sfile, flagname, "control.flags%d.name", i);
1987 /* Save the user flag help text even when it is undefined. That makes
1988 * the formatting code happy. The resulting "" is ignored when the
1989 * ruleset is loaded. */
1990 secfile_insert_str(sfile, helptxt, "control.flags%d.helptxt", i);
1994 for (i = 0; i < MAX_NUM_USER_EXTRA_FLAGS; i++) {
1995 const char *flagname = extra_flag_id_name_cb(i + EF_USER_FLAG_1);
1996 const char *helptxt = extra_flag_helptxt(i + EF_USER_FLAG_1);
1998 if (flagname != NULL) {
1999 secfile_insert_str(sfile, flagname, "control.extra_flags%d.name", i);
2001 /* Save the user flag help text even when it is undefined. That makes
2002 * the formatting code happy. The resulting "" is ignored when the
2003 * ruleset is loaded. */
2004 secfile_insert_str(sfile, helptxt,
2005 "control.extra_flags%d.helptxt", i);
2009 if (terrain_control.ocean_reclaim_requirement_pct <= 100) {
2010 secfile_insert_int(sfile, terrain_control.ocean_reclaim_requirement_pct,
2011 "parameters.ocean_reclaim_requirement");
2013 if (terrain_control.land_channel_requirement_pct <= 100) {
2014 secfile_insert_int(sfile, terrain_control.land_channel_requirement_pct,
2015 "parameters.land_channel_requirement");
2017 if (terrain_control.terrain_thaw_requirement_pct <= 100) {
2018 secfile_insert_int(sfile, terrain_control.terrain_thaw_requirement_pct,
2019 "parameters.thaw_requirement");
2021 if (terrain_control.terrain_freeze_requirement_pct <= 100) {
2022 secfile_insert_int(sfile, terrain_control.terrain_freeze_requirement_pct,
2023 "parameters.freeze_requirement");
2025 if (terrain_control.lake_max_size != 0) {
2026 secfile_insert_int(sfile, terrain_control.lake_max_size,
2027 "parameters.lake_max_size");
2029 if (terrain_control.min_start_native_area != 0) {
2030 secfile_insert_int(sfile, terrain_control.min_start_native_area,
2031 "parameters.min_start_native_area");
2033 if (terrain_control.move_fragments != 3) {
2034 secfile_insert_int(sfile, terrain_control.move_fragments,
2035 "parameters.move_fragments");
2037 if (terrain_control.igter_cost != 1) {
2038 secfile_insert_int(sfile, terrain_control.igter_cost,
2039 "parameters.igter_cost");
2041 if (terrain_control.pythagorean_diagonal != RS_DEFAULT_PYTHAGOREAN_DIAGONAL) {
2042 secfile_insert_bool(sfile, TRUE,
2043 "parameters.pythagorean_diagonal");
2045 if (wld.map.server.ocean_resources) {
2046 secfile_insert_bool(sfile, TRUE,
2047 "parameters.ocean_resources");
2050 comment_terrains(sfile);
2052 sect_idx = 0;
2053 terrain_type_iterate(pterr) {
2054 char path[512];
2055 char identifier[2];
2056 int r;
2057 const char *flag_names[TER_USER_LAST];
2058 const char *puc_names[UCL_LAST];
2059 int flagi;
2060 int set_count;
2062 fc_snprintf(path, sizeof(path), "terrain_%d", sect_idx++);
2064 save_name_translation(sfile, &(pterr->name), path);
2066 secfile_insert_str(sfile, pterr->graphic_str, "%s.graphic", path);
2067 secfile_insert_str(sfile, pterr->graphic_alt, "%s.graphic_alt", path);
2068 identifier[0] = pterr->identifier;
2069 identifier[1] = '\0';
2070 secfile_insert_str(sfile, identifier, "%s.identifier", path);
2072 secfile_insert_str(sfile, terrain_class_name(pterr->tclass),
2073 "%s.class", path);
2075 secfile_insert_int(sfile, pterr->movement_cost, "%s.movement_cost", path);
2076 secfile_insert_int(sfile, pterr->defense_bonus, "%s.defense_bonus", path);
2078 output_type_iterate(o) {
2079 if (pterr->output[o] != 0) {
2080 secfile_insert_int(sfile, pterr->output[o], "%s.%s", path,
2081 get_output_identifier(o));
2083 } output_type_iterate_end;
2085 /* Check resource count */
2086 for (r = 0; pterr->resources[r] != NULL; r++) {
2087 /* Just increasing r as long as there is resources */
2091 const char *resource_names[r];
2093 for (r = 0; pterr->resources[r] != NULL; r++) {
2094 resource_names[r] = extra_rule_name(pterr->resources[r]);
2097 secfile_insert_str_vec(sfile, resource_names, r,
2098 "%s.resources", path);
2101 output_type_iterate(o) {
2102 if (pterr->road_output_incr_pct[o] != 0) {
2103 secfile_insert_int(sfile, pterr->road_output_incr_pct[o],
2104 "%s.road_%s_incr_pct", path,
2105 get_output_identifier(o));
2107 } output_type_iterate_end;
2109 secfile_insert_int(sfile, pterr->base_time, "%s.base_time", path);
2110 secfile_insert_int(sfile, pterr->road_time, "%s.road_time", path);
2112 save_terrain_ref(sfile, pterr->irrigation_result, pterr, path,
2113 "irrigation_result");
2114 secfile_insert_int(sfile, pterr->irrigation_food_incr,
2115 "%s.irrigation_food_incr", path);
2116 secfile_insert_int(sfile, pterr->irrigation_time,
2117 "%s.irrigation_time", path);
2119 save_terrain_ref(sfile, pterr->mining_result, pterr, path,
2120 "mining_result");
2121 secfile_insert_int(sfile, pterr->mining_shield_incr,
2122 "%s.mining_shield_incr", path);
2123 secfile_insert_int(sfile, pterr->mining_time,
2124 "%s.mining_time", path);
2126 save_terrain_ref(sfile, pterr->transform_result, pterr, path,
2127 "transform_result");
2128 secfile_insert_int(sfile, pterr->transform_time,
2129 "%s.transform_time", path);
2131 if (pterr->animal != NULL) {
2132 secfile_insert_str(sfile, utype_rule_name(pterr->animal),
2133 "%s.animal", path);
2134 } else {
2135 secfile_insert_str(sfile, "None",
2136 "%s.animal", path);
2139 secfile_insert_int(sfile, pterr->pillage_time,
2140 "%s.pillage_time", path);
2141 secfile_insert_int(sfile, pterr->clean_pollution_time,
2142 "%s.clean_pollution_time", path);
2143 secfile_insert_int(sfile, pterr->clean_fallout_time,
2144 "%s.clean_fallout_time", path);
2146 save_terrain_ref(sfile, pterr->warmer_wetter_result, pterr, path,
2147 "warmer_wetter_result");
2148 save_terrain_ref(sfile, pterr->warmer_drier_result, pterr, path,
2149 "warmer_drier_result");
2150 save_terrain_ref(sfile, pterr->cooler_wetter_result, pterr, path,
2151 "cooler_wetter_result");
2152 save_terrain_ref(sfile, pterr->cooler_drier_result, pterr, path,
2153 "cooler_drier_result");
2155 set_count = 0;
2156 for (flagi = 0; flagi < TER_USER_LAST; flagi++) {
2157 if (terrain_has_flag(pterr, flagi)) {
2158 flag_names[set_count++] = terrain_flag_id_name(flagi);
2162 if (set_count > 0) {
2163 secfile_insert_str_vec(sfile, flag_names, set_count,
2164 "%s.flags", path);
2168 enum mapgen_terrain_property mtp;
2170 for (mtp = mapgen_terrain_property_begin();
2171 mtp != mapgen_terrain_property_end();
2172 mtp = mapgen_terrain_property_next(mtp)) {
2173 if (pterr->property[mtp] != 0) {
2174 secfile_insert_int(sfile, pterr->property[mtp],
2175 "%s.property_%s", path,
2176 mapgen_terrain_property_name(mtp));
2181 set_count = 0;
2182 unit_class_iterate(puc) {
2183 if (BV_ISSET(pterr->native_to, uclass_index(puc))) {
2184 puc_names[set_count++] = uclass_rule_name(puc);
2186 } unit_class_iterate_end;
2188 if (set_count > 0) {
2189 secfile_insert_str_vec(sfile, puc_names, set_count,
2190 "%s.native_to", path);
2193 rgbcolor_save(sfile, pterr->rgb, "%s.color", path);
2195 save_strvec(sfile, pterr->helptext, path, "helptext");
2197 } terrain_type_iterate_end;
2199 comment_resources(sfile);
2201 sect_idx = 0;
2202 extra_type_by_cause_iterate(EC_RESOURCE, pres) {
2203 if (!pres->disabled) {
2204 char path[512];
2205 char identifier[2];
2207 fc_snprintf(path, sizeof(path), "resource_%d", sect_idx++);
2209 secfile_insert_str(sfile, extra_rule_name(pres),
2210 "%s.extra", path);
2212 output_type_iterate(o) {
2213 if (pres->data.resource->output[o] != 0) {
2214 secfile_insert_int(sfile, pres->data.resource->output[o], "%s.%s",
2215 path, get_output_identifier(o));
2217 } output_type_iterate_end;
2219 identifier[0] = pres->data.resource->id_old_save;
2220 identifier[1] = '\0';
2221 secfile_insert_str(sfile, identifier, "%s.identifier", path);
2223 } extra_type_by_cause_iterate_end;
2225 secfile_insert_str(sfile, terrain_control.gui_type_base0,
2226 "extraui.ui_name_base_fortress");
2227 secfile_insert_str(sfile, terrain_control.gui_type_base1,
2228 "extraui.ui_name_base_airbase");
2230 comment_extras(sfile);
2232 sect_idx = 0;
2233 extra_active_type_iterate(pextra) {
2234 char path[512];
2235 const char *flag_names[EF_COUNT];
2236 const char *cause_names[EC_COUNT];
2237 const char *puc_names[UCL_LAST];
2238 const char *extra_names[MAX_EXTRA_TYPES];
2239 int flagi;
2240 int causei;
2241 int set_count;
2243 fc_snprintf(path, sizeof(path), "extra_%d", sect_idx++);
2245 save_name_translation(sfile, &(pextra->name), path);
2247 secfile_insert_str(sfile, extra_category_name(pextra->category),
2248 "%s.category", path);
2250 set_count = 0;
2251 for (causei = 0; causei < EC_COUNT; causei++) {
2252 if (is_extra_caused_by(pextra, causei)) {
2253 cause_names[set_count++] = extra_cause_name(causei);
2257 if (set_count > 0) {
2258 secfile_insert_str_vec(sfile, cause_names, set_count,
2259 "%s.causes", path);
2262 set_count = 0;
2263 for (causei = 0; causei < ERM_COUNT; causei++) {
2264 if (is_extra_removed_by(pextra, causei)) {
2265 cause_names[set_count++] = extra_rmcause_name(causei);
2269 if (set_count > 0) {
2270 secfile_insert_str_vec(sfile, cause_names, set_count,
2271 "%s.rmcauses", path);
2274 if (strcmp(pextra->graphic_str, "-")) {
2275 secfile_insert_str(sfile, pextra->graphic_str, "%s.graphic", path);
2277 if (strcmp(pextra->graphic_alt, "-")) {
2278 secfile_insert_str(sfile, pextra->graphic_alt, "%s.graphic_alt", path);
2280 if (strcmp(pextra->activity_gfx, "-")) {
2281 secfile_insert_str(sfile, pextra->activity_gfx, "%s.activity_gfx", path);
2283 if (strcmp(pextra->act_gfx_alt, "-")) {
2284 secfile_insert_str(sfile, pextra->act_gfx_alt, "%s.act_gfx_alt", path);
2286 if (strcmp(pextra->act_gfx_alt2, "-")) {
2287 secfile_insert_str(sfile, pextra->act_gfx_alt2, "%s.act_gfx_alt2", path);
2289 if (strcmp(pextra->rmact_gfx, "-")) {
2290 secfile_insert_str(sfile, pextra->rmact_gfx, "%s.rmact_gfx", path);
2292 if (strcmp(pextra->rmact_gfx_alt, "-")) {
2293 secfile_insert_str(sfile, pextra->rmact_gfx_alt, "%s.rmact_gfx_alt", path);
2296 save_reqs_vector(sfile, &(pextra->reqs), path, "reqs");
2297 save_reqs_vector(sfile, &(pextra->rmreqs), path, "rmreqs");
2298 save_reqs_vector(sfile, &(pextra->appearance_reqs), path, "appearance_reqs");
2299 save_reqs_vector(sfile, &(pextra->disappearance_reqs), path, "disappearance_reqs");
2301 if (!pextra->buildable) {
2302 secfile_insert_bool(sfile, pextra->buildable, "%s.buildable", path);
2304 secfile_insert_int(sfile, pextra->build_time, "%s.build_time", path);
2305 secfile_insert_int(sfile, pextra->removal_time, "%s.removal_time", path);
2306 if (pextra->build_time_factor != 1) {
2307 secfile_insert_int(sfile, pextra->build_time_factor, "%s.build_time_factor", path);
2309 if (pextra->removal_time_factor != 1) {
2310 secfile_insert_int(sfile, pextra->removal_time_factor, "%s.removal_time_factor", path);
2312 if (pextra->defense_bonus != 0) {
2313 secfile_insert_int(sfile, pextra->defense_bonus, "%s.defense_bonus", path);
2315 if (pextra->eus != EUS_NORMAL) {
2316 secfile_insert_str(sfile, extra_unit_seen_type_name(pextra->eus),
2317 "%s.unit_seen", path);
2319 if (is_extra_caused_by(pextra, EC_APPEARANCE)
2320 && pextra->appearance_chance != RS_DEFAULT_EXTRA_APPEARANCE) {
2321 secfile_insert_int(sfile, pextra->appearance_chance, "%s.appearance_chance", path);
2323 if (is_extra_removed_by(pextra, ERM_DISAPPEARANCE)
2324 && pextra->disappearance_chance != RS_DEFAULT_EXTRA_DISAPPEARANCE) {
2325 secfile_insert_int(sfile, pextra->disappearance_chance, "%s.disappearance_chance",
2326 path);
2329 set_count = 0;
2330 unit_class_iterate(puc) {
2331 if (BV_ISSET(pextra->native_to, uclass_index(puc))) {
2332 puc_names[set_count++] = uclass_rule_name(puc);
2334 } unit_class_iterate_end;
2336 if (set_count > 0) {
2337 secfile_insert_str_vec(sfile, puc_names, set_count,
2338 "%s.native_to", path);
2341 set_count = 0;
2342 for (flagi = 0; flagi < EF_COUNT; flagi++) {
2343 if (extra_has_flag(pextra, flagi)) {
2344 flag_names[set_count++] = extra_flag_id_name(flagi);
2348 if (set_count > 0) {
2349 secfile_insert_str_vec(sfile, flag_names, set_count,
2350 "%s.flags", path);
2353 set_count = 0;
2354 extra_type_iterate(confl) {
2355 if (!can_extras_coexist(pextra, confl)) {
2356 extra_names[set_count++] = extra_rule_name(confl);
2358 } extra_type_iterate_end;
2360 if (set_count > 0) {
2361 secfile_insert_str_vec(sfile, extra_names, set_count,
2362 "%s.conflicts", path);
2365 set_count = 0;
2366 extra_type_iterate(top) {
2367 if (BV_ISSET(pextra->hidden_by, extra_index(top))) {
2368 extra_names[set_count++] = extra_rule_name(top);
2370 } extra_type_iterate_end;
2372 if (set_count > 0) {
2373 secfile_insert_str_vec(sfile, extra_names, set_count,
2374 "%s.hidden_by", path);
2377 save_strvec(sfile, pextra->helptext, path, "helptext");
2379 } extra_active_type_iterate_end;
2381 comment_bases(sfile);
2383 sect_idx = 0;
2384 extra_type_by_cause_iterate(EC_BASE, pextra) {
2385 if (!pextra->disabled) {
2386 char path[512];
2387 struct base_type *pbase = extra_base_get(pextra);
2388 const char *flag_names[BF_COUNT];
2389 int flagi;
2390 int set_count;
2392 fc_snprintf(path, sizeof(path), "base_%d", sect_idx++);
2394 secfile_insert_str(sfile, extra_rule_name(pextra),
2395 "%s.extra", path);
2397 secfile_insert_str(sfile, base_gui_type_name(pbase->gui_type),
2398 "%s.gui_type", path);
2400 if (pbase->border_sq >= 0) {
2401 secfile_insert_int(sfile, pbase->border_sq, "%s.border_sq", path);
2403 if (pbase->vision_main_sq >= 0) {
2404 secfile_insert_int(sfile, pbase->vision_main_sq, "%s.vision_main_sq", path);
2407 set_count = 0;
2408 for (flagi = 0; flagi < BF_COUNT; flagi++) {
2409 if (base_has_flag(pbase, flagi)) {
2410 flag_names[set_count++] = base_flag_id_name(flagi);
2414 if (set_count > 0) {
2415 secfile_insert_str_vec(sfile, flag_names, set_count,
2416 "%s.flags", path);
2419 } extra_type_by_cause_iterate_end;
2421 comment_roads(sfile);
2423 sect_idx = 0;
2424 extra_type_by_cause_iterate(EC_ROAD, pextra) {
2425 if (!pextra->disabled) {
2426 struct road_type *proad = extra_road_get(pextra);
2427 char path[512];
2428 const char *flag_names[RF_COUNT];
2429 int flagi;
2430 int set_count;
2432 fc_snprintf(path, sizeof(path), "road_%d", sect_idx++);
2434 secfile_insert_str(sfile, extra_rule_name(pextra),
2435 "%s.extra", path);
2437 secfile_insert_int(sfile, proad->move_cost, "%s.move_cost", path);
2439 if (proad->move_mode != RMM_FAST_ALWAYS) {
2440 secfile_insert_str(sfile, road_move_mode_name(proad->move_mode),
2441 "%s.move_mode", path);
2444 output_type_iterate(o) {
2445 if (proad->tile_incr_const[o] != 0) {
2446 secfile_insert_int(sfile, proad->tile_incr_const[o],
2447 "%s.%s_incr_const", path, get_output_identifier(o));
2449 if (proad->tile_incr[o] != 0) {
2450 secfile_insert_int(sfile, proad->tile_incr[o],
2451 "%s.%s_incr", path, get_output_identifier(o));
2453 if (proad->tile_bonus[o] != 0) {
2454 secfile_insert_int(sfile, proad->tile_bonus[o],
2455 "%s.%s_bonus", path, get_output_identifier(o));
2457 } output_type_iterate_end;
2459 switch (proad->compat) {
2460 case ROCO_ROAD:
2461 secfile_insert_str(sfile, "Road", "%s.compat_special", path);
2462 break;
2463 case ROCO_RAILROAD:
2464 secfile_insert_str(sfile, "Railroad", "%s.compat_special", path);
2465 break;
2466 case ROCO_RIVER:
2467 secfile_insert_str(sfile, "River", "%s.compat_special", path);
2468 break;
2469 case ROCO_NONE:
2470 secfile_insert_str(sfile, "None", "%s.compat_special", path);
2471 break;
2474 set_count = 0;
2475 for (flagi = 0; flagi < RF_COUNT; flagi++) {
2476 if (road_has_flag(proad, flagi)) {
2477 flag_names[set_count++] = road_flag_id_name(flagi);
2481 if (set_count > 0) {
2482 secfile_insert_str_vec(sfile, flag_names, set_count,
2483 "%s.flags", path);
2486 } extra_type_by_cause_iterate_end;
2488 return save_ruleset_file(sfile, filename);
2491 /**************************************************************************
2492 Save one veteran system.
2493 **************************************************************************/
2494 static bool save_veteran_system(struct section_file *sfile, const char *path,
2495 struct veteran_system *vsystem)
2497 const char *vlist_name[vsystem->levels];
2498 int vlist_power[vsystem->levels];
2499 int vlist_raise[vsystem->levels];
2500 int vlist_wraise[vsystem->levels];
2501 int vlist_move[vsystem->levels];
2502 int i;
2504 for (i = 0; i < vsystem->levels; i++) {
2505 vlist_name[i] = rule_name_get(&(vsystem->definitions[i].name));
2506 vlist_power[i] = vsystem->definitions[i].power_fact;
2507 vlist_raise[i] = vsystem->definitions[i].raise_chance;
2508 vlist_wraise[i] = vsystem->definitions[i].work_raise_chance;
2509 vlist_move[i] = vsystem->definitions[i].move_bonus;
2512 secfile_insert_str_vec(sfile, vlist_name, vsystem->levels,
2513 "%s.veteran_names", path);
2514 secfile_insert_int_vec(sfile, vlist_power, vsystem->levels,
2515 "%s.veteran_power_fact", path);
2516 secfile_insert_int_vec(sfile, vlist_raise, vsystem->levels,
2517 "%s.veteran_raise_chance", path);
2518 secfile_insert_int_vec(sfile, vlist_wraise, vsystem->levels,
2519 "%s.veteran_work_raise_chance", path);
2520 secfile_insert_int_vec(sfile, vlist_move, vsystem->levels,
2521 "%s.veteran_move_bonus", path);
2523 return TRUE;
2526 /**************************************************************************
2527 Save unit combat bonuses list.
2528 **************************************************************************/
2529 static bool save_combat_bonuses(struct section_file *sfile,
2530 struct unit_type *put,
2531 char *path)
2533 int i;
2534 bool has_quiet = FALSE;
2536 combat_bonus_list_iterate(put->bonuses, pbonus) {
2537 if (pbonus->quiet) {
2538 has_quiet = TRUE;
2540 } combat_bonus_list_iterate_end;
2542 i = 0;
2544 combat_bonus_list_iterate(put->bonuses, pbonus) {
2545 secfile_insert_str(sfile, unit_type_flag_id_name(pbonus->flag),
2546 "%s.bonuses%d.flag", path, i);
2547 secfile_insert_str(sfile, combat_bonus_type_name(pbonus->type),
2548 "%s.bonuses%d.type", path, i);
2549 secfile_insert_int(sfile, pbonus->value,
2550 "%s.bonuses%d.value", path, i);
2552 if (has_quiet) {
2553 secfile_insert_bool(sfile, pbonus->quiet,
2554 "%s.bonuses%d.quiet", path, i);
2557 i++;
2558 } combat_bonus_list_iterate_end;
2560 return TRUE;
2563 /**************************************************************************
2564 Save units.ruleset
2565 **************************************************************************/
2566 static bool save_units_ruleset(const char *filename, const char *name)
2568 struct section_file *sfile = create_ruleset_file(name, "unit");
2569 int i;
2570 int sect_idx;
2572 if (sfile == NULL) {
2573 return FALSE;
2576 for (i = 0; i < MAX_NUM_USER_UNIT_FLAGS; i++) {
2577 const char *flagname = unit_type_flag_id_name_cb(i + UTYF_USER_FLAG_1);
2578 const char *helptxt = unit_type_flag_helptxt(i + UTYF_USER_FLAG_1);
2580 if (flagname != NULL) {
2581 secfile_insert_str(sfile, flagname, "control.flags%d.name", i);
2583 /* Save the user flag help text even when it is undefined. That makes
2584 * the formatting code happy. The resulting "" is ignored when the
2585 * ruleset is loaded. */
2586 secfile_insert_str(sfile, helptxt, "control.flags%d.helptxt", i);
2590 for (i = 0; i < MAX_NUM_USER_UCLASS_FLAGS; i++) {
2591 const char *flagname = unit_class_flag_id_name_cb(i + UCF_USER_FLAG_1);
2592 const char *helptxt = unit_class_flag_helptxt(i + UCF_USER_FLAG_1);
2594 if (flagname != NULL) {
2595 secfile_insert_str(sfile, flagname, "control.class_flags%d.name", i);
2597 /* Save the user flag help text even when it is undefined. That makes
2598 * the formatting code happy. The resulting "" is ignored when the
2599 * ruleset is loaded. */
2600 secfile_insert_str(sfile, helptxt,
2601 "control.class_flags%d.helptxt", i);
2605 save_veteran_system(sfile, "veteran_system", game.veteran);
2607 comment_uclasses(sfile);
2609 sect_idx = 0;
2610 unit_active_class_iterate(puc) {
2611 char path[512];
2612 char *hut_str = NULL;
2613 const char *flag_names[UCF_COUNT];
2614 int flagi;
2615 int set_count;
2617 fc_snprintf(path, sizeof(path), "unitclass_%d", sect_idx++);
2619 save_name_translation(sfile, &(puc->name), path);
2621 secfile_insert_int(sfile, puc->min_speed / SINGLE_MOVE,
2622 "%s.min_speed", path);
2623 secfile_insert_int(sfile, puc->hp_loss_pct, "%s.hp_loss_pct", path);
2624 if (puc->non_native_def_pct != 100) {
2625 secfile_insert_int(sfile, puc->non_native_def_pct,
2626 "%s.non_native_def_pct", path);
2628 if (puc->hut_behavior != HUT_NORMAL) {
2629 switch (puc->hut_behavior) {
2630 case HUT_NORMAL:
2631 hut_str = "Normal";
2632 break;
2633 case HUT_NOTHING:
2634 hut_str = "Nothing";
2635 break;
2636 case HUT_FRIGHTEN:
2637 hut_str = "Frighten";
2638 break;
2640 fc_assert(hut_str != NULL);
2641 secfile_insert_str(sfile, hut_str, "%s.hut_behavior", path);
2644 set_count = 0;
2645 for (flagi = 0; flagi < UCF_COUNT; flagi++) {
2646 if (uclass_has_flag(puc, flagi)) {
2647 flag_names[set_count++] = unit_class_flag_id_name(flagi);
2651 if (set_count > 0) {
2652 secfile_insert_str_vec(sfile, flag_names, set_count,
2653 "%s.flags", path);
2656 save_strvec(sfile, puc->helptext, path, "helptext");
2658 } unit_active_class_iterate_end;
2660 comment_utypes(sfile);
2662 sect_idx = 0;
2663 unit_active_type_iterate(put) {
2664 if (!put->disabled) {
2665 char path[512];
2666 const char *flag_names[UTYF_LAST_USER_FLAG + 1];
2667 int flagi;
2668 int set_count;
2670 fc_snprintf(path, sizeof(path), "unit_%d", sect_idx++);
2672 save_name_translation(sfile, &(put->name), path);
2674 secfile_insert_str(sfile, uclass_rule_name(put->uclass),
2675 "%s.class", path);
2677 save_tech_ref(sfile, put->require_advance, path, "tech_req");
2679 if (put->need_government != NULL) {
2680 secfile_insert_str(sfile, government_rule_name(put->need_government),
2681 "%s.gov_req", path);
2684 if (put->need_improvement != NULL) {
2685 secfile_insert_str(sfile, improvement_rule_name(put->need_improvement),
2686 "%s.impr_req", path);
2689 if (put->obsoleted_by != NULL) {
2690 secfile_insert_str(sfile, utype_rule_name(put->obsoleted_by),
2691 "%s.obsolete_by", path);
2694 secfile_insert_str(sfile, put->graphic_str, "%s.graphic", path);
2695 if (strcmp("-", put->graphic_alt)) {
2696 secfile_insert_str(sfile, put->graphic_alt, "%s.graphic_alt", path);
2698 if (strcmp("-", put->sound_move)) {
2699 secfile_insert_str(sfile, put->sound_move, "%s.sound_move", path);
2701 if (strcmp("-", put->sound_move_alt)) {
2702 secfile_insert_str(sfile, put->sound_move_alt, "%s.sound_move_alt", path);
2704 if (strcmp("-", put->sound_fight)) {
2705 secfile_insert_str(sfile, put->sound_fight, "%s.sound_fight", path);
2707 if (strcmp("-", put->sound_fight_alt)) {
2708 secfile_insert_str(sfile, put->sound_fight_alt, "%s.sound_fight_alt", path);
2711 secfile_insert_int(sfile, put->build_cost, "%s.build_cost", path);
2712 secfile_insert_int(sfile, put->pop_cost, "%s.pop_cost", path);
2713 secfile_insert_int(sfile, put->attack_strength, "%s.attack", path);
2714 secfile_insert_int(sfile, put->defense_strength, "%s.defense", path);
2715 secfile_insert_int(sfile, put->move_rate / SINGLE_MOVE, "%s.move_rate", path);
2716 secfile_insert_int(sfile, put->vision_radius_sq, "%s.vision_radius_sq", path);
2717 secfile_insert_int(sfile, put->transport_capacity, "%s.transport_cap", path);
2719 save_uclass_vec(sfile, &(put->cargo), path, "cargo", FALSE);
2720 save_uclass_vec(sfile, &(put->embarks), path, "embarks", TRUE);
2721 save_uclass_vec(sfile, &(put->disembarks), path, "disembarks", TRUE);
2723 secfile_insert_int(sfile, put->hp, "%s.hitpoints", path);
2724 secfile_insert_int(sfile, put->firepower, "%s.firepower", path);
2725 secfile_insert_int(sfile, put->fuel, "%s.fuel", path);
2726 secfile_insert_int(sfile, put->happy_cost, "%s.uk_happy", path);
2728 output_type_iterate(o) {
2729 if (put->upkeep[o] != 0) {
2730 secfile_insert_int(sfile, put->upkeep[o], "%s.uk_%s",
2731 path, get_output_identifier(o));
2733 } output_type_iterate_end;
2735 if (put->converted_to != NULL) {
2736 secfile_insert_str(sfile, utype_rule_name(put->converted_to),
2737 "%s.convert_to", path);
2739 if (put->convert_time != 1) {
2740 secfile_insert_int(sfile, put->convert_time, "%s.convert_time", path);
2743 save_combat_bonuses(sfile, put, path);
2744 save_uclass_vec(sfile, &(put->targets), path, "targets", TRUE);
2746 if (put->veteran != NULL) {
2747 save_veteran_system(sfile, path, put->veteran);
2750 if (put->paratroopers_range != 0) {
2751 secfile_insert_int(sfile, put->paratroopers_range,
2752 "%s.paratroopers_range", path);
2753 secfile_insert_int(sfile, put->paratroopers_mr_req / SINGLE_MOVE,
2754 "%s.paratroopers_mr_req", path);
2755 secfile_insert_int(sfile, put->paratroopers_mr_sub / SINGLE_MOVE,
2756 "%s.paratroopers_mr_sub", path);
2758 if (put->bombard_rate != 0) {
2759 secfile_insert_int(sfile, put->bombard_rate,
2760 "%s.bombard_rate", path);
2762 if (put->city_slots != 0) {
2763 secfile_insert_int(sfile, put->city_slots,
2764 "%s.city_slots", path);
2766 if (put->city_size != 1) {
2767 secfile_insert_int(sfile, put->city_size,
2768 "%s.city_size", path);
2771 set_count = 0;
2772 for (flagi = 0; flagi <= UTYF_LAST_USER_FLAG; flagi++) {
2773 if (utype_has_flag(put, flagi)) {
2774 flag_names[set_count++] = unit_type_flag_id_name(flagi);
2778 if (set_count > 0) {
2779 secfile_insert_str_vec(sfile, flag_names, set_count,
2780 "%s.flags", path);
2783 set_count = 0;
2784 for (flagi = L_FIRST; flagi < L_LAST; flagi++) {
2785 if (utype_has_role(put, flagi)) {
2786 flag_names[set_count++] = unit_role_id_name(flagi);
2790 if (set_count > 0) {
2791 secfile_insert_str_vec(sfile, flag_names, set_count,
2792 "%s.roles", path);
2795 save_strvec(sfile, put->helptext, path, "helptext");
2797 } unit_active_type_iterate_end;
2799 return save_ruleset_file(sfile, filename);
2802 /**************************************************************************
2803 Save script.lua
2804 **************************************************************************/
2805 static bool save_script_lua(const char *filename, const char *name,
2806 const char *buffer)
2808 if (buffer != NULL) {
2809 FILE *ffile = fc_fopen(filename, "w");
2810 int full_len = strlen(buffer);
2811 int len;
2813 if (ffile != NULL) {
2814 len = fwrite(buffer, 1, full_len, ffile);
2816 if (len != full_len) {
2817 return FALSE;
2820 fclose(ffile);
2821 } else {
2822 return FALSE;
2826 return TRUE;
2829 /**************************************************************************
2830 Save ruleset to directory given.
2831 **************************************************************************/
2832 bool save_ruleset(const char *path, const char *name, struct rule_data *data)
2834 if (make_dir(path)) {
2835 bool success = TRUE;
2836 char filename[500];
2838 if (success) {
2839 fc_snprintf(filename, sizeof(filename), "%s/buildings.ruleset", path);
2840 success = save_buildings_ruleset(filename, name);
2843 if (success) {
2844 fc_snprintf(filename, sizeof(filename), "%s/styles.ruleset", path);
2845 success = save_styles_ruleset(filename, name);
2848 if (success) {
2849 fc_snprintf(filename, sizeof(filename), "%s/cities.ruleset", path);
2850 success = save_cities_ruleset(filename, name);
2853 if (success) {
2854 fc_snprintf(filename, sizeof(filename), "%s/effects.ruleset", path);
2855 success = save_effects_ruleset(filename, name);
2858 if (success) {
2859 fc_snprintf(filename, sizeof(filename), "%s/game.ruleset", path);
2860 success = save_game_ruleset(filename, name);
2863 if (success) {
2864 fc_snprintf(filename, sizeof(filename), "%s/governments.ruleset", path);
2865 success = save_governments_ruleset(filename, name);
2868 if (success) {
2869 fc_snprintf(filename, sizeof(filename), "%s/nations.ruleset", path);
2870 success = save_nations_ruleset(filename, name, data);
2873 if (success) {
2874 fc_snprintf(filename, sizeof(filename), "%s/techs.ruleset", path);
2875 success = save_techs_ruleset(filename, name);
2878 if (success) {
2879 fc_snprintf(filename, sizeof(filename), "%s/terrain.ruleset", path);
2880 success = save_terrain_ruleset(filename, name);
2883 if (success) {
2884 fc_snprintf(filename, sizeof(filename), "%s/units.ruleset", path);
2885 success = save_units_ruleset(filename, name);
2888 if (success) {
2889 fc_snprintf(filename, sizeof(filename), "%s/script.lua", path);
2890 success = save_script_lua(filename, name, get_script_buffer());
2893 if (success) {
2894 fc_snprintf(filename, sizeof(filename), "%s/parser.lua", path);
2895 success = save_script_lua(filename, name, get_parser_buffer());
2898 return success;
2899 } else {
2900 log_error("Failed to create directory %s", path);
2901 return FALSE;
2904 return TRUE;