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 save_tech_list(sfile
, game
.rgame
.global_init_techs
,
864 "options", "global_init_techs");
865 save_building_list(sfile
, game
.rgame
.global_init_buildings
,
866 "options", "global_init_buildings");
868 save_default_bool(sfile
, game
.control
.popup_tech_help
,
870 "options.popup_tech_help", NULL
);
871 save_default_int(sfile
, game
.info
.base_pollution
,
872 RS_DEFAULT_BASE_POLLUTION
,
873 "civstyle.base_pollution", NULL
);
876 for (gs
= gameloss_style_begin(); gs
!= gameloss_style_end(); gs
= gameloss_style_next(gs
)) {
877 if (game
.info
.gameloss_style
& gs
) {
878 style_names
[set_count
++] = gameloss_style_name(gs
);
883 secfile_insert_str_vec(sfile
, style_names
, set_count
,
884 "civstyle.gameloss_style");
887 save_default_int(sfile
, game
.info
.happy_cost
,
888 RS_DEFAULT_HAPPY_COST
,
889 "civstyle.happy_cost", NULL
);
890 save_default_int(sfile
, game
.info
.food_cost
,
891 RS_DEFAULT_FOOD_COST
,
892 "civstyle.food_cost", NULL
);
893 save_default_bool(sfile
, game
.info
.civil_war_enabled
,
895 "civstyle.civil_war_enabled", NULL
);
896 save_default_bool(sfile
, game
.info
.paradrop_to_transport
,
898 "civstyle.paradrop_to_transport", NULL
);
899 save_default_int(sfile
, game
.info
.base_bribe_cost
,
900 RS_DEFAULT_BASE_BRIBE_COST
,
901 "civstyle.base_bribe_cost", NULL
);
902 save_default_int(sfile
, game
.server
.ransom_gold
,
903 RS_DEFAULT_RANSOM_GOLD
,
904 "civstyle.ransom_gold", NULL
);
905 save_default_bool(sfile
, game
.info
.pillage_select
,
906 RS_DEFAULT_PILLAGE_SELECT
,
907 "civstyle.pillage_select", NULL
);
908 save_default_bool(sfile
, game
.info
.tech_steal_allow_holes
,
909 RS_DEFAULT_TECH_STEAL_HOLES
,
910 "civstyle.tech_steal_allow_holes", NULL
);
911 save_default_bool(sfile
, game
.info
.tech_trade_allow_holes
,
912 RS_DEFAULT_TECH_TRADE_HOLES
,
913 "civstyle.tech_trade_allow_holes", NULL
);
914 save_default_bool(sfile
, game
.info
.tech_trade_loss_allow_holes
,
915 RS_DEFAULT_TECH_TRADE_LOSS_HOLES
,
916 "civstyle.tech_trade_loss_allow_holes", NULL
);
917 save_default_bool(sfile
, game
.info
.tech_parasite_allow_holes
,
918 RS_DEFAULT_TECH_PARASITE_HOLES
,
919 "civstyle.tech_parasite_allow_holes", NULL
);
920 save_default_bool(sfile
, game
.info
.tech_loss_allow_holes
,
921 RS_DEFAULT_TECH_LOSS_HOLES
,
922 "civstyle.tech_loss_allow_holes", NULL
);
923 save_default_int(sfile
, game
.server
.upgrade_veteran_loss
,
924 RS_DEFAULT_UPGRADE_VETERAN_LOSS
,
925 "civstyle.upgrade_veteran_loss", NULL
);
926 save_default_int(sfile
, game
.server
.autoupgrade_veteran_loss
,
927 RS_DEFAULT_UPGRADE_VETERAN_LOSS
,
928 "civstyle.autoupgrade_veteran_loss", NULL
);
930 secfile_insert_int_vec(sfile
, game
.info
.granary_food_ini
,
931 game
.info
.granary_num_inis
,
932 "civstyle.granary_food_ini");
934 save_default_int(sfile
, game
.info
.granary_food_inc
,
935 RS_DEFAULT_GRANARY_FOOD_INC
,
936 "civstyle.granary_food_inc", NULL
);
938 output_type_iterate(o
) {
941 fc_snprintf(buffer
, sizeof(buffer
),
942 "civstyle.min_city_center_%s",
943 get_output_identifier(o
));
945 save_default_int(sfile
, game
.info
.min_city_center_output
[o
],
946 RS_DEFAULT_CITY_CENTER_OUTPUT
,
948 } output_type_iterate_end
;
950 save_default_int(sfile
, game
.server
.init_vis_radius_sq
,
951 RS_DEFAULT_VIS_RADIUS_SQ
,
952 "civstyle.init_vis_radius_sq", NULL
);
953 save_default_int(sfile
, game
.info
.init_city_radius_sq
,
954 RS_DEFAULT_CITY_RADIUS_SQ
,
955 "civstyle.init_city_radius_sq", NULL
);
956 if (0 != fc_strcasecmp(gold_upkeep_style_name(game
.info
.gold_upkeep_style
),
957 RS_DEFAULT_GOLD_UPKEEP_STYLE
)) {
958 secfile_insert_str(sfile
,
959 gold_upkeep_style_name(game
.info
.gold_upkeep_style
),
960 "civstyle.gold_upkeep_style");
962 save_default_bool(sfile
, game
.info
.illness_on
,
963 RS_DEFAULT_ILLNESS_ON
,
964 "illness.illness_on", NULL
);
965 save_default_int(sfile
, game
.info
.illness_base_factor
,
966 RS_DEFAULT_ILLNESS_BASE_FACTOR
,
967 "illness.illness_base_factor", NULL
);
968 save_default_int(sfile
, game
.info
.illness_min_size
,
969 RS_DEFAULT_ILLNESS_MIN_SIZE
,
970 "illness.illness_min_size", NULL
);
971 save_default_int(sfile
, game
.info
.illness_trade_infection
,
972 RS_DEFAULT_ILLNESS_TRADE_INFECTION_PCT
,
973 "illness.illness_trade_infection", NULL
);
974 save_default_int(sfile
, game
.info
.illness_pollution_factor
,
975 RS_DEFAULT_ILLNESS_POLLUTION_PCT
,
976 "illness.illness_pollution_factor", NULL
);
977 save_default_int(sfile
, game
.server
.base_incite_cost
,
978 RS_DEFAULT_INCITE_BASE_COST
,
979 "incite_cost.base_incite_cost", NULL
);
980 save_default_int(sfile
, game
.server
.incite_improvement_factor
,
981 RS_DEFAULT_INCITE_IMPROVEMENT_FCT
,
982 "incite_cost.improvement_factor", NULL
);
983 save_default_int(sfile
, game
.server
.incite_unit_factor
,
984 RS_DEFAULT_INCITE_UNIT_FCT
,
985 "incite_cost.unit_factor", NULL
);
986 save_default_int(sfile
, game
.server
.incite_total_factor
,
987 RS_DEFAULT_INCITE_TOTAL_FCT
,
988 "incite_cost.total_factor", NULL
);
989 save_default_bool(sfile
, game
.info
.slow_invasions
,
990 RS_DEFAULT_SLOW_INVASIONS
,
991 "global_unit_options.slow_invasions", NULL
);
993 save_action_auto_uflag_block(sfile
, ACTION_AUTO_MOVED_ADJ
,
994 "auto_attack.will_never",
995 unexpected_auto_attack
);
997 save_default_bool(sfile
,
998 action_id_would_be_blocked_by(ACTION_MARKETPLACE
,
1000 RS_DEFAULT_FORCE_TRADE_ROUTE
,
1001 "actions.force_trade_route", NULL
);
1003 /* The ruleset options force_capture_units, force_bombard and
1004 * force_explode_nuclear sets their respective action to block many other
1005 * actions' blocked_by. Checking one is therefore enough. */
1006 force_capture_units
=
1007 BV_ISSET(action_by_number(ACTION_ATTACK
)->blocked_by
,
1008 ACTION_CAPTURE_UNITS
);
1009 save_default_bool(sfile
, force_capture_units
,
1010 RS_DEFAULT_FORCE_CAPTURE_UNITS
,
1011 "actions.force_capture_units", NULL
);
1013 BV_ISSET(action_by_number(ACTION_ATTACK
)->blocked_by
, ACTION_BOMBARD
);
1014 save_default_bool(sfile
, force_bombard
,
1015 RS_DEFAULT_FORCE_BOMBARD
,
1016 "actions.force_bombard", NULL
);
1017 force_explode_nuclear
=
1018 BV_ISSET(action_by_number(ACTION_ATTACK
)->blocked_by
, ACTION_NUKE
);
1019 save_default_bool(sfile
, force_explode_nuclear
,
1020 RS_DEFAULT_FORCE_EXPLODE_NUCLEAR
,
1021 "actions.force_explode_nuclear", NULL
);
1023 save_default_bool(sfile
, game
.info
.poison_empties_food_stock
,
1024 RS_DEFAULT_POISON_EMPTIES_FOOD_STOCK
,
1025 "actions.poison_empties_food_stock", NULL
);
1027 if (action_by_number(ACTION_BOMBARD
)->max_distance
1028 == ACTION_DISTANCE_UNLIMITED
) {
1029 secfile_insert_str(sfile
, RS_ACTION_NO_MAX_DISTANCE
,
1030 "actions.bombard_max_range");
1032 save_default_int(sfile
, action_by_number(ACTION_BOMBARD
)->max_distance
,
1033 RS_DEFAULT_BOMBARD_MAX_RANGE
,
1034 "actions.bombard_max_range", NULL
);
1037 secfile_insert_str(sfile
,
1038 action_by_number(ACTION_SPY_POISON
)->ui_name
,
1039 "actions.ui_name_poison_city");
1040 secfile_insert_str(sfile
,
1041 action_by_number(ACTION_SPY_SABOTAGE_UNIT
)->ui_name
,
1042 "actions.ui_name_sabotage_unit");
1043 secfile_insert_str(sfile
,
1044 action_by_number(ACTION_SPY_BRIBE_UNIT
)->ui_name
,
1045 "actions.ui_name_bribe_unit");
1046 secfile_insert_str(sfile
,
1047 action_by_number(ACTION_SPY_SABOTAGE_CITY
)->ui_name
,
1048 "actions.ui_name_sabotage_city");
1049 secfile_insert_str(sfile
,
1050 action_by_number(ACTION_SPY_TARGETED_SABOTAGE_CITY
)->ui_name
,
1051 "actions.ui_name_targeted_sabotage_city");
1052 secfile_insert_str(sfile
,
1053 action_by_number(ACTION_SPY_INCITE_CITY
)->ui_name
,
1054 "actions.ui_name_incite_city");
1055 secfile_insert_str(sfile
,
1056 action_by_number(ACTION_ESTABLISH_EMBASSY
)->ui_name
,
1057 "actions.ui_name_establish_embassy");
1058 secfile_insert_str(sfile
,
1059 action_by_number(ACTION_ESTABLISH_EMBASSY_STAY
)->ui_name
,
1060 "actions.ui_name_establish_embassy_stay");
1061 secfile_insert_str(sfile
,
1062 action_by_number(ACTION_SPY_STEAL_TECH
)->ui_name
,
1063 "actions.ui_name_steal_tech");
1064 secfile_insert_str(sfile
,
1065 action_by_number(ACTION_SPY_TARGETED_STEAL_TECH
)->ui_name
,
1066 "actions.ui_name_targeted_steal_tech");
1067 secfile_insert_str(sfile
,
1068 action_by_number(ACTION_SPY_INVESTIGATE_CITY
)->ui_name
,
1069 "actions.ui_name_investigate_city");
1070 secfile_insert_str(sfile
,
1071 action_by_number(ACTION_INV_CITY_SPEND
)->ui_name
,
1072 "actions.ui_name_investigate_city_spend_unit");
1073 secfile_insert_str(sfile
,
1074 action_by_number(ACTION_SPY_STEAL_GOLD
)->ui_name
,
1075 "actions.ui_name_steal_gold");
1076 secfile_insert_str(sfile
,
1077 action_by_number(ACTION_STEAL_MAPS
)->ui_name
,
1078 "actions.ui_name_steal_maps");
1079 secfile_insert_str(sfile
,
1080 action_by_number(ACTION_TRADE_ROUTE
)->ui_name
,
1081 "actions.ui_name_establish_trade_route");
1082 secfile_insert_str(sfile
,
1083 action_by_number(ACTION_MARKETPLACE
)->ui_name
,
1084 "actions.ui_name_enter_marketplace");
1085 secfile_insert_str(sfile
,
1086 action_by_number(ACTION_HELP_WONDER
)->ui_name
,
1087 "actions.ui_name_help_wonder");
1088 secfile_insert_str(sfile
,
1089 action_by_number(ACTION_CAPTURE_UNITS
)->ui_name
,
1090 "actions.ui_name_capture_units");
1091 secfile_insert_str(sfile
,
1092 action_by_number(ACTION_EXPEL_UNIT
)->ui_name
,
1093 "actions.ui_name_expel_unit");
1094 secfile_insert_str(sfile
,
1095 action_by_number(ACTION_FOUND_CITY
)->ui_name
,
1096 "actions.ui_name_found_city");
1097 secfile_insert_str(sfile
,
1098 action_by_number(ACTION_JOIN_CITY
)->ui_name
,
1099 "actions.ui_name_join_city");
1100 secfile_insert_str(sfile
,
1101 action_by_number(ACTION_BOMBARD
)->ui_name
,
1102 "actions.ui_name_bombard");
1103 secfile_insert_str(sfile
,
1104 action_by_number(ACTION_SPY_NUKE
)->ui_name
,
1105 "actions.ui_name_suitcase_nuke");
1106 secfile_insert_str(sfile
,
1107 action_by_number(ACTION_NUKE
)->ui_name
,
1108 "actions.ui_name_explode_nuclear");
1109 secfile_insert_str(sfile
,
1110 action_by_number(ACTION_DESTROY_CITY
)->ui_name
,
1111 "actions.ui_name_destroy_city");
1112 secfile_insert_str(sfile
,
1113 action_by_number(ACTION_RECYCLE_UNIT
)->ui_name
,
1114 "actions.ui_name_recycle_unit");
1115 secfile_insert_str(sfile
,
1116 action_by_number(ACTION_DISBAND_UNIT
)->ui_name
,
1117 "actions.ui_name_disband_unit");
1118 secfile_insert_str(sfile
,
1119 action_by_number(ACTION_HOME_CITY
)->ui_name
,
1120 "actions.ui_name_home_city");
1121 secfile_insert_str(sfile
,
1122 action_by_number(ACTION_UPGRADE_UNIT
)->ui_name
,
1123 "actions.ui_upgrade_unit");
1124 secfile_insert_str(sfile
,
1125 action_by_number(ACTION_PARADROP
)->ui_name
,
1126 "actions.ui_paradrop_unit");
1127 secfile_insert_str(sfile
,
1128 action_by_number(ACTION_AIRLIFT
)->ui_name
,
1129 "actions.ui_airlift_unit");
1130 secfile_insert_str(sfile
,
1131 action_by_number(ACTION_ATTACK
)->ui_name
,
1132 "actions.ui_name_attack");
1133 secfile_insert_str(sfile
,
1134 action_by_number(ACTION_CONQUER_CITY
)->ui_name
,
1135 "actions.ui_name_conquer_city");
1136 secfile_insert_str(sfile
,
1137 action_by_number(ACTION_HEAL_UNIT
)->ui_name
,
1138 "actions.ui_name_heal_unit");
1141 action_iterate(act
) {
1142 if (action_by_number(act
)->quiet
) {
1143 quiet_actions
[i
] = act
;
1146 } action_iterate_end
;
1148 if (secfile_insert_enum_vec(sfile
, &quiet_actions
, i
, gen_action
,
1149 "actions.quiet_actions") != i
) {
1150 log_error("Didn't save all quiet actions.");
1155 comment_enablers(sfile
);
1157 action_enablers_iterate(pae
) {
1160 fc_snprintf(path
, sizeof(path
), "actionenabler_%d", sect_idx
++);
1162 secfile_insert_str(sfile
, gen_action_name(pae
->action
),
1165 save_reqs_vector(sfile
, &(pae
->actor_reqs
), path
, "actor_reqs");
1166 save_reqs_vector(sfile
, &(pae
->target_reqs
), path
, "target_reqs");
1167 } action_enablers_iterate_end
;
1169 save_default_bool(sfile
, game
.info
.tired_attack
,
1170 RS_DEFAULT_TIRED_ATTACK
,
1171 "combat_rules.tired_attack", NULL
);
1172 save_default_int(sfile
, game
.info
.border_city_radius_sq
,
1173 RS_DEFAULT_BORDER_RADIUS_SQ_CITY
,
1174 "borders.radius_sq_city", NULL
);
1175 save_default_int(sfile
, game
.info
.border_size_effect
,
1176 RS_DEFAULT_BORDER_SIZE_EFFECT
,
1177 "borders.size_effect", NULL
);
1178 save_default_int(sfile
, game
.info
.border_city_permanent_radius_sq
,
1179 RS_DEFAULT_BORDER_RADIUS_SQ_CITY_PERMANENT
,
1180 "borders.radius_sq_city_permanent", NULL
);
1181 secfile_insert_str(sfile
, tech_cost_style_name(game
.info
.tech_cost_style
),
1182 "research.tech_cost_style");
1183 save_default_int(sfile
, game
.info
.base_tech_cost
,
1184 RS_DEFAULT_BASE_TECH_COST
,
1185 "research.base_tech_cost", NULL
);
1186 secfile_insert_str(sfile
, tech_leakage_style_name(game
.info
.tech_leakage
),
1187 "research.tech_leakage");
1188 secfile_insert_str(sfile
, tech_upkeep_style_name(game
.info
.tech_upkeep_style
),
1189 "research.tech_upkeep_style");
1190 save_default_int(sfile
, game
.info
.tech_upkeep_divider
,
1191 RS_DEFAULT_TECH_UPKEEP_DIVIDER
,
1192 "research.tech_upkeep_divider", NULL
);
1193 secfile_insert_str(sfile
, free_tech_method_name(game
.info
.free_tech_method
),
1194 "research.free_tech_method");
1196 save_default_int(sfile
, game
.info
.culture_vic_points
,
1197 RS_DEFAULT_CULTURE_VIC_POINTS
,
1198 "culture.victory_min_points", NULL
);
1199 save_default_int(sfile
, game
.info
.culture_vic_lead
,
1200 RS_DEFAULT_CULTURE_VIC_LEAD
,
1201 "culture.victory_lead_pct", NULL
);
1202 save_default_int(sfile
, game
.info
.culture_migration_pml
,
1203 RS_DEFAULT_CULTURE_MIGRATION_PML
,
1204 "culture.migration_pml", NULL
);
1206 save_default_bool(sfile
, game
.calendar
.calendar_skip_0
,
1207 RS_DEFAULT_CALENDAR_SKIP_0
,
1208 "calendar.skip_year_0", NULL
);
1209 save_default_int(sfile
, game
.server
.start_year
,
1211 "calendar.start_year", NULL
);
1212 save_default_int(sfile
, game
.calendar
.calendar_fragments
,
1213 0, "calendar.fragments", NULL
);
1215 for (i
= 0; i
< MAX_CALENDAR_FRAGMENTS
; i
++) {
1216 if (game
.calendar
.calendar_fragment_name
[i
][0] != '\0') {
1217 secfile_insert_str(sfile
, game
.calendar
.calendar_fragment_name
[i
],
1218 "calendar.fragment_name%d", i
);
1222 if (strcmp(game
.calendar
.positive_year_label
, RS_DEFAULT_POS_YEAR_LABEL
)) {
1223 secfile_insert_str(sfile
, game
.calendar
.positive_year_label
,
1224 "calendar.positive_label");
1226 if (strcmp(game
.calendar
.negative_year_label
, RS_DEFAULT_NEG_YEAR_LABEL
)) {
1227 secfile_insert_str(sfile
, game
.calendar
.negative_year_label
,
1228 "calendar.negative_label");
1231 if (game
.plr_bg_color
!= NULL
) {
1232 rgbcolor_save(sfile
, game
.plr_bg_color
, "playercolors.background");
1236 rgbcolor_list_iterate(game
.server
.plr_colors
, pcol
) {
1237 rgbcolor_save(sfile
, pcol
, "playercolors.colorlist%d", col_idx
++);
1238 } rgbcolor_list_iterate_end
;
1241 if (game
.server
.ruledit
.named_teams
> 0) {
1242 for (i
= 0; i
< game
.server
.ruledit
.named_teams
; i
++) {
1243 tnames
[i
] = team_slot_rule_name(team_slot_by_number(i
));
1246 secfile_insert_str_vec(sfile
, tnames
,
1247 game
.server
.ruledit
.named_teams
,
1251 comment_disasters(sfile
);
1254 disaster_type_iterate(pd
) {
1256 enum disaster_effect_id de
;
1257 const char *effect_names
[DE_COUNT
];
1259 fc_snprintf(path
, sizeof(path
), "disaster_%d", sect_idx
++);
1261 save_name_translation(sfile
, &(pd
->name
), path
);
1262 save_reqs_vector(sfile
, &(pd
->reqs
), path
, "reqs");
1263 if (pd
->frequency
!= GAME_DEFAULT_DISASTER_FREQ
) {
1264 secfile_insert_int(sfile
, pd
->frequency
,
1265 "%s.frequency", path
);
1269 for (de
= disaster_effect_id_begin();
1270 de
!= disaster_effect_id_end();
1271 de
= disaster_effect_id_next(de
)) {
1272 if (BV_ISSET(pd
->effects
, de
)) {
1273 effect_names
[set_count
++] = disaster_effect_id_name(de
);
1277 if (set_count
> 0) {
1278 secfile_insert_str_vec(sfile
, effect_names
, set_count
,
1279 "%s.effects", path
);
1281 } disaster_type_iterate_end
;
1283 comment_achievements(sfile
);
1286 achievements_iterate(pach
) {
1289 fc_snprintf(path
, sizeof(path
), "achievement_%d", sect_idx
++);
1291 save_name_translation(sfile
, &(pach
->name
), path
);
1293 secfile_insert_str(sfile
, achievement_type_name(pach
->type
),
1296 save_default_bool(sfile
, pach
->unique
,
1297 GAME_DEFAULT_ACH_UNIQUE
,
1299 save_default_int(sfile
, pach
->value
,
1300 GAME_DEFAULT_ACH_VALUE
,
1302 save_default_int(sfile
, pach
->culture
,
1303 0, path
, "culture");
1305 secfile_insert_str(sfile
, pach
->first_msg
, "%s.first_msg", path
);
1306 if (pach
->cons_msg
!= NULL
) {
1307 secfile_insert_str(sfile
, pach
->cons_msg
, "%s.cons_msg", path
);
1310 } achievements_iterate_end
;
1313 for (trt
= 0; trt
< TRT_LAST
; trt
++) {
1314 struct trade_route_settings
*set
= trade_route_settings_by_type(trt
);
1315 const char *cancelling
= traderoute_cancelling_type_name(set
->cancelling
);
1317 if (set
->trade_pct
!= 100 || strcmp(cancelling
, "Active")) {
1320 fc_snprintf(path
, sizeof(path
),
1321 "trade.settings%d", set_count
++);
1323 secfile_insert_str(sfile
, trade_route_type_name(trt
),
1325 secfile_insert_int(sfile
, set
->trade_pct
,
1327 secfile_insert_str(sfile
, cancelling
,
1328 "%s.cancelling", path
);
1329 secfile_insert_str(sfile
, traderoute_bonus_type_name(set
->bonus_type
),
1335 comment_goods(sfile
);
1338 goods_active_type_iterate(pgood
) {
1340 const char *flag_names
[GF_COUNT
];
1343 fc_snprintf(path
, sizeof(path
), "goods_%d", sect_idx
++);
1345 save_name_translation(sfile
, &(pgood
->name
), path
);
1347 save_reqs_vector(sfile
, &(pgood
->reqs
), path
, "reqs");
1349 save_default_int(sfile
, pgood
->from_pct
, 100, path
, "from_pct");
1350 save_default_int(sfile
, pgood
->to_pct
, 100, path
, "to_pct");
1353 for (flagi
= 0; flagi
< GF_COUNT
; flagi
++) {
1354 if (goods_has_flag(pgood
, flagi
)) {
1355 flag_names
[set_count
++] = goods_flag_id_name(flagi
);
1359 if (set_count
> 0) {
1360 secfile_insert_str_vec(sfile
, flag_names
, set_count
,
1364 save_strvec(sfile
, pgood
->helptext
, path
, "helptext");
1365 } goods_active_type_iterate_end
;
1368 settings_iterate(SSET_ALL
, pset
) {
1369 if (setting_locked(pset
)) {
1373 } settings_iterate_end
;
1376 settings_iterate(SSET_ALL
, pset
) {
1377 if (setting_get_setdef(pset
) == SETDEF_RULESET
|| setting_locked(pset
)) {
1378 secfile_insert_str(sfile
, setting_name(pset
),
1379 "settings.set%d.name", set_count
);
1380 switch (setting_type(pset
)) {
1382 secfile_insert_bool(sfile
, setting_bool_get(pset
),
1383 "settings.set%d.value", set_count
);
1386 secfile_insert_int(sfile
, setting_int_get(pset
),
1387 "settings.set%d.value", set_count
);
1390 secfile_insert_str(sfile
, setting_str_get(pset
),
1391 "settings.set%d.value", set_count
);
1394 secfile_insert_enum_data(sfile
, read_enum_value(pset
), FALSE
,
1395 setting_enum_secfile_str
, pset
,
1396 "settings.set%d.value", set_count
);
1399 secfile_insert_enum_data(sfile
, setting_bitwise_get(pset
), TRUE
,
1400 setting_bitwise_secfile_str
, pset
,
1401 "settings.set%d.value", set_count
);
1404 fc_assert(setting_type(pset
) != SST_COUNT
);
1405 secfile_insert_str(sfile
, "Unknown setting type",
1406 "settings.set%d.value", set_count
);
1411 secfile_insert_bool(sfile
, setting_locked(pset
),
1412 "settings.set%d.lock", set_count
);
1417 } settings_iterate_end
;
1419 return save_ruleset_file(sfile
, filename
);
1422 /**************************************************************************
1423 Save governments.ruleset
1424 **************************************************************************/
1425 static bool save_governments_ruleset(const char *filename
, const char *name
)
1427 struct section_file
*sfile
= create_ruleset_file(name
, "government");
1430 if (sfile
== NULL
) {
1434 save_gov_ref(sfile
, game
.government_during_revolution
, "governments",
1435 "during_revolution");
1437 comment_govs(sfile
);
1440 governments_active_iterate(pg
) {
1442 struct ruler_title
*prtitle
;
1444 fc_snprintf(path
, sizeof(path
), "government_%d", sect_idx
++);
1446 save_name_translation(sfile
, &(pg
->name
), path
);
1448 secfile_insert_str(sfile
, pg
->graphic_str
, "%s.graphic", path
);
1449 secfile_insert_str(sfile
, pg
->graphic_alt
, "%s.graphic_alt", path
);
1451 save_reqs_vector(sfile
, &(pg
->reqs
), path
, "reqs");
1453 if (pg
->ai
.better
!= NULL
) {
1454 save_gov_ref(sfile
, pg
->ai
.better
, path
,
1458 ruler_title_hash_lookup(pg
->ruler_titles
, NULL
,
1460 if (prtitle
!= NULL
) {
1463 title
= ruler_title_male_untranslated_name(prtitle
);
1464 if (title
!= NULL
) {
1465 secfile_insert_str(sfile
, title
,
1466 "%s.ruler_male_title", path
);
1469 title
= ruler_title_female_untranslated_name(prtitle
);
1470 if (title
!= NULL
) {
1471 secfile_insert_str(sfile
, title
,
1472 "%s.ruler_female_title", path
);
1476 save_strvec(sfile
, pg
->helptext
, path
, "helptext");
1478 } governments_active_iterate_end
;
1480 comment_policies(sfile
);
1483 multipliers_iterate(pmul
) {
1486 fc_snprintf(path
, sizeof(path
), "multiplier_%d", sect_idx
++);
1488 save_name_translation(sfile
, &(pmul
->name
), path
);
1490 secfile_insert_int(sfile
, pmul
->start
, "%s.start", path
);
1491 secfile_insert_int(sfile
, pmul
->stop
, "%s.stop", path
);
1492 secfile_insert_int(sfile
, pmul
->step
, "%s.step", path
);
1493 secfile_insert_int(sfile
, pmul
->def
, "%s.default", path
);
1495 save_strvec(sfile
, pmul
->helptext
, path
, "helptext");
1496 } multipliers_iterate_end
;
1498 return save_ruleset_file(sfile
, filename
);
1501 /**************************************************************************
1502 Save list of AI traits
1503 **************************************************************************/
1504 static bool save_traits(struct trait_limits
*traits
,
1505 struct trait_limits
*default_traits
,
1506 struct section_file
*sfile
,
1507 const char *secname
, const char *field_prefix
)
1511 /* FIXME: Use specenum trait names without duplicating them here.
1512 * Just needs to take care of case. */
1513 const char *trait_names
[] = {
1520 for (tr
= trait_begin(); tr
!= trait_end() && trait_names
[tr
] != NULL
;
1521 tr
= trait_next(tr
)) {
1522 int default_default
;
1524 default_default
= (traits
[tr
].min
+ traits
[tr
].max
) / 2;
1526 if ((default_traits
== NULL
&& traits
[tr
].min
!= TRAIT_DEFAULT_VALUE
)
1527 || (default_traits
!= NULL
&& traits
[tr
].min
!= default_traits
[tr
].min
)) {
1528 secfile_insert_int(sfile
, traits
[tr
].min
, "%s.%s%s_min", secname
, field_prefix
,
1531 if ((default_traits
== NULL
&& traits
[tr
].max
!= TRAIT_DEFAULT_VALUE
)
1532 || (default_traits
!= NULL
&& traits
[tr
].max
!= default_traits
[tr
].max
)) {
1533 secfile_insert_int(sfile
, traits
[tr
].max
, "%s.%s%s_max", secname
, field_prefix
,
1536 if (default_default
!= traits
[tr
].fixed
) {
1537 secfile_insert_int(sfile
, traits
[tr
].fixed
, "%s.%s%s_default", secname
, field_prefix
,
1545 /**************************************************************************
1546 Save a single nation.
1547 **************************************************************************/
1548 static bool save_nation(struct section_file
*sfile
, struct nation_type
*pnat
,
1552 int max_items
= nation_city_list_size(pnat
->server
.default_cities
);
1553 char *city_str
[max_items
];
1554 max_items
= MAX(max_items
, MAX_NUM_NATION_SETS
+ MAX_NUM_NATION_GROUPS
);
1555 max_items
= MAX(max_items
, game
.control
.nation_count
);
1556 const char *list_items
[max_items
];
1560 fc_snprintf(path
, sizeof(path
), "nation_%d", sect_idx
++);
1562 if (pnat
->translation_domain
== NULL
) {
1563 secfile_insert_str(sfile
, "freeciv", "%s.translation_domain", path
);
1565 secfile_insert_str(sfile
, pnat
->translation_domain
, "%s.translation_domain", path
);
1568 save_name_translation(sfile
, &(pnat
->adjective
), path
);
1569 secfile_insert_str(sfile
, untranslated_name(&(pnat
->noun_plural
)), "%s.plural", path
);
1572 nation_sets_iterate(pset
) {
1573 if (nation_is_in_set(pnat
, pset
)) {
1574 list_items
[set_count
++] = nation_set_rule_name(pset
);
1576 } nation_sets_iterate_end
;
1577 nation_groups_iterate(pgroup
) {
1578 if (nation_is_in_group(pnat
, pgroup
)) {
1579 list_items
[set_count
++] = nation_group_rule_name(pgroup
);
1581 } nation_groups_iterate_end
;
1583 if (set_count
> 0) {
1584 secfile_insert_str_vec(sfile
, list_items
, set_count
, "%s.groups", path
);
1588 nation_list_iterate(pnat
->server
.conflicts_with
, pconfl
) {
1589 list_items
[set_count
++] = nation_rule_name(pconfl
);
1590 } nation_list_iterate_end
;
1591 if (set_count
> 0) {
1592 secfile_insert_str_vec(sfile
, list_items
, set_count
, "%s.conflicts_with", path
);
1596 nation_leader_list_iterate(pnat
->leaders
, pleader
) {
1597 secfile_insert_str(sfile
, nation_leader_name(pleader
), "%s.leaders%d.name",
1599 secfile_insert_str(sfile
, nation_leader_is_male(pleader
) ? "Male" : "Female",
1600 "%s.leaders%d.sex", path
, subsect_idx
++);
1601 } nation_leader_list_iterate_end
;
1603 if (pnat
->server
.rgb
!= NULL
) {
1604 rgbcolor_save(sfile
, pnat
->server
.rgb
, "%s.color", path
);
1607 save_traits(pnat
->server
.traits
, game
.server
.default_traits
,
1608 sfile
, path
, "trait_");
1610 if (!pnat
->is_playable
) {
1611 secfile_insert_bool(sfile
, pnat
->is_playable
, "%s.is_playable", path
);
1614 if (pnat
->barb_type
!= NOT_A_BARBARIAN
) {
1615 secfile_insert_str(sfile
, barbarian_type_name(pnat
->barb_type
),
1616 "%s.barbarian_type", path
);
1619 if (strcmp(pnat
->flag_graphic_str
, "-")) {
1620 secfile_insert_str(sfile
, pnat
->flag_graphic_str
, "%s.flag", path
);
1622 if (strcmp(pnat
->flag_graphic_alt
, "-")) {
1623 secfile_insert_str(sfile
, pnat
->flag_graphic_alt
, "%s.flag_alt", path
);
1627 governments_iterate(pgov
) {
1628 struct ruler_title
*prtitle
;
1630 if (ruler_title_hash_lookup(pgov
->ruler_titles
, pnat
, &prtitle
)) {
1631 secfile_insert_str(sfile
, government_rule_name(pgov
),
1632 "%s.ruler_titles%d.government", path
, subsect_idx
);
1633 secfile_insert_str(sfile
, ruler_title_male_untranslated_name(prtitle
),
1634 "%s.ruler_titles%d.male_title", path
, subsect_idx
);
1635 secfile_insert_str(sfile
, ruler_title_female_untranslated_name(prtitle
),
1636 "%s.ruler_titles%d.female_title", path
, subsect_idx
++);
1638 } governments_iterate_end
;
1640 secfile_insert_str(sfile
, style_rule_name(pnat
->style
), "%s.style", path
);
1643 nation_list_iterate(pnat
->server
.civilwar_nations
, pconfl
) {
1644 list_items
[set_count
++] = nation_rule_name(pconfl
);
1645 } nation_list_iterate_end
;
1646 if (set_count
> 0) {
1647 secfile_insert_str_vec(sfile
, list_items
, set_count
, "%s.civilwar_nations", path
);
1650 save_tech_list(sfile
, pnat
->init_techs
, path
, "init_techs");
1651 save_building_list(sfile
, pnat
->init_buildings
, path
, "init_buildings");
1652 save_unit_list(sfile
, pnat
->init_units
, path
, "init_units");
1654 if (pnat
->init_government
) {
1655 secfile_insert_str(sfile
, government_rule_name(pnat
->init_government
),
1656 "%s.init_government", path
);
1660 nation_city_list_iterate(pnat
->server
.default_cities
, pncity
) {
1661 bool list_started
= FALSE
;
1663 city_str
[set_count
] = fc_malloc(strlen(nation_city_name(pncity
)) + strlen(" (!river")
1665 + MAX_NUM_TERRAINS
* (strlen(", ") + MAX_LEN_NAME
));
1667 strcpy(city_str
[set_count
], nation_city_name(pncity
));
1668 switch(nation_city_river_preference(pncity
)) {
1670 strcat(city_str
[set_count
], " (!river");
1671 list_started
= TRUE
;
1674 strcat(city_str
[set_count
], " (river");
1675 list_started
= TRUE
;
1681 terrain_type_iterate(pterr
) {
1682 const char *pref
= NULL
;
1684 switch(nation_city_terrain_preference(pncity
, pterr
)) {
1698 strcat(city_str
[set_count
], ", ");
1700 strcat(city_str
[set_count
], " (");
1701 list_started
= TRUE
;
1703 strcat(city_str
[set_count
], pref
);
1704 strcat(city_str
[set_count
], terrain_rule_name(pterr
));
1707 } terrain_type_iterate_end
;
1710 strcat(city_str
[set_count
], ")");
1713 list_items
[set_count
] = city_str
[set_count
];
1715 } nation_city_list_iterate_end
;
1716 if (set_count
> 0) {
1719 secfile_insert_str_vec(sfile
, list_items
, set_count
, "%s.cities", path
);
1721 for (i
= 0; i
< set_count
; i
++) {
1722 FC_FREE(city_str
[i
]);
1726 secfile_insert_str(sfile
, pnat
->legend
, "%s.legend", path
);
1731 /**************************************************************************
1732 Save nations.ruleset
1733 **************************************************************************/
1734 static bool save_nations_ruleset(const char *filename
, const char *name
,
1735 struct rule_data
*data
)
1737 struct section_file
*sfile
= create_ruleset_file(name
, "nation");
1739 if (sfile
== NULL
) {
1743 if (data
->nationlist
!= NULL
) {
1744 secfile_insert_str(sfile
, data
->nationlist
, "ruledit.nationlist");
1746 if (game
.server
.ruledit
.embedded_nations
!= NULL
) {
1748 const char **tmp
= fc_malloc(game
.server
.ruledit
.embedded_nations_count
* sizeof(char *));
1750 /* Dance around the secfile_insert_str_vec() parameter type (requires extra const)
1752 for (i
= 0; i
< game
.server
.ruledit
.embedded_nations_count
; i
++) {
1753 tmp
[i
] = game
.server
.ruledit
.embedded_nations
[i
];
1756 secfile_insert_str_vec(sfile
, tmp
,
1757 game
.server
.ruledit
.embedded_nations_count
,
1758 "ruledit.embedded_nations");
1762 save_traits(game
.server
.default_traits
, NULL
, sfile
,
1763 "default_traits", "");
1765 if (data
->nationlist
== NULL
) {
1766 if (game
.server
.ruledit
.allowed_govs
!= NULL
) {
1767 secfile_insert_str_vec(sfile
, game
.server
.ruledit
.allowed_govs
,
1768 game
.server
.ruledit
.ag_count
,
1769 "compatibility.allowed_govs");
1771 if (game
.server
.ruledit
.allowed_terrains
!= NULL
) {
1772 secfile_insert_str_vec(sfile
, game
.server
.ruledit
.allowed_terrains
,
1773 game
.server
.ruledit
.at_count
,
1774 "compatibility.allowed_terrains");
1776 if (game
.server
.ruledit
.allowed_styles
!= NULL
) {
1777 secfile_insert_str_vec(sfile
, game
.server
.ruledit
.allowed_styles
,
1778 game
.server
.ruledit
.as_count
,
1779 "compatibility.allowed_styles");
1783 if (game
.default_government
!= NULL
) {
1784 secfile_insert_str(sfile
, government_rule_name(game
.default_government
),
1785 "compatibility.default_government");
1788 if (data
->nationlist
!= NULL
) {
1789 secfile_insert_include(sfile
, data
->nationlist
);
1791 if (game
.server
.ruledit
.embedded_nations
!= NULL
) {
1794 comment_nations(sfile
);
1796 for (sect_idx
= 0; sect_idx
< game
.server
.ruledit
.embedded_nations_count
;
1798 struct nation_type
*pnat
1799 = nation_by_rule_name(game
.server
.ruledit
.embedded_nations
[sect_idx
]);
1802 log_error("Embedded nation \"%s\" not found!",
1803 game
.server
.ruledit
.embedded_nations
[sect_idx
]);
1805 save_nation(sfile
, pnat
, sect_idx
);
1812 comment_nationsets(sfile
);
1814 nation_sets_iterate(pset
) {
1817 fc_snprintf(path
, sizeof(path
), "nset_%d", sect_idx
++);
1819 /* We don't use save_name_translation() for this as name and rule_name must
1820 * always be saved separately */
1821 secfile_insert_str(sfile
, nation_set_untranslated_name(pset
), "%s.name", path
);
1822 secfile_insert_str(sfile
, nation_set_rule_name(pset
), "%s.rule_name", path
);
1823 secfile_insert_str(sfile
, nation_set_description(pset
), "%s.description", path
);
1824 } nation_sets_iterate_end
;
1826 comment_nationgroups(sfile
);
1829 nation_groups_iterate(pgroup
) {
1832 fc_snprintf(path
, sizeof(path
), "ngroup_%d", sect_idx
++);
1834 save_name_translation(sfile
, &(pgroup
->name
), path
);
1836 secfile_insert_int(sfile
, pgroup
->server
.match
, "%s.match", path
);
1837 if (pgroup
->hidden
) {
1838 secfile_insert_bool(sfile
, pgroup
->hidden
, "%s.hidden", path
);
1840 } nation_groups_iterate_end
;
1842 comment_nations(sfile
);
1845 nations_iterate(pnat
) {
1846 save_nation(sfile
, pnat
, sect_idx
++);
1847 } nations_iterate_end
;
1850 return save_ruleset_file(sfile
, filename
);
1853 /**************************************************************************
1855 **************************************************************************/
1856 static bool save_techs_ruleset(const char *filename
, const char *name
)
1858 struct section_file
*sfile
= create_ruleset_file(name
, "tech");
1861 struct advance
*a_none
= advance_by_number(A_NONE
);
1864 if (sfile
== NULL
) {
1868 for (i
= 0; i
< MAX_NUM_USER_TECH_FLAGS
; i
++) {
1869 const char *flagname
= tech_flag_id_name_cb(i
+ TECH_USER_1
);
1870 const char *helptxt
= tech_flag_helptxt(i
+ TECH_USER_1
);
1872 if (flagname
!= NULL
) {
1873 secfile_insert_str(sfile
, flagname
, "control.flags%d.name", i
);
1875 /* Save the user flag help text even when it is undefined. That makes
1876 * the formatting code happy. The resulting "" is ignored when the
1877 * ruleset is loaded. */
1878 secfile_insert_str(sfile
, helptxt
, "control.flags%d.helptxt", i
);
1882 comment_tech_classes(sfile
);
1885 tech_class_iterate(ptclass
) {
1888 fc_snprintf(path
, sizeof(path
), "techclass_%d", sect_idx
++);
1890 save_name_translation(sfile
, &(ptclass
->name
), path
);
1891 } tech_class_iterate_end
;
1893 comment_techs(sfile
);
1896 advance_active_iterate(pa
) {
1897 if (pa
->require
[AR_ONE
] != A_NEVER
) {
1899 const char *flag_names
[TF_COUNT
];
1903 fc_snprintf(path
, sizeof(path
), "advance_%d", sect_idx
++);
1905 save_name_translation(sfile
, &(pa
->name
), path
);
1907 if (game
.control
.num_tech_classes
> 0) {
1908 if (pa
->tclass
!= NULL
) {
1909 secfile_insert_str(sfile
, tech_class_rule_name(pa
->tclass
),
1914 save_tech_ref(sfile
, pa
->require
[AR_ONE
], path
, "req1");
1915 save_tech_ref(sfile
, pa
->require
[AR_TWO
], path
, "req2");
1916 if (pa
->require
[AR_ROOT
] != a_none
) {
1917 save_tech_ref(sfile
, pa
->require
[AR_ROOT
], path
, "root_req");
1920 save_reqs_vector(sfile
, &(pa
->research_reqs
), path
,
1923 secfile_insert_str(sfile
, pa
->graphic_str
, "%s.graphic", path
);
1924 if (strcmp("-", pa
->graphic_alt
)) {
1925 secfile_insert_str(sfile
, pa
->graphic_alt
, "%s.graphic_alt", path
);
1927 if (pa
->bonus_message
!= NULL
) {
1928 secfile_insert_str(sfile
, pa
->bonus_message
, "%s.bonus_message", path
);
1932 for (flagi
= 0; flagi
< TF_COUNT
; flagi
++) {
1933 if (advance_has_flag(advance_index(pa
), flagi
)) {
1934 flag_names
[set_count
++] = tech_flag_id_name(flagi
);
1938 if (set_count
> 0) {
1939 secfile_insert_str_vec(sfile
, flag_names
, set_count
,
1942 if (pa
->cost
>= 0) {
1943 secfile_insert_int(sfile
, pa
->cost
, "%s.cost", path
);
1946 save_strvec(sfile
, pa
->helptext
, path
, "helptext");
1949 } advance_active_iterate_end
;
1951 return save_ruleset_file(sfile
, filename
);
1954 /**************************************************************************
1955 Save terrain.ruleset
1956 **************************************************************************/
1957 static bool save_terrain_ruleset(const char *filename
, const char *name
)
1959 struct section_file
*sfile
= create_ruleset_file(name
, "terrain");
1963 if (sfile
== NULL
) {
1967 for (i
= 0; i
< MAX_NUM_USER_TER_FLAGS
; i
++) {
1968 const char *flagname
= terrain_flag_id_name_cb(i
+ TER_USER_1
);
1969 const char *helptxt
= terrain_flag_helptxt(i
+ TER_USER_1
);
1971 if (flagname
!= NULL
) {
1972 secfile_insert_str(sfile
, flagname
, "control.flags%d.name", i
);
1974 /* Save the user flag help text even when it is undefined. That makes
1975 * the formatting code happy. The resulting "" is ignored when the
1976 * ruleset is loaded. */
1977 secfile_insert_str(sfile
, helptxt
, "control.flags%d.helptxt", i
);
1981 for (i
= 0; i
< MAX_NUM_USER_EXTRA_FLAGS
; i
++) {
1982 const char *flagname
= extra_flag_id_name_cb(i
+ EF_USER_FLAG_1
);
1983 const char *helptxt
= extra_flag_helptxt(i
+ EF_USER_FLAG_1
);
1985 if (flagname
!= NULL
) {
1986 secfile_insert_str(sfile
, flagname
, "control.extra_flags%d.name", i
);
1988 /* Save the user flag help text even when it is undefined. That makes
1989 * the formatting code happy. The resulting "" is ignored when the
1990 * ruleset is loaded. */
1991 secfile_insert_str(sfile
, helptxt
,
1992 "control.extra_flags%d.helptxt", i
);
1996 if (terrain_control
.ocean_reclaim_requirement_pct
<= 100) {
1997 secfile_insert_int(sfile
, terrain_control
.ocean_reclaim_requirement_pct
,
1998 "parameters.ocean_reclaim_requirement");
2000 if (terrain_control
.land_channel_requirement_pct
<= 100) {
2001 secfile_insert_int(sfile
, terrain_control
.land_channel_requirement_pct
,
2002 "parameters.land_channel_requirement");
2004 if (terrain_control
.terrain_thaw_requirement_pct
<= 100) {
2005 secfile_insert_int(sfile
, terrain_control
.terrain_thaw_requirement_pct
,
2006 "parameters.thaw_requirement");
2008 if (terrain_control
.terrain_freeze_requirement_pct
<= 100) {
2009 secfile_insert_int(sfile
, terrain_control
.terrain_freeze_requirement_pct
,
2010 "parameters.freeze_requirement");
2012 if (terrain_control
.lake_max_size
!= 0) {
2013 secfile_insert_int(sfile
, terrain_control
.lake_max_size
,
2014 "parameters.lake_max_size");
2016 if (terrain_control
.min_start_native_area
!= 0) {
2017 secfile_insert_int(sfile
, terrain_control
.min_start_native_area
,
2018 "parameters.min_start_native_area");
2020 if (terrain_control
.move_fragments
!= 3) {
2021 secfile_insert_int(sfile
, terrain_control
.move_fragments
,
2022 "parameters.move_fragments");
2024 if (terrain_control
.igter_cost
!= 1) {
2025 secfile_insert_int(sfile
, terrain_control
.igter_cost
,
2026 "parameters.igter_cost");
2028 if (terrain_control
.pythagorean_diagonal
!= RS_DEFAULT_PYTHAGOREAN_DIAGONAL
) {
2029 secfile_insert_bool(sfile
, TRUE
,
2030 "parameters.pythagorean_diagonal");
2032 if (wld
.map
.server
.ocean_resources
) {
2033 secfile_insert_bool(sfile
, TRUE
,
2034 "parameters.ocean_resources");
2037 comment_terrains(sfile
);
2040 terrain_type_iterate(pterr
) {
2044 const char *flag_names
[TER_USER_LAST
];
2045 const char *puc_names
[UCL_LAST
];
2049 fc_snprintf(path
, sizeof(path
), "terrain_%d", sect_idx
++);
2051 save_name_translation(sfile
, &(pterr
->name
), path
);
2053 secfile_insert_str(sfile
, pterr
->graphic_str
, "%s.graphic", path
);
2054 secfile_insert_str(sfile
, pterr
->graphic_alt
, "%s.graphic_alt", path
);
2055 identifier
[0] = pterr
->identifier
;
2056 identifier
[1] = '\0';
2057 secfile_insert_str(sfile
, identifier
, "%s.identifier", path
);
2059 secfile_insert_str(sfile
, terrain_class_name(pterr
->tclass
),
2062 secfile_insert_int(sfile
, pterr
->movement_cost
, "%s.movement_cost", path
);
2063 secfile_insert_int(sfile
, pterr
->defense_bonus
, "%s.defense_bonus", path
);
2065 output_type_iterate(o
) {
2066 if (pterr
->output
[o
] != 0) {
2067 secfile_insert_int(sfile
, pterr
->output
[o
], "%s.%s", path
,
2068 get_output_identifier(o
));
2070 } output_type_iterate_end
;
2072 /* Check resource count */
2073 for (r
= 0; pterr
->resources
[r
] != NULL
; r
++) {
2074 /* Just increasing r as long as there is resources */
2078 const char *resource_names
[r
];
2080 for (r
= 0; pterr
->resources
[r
] != NULL
; r
++) {
2081 resource_names
[r
] = extra_rule_name(pterr
->resources
[r
]);
2084 secfile_insert_str_vec(sfile
, resource_names
, r
,
2085 "%s.resources", path
);
2088 output_type_iterate(o
) {
2089 if (pterr
->road_output_incr_pct
[o
] != 0) {
2090 secfile_insert_int(sfile
, pterr
->road_output_incr_pct
[o
],
2091 "%s.road_%s_incr_pct", path
,
2092 get_output_identifier(o
));
2094 } output_type_iterate_end
;
2096 secfile_insert_int(sfile
, pterr
->base_time
, "%s.base_time", path
);
2097 secfile_insert_int(sfile
, pterr
->road_time
, "%s.road_time", path
);
2099 save_terrain_ref(sfile
, pterr
->irrigation_result
, pterr
, path
,
2100 "irrigation_result");
2101 secfile_insert_int(sfile
, pterr
->irrigation_food_incr
,
2102 "%s.irrigation_food_incr", path
);
2103 secfile_insert_int(sfile
, pterr
->irrigation_time
,
2104 "%s.irrigation_time", path
);
2106 save_terrain_ref(sfile
, pterr
->mining_result
, pterr
, path
,
2108 secfile_insert_int(sfile
, pterr
->mining_shield_incr
,
2109 "%s.mining_shield_incr", path
);
2110 secfile_insert_int(sfile
, pterr
->mining_time
,
2111 "%s.mining_time", path
);
2113 save_terrain_ref(sfile
, pterr
->transform_result
, pterr
, path
,
2114 "transform_result");
2115 secfile_insert_int(sfile
, pterr
->transform_time
,
2116 "%s.transform_time", path
);
2118 if (pterr
->animal
!= NULL
) {
2119 secfile_insert_str(sfile
, utype_rule_name(pterr
->animal
),
2122 secfile_insert_str(sfile
, "None",
2126 secfile_insert_int(sfile
, pterr
->pillage_time
,
2127 "%s.pillage_time", path
);
2128 secfile_insert_int(sfile
, pterr
->clean_pollution_time
,
2129 "%s.clean_pollution_time", path
);
2130 secfile_insert_int(sfile
, pterr
->clean_fallout_time
,
2131 "%s.clean_fallout_time", path
);
2133 save_terrain_ref(sfile
, pterr
->warmer_wetter_result
, pterr
, path
,
2134 "warmer_wetter_result");
2135 save_terrain_ref(sfile
, pterr
->warmer_drier_result
, pterr
, path
,
2136 "warmer_drier_result");
2137 save_terrain_ref(sfile
, pterr
->cooler_wetter_result
, pterr
, path
,
2138 "cooler_wetter_result");
2139 save_terrain_ref(sfile
, pterr
->cooler_drier_result
, pterr
, path
,
2140 "cooler_drier_result");
2143 for (flagi
= 0; flagi
< TER_USER_LAST
; flagi
++) {
2144 if (terrain_has_flag(pterr
, flagi
)) {
2145 flag_names
[set_count
++] = terrain_flag_id_name(flagi
);
2149 if (set_count
> 0) {
2150 secfile_insert_str_vec(sfile
, flag_names
, set_count
,
2155 enum mapgen_terrain_property mtp
;
2157 for (mtp
= mapgen_terrain_property_begin();
2158 mtp
!= mapgen_terrain_property_end();
2159 mtp
= mapgen_terrain_property_next(mtp
)) {
2160 if (pterr
->property
[mtp
] != 0) {
2161 secfile_insert_int(sfile
, pterr
->property
[mtp
],
2162 "%s.property_%s", path
,
2163 mapgen_terrain_property_name(mtp
));
2169 unit_class_iterate(puc
) {
2170 if (BV_ISSET(pterr
->native_to
, uclass_index(puc
))) {
2171 puc_names
[set_count
++] = uclass_rule_name(puc
);
2173 } unit_class_iterate_end
;
2175 if (set_count
> 0) {
2176 secfile_insert_str_vec(sfile
, puc_names
, set_count
,
2177 "%s.native_to", path
);
2180 rgbcolor_save(sfile
, pterr
->rgb
, "%s.color", path
);
2182 save_strvec(sfile
, pterr
->helptext
, path
, "helptext");
2184 } terrain_type_iterate_end
;
2186 comment_resources(sfile
);
2189 extra_type_by_cause_iterate(EC_RESOURCE
, pres
) {
2190 if (!pres
->disabled
) {
2194 fc_snprintf(path
, sizeof(path
), "resource_%d", sect_idx
++);
2196 secfile_insert_str(sfile
, extra_rule_name(pres
),
2199 output_type_iterate(o
) {
2200 if (pres
->data
.resource
->output
[o
] != 0) {
2201 secfile_insert_int(sfile
, pres
->data
.resource
->output
[o
], "%s.%s",
2202 path
, get_output_identifier(o
));
2204 } output_type_iterate_end
;
2206 identifier
[0] = pres
->data
.resource
->id_old_save
;
2207 identifier
[1] = '\0';
2208 secfile_insert_str(sfile
, identifier
, "%s.identifier", path
);
2210 } extra_type_by_cause_iterate_end
;
2212 secfile_insert_str(sfile
, terrain_control
.gui_type_base0
,
2213 "extraui.ui_name_base_fortress");
2214 secfile_insert_str(sfile
, terrain_control
.gui_type_base1
,
2215 "extraui.ui_name_base_airbase");
2217 comment_extras(sfile
);
2220 extra_active_type_iterate(pextra
) {
2222 const char *flag_names
[EF_COUNT
];
2223 const char *cause_names
[EC_COUNT
];
2224 const char *puc_names
[UCL_LAST
];
2225 const char *extra_names
[MAX_EXTRA_TYPES
];
2230 fc_snprintf(path
, sizeof(path
), "extra_%d", sect_idx
++);
2232 save_name_translation(sfile
, &(pextra
->name
), path
);
2234 secfile_insert_str(sfile
, extra_category_name(pextra
->category
),
2235 "%s.category", path
);
2238 for (causei
= 0; causei
< EC_COUNT
; causei
++) {
2239 if (is_extra_caused_by(pextra
, causei
)) {
2240 cause_names
[set_count
++] = extra_cause_name(causei
);
2244 if (set_count
> 0) {
2245 secfile_insert_str_vec(sfile
, cause_names
, set_count
,
2250 for (causei
= 0; causei
< ERM_COUNT
; causei
++) {
2251 if (is_extra_removed_by(pextra
, causei
)) {
2252 cause_names
[set_count
++] = extra_rmcause_name(causei
);
2256 if (set_count
> 0) {
2257 secfile_insert_str_vec(sfile
, cause_names
, set_count
,
2258 "%s.rmcauses", path
);
2261 if (strcmp(pextra
->graphic_str
, "-")) {
2262 secfile_insert_str(sfile
, pextra
->graphic_str
, "%s.graphic", path
);
2264 if (strcmp(pextra
->graphic_alt
, "-")) {
2265 secfile_insert_str(sfile
, pextra
->graphic_alt
, "%s.graphic_alt", path
);
2267 if (strcmp(pextra
->activity_gfx
, "-")) {
2268 secfile_insert_str(sfile
, pextra
->activity_gfx
, "%s.activity_gfx", path
);
2270 if (strcmp(pextra
->act_gfx_alt
, "-")) {
2271 secfile_insert_str(sfile
, pextra
->act_gfx_alt
, "%s.act_gfx_alt", path
);
2273 if (strcmp(pextra
->act_gfx_alt2
, "-")) {
2274 secfile_insert_str(sfile
, pextra
->act_gfx_alt2
, "%s.act_gfx_alt2", path
);
2276 if (strcmp(pextra
->rmact_gfx
, "-")) {
2277 secfile_insert_str(sfile
, pextra
->rmact_gfx
, "%s.rmact_gfx", path
);
2279 if (strcmp(pextra
->rmact_gfx_alt
, "-")) {
2280 secfile_insert_str(sfile
, pextra
->rmact_gfx_alt
, "%s.rmact_gfx_alt", path
);
2283 save_reqs_vector(sfile
, &(pextra
->reqs
), path
, "reqs");
2284 save_reqs_vector(sfile
, &(pextra
->rmreqs
), path
, "rmreqs");
2285 save_reqs_vector(sfile
, &(pextra
->appearance_reqs
), path
, "appearance_reqs");
2286 save_reqs_vector(sfile
, &(pextra
->disappearance_reqs
), path
, "disappearance_reqs");
2288 if (!pextra
->buildable
) {
2289 secfile_insert_bool(sfile
, pextra
->buildable
, "%s.buildable", path
);
2291 secfile_insert_int(sfile
, pextra
->build_time
, "%s.build_time", path
);
2292 secfile_insert_int(sfile
, pextra
->removal_time
, "%s.removal_time", path
);
2293 if (pextra
->build_time_factor
!= 1) {
2294 secfile_insert_int(sfile
, pextra
->build_time_factor
, "%s.build_time_factor", path
);
2296 if (pextra
->removal_time_factor
!= 1) {
2297 secfile_insert_int(sfile
, pextra
->removal_time_factor
, "%s.removal_time_factor", path
);
2299 if (pextra
->defense_bonus
!= 0) {
2300 secfile_insert_int(sfile
, pextra
->defense_bonus
, "%s.defense_bonus", path
);
2302 if (pextra
->eus
!= EUS_NORMAL
) {
2303 secfile_insert_str(sfile
, extra_unit_seen_type_name(pextra
->eus
),
2304 "%s.unit_seen", path
);
2306 if (is_extra_caused_by(pextra
, EC_APPEARANCE
)
2307 && pextra
->appearance_chance
!= RS_DEFAULT_EXTRA_APPEARANCE
) {
2308 secfile_insert_int(sfile
, pextra
->appearance_chance
, "%s.appearance_chance", path
);
2310 if (is_extra_removed_by(pextra
, ERM_DISAPPEARANCE
)
2311 && pextra
->disappearance_chance
!= RS_DEFAULT_EXTRA_DISAPPEARANCE
) {
2312 secfile_insert_int(sfile
, pextra
->disappearance_chance
, "%s.disappearance_chance",
2317 unit_class_iterate(puc
) {
2318 if (BV_ISSET(pextra
->native_to
, uclass_index(puc
))) {
2319 puc_names
[set_count
++] = uclass_rule_name(puc
);
2321 } unit_class_iterate_end
;
2323 if (set_count
> 0) {
2324 secfile_insert_str_vec(sfile
, puc_names
, set_count
,
2325 "%s.native_to", path
);
2329 for (flagi
= 0; flagi
< EF_COUNT
; flagi
++) {
2330 if (extra_has_flag(pextra
, flagi
)) {
2331 flag_names
[set_count
++] = extra_flag_id_name(flagi
);
2335 if (set_count
> 0) {
2336 secfile_insert_str_vec(sfile
, flag_names
, set_count
,
2341 extra_type_iterate(confl
) {
2342 if (!can_extras_coexist(pextra
, confl
)) {
2343 extra_names
[set_count
++] = extra_rule_name(confl
);
2345 } extra_type_iterate_end
;
2347 if (set_count
> 0) {
2348 secfile_insert_str_vec(sfile
, extra_names
, set_count
,
2349 "%s.conflicts", path
);
2353 extra_type_iterate(top
) {
2354 if (BV_ISSET(pextra
->hidden_by
, extra_index(top
))) {
2355 extra_names
[set_count
++] = extra_rule_name(top
);
2357 } extra_type_iterate_end
;
2359 if (set_count
> 0) {
2360 secfile_insert_str_vec(sfile
, extra_names
, set_count
,
2361 "%s.hidden_by", path
);
2364 save_strvec(sfile
, pextra
->helptext
, path
, "helptext");
2366 } extra_active_type_iterate_end
;
2368 comment_bases(sfile
);
2371 extra_type_by_cause_iterate(EC_BASE
, pextra
) {
2372 if (!pextra
->disabled
) {
2374 struct base_type
*pbase
= extra_base_get(pextra
);
2375 const char *flag_names
[BF_COUNT
];
2379 fc_snprintf(path
, sizeof(path
), "base_%d", sect_idx
++);
2381 secfile_insert_str(sfile
, extra_rule_name(pextra
),
2384 secfile_insert_str(sfile
, base_gui_type_name(pbase
->gui_type
),
2385 "%s.gui_type", path
);
2387 if (pbase
->border_sq
>= 0) {
2388 secfile_insert_int(sfile
, pbase
->border_sq
, "%s.border_sq", path
);
2390 if (pbase
->vision_main_sq
>= 0) {
2391 secfile_insert_int(sfile
, pbase
->vision_main_sq
, "%s.vision_main_sq", path
);
2395 for (flagi
= 0; flagi
< BF_COUNT
; flagi
++) {
2396 if (base_has_flag(pbase
, flagi
)) {
2397 flag_names
[set_count
++] = base_flag_id_name(flagi
);
2401 if (set_count
> 0) {
2402 secfile_insert_str_vec(sfile
, flag_names
, set_count
,
2406 } extra_type_by_cause_iterate_end
;
2408 comment_roads(sfile
);
2411 extra_type_by_cause_iterate(EC_ROAD
, pextra
) {
2412 if (!pextra
->disabled
) {
2413 struct road_type
*proad
= extra_road_get(pextra
);
2415 const char *flag_names
[RF_COUNT
];
2419 fc_snprintf(path
, sizeof(path
), "road_%d", sect_idx
++);
2421 secfile_insert_str(sfile
, extra_rule_name(pextra
),
2424 secfile_insert_int(sfile
, proad
->move_cost
, "%s.move_cost", path
);
2426 if (proad
->move_mode
!= RMM_FAST_ALWAYS
) {
2427 secfile_insert_str(sfile
, road_move_mode_name(proad
->move_mode
),
2428 "%s.move_mode", path
);
2431 output_type_iterate(o
) {
2432 if (proad
->tile_incr_const
[o
] != 0) {
2433 secfile_insert_int(sfile
, proad
->tile_incr_const
[o
],
2434 "%s.%s_incr_const", path
, get_output_identifier(o
));
2436 if (proad
->tile_incr
[o
] != 0) {
2437 secfile_insert_int(sfile
, proad
->tile_incr
[o
],
2438 "%s.%s_incr", path
, get_output_identifier(o
));
2440 if (proad
->tile_bonus
[o
] != 0) {
2441 secfile_insert_int(sfile
, proad
->tile_bonus
[o
],
2442 "%s.%s_bonus", path
, get_output_identifier(o
));
2444 } output_type_iterate_end
;
2446 switch (proad
->compat
) {
2448 secfile_insert_str(sfile
, "Road", "%s.compat_special", path
);
2451 secfile_insert_str(sfile
, "Railroad", "%s.compat_special", path
);
2454 secfile_insert_str(sfile
, "River", "%s.compat_special", path
);
2457 secfile_insert_str(sfile
, "None", "%s.compat_special", path
);
2462 for (flagi
= 0; flagi
< RF_COUNT
; flagi
++) {
2463 if (road_has_flag(proad
, flagi
)) {
2464 flag_names
[set_count
++] = road_flag_id_name(flagi
);
2468 if (set_count
> 0) {
2469 secfile_insert_str_vec(sfile
, flag_names
, set_count
,
2473 } extra_type_by_cause_iterate_end
;
2475 return save_ruleset_file(sfile
, filename
);
2478 /**************************************************************************
2479 Save one veteran system.
2480 **************************************************************************/
2481 static bool save_veteran_system(struct section_file
*sfile
, const char *path
,
2482 struct veteran_system
*vsystem
)
2484 const char *vlist_name
[vsystem
->levels
];
2485 int vlist_power
[vsystem
->levels
];
2486 int vlist_raise
[vsystem
->levels
];
2487 int vlist_wraise
[vsystem
->levels
];
2488 int vlist_move
[vsystem
->levels
];
2491 for (i
= 0; i
< vsystem
->levels
; i
++) {
2492 vlist_name
[i
] = rule_name_get(&(vsystem
->definitions
[i
].name
));
2493 vlist_power
[i
] = vsystem
->definitions
[i
].power_fact
;
2494 vlist_raise
[i
] = vsystem
->definitions
[i
].raise_chance
;
2495 vlist_wraise
[i
] = vsystem
->definitions
[i
].work_raise_chance
;
2496 vlist_move
[i
] = vsystem
->definitions
[i
].move_bonus
;
2499 secfile_insert_str_vec(sfile
, vlist_name
, vsystem
->levels
,
2500 "%s.veteran_names", path
);
2501 secfile_insert_int_vec(sfile
, vlist_power
, vsystem
->levels
,
2502 "%s.veteran_power_fact", path
);
2503 secfile_insert_int_vec(sfile
, vlist_raise
, vsystem
->levels
,
2504 "%s.veteran_raise_chance", path
);
2505 secfile_insert_int_vec(sfile
, vlist_wraise
, vsystem
->levels
,
2506 "%s.veteran_work_raise_chance", path
);
2507 secfile_insert_int_vec(sfile
, vlist_move
, vsystem
->levels
,
2508 "%s.veteran_move_bonus", path
);
2513 /**************************************************************************
2514 Save unit combat bonuses list.
2515 **************************************************************************/
2516 static bool save_combat_bonuses(struct section_file
*sfile
,
2517 struct unit_type
*put
,
2521 bool has_quiet
= FALSE
;
2523 combat_bonus_list_iterate(put
->bonuses
, pbonus
) {
2524 if (pbonus
->quiet
) {
2527 } combat_bonus_list_iterate_end
;
2531 combat_bonus_list_iterate(put
->bonuses
, pbonus
) {
2532 secfile_insert_str(sfile
, unit_type_flag_id_name(pbonus
->flag
),
2533 "%s.bonuses%d.flag", path
, i
);
2534 secfile_insert_str(sfile
, combat_bonus_type_name(pbonus
->type
),
2535 "%s.bonuses%d.type", path
, i
);
2536 secfile_insert_int(sfile
, pbonus
->value
,
2537 "%s.bonuses%d.value", path
, i
);
2540 secfile_insert_bool(sfile
, pbonus
->quiet
,
2541 "%s.bonuses%d.quiet", path
, i
);
2545 } combat_bonus_list_iterate_end
;
2550 /**************************************************************************
2552 **************************************************************************/
2553 static bool save_units_ruleset(const char *filename
, const char *name
)
2555 struct section_file
*sfile
= create_ruleset_file(name
, "unit");
2559 if (sfile
== NULL
) {
2563 for (i
= 0; i
< MAX_NUM_USER_UNIT_FLAGS
; i
++) {
2564 const char *flagname
= unit_type_flag_id_name_cb(i
+ UTYF_USER_FLAG_1
);
2565 const char *helptxt
= unit_type_flag_helptxt(i
+ UTYF_USER_FLAG_1
);
2567 if (flagname
!= NULL
) {
2568 secfile_insert_str(sfile
, flagname
, "control.flags%d.name", i
);
2570 /* Save the user flag help text even when it is undefined. That makes
2571 * the formatting code happy. The resulting "" is ignored when the
2572 * ruleset is loaded. */
2573 secfile_insert_str(sfile
, helptxt
, "control.flags%d.helptxt", i
);
2577 for (i
= 0; i
< MAX_NUM_USER_UCLASS_FLAGS
; i
++) {
2578 const char *flagname
= unit_class_flag_id_name_cb(i
+ UCF_USER_FLAG_1
);
2579 const char *helptxt
= unit_class_flag_helptxt(i
+ UCF_USER_FLAG_1
);
2581 if (flagname
!= NULL
) {
2582 secfile_insert_str(sfile
, flagname
, "control.class_flags%d.name", i
);
2584 /* Save the user flag help text even when it is undefined. That makes
2585 * the formatting code happy. The resulting "" is ignored when the
2586 * ruleset is loaded. */
2587 secfile_insert_str(sfile
, helptxt
,
2588 "control.class_flags%d.helptxt", i
);
2592 save_veteran_system(sfile
, "veteran_system", game
.veteran
);
2594 comment_uclasses(sfile
);
2597 unit_active_class_iterate(puc
) {
2599 char *hut_str
= NULL
;
2600 const char *flag_names
[UCF_COUNT
];
2604 fc_snprintf(path
, sizeof(path
), "unitclass_%d", sect_idx
++);
2606 save_name_translation(sfile
, &(puc
->name
), path
);
2608 secfile_insert_int(sfile
, puc
->min_speed
/ SINGLE_MOVE
,
2609 "%s.min_speed", path
);
2610 secfile_insert_int(sfile
, puc
->hp_loss_pct
, "%s.hp_loss_pct", path
);
2611 if (puc
->non_native_def_pct
!= 100) {
2612 secfile_insert_int(sfile
, puc
->non_native_def_pct
,
2613 "%s.non_native_def_pct", path
);
2615 if (puc
->hut_behavior
!= HUT_NORMAL
) {
2616 switch (puc
->hut_behavior
) {
2621 hut_str
= "Nothing";
2624 hut_str
= "Frighten";
2627 fc_assert(hut_str
!= NULL
);
2628 secfile_insert_str(sfile
, hut_str
, "%s.hut_behavior", path
);
2632 for (flagi
= 0; flagi
< UCF_COUNT
; flagi
++) {
2633 if (uclass_has_flag(puc
, flagi
)) {
2634 flag_names
[set_count
++] = unit_class_flag_id_name(flagi
);
2638 if (set_count
> 0) {
2639 secfile_insert_str_vec(sfile
, flag_names
, set_count
,
2643 save_strvec(sfile
, puc
->helptext
, path
, "helptext");
2645 } unit_active_class_iterate_end
;
2647 comment_utypes(sfile
);
2650 unit_active_type_iterate(put
) {
2651 if (!put
->disabled
) {
2653 const char *flag_names
[UTYF_LAST_USER_FLAG
+ 1];
2657 fc_snprintf(path
, sizeof(path
), "unit_%d", sect_idx
++);
2659 save_name_translation(sfile
, &(put
->name
), path
);
2661 secfile_insert_str(sfile
, uclass_rule_name(put
->uclass
),
2664 save_tech_ref(sfile
, put
->require_advance
, path
, "tech_req");
2666 if (put
->need_government
!= NULL
) {
2667 secfile_insert_str(sfile
, government_rule_name(put
->need_government
),
2668 "%s.gov_req", path
);
2671 if (put
->need_improvement
!= NULL
) {
2672 secfile_insert_str(sfile
, improvement_rule_name(put
->need_improvement
),
2673 "%s.impr_req", path
);
2676 if (put
->obsoleted_by
!= NULL
) {
2677 secfile_insert_str(sfile
, utype_rule_name(put
->obsoleted_by
),
2678 "%s.obsolete_by", path
);
2681 secfile_insert_str(sfile
, put
->graphic_str
, "%s.graphic", path
);
2682 if (strcmp("-", put
->graphic_alt
)) {
2683 secfile_insert_str(sfile
, put
->graphic_alt
, "%s.graphic_alt", path
);
2685 if (strcmp("-", put
->sound_move
)) {
2686 secfile_insert_str(sfile
, put
->sound_move
, "%s.sound_move", path
);
2688 if (strcmp("-", put
->sound_move_alt
)) {
2689 secfile_insert_str(sfile
, put
->sound_move_alt
, "%s.sound_move_alt", path
);
2691 if (strcmp("-", put
->sound_fight
)) {
2692 secfile_insert_str(sfile
, put
->sound_fight
, "%s.sound_fight", path
);
2694 if (strcmp("-", put
->sound_fight_alt
)) {
2695 secfile_insert_str(sfile
, put
->sound_fight_alt
, "%s.sound_fight_alt", path
);
2698 secfile_insert_int(sfile
, put
->build_cost
, "%s.build_cost", path
);
2699 secfile_insert_int(sfile
, put
->pop_cost
, "%s.pop_cost", path
);
2700 secfile_insert_int(sfile
, put
->attack_strength
, "%s.attack", path
);
2701 secfile_insert_int(sfile
, put
->defense_strength
, "%s.defense", path
);
2702 secfile_insert_int(sfile
, put
->move_rate
/ SINGLE_MOVE
, "%s.move_rate", path
);
2703 secfile_insert_int(sfile
, put
->vision_radius_sq
, "%s.vision_radius_sq", path
);
2704 secfile_insert_int(sfile
, put
->transport_capacity
, "%s.transport_cap", path
);
2706 save_uclass_vec(sfile
, &(put
->cargo
), path
, "cargo", FALSE
);
2707 save_uclass_vec(sfile
, &(put
->embarks
), path
, "embarks", TRUE
);
2708 save_uclass_vec(sfile
, &(put
->disembarks
), path
, "disembarks", TRUE
);
2710 secfile_insert_int(sfile
, put
->hp
, "%s.hitpoints", path
);
2711 secfile_insert_int(sfile
, put
->firepower
, "%s.firepower", path
);
2712 secfile_insert_int(sfile
, put
->fuel
, "%s.fuel", path
);
2713 secfile_insert_int(sfile
, put
->happy_cost
, "%s.uk_happy", path
);
2715 output_type_iterate(o
) {
2716 if (put
->upkeep
[o
] != 0) {
2717 secfile_insert_int(sfile
, put
->upkeep
[o
], "%s.uk_%s",
2718 path
, get_output_identifier(o
));
2720 } output_type_iterate_end
;
2722 if (put
->converted_to
!= NULL
) {
2723 secfile_insert_str(sfile
, utype_rule_name(put
->converted_to
),
2724 "%s.convert_to", path
);
2726 if (put
->convert_time
!= 1) {
2727 secfile_insert_int(sfile
, put
->convert_time
, "%s.convert_time", path
);
2730 save_combat_bonuses(sfile
, put
, path
);
2731 save_uclass_vec(sfile
, &(put
->targets
), path
, "targets", TRUE
);
2733 if (put
->veteran
!= NULL
) {
2734 save_veteran_system(sfile
, path
, put
->veteran
);
2737 if (put
->paratroopers_range
!= 0) {
2738 secfile_insert_int(sfile
, put
->paratroopers_range
,
2739 "%s.paratroopers_range", path
);
2740 secfile_insert_int(sfile
, put
->paratroopers_mr_req
/ SINGLE_MOVE
,
2741 "%s.paratroopers_mr_req", path
);
2742 secfile_insert_int(sfile
, put
->paratroopers_mr_sub
/ SINGLE_MOVE
,
2743 "%s.paratroopers_mr_sub", path
);
2745 if (put
->bombard_rate
!= 0) {
2746 secfile_insert_int(sfile
, put
->bombard_rate
,
2747 "%s.bombard_rate", path
);
2749 if (put
->city_slots
!= 0) {
2750 secfile_insert_int(sfile
, put
->city_slots
,
2751 "%s.city_slots", path
);
2753 if (put
->city_size
!= 1) {
2754 secfile_insert_int(sfile
, put
->city_size
,
2755 "%s.city_size", path
);
2759 for (flagi
= 0; flagi
<= UTYF_LAST_USER_FLAG
; flagi
++) {
2760 if (utype_has_flag(put
, flagi
)) {
2761 flag_names
[set_count
++] = unit_type_flag_id_name(flagi
);
2765 if (set_count
> 0) {
2766 secfile_insert_str_vec(sfile
, flag_names
, set_count
,
2771 for (flagi
= L_FIRST
; flagi
< L_LAST
; flagi
++) {
2772 if (utype_has_role(put
, flagi
)) {
2773 flag_names
[set_count
++] = unit_role_id_name(flagi
);
2777 if (set_count
> 0) {
2778 secfile_insert_str_vec(sfile
, flag_names
, set_count
,
2782 save_strvec(sfile
, put
->helptext
, path
, "helptext");
2784 } unit_active_type_iterate_end
;
2786 return save_ruleset_file(sfile
, filename
);
2789 /**************************************************************************
2791 **************************************************************************/
2792 static bool save_script_lua(const char *filename
, const char *name
)
2794 char *buffer
= get_script_buffer();
2796 if (buffer
!= NULL
) {
2797 FILE *ffile
= fc_fopen(filename
, "w");
2798 int full_len
= strlen(buffer
);
2801 if (ffile
!= NULL
) {
2802 len
= fwrite(buffer
, 1, full_len
, ffile
);
2804 if (len
!= full_len
) {
2817 /**************************************************************************
2818 Save ruleset to directory given.
2819 **************************************************************************/
2820 bool save_ruleset(const char *path
, const char *name
, struct rule_data
*data
)
2822 if (make_dir(path
)) {
2823 bool success
= TRUE
;
2827 fc_snprintf(filename
, sizeof(filename
), "%s/buildings.ruleset", path
);
2828 success
= save_buildings_ruleset(filename
, name
);
2832 fc_snprintf(filename
, sizeof(filename
), "%s/styles.ruleset", path
);
2833 success
= save_styles_ruleset(filename
, name
);
2837 fc_snprintf(filename
, sizeof(filename
), "%s/cities.ruleset", path
);
2838 success
= save_cities_ruleset(filename
, name
);
2842 fc_snprintf(filename
, sizeof(filename
), "%s/effects.ruleset", path
);
2843 success
= save_effects_ruleset(filename
, name
);
2847 fc_snprintf(filename
, sizeof(filename
), "%s/game.ruleset", path
);
2848 success
= save_game_ruleset(filename
, name
);
2852 fc_snprintf(filename
, sizeof(filename
), "%s/governments.ruleset", path
);
2853 success
= save_governments_ruleset(filename
, name
);
2857 fc_snprintf(filename
, sizeof(filename
), "%s/nations.ruleset", path
);
2858 success
= save_nations_ruleset(filename
, name
, data
);
2862 fc_snprintf(filename
, sizeof(filename
), "%s/techs.ruleset", path
);
2863 success
= save_techs_ruleset(filename
, name
);
2867 fc_snprintf(filename
, sizeof(filename
), "%s/terrain.ruleset", path
);
2868 success
= save_terrain_ruleset(filename
, name
);
2872 fc_snprintf(filename
, sizeof(filename
), "%s/units.ruleset", path
);
2873 success
= save_units_ruleset(filename
, name
);
2877 fc_snprintf(filename
, sizeof(filename
), "%s/script.lua", path
);
2878 success
= save_script_lua(filename
, name
);
2883 log_error("Failed to create directory %s", path
);