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)
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
15 #include <fc_config.h>
20 #include "string_vector.h"
23 #include "achievements.h"
25 #include "government.h"
28 #include "multipliers.h"
29 #include "specialist.h"
43 /* Ruleset format version */
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
,
57 struct section_file
*sfile
= secfile_new(TRUE
);
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
);
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");
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
) {
84 secfile_insert_int(sfile
, value
,
85 "%s.%s", path
, entry
);
87 secfile_insert_int(sfile
, value
,
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
)) {
104 secfile_insert_bool(sfile
, value
,
105 "%s.%s", path
, entry
);
107 secfile_insert_bool(sfile
, value
,
115 /**************************************************************************
116 Save name of the object.
117 **************************************************************************/
118 static bool save_name_translation(struct section_file
*sfile
,
119 struct name_translation
*name
,
122 struct entry
*mod_entry
;
124 mod_entry
= secfile_insert_str(sfile
,
125 untranslated_name(name
),
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
,
132 "%s.rule_name", path
);
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
)
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
;
158 includes_quiet
= TRUE
;
160 } requirement_vector_iterate_end
;
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
,
177 "%s.%s%d.survives", path
, entry
, i
);
180 if (includes_negated
) {
181 secfile_insert_bool(sfile
,
183 "%s.%s%d.present", path
, entry
, i
);
186 if (includes_quiet
) {
187 secfile_insert_bool(sfile
,
189 "%s.%s%d.quiet", path
, entry
, i
);
193 } requirement_vector_iterate_end
;
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
];
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
]));
214 secfile_insert_str_vec(sfile
, tech_names
, set_count
,
215 "%s.%s", path
, entry
);
221 /**************************************************************************
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
);
231 secfile_insert_str(sfile
, advance_rule_name(padv
),
232 "%s.%s", path
, entry
);
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
)
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
);
251 secfile_insert_str(sfile
, terrain_rule_name(save
),
252 "%s.%s", path
, entry
);
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
);
270 /**************************************************************************
271 Save buildings vector. Input is B_LAST terminated array of buildings
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
];
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
]));
287 secfile_insert_str_vec(sfile
, building_names
, set_count
,
288 "%s.%s", path
, entry
);
294 /**************************************************************************
295 Save units vector. Input is NULL terminated array of units
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
];
306 for (i
= 0; input
[i
] != NULL
&& i
< MAX_NUM_UNIT_LIST
; i
++) {
307 unit_names
[set_count
++] = utype_rule_name(input
[i
]);
311 secfile_insert_str_vec(sfile
, unit_names
, set_count
,
312 "%s.%s", path
, entry
);
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
];
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
;
338 secfile_insert_str_vec(sfile
, class_names
, classes
,
339 "%s.%s", path
, entry
);
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
];
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
);
367 /**************************************************************************
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");
387 comment_buildings(sfile
);
390 improvement_active_iterate(pb
) {
393 const char *flag_names
[IF_COUNT
];
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
),
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
);
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
);
432 secfile_insert_str_vec(sfile
, flag_names
, set_count
,
436 save_strvec(sfile
, pb
->helptext
, path
, "helptext");
438 } improvement_active_iterate_end
;
440 return save_ruleset_file(sfile
, filename
);
443 /**************************************************************************
445 **************************************************************************/
446 static bool save_styles_ruleset(const char *filename
, const char *name
)
448 struct section_file
*sfile
= create_ruleset_file(name
, "styles");
456 comment_styles(sfile
);
459 styles_active_iterate(pstyle
) {
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
);
470 for (i
= 0; i
< game
.control
.styles_count
; i
++) {
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
);
494 music_styles_iterate(pmus
) {
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
,
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
];
524 const struct action_auto_perf
*auto_perf
=
525 action_auto_perf_by_number(aap
);
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
));
541 } requirement_vector_iterate_end
;
543 ret
= secfile_insert_enum_vec(sfile
, &protecor_flag
, i
,
548 log_error("%s: didn't save all unit type flags.", uflags_path
);
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
,
564 const char *actions_path
)
566 enum gen_action unit_acts
[MAX_NUM_ACTIONS
];
570 const struct action_auto_perf
*auto_perf
=
571 action_auto_perf_by_number(aap
);
575 i
< NUM_ACTIONS
&& auto_perf
->alternatives
[i
] != ACTION_NONE
;
577 unit_acts
[i
] = auto_perf
->alternatives
[i
];
580 ret
= secfile_insert_enum_vec(sfile
, &unit_acts
, i
, gen_action
,
584 log_error("%s: didn't save all actions.", actions_path
);
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
,
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 /**************************************************************************
625 **************************************************************************/
626 static bool save_cities_ruleset(const char *filename
, const char *name
)
628 struct section_file
*sfile
= create_ruleset_file(name
, "cities");
635 comment_specialists(sfile
);
638 specialist_type_iterate(sp
) {
639 struct specialist
*s
= specialist_by_number(sp
);
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
,
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
,
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
,
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 **************************************************************************/
739 struct section_file
*sfile
;
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
;
750 fc_snprintf(path
, sizeof(path
), "effect_%d", cbdata
->idx
++);
752 secfile_insert_str(cbdata
->sfile
,
753 effect_type_name(peffect
->type
),
755 secfile_insert_int(cbdata
->sfile
, peffect
->value
, "%s.value", path
);
757 save_reqs_vector(cbdata
->sfile
, &peffect
->reqs
, path
, "reqs");
762 /**************************************************************************
764 **************************************************************************/
765 static bool save_effects_ruleset(const char *filename
, const char *name
)
767 struct section_file
*sfile
= create_ruleset_file(name
, "effect");
777 comment_effects(sfile
);
779 if (!iterate_effect_cache(effect_save
, &data
)) {
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
795 || (req
->source
.kind
== VUT_MINMOVES
796 && req
->source
.value
.minmoves
== 1
800 /**************************************************************************
802 **************************************************************************/
803 static bool save_game_ruleset(const char *filename
, const char *name
)
805 struct section_file
*sfile
= create_ruleset_file(name
, "game");
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
;
816 enum gen_action quiet_actions
[MAX_NUM_ACTIONS
];
818 int force_capture_units
, force_bombard
, force_explode_nuclear
;
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
,
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");
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");
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
,
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
);
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
);
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
,
902 "civstyle.civil_war_enabled", NULL
);
903 save_default_bool(sfile
, game
.info
.paradrop_to_transport
,
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
) {
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
,
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
);
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");
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");
1154 action_iterate(act
) {
1155 if (action_by_number(act
)->quiet
) {
1156 quiet_actions
[i
] = act
;
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.");
1168 comment_enablers(sfile
);
1170 action_enablers_iterate(pae
) {
1173 fc_snprintf(path
, sizeof(path
), "actionenabler_%d", sect_idx
++);
1175 secfile_insert_str(sfile
, gen_action_name(pae
->action
),
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
,
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");
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
,
1264 comment_disasters(sfile
);
1267 disaster_type_iterate(pd
) {
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
);
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
);
1299 achievements_iterate(pach
) {
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
),
1309 save_default_bool(sfile
, pach
->unique
,
1310 GAME_DEFAULT_ACH_UNIQUE
,
1312 save_default_int(sfile
, pach
->value
,
1313 GAME_DEFAULT_ACH_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
;
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")) {
1333 fc_snprintf(path
, sizeof(path
),
1334 "trade.settings%d", set_count
++);
1336 secfile_insert_str(sfile
, trade_route_type_name(trt
),
1338 secfile_insert_int(sfile
, set
->trade_pct
,
1340 secfile_insert_str(sfile
, cancelling
,
1341 "%s.cancelling", path
);
1342 secfile_insert_str(sfile
, traderoute_bonus_type_name(set
->bonus_type
),
1348 comment_goods(sfile
);
1351 goods_active_type_iterate(pgood
) {
1353 const char *flag_names
[GF_COUNT
];
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");
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
,
1377 save_strvec(sfile
, pgood
->helptext
, path
, "helptext");
1378 } goods_active_type_iterate_end
;
1381 settings_iterate(SSET_ALL
, pset
) {
1382 if (setting_locked(pset
)) {
1386 } settings_iterate_end
;
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
)) {
1395 secfile_insert_bool(sfile
, setting_bool_get(pset
),
1396 "settings.set%d.value", set_count
);
1399 secfile_insert_int(sfile
, setting_int_get(pset
),
1400 "settings.set%d.value", set_count
);
1403 secfile_insert_str(sfile
, setting_str_get(pset
),
1404 "settings.set%d.value", set_count
);
1407 secfile_insert_enum_data(sfile
, read_enum_value(pset
), FALSE
,
1408 setting_enum_secfile_str
, pset
,
1409 "settings.set%d.value", set_count
);
1412 secfile_insert_enum_data(sfile
, setting_bitwise_get(pset
), TRUE
,
1413 setting_bitwise_secfile_str
, pset
,
1414 "settings.set%d.value", set_count
);
1417 fc_assert(setting_type(pset
) != SST_COUNT
);
1418 secfile_insert_str(sfile
, "Unknown setting type",
1419 "settings.set%d.value", set_count
);
1424 secfile_insert_bool(sfile
, setting_locked(pset
),
1425 "settings.set%d.lock", 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");
1443 if (sfile
== NULL
) {
1447 save_gov_ref(sfile
, game
.government_during_revolution
, "governments",
1448 "during_revolution");
1450 comment_govs(sfile
);
1453 governments_active_iterate(pg
) {
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
,
1471 ruler_title_hash_lookup(pg
->ruler_titles
, NULL
,
1473 if (prtitle
!= NULL
) {
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
);
1496 multipliers_iterate(pmul
) {
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
)
1524 /* FIXME: Use specenum trait names without duplicating them here.
1525 * Just needs to take care of case. */
1526 const char *trait_names
[] = {
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
,
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
,
1549 if (default_default
!= traits
[tr
].fixed
) {
1550 secfile_insert_int(sfile
, traits
[tr
].fixed
, "%s.%s%s_default", secname
, field_prefix
,
1558 /**************************************************************************
1559 Save a single nation.
1560 **************************************************************************/
1561 static bool save_nation(struct section_file
*sfile
, struct nation_type
*pnat
,
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
];
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
);
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
);
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
);
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
);
1609 nation_leader_list_iterate(pnat
->leaders
, pleader
) {
1610 secfile_insert_str(sfile
, nation_leader_name(pleader
), "%s.leaders%d.name",
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
);
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
);
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
);
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")
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
)) {
1683 strcat(city_str
[set_count
], " (!river");
1684 list_started
= TRUE
;
1687 strcat(city_str
[set_count
], " (river");
1688 list_started
= TRUE
;
1694 terrain_type_iterate(pterr
) {
1695 const char *pref
= NULL
;
1697 switch(nation_city_terrain_preference(pncity
, pterr
)) {
1711 strcat(city_str
[set_count
], ", ");
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
;
1723 strcat(city_str
[set_count
], ")");
1726 list_items
[set_count
] = city_str
[set_count
];
1728 } nation_city_list_iterate_end
;
1729 if (set_count
> 0) {
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
);
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
) {
1756 if (data
->nationlist
!= NULL
) {
1757 secfile_insert_str(sfile
, data
->nationlist
, "ruledit.nationlist");
1759 if (game
.server
.ruledit
.embedded_nations
!= NULL
) {
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)
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");
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
) {
1807 comment_nations(sfile
);
1809 for (sect_idx
= 0; sect_idx
< game
.server
.ruledit
.embedded_nations_count
;
1811 struct nation_type
*pnat
1812 = nation_by_rule_name(game
.server
.ruledit
.embedded_nations
[sect_idx
]);
1815 log_error("Embedded nation \"%s\" not found!",
1816 game
.server
.ruledit
.embedded_nations
[sect_idx
]);
1818 save_nation(sfile
, pnat
, sect_idx
);
1825 comment_nationsets(sfile
);
1827 nation_sets_iterate(pset
) {
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
);
1842 nation_groups_iterate(pgroup
) {
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
);
1858 nations_iterate(pnat
) {
1859 save_nation(sfile
, pnat
, sect_idx
++);
1860 } nations_iterate_end
;
1863 return save_ruleset_file(sfile
, filename
);
1866 /**************************************************************************
1868 **************************************************************************/
1869 static bool save_techs_ruleset(const char *filename
, const char *name
)
1871 struct section_file
*sfile
= create_ruleset_file(name
, "tech");
1874 struct advance
*a_none
= advance_by_number(A_NONE
);
1877 if (sfile
== NULL
) {
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
);
1898 tech_class_iterate(ptclass
) {
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
);
1909 advance_active_iterate(pa
) {
1910 if (pa
->require
[AR_ONE
] != A_NEVER
) {
1912 const char *flag_names
[TF_COUNT
];
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
),
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
,
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
);
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
,
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");
1976 if (sfile
== NULL
) {
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
);
2053 terrain_type_iterate(pterr
) {
2057 const char *flag_names
[TER_USER_LAST
];
2058 const char *puc_names
[UCL_LAST
];
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
),
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
,
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
),
2135 secfile_insert_str(sfile
, "None",
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");
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
,
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
));
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
);
2202 extra_type_by_cause_iterate(EC_RESOURCE
, pres
) {
2203 if (!pres
->disabled
) {
2207 fc_snprintf(path
, sizeof(path
), "resource_%d", sect_idx
++);
2209 secfile_insert_str(sfile
, extra_rule_name(pres
),
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
);
2233 extra_active_type_iterate(pextra
) {
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
];
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
);
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
,
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",
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
);
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
,
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
);
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
);
2384 extra_type_by_cause_iterate(EC_BASE
, pextra
) {
2385 if (!pextra
->disabled
) {
2387 struct base_type
*pbase
= extra_base_get(pextra
);
2388 const char *flag_names
[BF_COUNT
];
2392 fc_snprintf(path
, sizeof(path
), "base_%d", sect_idx
++);
2394 secfile_insert_str(sfile
, extra_rule_name(pextra
),
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
);
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
,
2419 } extra_type_by_cause_iterate_end
;
2421 comment_roads(sfile
);
2424 extra_type_by_cause_iterate(EC_ROAD
, pextra
) {
2425 if (!pextra
->disabled
) {
2426 struct road_type
*proad
= extra_road_get(pextra
);
2428 const char *flag_names
[RF_COUNT
];
2432 fc_snprintf(path
, sizeof(path
), "road_%d", sect_idx
++);
2434 secfile_insert_str(sfile
, extra_rule_name(pextra
),
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
) {
2461 secfile_insert_str(sfile
, "Road", "%s.compat_special", path
);
2464 secfile_insert_str(sfile
, "Railroad", "%s.compat_special", path
);
2467 secfile_insert_str(sfile
, "River", "%s.compat_special", path
);
2470 secfile_insert_str(sfile
, "None", "%s.compat_special", path
);
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
,
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
];
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
);
2526 /**************************************************************************
2527 Save unit combat bonuses list.
2528 **************************************************************************/
2529 static bool save_combat_bonuses(struct section_file
*sfile
,
2530 struct unit_type
*put
,
2534 bool has_quiet
= FALSE
;
2536 combat_bonus_list_iterate(put
->bonuses
, pbonus
) {
2537 if (pbonus
->quiet
) {
2540 } combat_bonus_list_iterate_end
;
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
);
2553 secfile_insert_bool(sfile
, pbonus
->quiet
,
2554 "%s.bonuses%d.quiet", path
, i
);
2558 } combat_bonus_list_iterate_end
;
2563 /**************************************************************************
2565 **************************************************************************/
2566 static bool save_units_ruleset(const char *filename
, const char *name
)
2568 struct section_file
*sfile
= create_ruleset_file(name
, "unit");
2572 if (sfile
== NULL
) {
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
);
2610 unit_active_class_iterate(puc
) {
2612 char *hut_str
= NULL
;
2613 const char *flag_names
[UCF_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
) {
2634 hut_str
= "Nothing";
2637 hut_str
= "Frighten";
2640 fc_assert(hut_str
!= NULL
);
2641 secfile_insert_str(sfile
, hut_str
, "%s.hut_behavior", path
);
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
,
2656 save_strvec(sfile
, puc
->helptext
, path
, "helptext");
2658 } unit_active_class_iterate_end
;
2660 comment_utypes(sfile
);
2663 unit_active_type_iterate(put
) {
2664 if (!put
->disabled
) {
2666 const char *flag_names
[UTYF_LAST_USER_FLAG
+ 1];
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
),
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
);
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
,
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
,
2795 save_strvec(sfile
, put
->helptext
, path
, "helptext");
2797 } unit_active_type_iterate_end
;
2799 return save_ruleset_file(sfile
, filename
);
2802 /**************************************************************************
2804 **************************************************************************/
2805 static bool save_script_lua(const char *filename
, const char *name
,
2808 if (buffer
!= NULL
) {
2809 FILE *ffile
= fc_fopen(filename
, "w");
2810 int full_len
= strlen(buffer
);
2813 if (ffile
!= NULL
) {
2814 len
= fwrite(buffer
, 1, full_len
, ffile
);
2816 if (len
!= full_len
) {
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
;
2839 fc_snprintf(filename
, sizeof(filename
), "%s/buildings.ruleset", path
);
2840 success
= save_buildings_ruleset(filename
, name
);
2844 fc_snprintf(filename
, sizeof(filename
), "%s/styles.ruleset", path
);
2845 success
= save_styles_ruleset(filename
, name
);
2849 fc_snprintf(filename
, sizeof(filename
), "%s/cities.ruleset", path
);
2850 success
= save_cities_ruleset(filename
, name
);
2854 fc_snprintf(filename
, sizeof(filename
), "%s/effects.ruleset", path
);
2855 success
= save_effects_ruleset(filename
, name
);
2859 fc_snprintf(filename
, sizeof(filename
), "%s/game.ruleset", path
);
2860 success
= save_game_ruleset(filename
, name
);
2864 fc_snprintf(filename
, sizeof(filename
), "%s/governments.ruleset", path
);
2865 success
= save_governments_ruleset(filename
, name
);
2869 fc_snprintf(filename
, sizeof(filename
), "%s/nations.ruleset", path
);
2870 success
= save_nations_ruleset(filename
, name
, data
);
2874 fc_snprintf(filename
, sizeof(filename
), "%s/techs.ruleset", path
);
2875 success
= save_techs_ruleset(filename
, name
);
2879 fc_snprintf(filename
, sizeof(filename
), "%s/terrain.ruleset", path
);
2880 success
= save_terrain_ruleset(filename
, name
);
2884 fc_snprintf(filename
, sizeof(filename
), "%s/units.ruleset", path
);
2885 success
= save_units_ruleset(filename
, name
);
2889 fc_snprintf(filename
, sizeof(filename
), "%s/script.lua", path
);
2890 success
= save_script_lua(filename
, name
, get_script_buffer());
2894 fc_snprintf(filename
, sizeof(filename
), "%s/parser.lua", path
);
2895 success
= save_script_lua(filename
, name
, get_parser_buffer());
2900 log_error("Failed to create directory %s", path
);