Feature #616 » 0007-Add-MaxDistanceSq-requirement-maximum-squared-distan.patch
| ai/default/daieffects.c | ||
|---|---|---|
|   case VUT_ACTION: | ||
|   case VUT_GOOD: | ||
|   case VUT_MINCALFRAG: | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|   case VUT_COUNT: | ||
|     /* No sensible implementation possible with data available. */ | ||
|     break; | ||
| ai/default/daimilitary.c | ||
|---|---|---|
|   case VUT_TERRAINCLASS: | ||
|   case VUT_TERRFLAG: | ||
|   case VUT_TERRAINALTER: | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|   case VUT_NONE: | ||
|     return tri_req_active(context, other_context, req); | ||
|   case VUT_COUNT: | ||
| common/fc_types.h | ||
|---|---|---|
|   int min_techs; | ||
|   int min_cities; | ||
|   int latitude; | ||
|   int distance_sq; | ||
|   enum topo_flag topo_property; | ||
|   enum wrap_flag wrap_property; | ||
| common/reqtext.c | ||
|---|---|---|
|     } | ||
|     break; | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|     switch (preq->range) { | ||
|     case REQ_RANGE_TILE: | ||
|       fc_strlcat(buf, prefix, bufsz); | ||
|       /* Test some special cases */ | ||
|       switch (preq->source.value.distance_sq) | ||
|       { | ||
|       case 0: | ||
|         if (preq->present) { | ||
|           fc_strlcat(buf, _("Must be the same tile."), bufsz); | ||
|         } else { | ||
|           fc_strlcat(buf, _("Must not be the same tile."), bufsz); | ||
|         } | ||
|         break; | ||
|       case 1: | ||
|         if (preq->present) { | ||
|           fc_strlcat(buf, _("Must be cardinally adjacent."), bufsz); | ||
|         } else { | ||
|           fc_strlcat(buf, _("Must not be cardinally adjacent."), bufsz); | ||
|         } | ||
|         break; | ||
|       case 2: | ||
|       case 3: | ||
|         if (preq->present) { | ||
|           fc_strlcat(buf, _("Must be adjacent."), bufsz); | ||
|         } else { | ||
|           fc_strlcat(buf, _("Must not be adjacent."), bufsz); | ||
|         } | ||
|         break; | ||
|       default: | ||
|         if (preq->present) { | ||
|           cat_snprintf(buf, bufsz, | ||
|                       _("The squared distance between the tiles " | ||
|                         "must be at most %d."), | ||
|                       preq->source.value.distance_sq); | ||
|         } else { | ||
|           cat_snprintf(buf, bufsz, | ||
|                       _("The squared distance between the tiles " | ||
|                         "must be at least %d."), | ||
|                       preq->source.value.distance_sq + 1); | ||
|         } | ||
|         break; | ||
|       } | ||
|       return TRUE; | ||
|     case REQ_RANGE_CADJACENT: | ||
|     case REQ_RANGE_ADJACENT: | ||
|     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 VUT_COUNT: | ||
|     break; | ||
|   } | ||
| common/requirements.c | ||
|---|---|---|
|       return; | ||
|    } | ||
|    break; | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|     source->value.distance_sq = atoi(value); | ||
|     if (0 <= source->value.distance_sq) { | ||
|       return; | ||
|     } | ||
|     break; | ||
|   case VUT_COUNT: | ||
|     break; | ||
|   } | ||
| ... | ... | |
|   case VUT_MAXLATITUDE: | ||
|     source.value.latitude = value; | ||
|     return source; | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|     source.value.distance_sq = value; | ||
|     return source; | ||
|   case VUT_COUNT: | ||
|     break; | ||
|   } | ||
| ... | ... | |
|   case VUT_MINLATITUDE: | ||
|   case VUT_MAXLATITUDE: | ||
|     return source->value.latitude; | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|     return source->value.distance_sq; | ||
|   case VUT_COUNT: | ||
|     break; | ||
|   } | ||
| ... | ... | |
|       case VUT_MAXTILEUNITS: | ||
|       case VUT_MINLATITUDE: | ||
|       case VUT_MAXLATITUDE: | ||
|       case VUT_MAX_DISTANCE_SQ: | ||
|         req.range = REQ_RANGE_TILE; | ||
|         break; | ||
|       case VUT_COUNTER: | ||
| ... | ... | |
|                  && req.range != REQ_RANGE_ADJACENT); | ||
|       break; | ||
|     case VUT_TERRAINALTER: /* XXX could in principle support C/ADJACENT */ | ||
|     case VUT_MAX_DISTANCE_SQ: | ||
|       invalid = (req.range != REQ_RANGE_TILE); | ||
|       break; | ||
|     case VUT_CITYTILE: | ||
| ... | ... | |
|     case VUT_MINCITIES: | ||
|     case VUT_MINLATITUDE: | ||
|     case VUT_MAXLATITUDE: | ||
|     case VUT_MAX_DISTANCE_SQ: | ||
|       /* Most requirements don't support 'survives'. */ | ||
|       invalid = survives; | ||
|       break; | ||
| ... | ... | |
|     } | ||
|     return FALSE; | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|     if (req2->source.kind != VUT_MAX_DISTANCE_SQ) { | ||
|       /* 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 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); | ||
|   default: | ||
|     /* No special knowledge exists. The requirements aren't the exact | ||
|      * opposite of each other per the initial check. */ | ||
| ... | ... | |
| #define IS_REQ_ACTIVE_VARIANT_ASSERT(_kind)                \ | ||
| {                                                          \ | ||
|   fc_assert_ret_val(req != NULL, TRI_MAYBE);               \ | ||
|   fc_assert_ret_val(req != nullptr, TRI_MAYBE);            \ | ||
|   fc_assert_ret_val(req->source.kind == _kind, TRI_MAYBE); \ | ||
|   fc_assert(context != NULL);                              \ | ||
|   fc_assert(context != nullptr);                           \ | ||
|   fc_assert(other_context != nullptr);                     \ | ||
| } | ||
| /**********************************************************************//** | ||
| ... | ... | |
|   return TRI_MAYBE; | ||
| } | ||
| /**********************************************************************//** | ||
|   Determine whether a maximum squared distance 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 squared distance requirement | ||
| **************************************************************************/ | ||
| static enum fc_tristate | ||
| is_max_distance_sq_req_active(const struct civ_map *nmap, | ||
|                               const struct req_context *context, | ||
|                               const struct req_context *other_context, | ||
|                               const struct requirement *req) | ||
| { | ||
|   IS_REQ_ACTIVE_VARIANT_ASSERT(VUT_MAX_DISTANCE_SQ); | ||
|   switch (req->range) { | ||
|   case REQ_RANGE_TILE: | ||
|     if (context->tile == nullptr || other_context->tile == nullptr) { | ||
|       return TRI_MAYBE; | ||
|     } | ||
|     return BOOL_TO_TRISTATE( | ||
|       sq_map_distance(context->tile, other_context->tile) | ||
|       <= req->source.value.distance_sq | ||
|     ); | ||
|   default: | ||
|     break; | ||
|   } | ||
|   fc_assert_msg(FALSE, | ||
|                 "Illegal range %d for max squared distance requirement.", | ||
|                 req->range); | ||
|   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_IMPR_FLAG] = {is_buildingflag_req_active, REQUCH_YES}, | ||
|   [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_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_TERRFLAG: | ||
|   case VUT_TERRAINALTER: | ||
|   case VUT_MINYEAR: | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|   case VUT_NONE: | ||
|   case VUT_COUNT: | ||
|     /* Not implemented. */ | ||
| ... | ... | |
|   case VUT_MINLATITUDE: | ||
|   case VUT_MAXLATITUDE: | ||
|     return psource1->value.latitude == psource2->value.latitude; | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|   return psource1->value.distance_sq == psource2->value.distance_sq; | ||
|   case VUT_COUNT: | ||
|     break; | ||
|   } | ||
| ... | ... | |
|   case VUT_MAXLATITUDE: | ||
|     fc_snprintf(buffer, sizeof(buffer), "%d", psource->value.latitude); | ||
|     return buffer; | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|     fc_snprintf(buffer, sizeof(buffer), "%d", psource->value.distance_sq); | ||
|     return buffer; | ||
|   case VUT_COUNT: | ||
|     break; | ||
| ... | ... | |
|     cat_snprintf(buf, bufsz, _("Latitude <= %d"), | ||
|                  psource->value.latitude); | ||
|     return buf; | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|     /* TRANS: here <= means 'less than or equal'. */ | ||
|     cat_snprintf(buf, bufsz, _("Squared distance <= %d"), | ||
|                  psource->value.distance_sq); | ||
|     return buf; | ||
|   case VUT_COUNT: | ||
|     break; | ||
|   } | ||
| doc/README.actions | ||
|---|---|---|
|    target requirement vector to the actor requirement vector. | ||
|  * Asymmetric local DiplRel requirements must test for the same thing in | ||
|    the opposite direction. Example: "Hosts embassy" -> "Has embassy" | ||
| A "MaxDistanceSq" requirement with the range "Tile" should always be put in | ||
| the actor requirements, for the same reasons. | ||
| Actions and Lua | ||
| =============== | ||
| doc/README.effects | ||
|---|---|---|
| MinMoveFrags:    Local | ||
| MinVeteran:      Local | ||
| MinHitPoints:    Local | ||
| MaxDistanceSq:   Tile | ||
| MinSize is the minimum size of a city required. | ||
| ... | ... | |
|  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). | ||
| MaxDistanceSq is about the (squared) distance between two tiles; currently | ||
|  only available for action enablers (see also README.actions) and a select | ||
|  few effects. | ||
| CityStatus is "OwnedByOriginal", "Transferred", "Starved", | ||
|  "Disorder", or "Celebration" | ||
|  The difference between "OwnedByOriginal" and "Transferred" is that | ||
| gen_headers/enums/fc_types_enums.def | ||
|---|---|---|
|   PLAYER_STATE       "PlayerState" | ||
|   FORM_AGE           "FormAge" | ||
|   MINCITIES          "MinCities" | ||
|   MAX_DISTANCE_SQ    "MaxDistanceSq" | ||
| end | ||
| server/cityturn.c | ||
|---|---|---|
|       case VUT_ACTION: | ||
|       case VUT_OTYPE: | ||
|       case VUT_SPECIALIST: | ||
|       case VUT_MAX_DISTANCE_SQ: | ||
|       case VUT_TERRAINALTER: /* XXX could do this in principle */ | ||
|         /* Will only happen with a bogus ruleset. */ | ||
|         log_error("worklist_change_build_target() has bogus preq"); | ||
| server/ruleset/rssanity.c | ||
|---|---|---|
|     case VUT_IMPR_GENUS: | ||
|     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 */ | ||
|       /* There can be only one requirement of these types (with current | ||
|        * range limitations) | ||
|        * Requirements might be identical, but we consider multiple | ||
| ... | ... | |
|                         req_to_fstring(preq, &astr)); | ||
|           astr_free(&astr); | ||
|           ok = FALSE; | ||
|         } else if (preq->source.kind == VUT_MAX_DISTANCE_SQ | ||
|                    && preq->range == REQ_RANGE_TILE) { | ||
|           struct astring astr; | ||
|           /* A Tile-ranged MaxDistanceSq requirement can be expressed as a | ||
|            * requirement in actor_reqs. Demand that it is there. */ | ||
|           ruleset_error(logger, LOG_ERROR, | ||
|                         _("Action enabler for %s has a tile MaxDistanceSq " | ||
|                           "requirement %s in target_reqs! Please read the " | ||
|                           "section \"Requirement vector rules\" in " | ||
|                           "doc/README.actions"), | ||
|                         action_id_rule_name(act), | ||
|                         req_to_fstring(preq, &astr)); | ||
|           astr_free(&astr); | ||
|           ok = FALSE; | ||
|         } | ||
|       } requirement_vector_iterate_end; | ||
| tools/ruledit/univ_value.c | ||
|---|---|---|
|   case VUT_MAXLATITUDE: | ||
|     src->value.latitude = 0; | ||
|     return TRUE; | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|     src->value.distance_sq = 0; | ||
|     return TRUE; | ||
|   case VUT_COUNT: | ||
|     fc_assert(src->kind != VUT_COUNT); | ||
|     return FALSE; | ||
| ... | ... | |
|   case VUT_MINCITIES: | ||
|   case VUT_MINLATITUDE: | ||
|   case VUT_MAXLATITUDE: | ||
|   case VUT_MAX_DISTANCE_SQ: | ||
|     /* Requirement types having numerical value */ | ||
|     cb(nullptr, FALSE, data); | ||
|     break; | ||