From c1150be47302ed2426f8ef93e07e9b12d3ef4ef0 Mon Sep 17 00:00:00 2001 From: Marko Lindqvist Date: Sun, 29 May 2022 05:43:52 +0300 Subject: [PATCH 41/41] Support AI evaluated user effects Make it possible to define in the ruleset, what kind of value the AI should give to the user effects. That's done by referring to one of the hardcoded effect types with matching evaluation code. Requested by ihnatus See osdn #44711 Signed-off-by: Marko Lindqvist --- ai/default/daieffects.c | 5 ++- common/effects.c | 51 +++++++++++++++++++++++ common/effects.h | 8 ++++ data/alien/effects.ruleset | 13 ++++++ data/civ1/effects.ruleset | 13 ++++++ data/civ2/effects.ruleset | 13 ++++++ data/civ2civ3/effects.ruleset | 13 ++++++ data/classic/effects.ruleset | 13 ++++++ data/goldkeep/effects.ruleset | 13 ++++++ data/granularity/effects.ruleset | 13 ++++++ data/multiplayer/effects.ruleset | 13 ++++++ data/ruledit/comments-3.2.txt | 12 ++++++ data/sandbox/effects.ruleset | 13 ++++++ data/stub/effects.ruleset | 13 ++++++ data/webperimental/effects.ruleset | 13 ++++++ server/ruleset.c | 66 +++++++++++++++++++++++++++++- tools/ruleutil/comments.c | 10 +++++ tools/ruleutil/comments.h | 1 + tools/ruleutil/rulesave.c | 20 +++++++++ 19 files changed, 314 insertions(+), 2 deletions(-) diff --git a/ai/default/daieffects.c b/ai/default/daieffects.c index 0c8d1739e2..a7ea83911a 100644 --- a/ai/default/daieffects.c +++ b/ai/default/daieffects.c @@ -145,6 +145,7 @@ adv_want dai_effect_value(struct player *pplayer, struct government *gov, int num; int trait; adv_want v = 0; + enum effect_type value_as; if (peffect->multiplier) { if (pplayer) { @@ -166,7 +167,9 @@ adv_want dai_effect_value(struct player *pplayer, struct government *gov, return 0; } - switch (peffect->type) { + value_as = user_effect_ai_valued_as(peffect->type); + + switch (value_as) { /* These effects have already been evaluated in base_want() */ case EFT_CAPITAL_CITY: case EFT_GOV_CENTER: diff --git a/common/effects.c b/common/effects.c index c208cd1228..04bab2dd86 100644 --- a/common/effects.c +++ b/common/effects.c @@ -41,6 +41,12 @@ static bool initialized = FALSE; +struct user_effect { + enum effect_type ai_value_as; +}; + +struct user_effect ueffects[EFT_USER_EFFECT_LAST + 1 - EFT_USER_EFFECT_1]; + /************************************************************************** The code creates a ruleset cache on ruleset load. This constant cache is used to speed up effects queries. After the cache is created it is @@ -255,6 +261,12 @@ void ruleset_cache_init(void) for (i = 0; i < ARRAY_SIZE(ruleset_cache.reqs.advances); i++) { ruleset_cache.reqs.advances[i] = effect_list_new(); } + + /* By default, user effects are valued as themselves + * (currently meaning that they get no value at all) */ + for (i = EFT_USER_EFFECT_1 ; i <= EFT_USER_EFFECT_LAST; i++) { + ueffects[USER_EFFECT_NUMBER(i)].ai_value_as = i; + } } /**********************************************************************//** @@ -1241,3 +1253,42 @@ bool iterate_effect_cache(iec_cb cb, void *data) return TRUE; } + +/**********************************************************************//** + Is the effect type an user effect type? +**************************************************************************/ +bool is_user_effect(enum effect_type eff) +{ + return eff >= EFT_USER_EFFECT_1 && eff <= EFT_USER_EFFECT_LAST; +} + +/**********************************************************************//** + Set the ai_valued_as effect type for the target effect. + Target must be an user effect. +**************************************************************************/ +void user_effect_ai_valued_set(enum effect_type tgt, enum effect_type valued_as) +{ + fc_assert(is_user_effect(tgt)); + + ueffects[USER_EFFECT_NUMBER(tgt)].ai_value_as = valued_as; +} + +/**********************************************************************//** + Get the ai_valued_as effect type for effect. Can be used also + for non-user effects - then it just returns the effect itself. +**************************************************************************/ +enum effect_type user_effect_ai_valued_as(enum effect_type real) +{ + /* Client doesn't know these. */ + fc_assert(is_server()); + + if (!is_user_effect(real)) { + return real; + } + + if (!is_user_effect(ueffects[USER_EFFECT_NUMBER(real)].ai_value_as)) { + return ueffects[USER_EFFECT_NUMBER(real)].ai_value_as; + } + + return real; +} diff --git a/common/effects.h b/common/effects.h index b907c35306..b5bb4c6dfb 100644 --- a/common/effects.h +++ b/common/effects.h @@ -341,6 +341,10 @@ extern "C" { #define SPECENUM_COUNT EFT_COUNT #include "specenum_gen.h" +#define EFT_USER_EFFECT_LAST EFT_USER_EFFECT_4 + +#define USER_EFFECT_NUMBER(eff) (eff - EFT_USER_EFFECT_1) + /* An effect is provided by a source. If the source is present, and the * other conditions (described below) are met, the effect will be active. * Note the difference between effect and effect_type. */ @@ -478,6 +482,10 @@ struct effect_list *get_effects(enum effect_type effect_type); typedef bool (*iec_cb)(struct effect*, void *data); bool iterate_effect_cache(iec_cb cb, void *data); +bool is_user_effect(enum effect_type eff); +enum effect_type user_effect_ai_valued_as(enum effect_type); +void user_effect_ai_valued_set(enum effect_type tgt, enum effect_type valued_as); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/data/alien/effects.ruleset b/data/alien/effects.ruleset index a9422520c4..164c7fe7dd 100644 --- a/data/alien/effects.ruleset +++ b/data/alien/effects.ruleset @@ -16,6 +16,19 @@ description = "Alien World effects data for Freeciv" options = "+Freeciv-ruleset-3.2-Devel-2022.Feb.02" format_version = 30 +; /* <-- avoid gettext warnings +; +; User effect types +; +; type = Effect type to define. Must be one of user effects. +; ai_valued_as = Hardcoded effect type that AI considers this to match +; +; */ <-- avoid gettext warnings + +;[ueff_1] +;type = "User_Effect_1" +;ai_valued_as = "" + ; /* <-- avoid gettext warnings ; ; Effects diff --git a/data/civ1/effects.ruleset b/data/civ1/effects.ruleset index 5d92cfc5f3..e55c58f741 100644 --- a/data/civ1/effects.ruleset +++ b/data/civ1/effects.ruleset @@ -14,6 +14,19 @@ description = "Civ1 effects data for Freeciv (approximate)" options = "+Freeciv-ruleset-3.2-Devel-2022.Feb.02" format_version = 30 +; /* <-- avoid gettext warnings +; +; User effect types +; +; type = Effect type to define. Must be one of user effects. +; ai_valued_as = Hardcoded effect type that AI considers this to match +; +; */ <-- avoid gettext warnings + +;[ueff_1] +;type = "User_Effect_1" +;ai_valued_as = "" + ; /* <-- avoid gettext warnings ; ; Effects diff --git a/data/civ2/effects.ruleset b/data/civ2/effects.ruleset index 534a9f37ae..26d3796755 100644 --- a/data/civ2/effects.ruleset +++ b/data/civ2/effects.ruleset @@ -14,6 +14,19 @@ description = "Civ2 effects data for Freeciv (incomplete)" options = "+Freeciv-ruleset-3.2-Devel-2022.Feb.02" format_version = 30 +; /* <-- avoid gettext warnings +; +; User effect types +; +; type = Effect type to define. Must be one of user effects. +; ai_valued_as = Hardcoded effect type that AI considers this to match +; +; */ <-- avoid gettext warnings + +;[ueff_1] +;type = "User_Effect_1" +;ai_valued_as = "" + ; /* <-- avoid gettext warnings ; ; Effects diff --git a/data/civ2civ3/effects.ruleset b/data/civ2civ3/effects.ruleset index 824ee37508..ed47779617 100644 --- a/data/civ2civ3/effects.ruleset +++ b/data/civ2civ3/effects.ruleset @@ -14,6 +14,19 @@ description = "Civ2Civ3 effects data for Freeciv" options = "+Freeciv-ruleset-3.2-Devel-2022.Feb.02 web-compatible" format_version = 30 +; /* <-- avoid gettext warnings +; +; User effect types +; +; type = Effect type to define. Must be one of user effects. +; ai_valued_as = Hardcoded effect type that AI considers this to match +; +; */ <-- avoid gettext warnings + +;[ueff_1] +;type = "User_Effect_1" +;ai_valued_as = "" + ; /* <-- avoid gettext warnings ; ; Effects diff --git a/data/classic/effects.ruleset b/data/classic/effects.ruleset index ae12989ae5..2a52060bc3 100644 --- a/data/classic/effects.ruleset +++ b/data/classic/effects.ruleset @@ -14,6 +14,19 @@ description = "Classic effects data for Freeciv" options = "+Freeciv-ruleset-3.2-Devel-2022.Feb.02 web-compatible" format_version = 30 +; /* <-- avoid gettext warnings +; +; User effect types +; +; type = Effect type to define. Must be one of user effects. +; ai_valued_as = Hardcoded effect type that AI considers this to match +; +; */ <-- avoid gettext warnings + +;[ueff_1] +;type = "User_Effect_1" +;ai_valued_as = "" + ; /* <-- avoid gettext warnings ; ; Effects diff --git a/data/goldkeep/effects.ruleset b/data/goldkeep/effects.ruleset index 522c9f6331..0e782b4651 100644 --- a/data/goldkeep/effects.ruleset +++ b/data/goldkeep/effects.ruleset @@ -17,6 +17,19 @@ description = "Goldkeep effects data for Freeciv" options = "+Freeciv-ruleset-3.2-Devel-2022.Feb.02" format_version = 30 +; /* <-- avoid gettext warnings +; +; User effect types +; +; type = Effect type to define. Must be one of user effects. +; ai_valued_as = Hardcoded effect type that AI considers this to match +; +; */ <-- avoid gettext warnings + +;[ueff_1] +;type = "User_Effect_1" +;ai_valued_as = "" + ; /* <-- avoid gettext warnings ; ; Effects diff --git a/data/granularity/effects.ruleset b/data/granularity/effects.ruleset index 77c2916ee7..9dc57b1e19 100644 --- a/data/granularity/effects.ruleset +++ b/data/granularity/effects.ruleset @@ -14,6 +14,19 @@ description = "Granularity effects data for Freeciv" options = "+Freeciv-ruleset-3.2-Devel-2022.Feb.02" format_version = 30 +; /* <-- avoid gettext warnings +; +; User effect types +; +; type = Effect type to define. Must be one of user effects. +; ai_valued_as = Hardcoded effect type that AI considers this to match +; +; */ <-- avoid gettext warnings + +;[ueff_1] +;type = "User_Effect_1" +;ai_valued_as = "" + ; /* <-- avoid gettext warnings ; ; Effects diff --git a/data/multiplayer/effects.ruleset b/data/multiplayer/effects.ruleset index fea7de5cb6..cf1c485415 100644 --- a/data/multiplayer/effects.ruleset +++ b/data/multiplayer/effects.ruleset @@ -14,6 +14,19 @@ description = "Multiplayer effects data for Freeciv" options = "+Freeciv-ruleset-3.2-Devel-2022.Feb.02 web-compatible" format_version = 30 +; /* <-- avoid gettext warnings +; +; User effect types +; +; type = Effect type to define. Must be one of user effects. +; ai_valued_as = Hardcoded effect type that AI considers this to match +; +; */ <-- avoid gettext warnings + +;[ueff_1] +;type = "User_Effect_1" +;ai_valued_as = "" + ; /* <-- avoid gettext warnings ; ; Effects diff --git a/data/ruledit/comments-3.2.txt b/data/ruledit/comments-3.2.txt index a81aeb9895..377d94103d 100644 --- a/data/ruledit/comments-3.2.txt +++ b/data/ruledit/comments-3.2.txt @@ -1009,6 +1009,18 @@ effects = "\ ; */ <-- avoid gettext warnings\n\ " +ueffs = "\ +\n\ +; /* <-- avoid gettext warnings\n\ +;\n\ +; User effect types\n\ +;\n\ +; type = Effect type to define. Must be one of user effects.\n\ +; ai_valued_as = Hardcoded effect type that AI considers this to match\n\ +;\n\ +; */ <-- avoid gettext warnings\n\ +" + disasters = "\ \n\ ; /* <-- avoid gettext warnings\n\ diff --git a/data/sandbox/effects.ruleset b/data/sandbox/effects.ruleset index 3b1668e25e..c204910511 100644 --- a/data/sandbox/effects.ruleset +++ b/data/sandbox/effects.ruleset @@ -14,6 +14,19 @@ description = "Sandbox effects data for Freeciv" options = "+Freeciv-ruleset-3.2-Devel-2022.Feb.02" format_version = 30 +; /* <-- avoid gettext warnings +; +; User effect types +; +; type = Effect type to define. Must be one of user effects. +; ai_valued_as = Hardcoded effect type that AI considers this to match +; +; */ <-- avoid gettext warnings + +;[ueff_1] +;type = "User_Effect_1" +;ai_valued_as = "" + ; /* <-- avoid gettext warnings ; ; Effects diff --git a/data/stub/effects.ruleset b/data/stub/effects.ruleset index c418f2e5b5..043e6d3e9f 100644 --- a/data/stub/effects.ruleset +++ b/data/stub/effects.ruleset @@ -6,6 +6,19 @@ description = " effects data for Freeciv" options = "+Freeciv-ruleset-3.2-Devel-2022.Feb.02" format_version = 30 +; /* <-- avoid gettext warnings +; +; User effect types +; +; type = Effect type to define. Must be one of user effects. +; ai_valued_as = Hardcoded effect type that AI considers this to match +; +; */ <-- avoid gettext warnings + +;[ueff_1] +;type = "User_Effect_1" +;ai_valued_as = "" + ; /* <-- avoid gettext warnings ; ; Effects diff --git a/data/webperimental/effects.ruleset b/data/webperimental/effects.ruleset index b4d901390d..4123465f5d 100644 --- a/data/webperimental/effects.ruleset +++ b/data/webperimental/effects.ruleset @@ -14,6 +14,19 @@ description = "Experimental web-client effects data for Freeciv" options = "+Freeciv-ruleset-3.2-Devel-2022.Feb.02 web-compatible" format_version = 30 +; /* <-- avoid gettext warnings +; +; User effect types +; +; type = Effect type to define. Must be one of user effects. +; ai_valued_as = Hardcoded effect type that AI considers this to match +; +; */ <-- avoid gettext warnings + +;[ueff_1] +;type = "User_Effect_1" +;ai_valued_as = "" + ; /* <-- avoid gettext warnings ; ; Effects diff --git a/server/ruleset.c b/server/ruleset.c index 08a08a6a01..09e40c421c 100644 --- a/server/ruleset.c +++ b/server/ruleset.c @@ -88,6 +88,7 @@ #define BUILDING_SECTION_PREFIX "building_" #define CITYSTYLE_SECTION_PREFIX "citystyle_" #define MUSICSTYLE_SECTION_PREFIX "musicstyle_" +#define UEFF_SECTION_PREFIX "ueff_" #define EFFECT_SECTION_PREFIX "effect_" #define GOVERNMENT_SECTION_PREFIX "government_" #define NATION_SET_SECTION_PREFIX "nset" /* without underscore? */ @@ -5946,7 +5947,6 @@ static bool load_ruleset_effects(struct section_file *file, struct rscompat_info *compat) { struct section_list *sec; - const char *type; const char *filename; bool ok = TRUE; @@ -5959,6 +5959,69 @@ static bool load_ruleset_effects(struct section_file *file, (void) secfile_entry_by_path(file, "datafile.description"); /* unused */ (void) secfile_entry_by_path(file, "datafile.ruledit"); /* unused */ + sec = secfile_sections_by_name_prefix(file, UEFF_SECTION_PREFIX); + + if (sec != NULL) { + section_list_iterate(sec, psection) { + const char *sec_name = section_name(psection); + enum effect_type main_type; + enum effect_type ai_valued_as; + const char *type; + + type = secfile_lookup_str(file, "%s.type", sec_name); + if (type == NULL) { + ruleset_error(LOG_ERROR, "\"%s\" [%s] missing effect type.", filename, sec_name); + ok = FALSE; + break; + } + main_type = effect_type_by_name(type, fc_strcasecmp); + if (!effect_type_is_valid(main_type)) { + ruleset_error(LOG_ERROR, "\"%s\" [%s] lists unknown effect type \"%s\".", + filename, sec_name, type); + ok = FALSE; + break; + } + if (!is_user_effect(main_type)) { + ruleset_error(LOG_ERROR, "\"%s\" [%s] type \"%s\" is not an user effect.", + filename, sec_name, type); + ok = FALSE; + break; + } + + type = secfile_lookup_str(file, "%s.ai_valued_as", sec_name); + if (type == NULL) { + ruleset_error(LOG_ERROR, "\"%s\" [%s] missing ai_valued_as.", filename, sec_name); + ok = FALSE; + break; + } + ai_valued_as = effect_type_by_name(type, fc_strcasecmp); + if (!effect_type_is_valid(ai_valued_as)) { + ruleset_error(LOG_ERROR, "\"%s\" [%s] lists unknown ai_valued_as \"%s\".", + filename, sec_name, type); + ok = FALSE; + break; + } + if (is_user_effect(ai_valued_as)) { + ruleset_error(LOG_ERROR, "\"%s\" [%s] ai_valued_as \"%s\" is an user effect.", + filename, sec_name, type); + ok = FALSE; + break; + } + + if (user_effect_ai_valued_as(main_type) != main_type) { + /* It has been set already! */ + ruleset_error(LOG_ERROR, "\"%s\" [%s] Duplicate \"%s\" entry.", + filename, sec_name, type); + ok = FALSE; + break; + } + + user_effect_ai_valued_set(main_type, ai_valued_as); + } section_list_iterate_end; + + section_list_destroy(sec); + } + /* Parse effects and add them to the effects ruleset cache. */ sec = secfile_sections_by_name_prefix(file, EFFECT_SECTION_PREFIX); section_list_iterate(sec, psection) { @@ -5968,6 +6031,7 @@ static bool load_ruleset_effects(struct section_file *file, struct effect *peffect; const char *sec_name = section_name(psection); struct requirement_vector *reqs; + const char *type; type = secfile_lookup_str(file, "%s.type", sec_name); diff --git a/tools/ruleutil/comments.c b/tools/ruleutil/comments.c index 5e08a6f6c3..9b0fff6af1 100644 --- a/tools/ruleutil/comments.c +++ b/tools/ruleutil/comments.c @@ -41,6 +41,7 @@ static struct { char *citystyles; char *musicstyles; char *effects; + char *ueffs; char *disasters; char *achievements; char *goods; @@ -144,6 +145,7 @@ bool comments_load(void) comment_load(comments_storage.musicstyles, comment_file, "typedoc.musicstyles"); comment_load(comments_storage.effects, comment_file, "typedoc.effects"); + comment_load(comments_storage.ueffs, comment_file, "typedoc.ueffs"); comment_load(comments_storage.disasters, comment_file, "typedoc.disasters"); comment_load(comments_storage.achievements, @@ -406,6 +408,14 @@ void comment_effects(struct section_file *sfile) comment_write(sfile, comments_storage.effects, "Effects"); } +/**********************************************************************//** + Write User effects header. +**************************************************************************/ +void comment_ueffs(struct section_file *sfile) +{ + comment_write(sfile, comments_storage.ueffs, "User Effects"); +} + /**********************************************************************//** Write disasters header. **************************************************************************/ diff --git a/tools/ruleutil/comments.h b/tools/ruleutil/comments.h index 43716725d8..0a33711a8d 100644 --- a/tools/ruleutil/comments.h +++ b/tools/ruleutil/comments.h @@ -44,6 +44,7 @@ void comment_styles(struct section_file *sfile); void comment_citystyles(struct section_file *sfile); void comment_musicstyles(struct section_file *sfile); void comment_effects(struct section_file *sfile); +void comment_ueffs(struct section_file *sfile); void comment_disasters(struct section_file *sfile); void comment_achievements(struct section_file *sfile); void comment_goods(struct section_file *sfile); diff --git a/tools/ruleutil/rulesave.c b/tools/ruleutil/rulesave.c index cd8b515173..5f75e9aa81 100644 --- a/tools/ruleutil/rulesave.c +++ b/tools/ruleutil/rulesave.c @@ -784,6 +784,8 @@ static bool save_effects_ruleset(const char *filename, const char *name) { struct section_file *sfile = create_ruleset_file(name, "effect"); effect_cb_data data; + int i; + int sidx; if (sfile == NULL) { return FALSE; @@ -792,6 +794,24 @@ static bool save_effects_ruleset(const char *filename, const char *name) data.idx = 0; data.sfile = sfile; + comment_ueffs(sfile); + + sidx = 0; + for (i = EFT_USER_EFFECT_1 ; i <= EFT_USER_EFFECT_LAST; i++) { + enum effect_type val = user_effect_ai_valued_as(i); + + if (val != i) { + char path[512]; + + fc_snprintf(path, sizeof(path), "ueff_%d", sidx++); + + secfile_insert_str(sfile, effect_type_name(i), + "%s.type", path); + secfile_insert_str(sfile, effect_type_name(val), + "%s.ai_valued_as", path); + } + } + comment_effects(sfile); if (!iterate_effect_cache(effect_save, &data)) { -- 2.35.1