From e53e3b31a981d84f07a5b08e113e812465794f8f Mon Sep 17 00:00:00 2001 From: Alina Lenk Date: Sun, 19 May 2024 23:22:21 +0200 Subject: [PATCH 06/10] Support MaxRegionTiles at C/Adjacent ranges See RM #652 Signed-off-by: Alina Lenk --- common/metaknowledge.c | 38 +++++++++++++++++++++++ common/reqtext.c | 64 +++++++++++++++++++++++++++++++++++++-- common/requirements.c | 36 +++++++++++++++++++++- doc/README.effects | 6 ++-- server/ruleset/rssanity.c | 2 +- 5 files changed, 140 insertions(+), 6 deletions(-) diff --git a/common/metaknowledge.c b/common/metaknowledge.c index 7e1303cc52..38ff4bb7b9 100644 --- a/common/metaknowledge.c +++ b/common/metaknowledge.c @@ -711,6 +711,44 @@ static bool is_req_knowable(const struct player *pov_player, return TRUE; } + if (req->source.kind == VUT_MAX_REGION_TILES) { + if (context->tile == NULL) { + /* The tile may exist but not be passed when the problem type is + * RPT_POSSIBLE. */ + return prob_type == RPT_CERTAIN; + } + + switch (req->range) { + case REQ_RANGE_CADJACENT: + case REQ_RANGE_ADJACENT: + /* TODO: Known tiles might be enough to determine the answer already; + * should we check on an individual requirement basis? */ + if (tile_get_known(context->tile, pov_player) == TILE_UNKNOWN) { + return FALSE; + } + range_adjc_iterate(&(wld.map), context->tile, req->range, adj_tile) { + if (tile_get_known(adj_tile, pov_player) == TILE_UNKNOWN) { + return FALSE; + } + } range_adjc_iterate_end; + return TRUE; + case REQ_RANGE_CONTINENT: + /* Too complicated to figure out */ + return FALSE; + case REQ_RANGE_CITY: + case REQ_RANGE_TRADE_ROUTE: + case REQ_RANGE_PLAYER: + case REQ_RANGE_ALLIANCE: + case REQ_RANGE_TEAM: + case REQ_RANGE_WORLD: + case REQ_RANGE_LOCAL: + case REQ_RANGE_TILE: + case REQ_RANGE_COUNT: + /* Non existing range for requirement types. */ + return FALSE; + } + } + if (req->source.kind == VUT_ACTION || req->source.kind == VUT_OTYPE) { /* This requirement type is intended to specify the situation. */ diff --git a/common/reqtext.c b/common/reqtext.c index d2f50bf5e7..eccf61734a 100644 --- a/common/reqtext.c +++ b/common/reqtext.c @@ -3355,6 +3355,68 @@ bool req_text_insert(char *buf, size_t bufsz, struct player *pplayer, case VUT_MAX_REGION_TILES: switch (preq->range) { + case REQ_RANGE_CADJACENT: + fc_strlcat(buf, prefix, bufsz); + /* Off-by-one: The requirement counts the tile itself, we're phrasing + * the helptext in terms of *other* tiles only. */ + if (preq->present) { + if (preq->source.value.region_tiles == 1) { + /* Special case for zero */ + fc_strlcat(buf, + _("No other cardinally adjacent tile may be part of " + "the same continent or ocean."), + bufsz); + } else { + cat_snprintf(buf, bufsz, + PL_("No more than %d other cardinally adjacent tile " + "may be part of the same continent or ocean.", + "No more than %d other cardinally adjacent tiles " + "may be part of the same continent or ocean.", + preq->source.value.region_tiles - 1), + preq->source.value.region_tiles - 1); + } + } else { + cat_snprintf(buf, bufsz, + PL_("Requires at least %d other cardinally adjacent " + "tile of the same continent or ocean.", + "Requires at least %d other cardinally adjacent " + "tiles of the same continent or ocean.", + preq->source.value.region_tiles), + preq->source.value.region_tiles); + } + + return TRUE; + case REQ_RANGE_ADJACENT: + fc_strlcat(buf, prefix, bufsz); + /* Off-by-one: The requirement counts the tile itself, we're phrasing + * the helptext in terms of *other* tiles only. */ + if (preq->present) { + if (preq->source.value.region_tiles == 1) { + /* Special case for zero */ + fc_strlcat(buf, + _("No other adjacent tile may be part of the same " + "continent or ocean."), + bufsz); + } else { + cat_snprintf(buf, bufsz, + PL_("No more than %d other adjacent tile may be " + "part of the same continent or ocean.", + "No more than %d other adjacent tiles may be " + "part of the same continent or ocean.", + preq->source.value.region_tiles - 1), + preq->source.value.region_tiles - 1); + } + } else { + cat_snprintf(buf, bufsz, + PL_("Requires at least %d other adjacent tile of the " + "same continent or ocean.", + "Requires at least %d other adjacent tiles of the " + "same continent or ocean.", + preq->source.value.region_tiles), + preq->source.value.region_tiles); + } + + return TRUE; case REQ_RANGE_CONTINENT: fc_strlcat(buf, prefix, bufsz); if (preq->present) { @@ -3374,8 +3436,6 @@ bool req_text_insert(char *buf, size_t bufsz, struct player *pplayer, case REQ_RANGE_WORLD: case REQ_RANGE_LOCAL: case REQ_RANGE_TILE: - case REQ_RANGE_CADJACENT: - case REQ_RANGE_ADJACENT: case REQ_RANGE_CITY: case REQ_RANGE_TRADE_ROUTE: case REQ_RANGE_COUNT: diff --git a/common/requirements.c b/common/requirements.c index 70da6d6970..ca29a351f8 100644 --- a/common/requirements.c +++ b/common/requirements.c @@ -1342,7 +1342,9 @@ struct requirement req_from_str(const char *type, const char *range, invalid = (req.range != REQ_RANGE_PLAYER); break; case VUT_MAX_REGION_TILES: - invalid = (req.range != REQ_RANGE_CONTINENT); + invalid = (req.range != REQ_RANGE_CONTINENT + && req.range != REQ_RANGE_CADJACENT + && req.range != REQ_RANGE_ADJACENT); break; case VUT_IMPROVEMENT: /* Valid ranges depend on the building genus (wonder/improvement), @@ -1910,6 +1912,11 @@ bool are_requirements_contradictions(const struct requirement *req1, /* Finding contradictions across requirement kinds isn't supported * for MaxRegionTiles requirements. */ return FALSE; + } else if (req1->range != req2->range) { + /* FIXME: Finding contradictions across ranges not yet supported. + * In particular, a max at a small range and a min at a larger range + * needs extra work to figure out. */ + return FALSE; } return are_bounds_contradictions( req1->source.value.region_tiles, req1->present, @@ -5648,6 +5655,33 @@ is_max_region_tiles_req_active(const struct civ_map *nmap, IS_REQ_ACTIVE_VARIANT_ASSERT(VUT_MAX_REGION_TILES); switch (req->range) { + case REQ_RANGE_CADJACENT: + case REQ_RANGE_ADJACENT: + if (context->tile == nullptr) { + /* The tile itself is included in the range */ + max_tiles = 1 + ((req->range == REQ_RANGE_CADJACENT) + ? nmap->num_cardinal_dirs + : nmap->num_valid_dirs); + + break; + } else { + Continent_id cont = tile_continent(context->tile); + + /* Count how many adjacent tiles there actually are as we go along */ + max_tiles = 1; + + range_adjc_iterate(nmap, context->tile, req->range, adj_tile) { + Continent_id adj_cont = tile_continent(adj_tile); + + if (adj_cont == 0 || cont == 0) { + max_tiles++; + } else if (adj_cont == cont) { + min_tiles++; + max_tiles++; + } + } range_adjc_iterate_end; + } + break; case REQ_RANGE_CONTINENT: { Continent_id cont = context->tile ? tile_continent(context->tile) : 0; diff --git a/doc/README.effects b/doc/README.effects index 1fa1f8901c..8ee0056d5a 100644 --- a/doc/README.effects +++ b/doc/README.effects @@ -119,7 +119,7 @@ MinMoveFrags: Local MinVeteran: Local MinHitPoints: Local MaxDistanceSq: Tile -MaxRegionTiles: Continent +MaxRegionTiles: Continent, Adjacent, CAdjacent MinSize is the minimum size of a city required. @@ -135,7 +135,9 @@ MinLatitude and MaxLatitude are numbers from -1000 (south pole) to 1000 MaxDistanceSq is about the (squared) distance between two tiles; currently only available for action enablers (see also README.actions) and a select few effects. -MaxRegionTiles is continent or ocean size (number of tiles). +MaxRegionTiles is about the number of tiles of the same continent or ocean as + the target tile in the given range (including the tile itself). At "Continent" + range this is just the full continent/ocean size. CityStatus is "OwnedByOriginal", "Transferred", "Starved", "Disorder", or "Celebration" The difference between "OwnedByOriginal" and "Transferred" is that diff --git a/server/ruleset/rssanity.c b/server/ruleset/rssanity.c index f57655cdff..e28056da49 100644 --- a/server/ruleset/rssanity.c +++ b/server/ruleset/rssanity.c @@ -355,7 +355,6 @@ static bool sanity_check_req_set(rs_conversion_logger logger, case VUT_ORIGINAL_OWNER: /* City range -> only one original owner */ case VUT_FORM_AGE: case VUT_MAX_DISTANCE_SQ: /* Breaks nothing, but has no sense either */ - case VUT_MAX_REGION_TILES: /* Breaks nothing, but has no sense either */ /* There can be only one requirement of these types (with current * range limitations) * Requirements might be identical, but we consider multiple @@ -456,6 +455,7 @@ static bool sanity_check_req_set(rs_conversion_logger logger, /* Can have multiple requirements of these types */ case VUT_MINLATITUDE: case VUT_MAXLATITUDE: + case VUT_MAX_REGION_TILES: /* Can have multiple requirements at different ranges. * TODO: Compare to number of legal ranges? */ break; -- 2.34.1