From 760b295a11781e5034a99e07c2f65f171106a465 Mon Sep 17 00:00:00 2001 From: Ihnatus Date: Fri, 25 Jun 2021 00:37:24 +0300 Subject: [PATCH] AI: Support sea moving caravans. See OSDN #42567 Signed-off-by: Ihnatus --- ai/default/aiunit.c | 64 ++++++++++++++++++++++----- ai/default/aiunit.h | 2 + ai/default/daidomestic.c | 83 ++++++++++++++++++++++-------------- common/unittype.h | 1 + server/advisors/advruleset.c | 9 ++++ 5 files changed, 118 insertions(+), 41 deletions(-) diff --git a/ai/default/aiunit.c b/ai/default/aiunit.c index 2ad1e0c9e1..95c85e2fe2 100644 --- a/ai/default/aiunit.c +++ b/ai/default/aiunit.c @@ -1916,6 +1916,39 @@ static bool dai_find_boat_for_unit(struct ai_type *ait, struct unit *punit) return alive; } +/*****************************************************************//****** + If a unit of pclass needs some transport or road + to go from ctile to ptile, maybe omitting way ends. + Maybe it should return fc_tristate? +*************************************************************************/ +bool uclass_need_trans_between(struct unit_class *pclass, + struct tile *ctile, struct tile *ptile) +{ + /* We usually have Inaccessible terrain, so not testing MOVE_FULL == */ + bool lm = MOVE_NONE != pclass->adv.land_move, + sm = MOVE_NONE != pclass->adv.sea_move; + + if (lm && sm) { + return FALSE; + } + /* We could use adjc_iterate() but likely often tiles are on the same + * continent and it will be more time to find where they connect */ + iterate_outward(ctile, 1, atile) { + int acont = tile_continent(atile); + if (is_ocean_tile(atile) ? sm : lm) { + iterate_outward(ptile, 1, btile) { + if (tile_continent(btile) == acont) { + return FALSE; + } + } iterate_outward_end; + } + } iterate_outward_end; + if (is_tiles_adjacent(ctile, ptile)) { + return FALSE; + } + return TRUE; +} + /************************************************************************* Send the caravan to the specified city, or make it help the wonder / trade, if it's already there. After this call, the unit may no longer @@ -1951,13 +1984,15 @@ static void dai_caravan_goto(struct ai_type *ait, struct player *pplayer, /* if we are not being transported then ask for a boat again */ alive = TRUE; if (!unit_transported(punit) && - (tile_continent(unit_tile(punit)) - != tile_continent(dest_city->tile))) { + uclass_need_trans_between(unit_class_get(punit), + unit_tile(punit), dest_city->tile)) { alive = dai_find_boat_for_unit(ait, punit); } } if (alive) { - alive = dai_gothere(ait, pplayer, punit, dest_city->tile); + /* FIXME: sometimes we get FALSE here just because + * a trireme that we've boarded can't go over an ocean. */ + alive = dai_gothere(ait, pplayer, punit, dest_city->tile); } } else { /* to trade without boat */ @@ -2068,7 +2103,7 @@ static bool dai_is_unit_tired_waiting_boat(struct ai_type *ait, if (src == NULL || dest == NULL) { return FALSE; } - /* if we're not at home continent */ + /* if we're not at home continent (FIXME: well, why?) */ if (tile_continent(src) != tile_continent(src_home_city)) { return FALSE; } @@ -2100,7 +2135,9 @@ static bool dai_is_unit_tired_waiting_boat(struct ai_type *ait, /***************************************************************************** Check if a caravan can make a trade route to a city on a different - continent. + continent (means, need a boat). + FIXME: in a one-continent game it can be much more advantageous + to cross straits on a trireme than to march through all the world. *****************************************************************************/ static bool dai_caravan_can_trade_cities_diff_cont(struct player *pplayer, struct unit *punit) { @@ -2109,6 +2146,10 @@ static bool dai_caravan_can_trade_cities_diff_cont(struct player *pplayer, fc_assert(pcity != NULL); + if (unit_class_get(punit)->adv.ferry_types <= 0) { + /* There is just no possible transporters. */ + return FALSE; + } continent = tile_continent(pcity->tile); /* Look for proper destination city at different continent. */ @@ -2187,6 +2228,9 @@ static void dai_manage_caravan(struct ai_type *ait, struct player *pplayer, const struct city *homecity; const struct city *dest = NULL; struct unit_ai *unit_data; + struct unit_class *pclass = unit_class_get(punit); + bool expect_boats = pclass->adv.ferry_types > 0; + /* TODO: will pplayer have a boat for punit in a reasonable time? */ bool help_wonder = FALSE; bool required_boat = FALSE; bool request_boat = FALSE; @@ -2252,7 +2296,7 @@ static void dai_manage_caravan(struct ai_type *ait, struct player *pplayer, unit_rule_name(punit), punit->id, TILE_XY(unit_tile(punit))); } else { /* destination valid, are we tired of waiting for a boat? */ - if (dai_is_unit_tired_waiting_boat(ait, punit)) { + if (expect_boats && dai_is_unit_tired_waiting_boat(ait, punit)) { aiferry_clear_boat(ait, punit); dai_unit_new_task(ait, punit, AIUNIT_NONE, NULL); log_base(LOG_CARAVAN2, "%s %s[%d](%d,%d) unit tired of waiting!", @@ -2262,8 +2306,8 @@ static void dai_manage_caravan(struct ai_type *ait, struct player *pplayer, } else { dest = city_dest; help_wonder = (unit_data->task == AIUNIT_WONDER) ? TRUE : FALSE; - required_boat = (tile_continent(unit_tile(punit)) == - tile_continent(dest->tile)) ? FALSE : TRUE; + required_boat = expect_boats + && uclass_need_trans_between(pclass, unit_tile(punit), dest->tile); request_boat = FALSE; } } @@ -2310,8 +2354,8 @@ static void dai_manage_caravan(struct ai_type *ait, struct player *pplayer, /* we did find a new destination for the unit */ dest = result.dest; help_wonder = result.help_wonder; - required_boat = (tile_continent(unit_tile(punit)) == - tile_continent(dest->tile)) ? FALSE : TRUE; + required_boat = expect_boats + && uclass_need_trans_between(pclass, unit_tile(punit), dest->tile); request_boat = required_boat; dai_unit_new_task(ait, punit, (help_wonder) ? AIUNIT_WONDER : AIUNIT_TRADE, diff --git a/ai/default/aiunit.h b/ai/default/aiunit.h index 89ec82eccb..af720f3742 100644 --- a/ai/default/aiunit.h +++ b/ai/default/aiunit.h @@ -93,6 +93,8 @@ void dai_manage_unit(struct ai_type *ait, struct player *pplayer, void dai_manage_military(struct ai_type *ait, struct player *pplayer, struct unit *punit); struct city *find_nearest_safe_city(struct unit *punit); +bool uclass_need_trans_between(struct unit_class *pclass, + struct tile *ctile, struct tile *ptile); int look_for_charge(struct ai_type *ait, struct player *pplayer, struct unit *punit, struct unit **aunit, struct city **acity); diff --git a/ai/default/daidomestic.c b/ai/default/daidomestic.c index 6eeb4eb615..5ee243a286 100644 --- a/ai/default/daidomestic.c +++ b/ai/default/daidomestic.c @@ -193,6 +193,7 @@ static void dai_choose_trade_route(struct ai_type *ait, struct city *pcity, bool prefer_different_cont; int pct = 0; int trader_trait; + bool can_move_ic = FALSE, has_boats = TRUE; bool need_boat = FALSE; int trade_action; @@ -208,8 +209,45 @@ static void dai_choose_trade_route(struct ai_type *ait, struct city *pcity, return; } + unit_type = best_role_unit(pcity, + action_id_get_role(ACTION_TRADE_ROUTE)); + + if (!unit_type) { + /* Can't establish trade route yet. What about entering a marketplace? */ + /* TODO: Should a future unit capable of establishing trade routes be + * prioritized above a present unit capable of entering a market place? + * In that case this should be below the check for a future unit + * capable of establishing a trade route. */ + unit_type = best_role_unit(pcity, + action_id_get_role(ACTION_MARKETPLACE)); + } + + if (!unit_type) { + /* We cannot build such units yet + * but we will consider it to stimulate science */ + unit_type = get_role_unit(action_id_get_role(ACTION_TRADE_ROUTE), 0); + } + + if (!unit_type) { + /* We'll never be able to establish a trade route. Consider a unit that + * can enter the marketplace in stead to stimulate science. */ + unit_type = get_role_unit(action_id_get_role(ACTION_MARKETPLACE), 0); + } + + fc_assert_msg(unit_type, + "Non existance of trade unit not caught"); + + if (unit_type) { + struct unit_class *pclass = utype_class(unit_type); + + /* Hope there is no "water cities in lakes" ruleset? */ + can_move_ic = pclass->adv.sea_move != MOVE_NONE; + has_boats = pclass->adv.ferry_types > 0; + } + if (trade_route_type_trade_pct(TRT_NATIONAL_IC) > - trade_route_type_trade_pct(TRT_NATIONAL)) { + trade_route_type_trade_pct(TRT_NATIONAL) + && (can_move_ic || has_boats)) { prefer_different_cont = TRUE; } else { prefer_different_cont = FALSE; @@ -221,13 +259,18 @@ static void dai_choose_trade_route(struct ai_type *ait, struct city *pcity, /* National traderoutes have value */ city_list_iterate(pplayer->cities, acity) { if (can_cities_trade(pcity, acity)) { - dest_city_found = TRUE; if (tile_continent(acity->tile) != continent) { + if (!(can_move_ic || has_boats)) { + /* FIXME: get by roads/rivers? */ + continue; + } + dest_city_found = TRUE; dest_city_nat_different_cont = TRUE; if (prefer_different_cont) { break; } } else { + dest_city_found = TRUE; dest_city_nat_same_cont = TRUE; if (!prefer_different_cont) { break; @@ -255,13 +298,18 @@ static void dai_choose_trade_route(struct ai_type *ait, struct city *pcity, if (pplayers_allied(pplayer, aplayer)) { city_list_iterate(aplayer->cities, acity) { if (can_cities_trade(pcity, acity)) { - dest_city_found = TRUE; if (tile_continent(acity->tile) != continent) { + if (!(can_move_ic || has_boats)) { + /* FIXME: get by roads/rivers? */ + continue; + } + dest_city_found = TRUE; dest_city_in_different_cont = TRUE; if (prefer_different_cont) { break; } } else { + dest_city_found = TRUE; dest_city_in_same_cont = TRUE; if (!prefer_different_cont) { break; @@ -282,34 +330,6 @@ static void dai_choose_trade_route(struct ai_type *ait, struct city *pcity, return; } - unit_type = best_role_unit(pcity, - action_id_get_role(ACTION_TRADE_ROUTE)); - - if (!unit_type) { - /* Can't establish trade route yet. What about entering a marketplace? */ - /* TODO: Should a future unit capable of establishing trade routes be - * prioritized above a present unit capable of entering a market place? - * In that case this should be below the check for a future unit - * capable of establishing a trade route. */ - unit_type = best_role_unit(pcity, - action_id_get_role(ACTION_MARKETPLACE)); - } - - if (!unit_type) { - /* We cannot build such units yet - * but we will consider it to stimulate science */ - unit_type = get_role_unit(action_id_get_role(ACTION_TRADE_ROUTE), 0); - } - - if (!unit_type) { - /* We'll never be able to establish a trade route. Consider a unit that - * can enter the marketplace in stead to stimulate science. */ - unit_type = get_role_unit(action_id_get_role(ACTION_MARKETPLACE), 0); - } - - fc_assert_msg(unit_type, - "Non existance of trade unit not caught"); - trade_routes = city_num_trade_routes(pcity); /* Count also caravans enroute to establish traderoutes */ caravan_units = 0; @@ -377,6 +397,7 @@ static void dai_choose_trade_route(struct ai_type *ait, struct city *pcity, } } + need_boat = need_boat && has_boats && !can_move_ic; income = pct * income / 100; want = income * ai->gold_priority + income * ai->science_priority; diff --git a/common/unittype.h b/common/unittype.h index 57a32cf250..f07cdb0d4c 100644 --- a/common/unittype.h +++ b/common/unittype.h @@ -144,6 +144,7 @@ struct unit_class { struct { enum move_level land_move; enum move_level sea_move; + int ferry_types; } adv; struct { diff --git a/server/advisors/advruleset.c b/server/advisors/advruleset.c index ed43ce1e23..8e38861a1e 100644 --- a/server/advisors/advruleset.c +++ b/server/advisors/advruleset.c @@ -70,6 +70,7 @@ void adv_units_ruleset_init(void) pclass->adv.sea_move = MOVE_NONE; } + pclass->adv.ferry_types = 0; } unit_class_iterate_end; unit_type_iterate(ptype) { @@ -89,5 +90,13 @@ void adv_units_ruleset_init(void) break; } } effect_list_iterate_end; + + if (utype_has_role(ptype, L_FERRYBOAT)) { + unit_class_iterate(aclass) { + if (BV_ISSET(ptype->cargo, uclass_index(aclass))) { + aclass->adv.ferry_types++; + } + } unit_class_iterate_end; + } } unit_type_iterate_end; } -- 2.27.0