From a8a97b66804b5e9598ad511e930ddb06071359a2 Mon Sep 17 00:00:00 2001 From: Ihnatus Date: Fri, 21 Oct 2022 22:24:57 +0300 Subject: [PATCH] Add continent control to "CityTile" reqs "Same Continent" checks the tile's continent relatively to city center, "Bordering TClass Region" checks it towards any tile around city center that is of a different terrain class from it. Fixes in process that "CityTile", "CityCenter" without a city specified is considered unknowable at RPT_CERTAIN metaknowledge.c. See OSDN#45907 Signed-off-by: Ihnatus --- common/fc_types.h | 4 + common/metaknowledge.c | 19 +++++ common/reqtext.c | 26 ++++++ common/requirements.c | 187 ++++++++++++++++++++++++++++++++++++++++- doc/README.effects | 20 +++-- 5 files changed, 246 insertions(+), 10 deletions(-) diff --git a/common/fc_types.h b/common/fc_types.h index 57ee66479e..7db08badd7 100644 --- a/common/fc_types.h +++ b/common/fc_types.h @@ -536,6 +536,10 @@ const char *ai_level_name_update_cb(const char *old); #define SPECENUM_VALUE2NAME "Extras Owned" #define SPECENUM_VALUE3 CITYT_WORKED #define SPECENUM_VALUE3NAME "Worked" +#define SPECENUM_VALUE4 CITYT_SAME_CONTINENT +#define SPECENUM_VALUE4NAME "Same Continent" +#define SPECENUM_VALUE5 CITYT_BORDERING_TCLASS_REGION +#define SPECENUM_VALUE5NAME "Bordering TClass Region" #define SPECENUM_COUNT CITYT_LAST #include "specenum_gen.h" diff --git a/common/metaknowledge.c b/common/metaknowledge.c index 687cf0a041..8010aa8412 100644 --- a/common/metaknowledge.c +++ b/common/metaknowledge.c @@ -358,6 +358,25 @@ static bool is_req_knowable(const struct player *pov_player, * RPT_POSSIBLE. */ return prob_type == RPT_CERTAIN; } + if (context->city == NULL) { + switch (req->source.value.citytile) { + case CITYT_CENTER: + case CITYT_SAME_CONTINENT: + case CITYT_BORDERING_TCLASS_REGION: + /* Require the city, not passed */ + return prob_type == RPT_CERTAIN; + case CITYT_CLAIMED: + case CITYT_WORKED: + case CITYT_EXTRAS_OWNED: + /* Do not require a city passed */ + break; + case CITYT_LAST: + /* Invalid */ + fc_assert_msg(req->source.value.citytile != CITYT_LAST, + "Invalid city tile property."); + return FALSE; + } + } switch (req->range) { case REQ_RANGE_TILE: diff --git a/common/reqtext.c b/common/reqtext.c index 8e81381183..ffc1593dd5 100644 --- a/common/reqtext.c +++ b/common/reqtext.c @@ -33,6 +33,7 @@ #include "requirements.h" #include "server_settings.h" #include "specialist.h" +#include "terrain.h" #include "reqtext.h" @@ -2883,6 +2884,31 @@ bool req_text_insert(char *buf, size_t bufsz, struct player *pplayer, case CITYT_WORKED: tile_property = _("worked tiles"); break; + case CITYT_SAME_CONTINENT: + /* TRANS: a specific city for each use case */ + tile_property = _("tiles on the same continent as the city"); + break; + case CITYT_BORDERING_TCLASS_REGION: + { + bool oceanic_cities = FALSE; /* FIXME: maybe cache globally? */ + + terrain_type_iterate(tt) { + if (terrain_type_terrain_class(tt) == TC_OCEAN + && !terrain_has_flag(tt, TER_NO_CITIES)) { + oceanic_cities = TRUE; + /* TRANS: a specific city for each use case */ + tile_property = _("tiles of a mass of a different " + "terrain class next to the city"); + break; + } + } terrain_type_iterate_end; + if (oceanic_cities) { + break; + } + /* TRANS: a specific city for each use case */ + tile_property = _("tiles of a body of water next to the city"); + } + break; case CITYT_LAST: fc_assert(preq->source.value.citytile != CITYT_LAST); break; diff --git a/common/requirements.c b/common/requirements.c index e55039613a..a7a40a4897 100644 --- a/common/requirements.c +++ b/common/requirements.c @@ -3205,6 +3205,184 @@ static enum fc_tristate is_citytile_in_range(const struct tile *target_tile, break; } + return TRI_MAYBE; + case CITYT_SAME_CONTINENT: + { + int cc; + + if (!target_city) { + return TRI_MAYBE; + } + cc = tile_continent(city_tile(target_city)); + /* Note: No special treatment of 0 == cc here*/ + switch (range) { + case REQ_RANGE_TILE: + return BOOL_TO_TRISTATE(tile_continent(target_tile) == cc); + case REQ_RANGE_CADJACENT: + if (tile_continent(target_tile) == cc) { + return TRI_YES; + } + cardinal_adjc_iterate(&(wld.map), target_tile, adjc_tile) { + if (tile_continent(adjc_tile) == cc) { + return TRI_YES; + } + } cardinal_adjc_iterate_end; + + return TRI_NO; + case REQ_RANGE_ADJACENT: + if (tile_continent(target_tile) == cc) { + return TRI_YES; + } + adjc_iterate(&(wld.map), target_tile, adjc_tile) { + if (tile_continent(adjc_tile) == cc) { + return TRI_YES; + } + } adjc_iterate_end; + + return TRI_NO; + case REQ_RANGE_CITY: + case REQ_RANGE_TRADEROUTE: + 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: + fc_assert_msg(FALSE, "Invalid range %d for citytile.", range); + break; + } + } + + return TRI_MAYBE; + case CITYT_BORDERING_TCLASS_REGION: + { + int n = 0, adjc_cont[8], cc; + bool ukt = FALSE; + + if (!target_city) { + return TRI_MAYBE; + } + cc = tile_continent(city_tile(target_city)); + if (!cc) { + /* Don't know the city center terrain class. + * Maybe, the city floats? Even if the rules prohibit it... */ + return TRI_MAYBE; + } + adjc_iterate(&(wld.map), city_tile(target_city), adjc_tile) { + int tc = tile_continent(adjc_tile); + + if (0 != tc) { + bool seen = FALSE; + int i = n; + + if (tc == cc) { + continue; + } + while (--i >= 0) { + if (adjc_cont[i] == tc) { + seen = TRUE; + break; + } + } + if (seen) { + continue; + } + adjc_cont[n++] = tile_continent(adjc_tile); + } else { + /* Likely, it's a black tile in client and we don't know + * We possibly can calculate, but keep it simple. */ + ukt = TRUE; + } + } adjc_iterate_end; + if (0 == n) { + return TRI_MAYBE; + } + + switch (range) { + case REQ_RANGE_TILE: + { + int tc = tile_continent(target_tile); + + if (cc == tc) { + return TRI_NO; + } + if (0 == tc || ukt) { + return TRI_MAYBE; + } + for (int i = 0; i < n; i++) { + if (tc == adjc_cont[i]) { + return TRI_YES; + } + } + } + + return TRI_NO; + case REQ_RANGE_ADJACENT: + if (ukt) { + /* If ALL the tiles in range are on cc, we can say it's false */ + square_iterate(&(wld.map), target_tile, 1, adjc_tile) { + if (tile_continent(adjc_tile) != cc) { + return TRI_MAYBE; + } + } square_iterate_end; + + return TRI_NO; + } else { + square_iterate(&(wld.map), target_tile, 1, adjc_tile) { + int tc = tile_continent(adjc_tile); + + if (0 == tc) { + return TRI_MAYBE; + } + for (int i = 0; i < n; i++) { + if (tc == adjc_cont[i]) { + return TRI_YES; + } + } + } square_iterate_end; + } + + return TRI_NO; + case REQ_RANGE_CADJACENT: + /* Do the same in a 5x5 square without corners (adjc of cadjc) */ + if (ukt) { + /* If ALL the tiles in range are on cc, we can say it's false */ + circle_iterate(&(wld.map), target_tile, 1, cadjc_tile) { + if (tile_continent(cadjc_tile) != cc) { + return TRI_MAYBE; + } + } circle_iterate_end; + } else { + circle_iterate(&(wld.map), target_tile, 1, cadjc_tile) { + int tc = tile_continent(cadjc_tile); + + if (0 == tc) { + return TRI_MAYBE; + } + for (int i = 0; i < n; i++) { + if (tc == adjc_cont[i]) { + return TRI_YES; + } + } + } circle_iterate_end; + } + + return TRI_NO; + case REQ_RANGE_CITY: + case REQ_RANGE_TRADEROUTE: + 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: + fc_assert_msg(FALSE, "Invalid range %d for citytile.", range); + break; + } + } + return TRI_MAYBE; case CITYT_LAST: /* Handled below */ @@ -3975,7 +4153,7 @@ bool is_req_unchanging(const struct requirement *req) case VUT_OTYPE: case VUT_SPECIALIST: /* Only so long as it's at local range only */ case VUT_AI_LEVEL: - case VUT_CITYTILE: + case VUT_CITYTILE: /* FIXME: actually, some terrain alterations are easy */ case VUT_CITYSTATUS: /* We don't *want* owner of our city to change */ case VUT_STYLE: case VUT_TOPO: @@ -5204,6 +5382,13 @@ const char *universal_name_translation(const struct universal *psource, case CITYT_WORKED: fc_strlcat(buf, _("Worked tile"), bufsz); break; + case CITYT_SAME_CONTINENT: + fc_strlcat(buf, _("Tile on the same continent"), bufsz); + break; + case CITYT_BORDERING_TCLASS_REGION: + fc_strlcat(buf, _("Tile of a nearby another terrain class region"), + bufsz); + break; case CITYT_LAST: fc_assert(psource->value.citytile != CITYT_LAST); fc_strlcat(buf, "error", bufsz); diff --git a/doc/README.effects b/doc/README.effects index 836f46cbf0..27a31b4f95 100644 --- a/doc/README.effects +++ b/doc/README.effects @@ -111,26 +111,28 @@ MinSize is the minimum size of a city required. AI is ai player difficulty level. TerrainClass is either "Land" or "Oceanic". TerrainAlter is "CanIrrigate", "CanMine", "CanRoad", "CanBase", or "CanPlace" -CityTile is "Center" (city center), "Claimed" (tile owned), -"Extras Owned" (extra on tile owned), or "Worked" (worked by any city) +CityTile is "Center" (city center), "Claimed" (tile owned by any city), + "Extras Owned" (extra on tile owned), "Worked" (worked by any city), + "Same Continent" (as the city center) or "Bordering TClass Region" (if the city + is a port, it's a tile of the nearby ocean but not of its continent). MinLatitude and MaxLatitude are numbers from -1000 (south pole) to 1000 -(north pole). + (north pole). CityStatus is "OwnedByOriginal", "Starved", "Disorder", or "Celebration" DiplRel is a diplomatic relationship. MaxUnitsOnTile is about the number of units present on a tile. UnitState is "Transported", "Transporting", "OnNativeTile", "OnLivableTile", -"InNativeExtra", "MovedThisTurn" or "HasHomeCity". + "InNativeExtra", "MovedThisTurn" or "HasHomeCity". Activity is "Idle", "Pollution", "Mine", "Irrigate", "Fortified", -"Pillage", "Transform", "Fortifying", "Fallout", -"Base", "Road", "Convert", "Cultivate", or "Plant". + "Pillage", "Transform", "Fortifying", "Fallout", + "Base", "Road", "Convert", "Cultivate", or "Plant". MinMoveFrags is the minimum move fragments the unit must have left. MinCalFrag is the minimum sub-year division the calendar must have reached, -if enabled (see [calendar].fragments in game.ruleset). + if enabled (see [calendar].fragments in game.ruleset). Nationality is fulfilled by any citizens of the given nationality -present in the city. + present in the city. OriginalOwner is the city founding nation ServerSetting is if a Boolean server setting is enabled. The setting must be -visible to all players and affect the game rules. + visible to all players and affect the game rules. Effect types ============ -- 2.34.1