Feature #629 » 0006-Add-MaxNumTiles-continent-size-requirement-type.patch
| ai/default/daieffects.c | ||
|---|---|---|
|   case VUT_GOOD: | ||
|   case VUT_MINCALFRAG: | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|   case VUT_MAX_NUM_TILES: | ||
|   case VUT_COUNT: | ||
|     /* No sensible implementation possible with data available. */ | ||
|     break; | ||
| ai/default/daimilitary.c | ||
|---|---|---|
|   case VUT_TERRFLAG: | ||
|   case VUT_TERRAINALTER: | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|   case VUT_MAX_NUM_TILES: | ||
|   case VUT_NONE: | ||
|     return tri_req_active(context, other_context, req); | ||
|   case VUT_COUNT: | ||
| common/fc_types.h | ||
|---|---|---|
|   int min_cities; | ||
|   int latitude; | ||
|   int distance_sq; | ||
|   int num_tiles; | ||
|   enum topo_flag topo_property; | ||
|   enum wrap_flag wrap_property; | ||
| common/reqtext.c | ||
|---|---|---|
|     } | ||
|     break; | ||
|   case VUT_MAX_NUM_TILES: | ||
|     switch (preq->range) { | ||
|     case REQ_RANGE_CONTINENT: | ||
|       fc_strlcat(buf, prefix, bufsz); | ||
|       if (preq->present) { | ||
|         cat_snprintf(buf, bufsz, | ||
|                     _("Requires a continent or ocean size of at most %d."), | ||
|                     preq->source.value.num_tiles); | ||
|       } else { | ||
|         cat_snprintf(buf, bufsz, | ||
|                     _("Requires a continent or ocean size of at least %d."), | ||
|                     preq->source.value.num_tiles + 1); | ||
|       } | ||
|       return TRUE; | ||
|     case REQ_RANGE_PLAYER: | ||
|     case REQ_RANGE_TEAM: | ||
|     case REQ_RANGE_ALLIANCE: | ||
|     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: | ||
|       /* Not supported. */ | ||
|       break; | ||
|     } | ||
|     break; | ||
|   case VUT_COUNT: | ||
|     break; | ||
|   } | ||
| common/requirements.c | ||
|---|---|---|
|       return; | ||
|     } | ||
|     break; | ||
|   case VUT_MAX_NUM_TILES: | ||
|     source->value.num_tiles = atoi(value); | ||
|     if (0 <= source->value.num_tiles) { | ||
|       return; | ||
|     } | ||
|     break; | ||
|   case VUT_COUNT: | ||
|     break; | ||
|   } | ||
| ... | ... | |
|   case VUT_MAX_DISTANCE_SQ: | ||
|     source.value.distance_sq = value; | ||
|     return source; | ||
|   case VUT_MAX_NUM_TILES: | ||
|     source.value.num_tiles = value; | ||
|     return source; | ||
|   case VUT_COUNT: | ||
|     break; | ||
|   } | ||
| ... | ... | |
|     return source->value.latitude; | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|     return source->value.distance_sq; | ||
|   case VUT_MAX_NUM_TILES: | ||
|     return source->value.num_tiles; | ||
|   case VUT_COUNT: | ||
|     break; | ||
|   } | ||
| ... | ... | |
|       case VUT_SERVERSETTING: | ||
|         req.range = REQ_RANGE_WORLD; | ||
|         break; | ||
|       case VUT_MAX_NUM_TILES: | ||
|         req.range = REQ_RANGE_CONTINENT; | ||
|         break; | ||
|       } | ||
|     } | ||
| ... | ... | |
|     case VUT_PLAYER_STATE: | ||
|       invalid = (req.range != REQ_RANGE_PLAYER); | ||
|       break; | ||
|     case VUT_MAX_NUM_TILES: | ||
|       invalid = (req.range != REQ_RANGE_CONTINENT); | ||
|       break; | ||
|     case VUT_IMPROVEMENT: | ||
|       /* Valid ranges depend on the building genus (wonder/improvement), | ||
|        * which might not have been loaded from the ruleset yet. | ||
| ... | ... | |
|     case VUT_MINLATITUDE: | ||
|     case VUT_MAXLATITUDE: | ||
|     case VUT_MAX_DISTANCE_SQ: | ||
|     case VUT_MAX_NUM_TILES: | ||
|       /* Most requirements don't support 'survives'. */ | ||
|       invalid = survives; | ||
|       break; | ||
| ... | ... | |
|   return are_requirements_contradictions(req1, &nreq2); | ||
| } | ||
| /**********************************************************************//** | ||
|   Returns TRUE iff two bounds that could each be either an upper or lower | ||
|   bound are contradicting each other. This function assumes that of the | ||
|   upper and lower bounds, one will be inclusive, the other exclusive. | ||
| **************************************************************************/ | ||
| static inline bool are_bounds_contradictions(int bound1, bool is_upper1, | ||
|                                              int bound2, bool is_upper2) | ||
| { | ||
|   /* If the bounds are on opposite sides, and one is inclusive, the other | ||
|    * exclusive, the number of values that satisfy both bounds is exactly | ||
|    * their difference, (upper bound) - (lower bound). | ||
|    * The bounds contradict each other iff this difference is 0 or less, | ||
|    * i.e. iff (upper bound) <= (lower bound) */ | ||
|   if (is_upper1 && !is_upper2) { | ||
|     return bound1 <= bound2; | ||
|   } else if (!is_upper1 && is_upper2) { | ||
|     return bound1 >= bound2; | ||
|   } | ||
|   /* Both are upper or both are lower ~> no contradiction possible */ | ||
|   return FALSE; | ||
| } | ||
| /**********************************************************************//** | ||
|   Returns TRUE if req1 and req2 contradicts each other. | ||
| ... | ... | |
|       /* Finding contradictions across requirement kinds aren't supported | ||
|        * for MinMoveFrags requirements. */ | ||
|       return FALSE; | ||
|     } else if (req1->present == req2->present) { | ||
|       /* No contradiction possible. */ | ||
|       return FALSE; | ||
|     } else { | ||
|       /* Number of move fragments left can't be larger than the number | ||
|        * required to be present and smaller than the number required to not | ||
|        * be present when the number required to be present is smaller than | ||
|        * the number required to not be present. */ | ||
|       if (req1->present) { | ||
|         return req1->source.value.minmoves >= req2->source.value.minmoves; | ||
|       } else { | ||
|         return req1->source.value.minmoves <= req2->source.value.minmoves; | ||
|       } | ||
|     } | ||
|     break; | ||
|     return are_bounds_contradictions( | ||
|         req1->source.value.minmoves, !req1->present, | ||
|         req2->source.value.minmoves, !req2->present); | ||
|   case VUT_MINLATITUDE: | ||
|   case VUT_MAXLATITUDE: | ||
|     if (req2->source.kind != VUT_MINLATITUDE | ||
| ... | ... | |
|       /* Finding contradictions across requirement kinds isn't supported | ||
|        * for MaxDistanceSq requirements. */ | ||
|       return FALSE; | ||
|     } else if (req1->present == req2->present) { | ||
|       /* Both bounds are on the same side, can't contradict */ | ||
|     } | ||
|     return are_bounds_contradictions( | ||
|         req1->source.value.distance_sq, req1->present, | ||
|         req2->source.value.distance_sq, req2->present); | ||
|   case VUT_MAX_NUM_TILES: | ||
|     if (req2->source.kind != VUT_MAX_NUM_TILES) { | ||
|       /* Finding contradictions across requirement kinds isn't supported | ||
|        * for MaxNumTiles requirements. */ | ||
|       return FALSE; | ||
|     } else if (req1->range != req2->range) { | ||
|       /* Counting completely separate things */ | ||
|       return FALSE; | ||
|     } | ||
|     /* Different bounds ~> contradiction possible. | ||
|      * Need (negated req value) < (actual value) <= (present req value) | ||
|      * to be satisfied; impossible iff (present) <= (negated). */ | ||
|     return req1->present | ||
|       ? (req1->source.value.distance_sq <= req2->source.value.distance_sq) | ||
|       : (req1->source.value.distance_sq >= req2->source.value.distance_sq); | ||
|     return are_bounds_contradictions( | ||
|         req1->source.value.num_tiles, req1->present, | ||
|         req2->source.value.num_tiles, req2->present); | ||
|   default: | ||
|     /* No special knowledge exists. The requirements aren't the exact | ||
|      * opposite of each other per the initial check. */ | ||
| ... | ... | |
|   return TRI_MAYBE; | ||
| } | ||
| /**********************************************************************//** | ||
|   Determine whether a maximum number of tiles requirement is satisfied in | ||
|   a given context, ignoring parts of the requirement that can be handled | ||
|   uniformly for all requirement types. | ||
|   context, other_context and req must not be null, | ||
|   and req must be a max tile number requirement | ||
| **************************************************************************/ | ||
| static enum fc_tristate | ||
| is_max_num_tiles_req_active(const struct civ_map *nmap, | ||
|                             const struct req_context *context, | ||
|                             const struct req_context *other_context, | ||
|                             const struct requirement *req) | ||
| { | ||
|   int known_tiles; | ||
|   bool known_accurate; | ||
|   IS_REQ_ACTIVE_VARIANT_ASSERT(VUT_MAX_NUM_TILES); | ||
|   switch (req->range) { | ||
|   case REQ_RANGE_CONTINENT: | ||
|     { | ||
|       Continent_id cont = context->tile ? tile_continent(context->tile) : 0; | ||
|       fc_assert_ret_val(cont <= nmap->num_continents, TRI_MAYBE); | ||
|       fc_assert_ret_val(-cont <= nmap->num_oceans, TRI_MAYBE); | ||
|       if (cont > 0) { | ||
|         known_tiles = nmap->continent_sizes[cont]; | ||
|         known_accurate = (is_server() | ||
|             || (nmap->client.continent_unknown_adj_counts[cont] == 0)); | ||
|       } else if (cont < 0) { | ||
|         known_tiles = nmap->ocean_sizes[-cont]; | ||
|         known_accurate = (is_server() | ||
|             || (nmap->client.ocean_unknown_adj_counts[-cont] == 0)); | ||
|       } else { | ||
|         /* Even if we don't know the continent, we know it has at least one | ||
|          * tile (the target tile) */ | ||
|         known_tiles = 1; | ||
|         known_accurate = FALSE; | ||
|       } | ||
|     } | ||
|     break; | ||
|   default: | ||
|     fc_assert_msg(FALSE, | ||
|                   "Illegal range %d for max tile number requirement.", | ||
|                   req->range); | ||
|     return TRI_MAYBE; | ||
|   } | ||
|   if (known_tiles > req->source.value.num_tiles) { | ||
|     /* We already know about more tiles than the limit */ | ||
|     return TRI_NO; | ||
|   } else if (known_accurate) { | ||
|     /* We know there aren't any more tiles that could break the limit */ | ||
|     return TRI_YES; | ||
|   } | ||
|   return TRI_MAYBE; | ||
| } | ||
| /**********************************************************************//** | ||
|   Determine whether a minimum year requirement is satisfied in a given | ||
|   context, ignoring parts of the requirement that can be handled uniformly | ||
| ... | ... | |
|   [VUT_PLAYER_FLAG] = {is_plr_flag_req_active, REQUCH_NO}, | ||
|   [VUT_PLAYER_STATE] = {is_plr_state_req_active, REQUCH_NO}, | ||
|   [VUT_MAX_DISTANCE_SQ] = {is_max_distance_sq_req_active, REQUCH_YES}, | ||
|   [VUT_MAX_NUM_TILES] = {is_max_num_tiles_req_active, REQUCH_NO}, | ||
|   [VUT_MAXLATITUDE] = {is_latitude_req_active, REQUCH_YES}, | ||
|   [VUT_MAXTILEUNITS] = {is_maxunitsontile_req_active, REQUCH_NO}, | ||
|   [VUT_MINCALFRAG] = {is_mincalfrag_req_active, REQUCH_NO}, | ||
| ... | ... | |
|   case VUT_TERRAINALTER: | ||
|   case VUT_MINYEAR: | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|   case VUT_MAX_NUM_TILES: | ||
|   case VUT_NONE: | ||
|   case VUT_COUNT: | ||
|     /* Not implemented. */ | ||
| ... | ... | |
|     return psource1->value.latitude == psource2->value.latitude; | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|     return psource1->value.distance_sq == psource2->value.distance_sq; | ||
|   case VUT_MAX_NUM_TILES: | ||
|     return psource1->value.num_tiles == psource2->value.num_tiles; | ||
|   case VUT_COUNT: | ||
|     break; | ||
|   } | ||
| ... | ... | |
|   case VUT_MAX_DISTANCE_SQ: | ||
|     fc_snprintf(buffer, sizeof(buffer), "%d", psource->value.distance_sq); | ||
|     return buffer; | ||
|   case VUT_MAX_NUM_TILES: | ||
|     fc_snprintf(buffer, sizeof(buffer), "%d", psource->value.num_tiles); | ||
|     return buffer; | ||
|   case VUT_COUNT: | ||
|     break; | ||
| ... | ... | |
|     cat_snprintf(buf, bufsz, _("Squared distance <= %d"), | ||
|                  psource->value.distance_sq); | ||
|     return buf; | ||
|   case VUT_MAX_NUM_TILES: | ||
|     cat_snprintf(buf, bufsz, _("%d or fewer tiles"), | ||
|                  psource->value.num_tiles); | ||
|     return buf; | ||
|   case VUT_COUNT: | ||
|     break; | ||
|   } | ||
| doc/README.effects | ||
|---|---|---|
| MinVeteran:      Local | ||
| MinHitPoints:    Local | ||
| MaxDistanceSq:   Tile | ||
| MaxNumTiles:     Continent | ||
| MinSize is the minimum size of a city required. | ||
| ... | ... | |
| MaxDistanceSq is about the (squared) distance between two tiles; currently | ||
|  only available for action enablers (see also README.actions) and a select | ||
|  few effects. | ||
| MaxNumTiles is about continent *or ocean* size (number of tiles). | ||
| CityStatus is "OwnedByOriginal", "Transferred", "Starved", | ||
|  "Disorder", or "Celebration" | ||
|  The difference between "OwnedByOriginal" and "Transferred" is that | ||
| gen_headers/enums/fc_types_enums.def | ||
|---|---|---|
|   FORM_AGE           "FormAge" | ||
|   MINCITIES          "MinCities" | ||
|   MAX_DISTANCE_SQ    "MaxDistanceSq" | ||
|   MAX_NUM_TILES      "MaxNumTiles" | ||
| end | ||
| server/cityturn.c | ||
|---|---|---|
|                                     pcity, "have_terrainflag"); | ||
|         } | ||
|         break; | ||
|       case VUT_MAX_NUM_TILES: | ||
|         /* Changing the continent size is hard; cf. VUT_TERRAINCLASS above. | ||
|          * Change this when we support other ranges. */ | ||
|         purge = TRUE; | ||
|         break; | ||
|       case VUT_ROADFLAG: | ||
|         if (preq->present) { | ||
|           notify_player(pplayer, city_tile(pcity), | ||
| server/ruleset/rssanity.c | ||
|---|---|---|
|     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_NUM_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 | ||
| tools/ruledit/univ_value.c | ||
|---|---|---|
|   case VUT_MAX_DISTANCE_SQ: | ||
|     src->value.distance_sq = 0; | ||
|     return TRUE; | ||
|   case VUT_MAX_NUM_TILES: | ||
|     src->value.num_tiles = 0; | ||
|     return TRUE; | ||
|   case VUT_COUNT: | ||
|     fc_assert(src->kind != VUT_COUNT); | ||
|     return FALSE; | ||
| ... | ... | |
|   case VUT_MINLATITUDE: | ||
|   case VUT_MAXLATITUDE: | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|   case VUT_MAX_NUM_TILES: | ||
|     /* Requirement types having numerical value */ | ||
|     cb(nullptr, FALSE, data); | ||
|     break; | ||