From f4d2125420ce81c6d1467ee5882db1ee155f2f27 Mon Sep 17 00:00:00 2001 From: Sveinung Kvilhaugsvik Date: Sat, 19 Jun 2021 14:00:48 +0200 Subject: [PATCH 3/7] Add the new requirement type DiplRelUnitAny. Check if any unit at the tile has the specified diplomatic relationship. Note that it says any - not each. It being true for one unit is enough. See osdn #42562 --- ai/default/daieffects.c | 1 + common/fc_types.h | 2 + common/metaknowledge.c | 3 +- common/player.c | 7 +++- common/reqtext.c | 82 ++++++++++++++++++++++++++++++++++++++ common/requirements.c | 54 ++++++++++++++++++++++++- doc/README.effects | 9 +++++ fc_version | 2 +- server/cityturn.c | 30 ++++++++++++++ server/rssanity.c | 1 + tools/ruledit/univ_value.c | 2 + 11 files changed, 188 insertions(+), 5 deletions(-) diff --git a/ai/default/daieffects.c b/ai/default/daieffects.c index 389410b2e1..de437ad953 100644 --- a/ai/default/daieffects.c +++ b/ai/default/daieffects.c @@ -810,6 +810,7 @@ bool dai_can_requirement_be_met_in_city(const struct requirement *preq, case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: case VUT_MAXTILEUNITS: case VUT_STYLE: case VUT_UNITSTATE: diff --git a/common/fc_types.h b/common/fc_types.h index b058a73a53..63cdce2c6b 100644 --- a/common/fc_types.h +++ b/common/fc_types.h @@ -754,6 +754,8 @@ typedef union { #define SPECENUM_VALUE45NAME "DiplRelTile" #define SPECENUM_VALUE46 VUT_DIPLREL_TILE_O #define SPECENUM_VALUE46NAME "DiplRelTileOther" +#define SPECENUM_VALUE47 VUT_DIPLREL_UNITANY +#define SPECENUM_VALUE47NAME "DiplRelUnitAny" /* Keep this last. */ #define SPECENUM_COUNT VUT_COUNT #include "specenum_gen.h" diff --git a/common/metaknowledge.c b/common/metaknowledge.c index 4796452995..af0fa41f8f 100644 --- a/common/metaknowledge.c +++ b/common/metaknowledge.c @@ -273,7 +273,8 @@ static bool is_req_knowable(const struct player *pow_player, if (req->source.kind == VUT_DIPLREL || req->source.kind == VUT_DIPLREL_TILE - || req->source.kind == VUT_DIPLREL_TILE_O) { + || req->source.kind == VUT_DIPLREL_TILE_O + || req->source.kind == VUT_DIPLREL_UNITANY) { switch (req->range) { case REQ_RANGE_LOCAL: if (other_player == NULL diff --git a/common/player.c b/common/player.c index 72d7aa7a40..ccb745ff87 100644 --- a/common/player.c +++ b/common/player.c @@ -1756,10 +1756,13 @@ bv_diplrel_all_reqs diplrel_req_contradicts(const struct requirement *req) if (!(req->source.kind == VUT_DIPLREL || req->source.kind == VUT_DIPLREL_TILE - || req->source.kind == VUT_DIPLREL_TILE_O)) { + || req->source.kind == VUT_DIPLREL_TILE_O + || req->source.kind == VUT_DIPLREL_UNITANY)) { /* No known contradiction of a requirement of any other kind. */ fc_assert(req->source.kind == VUT_DIPLREL - || req->source.kind == VUT_DIPLREL_TILE); + || req->source.kind == VUT_DIPLREL_TILE + || req->source.kind == VUT_DIPLREL_TILE_O + || req->source.kind == VUT_DIPLREL_UNITANY); return known; } diff --git a/common/reqtext.c b/common/reqtext.c index da3991e571..613fc4ae54 100644 --- a/common/reqtext.c +++ b/common/reqtext.c @@ -1374,6 +1374,88 @@ bool req_text_insert(char *buf, size_t bufsz, struct player *pplayer, } break; + case VUT_DIPLREL_UNITANY: + switch (preq->range) { + case REQ_RANGE_PLAYER: + fc_strlcat(buf, prefix, bufsz); + if (preq->present) { + cat_snprintf(buf, bufsz, + /* TRANS: in this and following strings, '%s' can be one + * of a wide range of relationships; e.g., 'Peace', + * 'Never met', 'Foreign', 'Hosts embassy', + * 'Provided Casus Belli' */ + _("Requires that there exists a unit at the tile with" + " an owner with the relationship" + " '%s' with at least one other living player."), + diplrel_name_translation(preq->source.value.diplrel)); + } else { + cat_snprintf(buf, bufsz, + _("Requires that no unit at the tile has an owner with" + " the relationship '%s' with any living player."), + diplrel_name_translation(preq->source.value.diplrel)); + } + return TRUE; + case REQ_RANGE_TEAM: + fc_strlcat(buf, prefix, bufsz); + if (preq->present) { + cat_snprintf(buf, bufsz, + _("Requires that there exists a unit at the tile with" + " an owner on a team where a member" + " has the relationship '%s' with at least one other" + " living player."), + diplrel_name_translation(preq->source.value.diplrel)); + } else { + cat_snprintf(buf, bufsz, + _("Requires that no unit at the tile has an owner" + " on a team where a member" + " has the relationship '%s' with at least one other" + " living player."), + diplrel_name_translation(preq->source.value.diplrel)); + } + return TRUE; + case REQ_RANGE_ALLIANCE: + fc_strlcat(buf, prefix, bufsz); + if (preq->present) { + cat_snprintf(buf, bufsz, + _("Requires that there exists a unit at the tile with" + " an owner allied to someone that " + " has the relationship '%s' with at least one other " + "living player."), + diplrel_name_translation(preq->source.value.diplrel)); + } else { + cat_snprintf(buf, bufsz, + _("Requires that no unit at the tile has an owner" + " allied to someone that" + " has the relationship '%s' with any living player."), + diplrel_name_translation(preq->source.value.diplrel)); + } + return TRUE; + case REQ_RANGE_LOCAL: + fc_strlcat(buf, prefix, bufsz); + if (preq->present) { + cat_snprintf(buf, bufsz, + _("Requires that you have the relationship '%s' with" + " the owner of at least one unit at the tile."), + diplrel_name_translation(preq->source.value.diplrel)); + } else { + cat_snprintf(buf, bufsz, + _("Requires that you do not have the relationship '%s'" + " with the owner of at least one unit at the tile."), + diplrel_name_translation(preq->source.value.diplrel)); + } + return TRUE; + case REQ_RANGE_CADJACENT: + case REQ_RANGE_ADJACENT: + case REQ_RANGE_CITY: + case REQ_RANGE_TRADEROUTE: + case REQ_RANGE_CONTINENT: + case REQ_RANGE_WORLD: + case REQ_RANGE_COUNT: + /* Not supported. */ + break; + } + break; + case VUT_UTYPE: switch (preq->range) { case REQ_RANGE_LOCAL: diff --git a/common/requirements.c b/common/requirements.c index fa90aa1606..d96c361658 100644 --- a/common/requirements.c +++ b/common/requirements.c @@ -195,6 +195,7 @@ void universal_value_from_str(struct universal *source, const char *value) case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: source->value.diplrel = diplrel_by_rule_name(value); if (source->value.diplrel != diplrel_other_invalid()) { return; @@ -468,6 +469,7 @@ struct universal universal_by_number(const enum universals_n kind, case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: source.value.diplrel = value; if (source.value.diplrel != diplrel_other_invalid()) { return source; @@ -637,6 +639,7 @@ int universal_number(const struct universal *source) case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: return source->value.diplrel; case VUT_UTYPE: return utype_number(source->value.utype); @@ -798,6 +801,7 @@ struct requirement req_from_str(const char *type, const char *range, case VUT_NATIONGROUP: case VUT_DIPLREL: case VUT_DIPLREL_TILE: + case VUT_DIPLREL_UNITANY: case VUT_AI_LEVEL: req.range = REQ_RANGE_PLAYER; break; @@ -881,6 +885,15 @@ struct requirement req_from_str(const char *type, const char *range, case VUT_DIPLREL_TILE_O: invalid = (req.range != REQ_RANGE_LOCAL); break; + case VUT_DIPLREL_UNITANY: + invalid = (req.range != REQ_RANGE_LOCAL + && req.range != REQ_RANGE_PLAYER + && req.range != REQ_RANGE_TEAM + && req.range != REQ_RANGE_ALLIANCE) + /* Non local foreign makes no sense. */ + || (req.source.value.diplrel == DRO_FOREIGN + && req.range != REQ_RANGE_LOCAL); + break; case VUT_NATION: case VUT_NATIONGROUP: invalid = (req.range != REQ_RANGE_PLAYER @@ -993,6 +1006,7 @@ struct requirement req_from_str(const char *type, const char *range, case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: case VUT_MAXTILEUNITS: case VUT_MINTECHS: /* Most requirements don't support 'survives'. */ @@ -1217,6 +1231,7 @@ bool are_requirements_contradictions(const struct requirement *req1, case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: if (req2->source.kind != req1->source.kind) { /* Finding contradictions across requirement kinds aren't supported * for DiplRel requirements. */ @@ -2558,6 +2573,31 @@ static enum fc_tristate is_diplrel_in_range(const struct player *target_player, return TRI_MAYBE; } +/**********************************************************************//** + Is the diplomatic state within range of anny unit at the target tile? +**************************************************************************/ +static enum fc_tristate +is_diplrel_unitany_in_range(const struct tile *target_tile, + const struct player *other_player, + enum req_range range, + int diplrel) +{ + enum fc_tristate out = TRI_NO; + + if (target_tile == NULL) { + return TRI_MAYBE; + } + + unit_list_iterate(target_tile->units, target_unit) { + enum fc_tristate for_target_unit = is_diplrel_in_range( + unit_owner(target_unit), other_player, range, diplrel); + + out = fc_tristate_or(out, for_target_unit); + } unit_list_iterate_end; + + return out; +} + /**********************************************************************//** Is there a unit of the given type within range of the target? **************************************************************************/ @@ -3174,6 +3214,11 @@ bool is_req_active(const struct player *target_player, req->range, req->source.value.diplrel); break; + case VUT_DIPLREL_UNITANY: + eval = is_diplrel_unitany_in_range(target_tile, target_player, + req->range, + req->source.value.diplrel); + break; case VUT_UTYPE: if (target_unittype == NULL) { eval = TRI_MAYBE; @@ -3503,6 +3548,7 @@ bool is_req_unchanging(const struct requirement *req) case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: case VUT_MAXTILEUNITS: case VUT_UTYPE: /* Not sure about this one */ case VUT_UTFLAG: /* Not sure about this one */ @@ -3614,6 +3660,7 @@ bool universal_never_there(const struct universal *source) case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: case VUT_MAXTILEUNITS: case VUT_UTYPE: case VUT_UCLASS: @@ -4183,6 +4230,7 @@ bool are_universals_equal(const struct universal *psource1, case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: return psource1->value.diplrel == psource2->value.diplrel; case VUT_UTYPE: return psource1->value.utype == psource2->value.utype; @@ -4308,6 +4356,7 @@ const char *universal_rule_name(const struct universal *psource) case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: return diplrel_rule_name(psource->value.diplrel); case VUT_NATIONALITY: return nation_rule_name(psource->value.nationality); @@ -4451,6 +4500,7 @@ const char *universal_name_translation(const struct universal *psource, case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: fc_strlcat(buf, diplrel_name_translation(psource->value.diplrel), bufsz); return buf; @@ -5129,7 +5179,8 @@ static enum req_item_found diplrel_found(const struct requirement *preq, { fc_assert_ret_val((source->kind == VUT_DIPLREL || source->kind == VUT_DIPLREL_TILE - || source->kind == VUT_DIPLREL_TILE_O), + || source->kind == VUT_DIPLREL_TILE_O + || source->kind == VUT_DIPLREL_UNITANY), ITF_NOT_APPLICABLE); if (preq->source.kind == source->kind) { @@ -5240,6 +5291,7 @@ void universal_found_functions_init(void) universal_found_function[VUT_DIPLREL] = &diplrel_found; universal_found_function[VUT_DIPLREL_TILE] = &diplrel_found; universal_found_function[VUT_DIPLREL_TILE_O] = &diplrel_found; + universal_found_function[VUT_DIPLREL_UNITANY] = &diplrel_found; universal_found_function[VUT_UNITSTATE] = &ustate_found; } diff --git a/doc/README.effects b/doc/README.effects index e832e21b9e..4209fe46ac 100644 --- a/doc/README.effects +++ b/doc/README.effects @@ -72,6 +72,7 @@ Nationality: Traderoute, City DiplRel: World, Alliance, Team, Player, Local DiplRelTile: Alliance, Team, Player, Local DiplRelTileOther:Local +DiplRelUnitAny: Alliance, Team, Player, Local Action: Local OutputType: Local Specialist: Local @@ -740,6 +741,14 @@ The player is the tile owner. The second player is DiplRel's second player. Only works in the Local requirement range as DiplRel and DiplRelTile covers the rest. +The DiplRelUnitAny requirement type +----------------------------------- +Check if any unit at the tile has the specified diplomatic relationship. +Note that it says any - not each. It being true for one unit is enough. +The player is the owner of a unit at the tile. +The second player is DiplRel's player. +Doesn't have the World requirement range as that would be redundant. + The MaxUnitsOnTile requirement type ----------------------------------- Check the number of units present on a tile. Is true if no more than the diff --git a/fc_version b/fc_version index 11c1acc8c4..83b3a9edea 100755 --- a/fc_version +++ b/fc_version @@ -56,7 +56,7 @@ DEFAULT_FOLLOW_TAG=S3_2 # - No new mandatory capabilities can be added to the release branch; doing # so would break network capability of supposedly "compatible" releases. # -NETWORK_CAPSTRING="+Freeciv.Devel-3.2-2021.Jun.19" +NETWORK_CAPSTRING="+Freeciv.Devel-3.2-2021.Jun.25" FREECIV_DISTRIBUTOR="" diff --git a/server/cityturn.c b/server/cityturn.c index 8cdff9a2a0..5592d2b68c 100644 --- a/server/cityturn.c +++ b/server/cityturn.c @@ -1489,6 +1489,36 @@ static bool worklist_item_postpone_req_vec(struct universal *target, pcity, "have_diplrel"); } break; + case VUT_DIPLREL_UNITANY: + if (preq->present) { + notify_player(pplayer, city_tile(pcity), + E_CITY_CANTBUILD, ftc_server, + /* TRANS: '%s' is a wide range of relationships; + * e.g., 'Peace', 'Never met', 'Foreign', + * 'Hosts embassy', 'Provided Casus Belli' */ + _("%s can't build %s from the worklist; " + "unit with the relationship '%s' is required." + " Postponing..."), + city_link(pcity), + tgt_name, + diplrel_name_translation( + preq->source.value.diplrel)); + script_server_signal_emit(signal_name, ptarget, + pcity, "need_diplrel"); + } else { + notify_player(pplayer, city_tile(pcity), + E_CITY_CANTBUILD, ftc_server, + _("%s can't build %s from the worklist; " + "unit with the relationship '%s' is prohibited." + " Postponing..."), + city_link(pcity), + tgt_name, + diplrel_name_translation( + preq->source.value.diplrel)); + script_server_signal_emit(signal_name, ptarget, + pcity, "have_diplrel"); + } + break; case VUT_MINSIZE: if (preq->present) { notify_player(pplayer, city_tile(pcity), diff --git a/server/rssanity.c b/server/rssanity.c index e61720a70a..ac9e68aa1b 100644 --- a/server/rssanity.c +++ b/server/rssanity.c @@ -376,6 +376,7 @@ static bool sanity_check_req_set(int reqs_of_type[], int local_reqs_of_type[], case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: /* Can have multiple requirements of these types */ break; case VUT_COUNT: diff --git a/tools/ruledit/univ_value.c b/tools/ruledit/univ_value.c index 3b2d6b0016..2e81bf62ab 100644 --- a/tools/ruledit/univ_value.c +++ b/tools/ruledit/univ_value.c @@ -154,6 +154,7 @@ bool universal_value_initial(struct universal *src) case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: src->value.diplrel = DS_WAR; return TRUE; case VUT_MAXTILEUNITS: @@ -361,6 +362,7 @@ void universal_kind_values(struct universal *univ, case VUT_DIPLREL: case VUT_DIPLREL_TILE: case VUT_DIPLREL_TILE_O: + case VUT_DIPLREL_UNITANY: for (i = 0; i < DS_LAST; i++) { cb(diplstate_type_name(i), univ->value.diplrel == i, data); } -- 2.30.2