From e8cca62d458525d6c63ecc1fce17d2be4aa947ae Mon Sep 17 00:00:00 2001 From: Alina Lenk Date: Thu, 23 May 2024 23:22:22 +0200 Subject: [PATCH 09/11] Add "Same Terrain Class" TileRel requirement See RM #678 Signed-off-by: Alina Lenk --- common/metaknowledge.c | 20 +++++++--- common/reqtext.c | 48 +++++++++++++++++++++++ common/requirements.c | 58 +++++++++++++++++++++++++++- doc/README.effects | 11 +++--- gen_headers/enums/fc_types_enums.def | 1 + 5 files changed, 126 insertions(+), 12 deletions(-) diff --git a/common/metaknowledge.c b/common/metaknowledge.c index 77e448b84e..432488debd 100644 --- a/common/metaknowledge.c +++ b/common/metaknowledge.c @@ -750,16 +750,26 @@ static bool is_req_knowable(const struct player *pov_player, } if (req->source.kind == VUT_TILE_REL) { + enum known_type needed; if (context->tile == NULL || other_context->tile == NULL) { /* The tile may exist but not be passed when the problem type is * RPT_POSSIBLE. */ return prob_type == RPT_CERTAIN; } - if (tile_get_known(other_context->tile, pov_player) == TILE_UNKNOWN) { + switch (req->source.value.tilerel) { + case TREL_REGION_SURROUNDED: + /* Too complicated to figure out */ return FALSE; + case TREL_SAME_TCLASS: + /* Evaluated based on actual terrain type's class */ + needed = TILE_KNOWN_SEEN; + break; + default: + /* Only need the continent ID; known is enough */ + needed = TILE_KNOWN_UNSEEN; + break; } - if (req->source.value.tilerel == TREL_REGION_SURROUNDED) { - /* Too complicated to figure out */ + if (tile_get_known(other_context->tile, pov_player) < needed) { return FALSE; } @@ -769,11 +779,11 @@ static bool is_req_knowable(const struct player *pov_player, 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) { + if (tile_get_known(context->tile, pov_player) < needed) { return FALSE; } range_adjc_iterate(&(wld.map), context->tile, req->range, adj_tile) { - if (tile_get_known(adj_tile, pov_player) == TILE_UNKNOWN) { + if (tile_get_known(adj_tile, pov_player) < needed) { return FALSE; } } range_adjc_iterate_end; diff --git a/common/reqtext.c b/common/reqtext.c index df04820c7e..d6af4e9cac 100644 --- a/common/reqtext.c +++ b/common/reqtext.c @@ -3446,6 +3446,54 @@ bool req_text_insert(char *buf, size_t bufsz, struct player *pplayer, case VUT_TILE_REL: switch (preq->source.value.tilerel) { + case TREL_SAME_TCLASS: + switch (preq->range) { + case REQ_RANGE_TILE: + fc_strlcat(buf, prefix, bufsz); + if (preq->present) { + fc_strlcat(buf, _("Must be on the same terrain class."), + bufsz); + } else { + fc_strlcat(buf, _("Must be on a different terrain class."), + bufsz); + } + return TRUE; + case REQ_RANGE_CADJACENT: + fc_strlcat(buf, prefix, bufsz); + if (preq->present) { + fc_strlcat(buf, _("Must be cardinally adjacent to the same " + "terrain class."), + bufsz); + } else { + fc_strlcat(buf, _("Must not be cardinally adjacent to the same " + "terrain class."), + bufsz); + } + return TRUE; + case REQ_RANGE_ADJACENT: + fc_strlcat(buf, prefix, bufsz); + if (preq->present) { + fc_strlcat(buf, _("Must be adjacent to the same terrain class."), + bufsz); + } else { + fc_strlcat(buf, _("Must not be adjacent to the same terrain " + "class."), + bufsz); + } + return TRUE; + case REQ_RANGE_CITY: + case REQ_RANGE_TRADE_ROUTE: + case REQ_RANGE_CONTINENT: + case REQ_RANGE_PLAYER: + case REQ_RANGE_TEAM: + case REQ_RANGE_ALLIANCE: + case REQ_RANGE_WORLD: + case REQ_RANGE_LOCAL: + case REQ_RANGE_COUNT: + /* Not supported. */ + break; + } + break; case TREL_SAME_REGION: switch (preq->range) { case REQ_RANGE_TILE: diff --git a/common/requirements.c b/common/requirements.c index dc71969528..8ae64ca087 100644 --- a/common/requirements.c +++ b/common/requirements.c @@ -1958,6 +1958,7 @@ bool are_requirements_contradictions(const struct requirement *req1, /* Same requirement at different ranges. Note that same range is * already covered by are_requirements_opposites() above. */ switch (req1->source.value.tilerel) { + case TREL_SAME_TCLASS: case TREL_SAME_REGION: case TREL_REGION_SURROUNDED: /* Negated req at larger range contradicts present req at @@ -1981,8 +1982,19 @@ bool are_requirements_contradictions(const struct requirement *req1, return FALSE; } } - if (req1->source.value.tilerel == TREL_REGION_SURROUNDED - || req2->source.value.tilerel == TREL_REGION_SURROUNDED) { + if (req1->source.value.tilerel == TREL_SAME_TCLASS + && req2->source.value.tilerel == TREL_SAME_REGION) { + /* Same region at any range implies same terrain class at that range + * and any larger range ~> contradicts negated */ + return (!req1->present && req2->present + && (req1->range >= req2->range)); + } else if (req2->source.value.tilerel == TREL_SAME_TCLASS + && req1->source.value.tilerel == TREL_SAME_REGION) { + /* Same as above */ + return (req1->present && !req2->present + && (req1->range <= req2->range)); + } else if (req1->source.value.tilerel == TREL_REGION_SURROUNDED + || req2->source.value.tilerel == TREL_REGION_SURROUNDED) { const struct requirement *surr, *other; if (req1->source.value.tilerel == TREL_REGION_SURROUNDED) { surr = req1; @@ -1993,9 +2005,11 @@ bool are_requirements_contradictions(const struct requirement *req1, } if (surr->present && surr->range == REQ_RANGE_TILE) { /* Target tile must be part of a surrounded region + * ~> not the same terrain class * ~> not the same region * ~> not touched by a third region */ switch (other->source.value.tilerel) { + case TREL_SAME_TCLASS: case TREL_SAME_REGION: return (other->present && other->range == REQ_RANGE_TILE); case TREL_ONLY_OTHER_REGION: @@ -4896,6 +4910,43 @@ is_tile_rel_req_active(const struct civ_map *nmap, } switch (req->source.value.tilerel) { + case TREL_SAME_TCLASS: + if (tile_terrain(other_context->tile) == T_UNKNOWN) { + return TRI_MAYBE; + } + fc_assert_ret_val_msg((req->range == REQ_RANGE_TILE + || req->range == REQ_RANGE_CADJACENT + || req->range == REQ_RANGE_ADJACENT), + TRI_MAYBE, + "Invalid range %d for tile relation \"%s\" req", + req->range, tilerel_type_name(TREL_SAME_TCLASS)); + { + enum terrain_class cls = terrain_type_terrain_class( + tile_terrain(other_context->tile)); + bool seen_unknown = FALSE; + const struct terrain *terr; + + if ((terr = tile_terrain(context->tile)) == T_UNKNOWN) { + seen_unknown = TRUE; + } else if (terrain_type_terrain_class(terr) == cls) { + return TRUE; + } + + range_adjc_iterate(nmap, context->tile, req->range, adj_tile) { + if ((terr = tile_terrain(adj_tile)) == T_UNKNOWN) { + seen_unknown = TRUE; + } else if (terrain_type_terrain_class(terr) == cls) { + return TRUE; + } + } range_adjc_iterate_end; + + if (seen_unknown) { + return TRI_MAYBE; + } else { + return TRI_NO; + } + } + break; case TREL_SAME_REGION: if (tile_continent(other_context->tile) == 0) { return TRI_MAYBE; @@ -7884,6 +7935,9 @@ const char *universal_name_translation(const struct universal *psource, return buf; case VUT_TILE_REL: switch (psource->value.tilerel) { + case TREL_SAME_TCLASS: + fc_strlcat(buf, _("Same terrain class"), bufsz); + break; case TREL_SAME_REGION: fc_strlcat(buf, _("Same continent/ocean"), bufsz); break; diff --git a/doc/README.effects b/doc/README.effects index 01b22367d0..30be2bbb50 100644 --- a/doc/README.effects +++ b/doc/README.effects @@ -135,11 +135,12 @@ MinLatitude and MaxLatitude are numbers from -1000 (south pole) to 1000 (north pole). TileRel is about the relation to a specific other tile; currently only available for action enablers (see also README.actions) and a select few effects. It is - one of "Same Region" (on or adjacent to the same continent/ocean as the other - tile, depending on range), "Only Other Region" (only adjacent to tiles of its - own or the other tile's continent/ocean, not a third one; not available at Tile - range), or "Region Surrounded" (on or adjacent to a lake/island touching only - the other tile's continent/ocean). + one of "Same Terrain Class" (on or adjacent to the same terrain class as the + other tile, depending on range), "Same Region" (on or adjacent to the same + continent/ocean as the other tile), "Only Other Region" (only adjacent to tiles + of its own or the other tile's continent/ocean, not a third one; not available + at Tile range), or "Region Surrounded" (on or adjacent to a lake/island + touching only the other tile's continent/ocean). MaxDistanceSq is about the (squared) distance between two tiles; currently only available for action enablers (see also README.actions) and a select few effects. diff --git a/gen_headers/enums/fc_types_enums.def b/gen_headers/enums/fc_types_enums.def index 22b54cd3a3..6327674e3d 100644 --- a/gen_headers/enums/fc_types_enums.def +++ b/gen_headers/enums/fc_types_enums.def @@ -109,6 +109,7 @@ enum tilerel_type prefix TREL_ count values + SAME_TCLASS "Same Terrain Class" SAME_REGION "Same Region" ONLY_OTHER_REGION "Only Other Region" REGION_SURROUNDED "Region Surrounded" -- 2.34.1