From e2d663a900ab2f68cdaaf0403e2e52cd077fcabe Mon Sep 17 00:00:00 2001 From: Sveinung Kvilhaugsvik Date: Thu, 15 Apr 2021 12:08:16 +0200 Subject: [PATCH] unit_attack_unit_at_tile_result(): action param. Take what attack action is being checked as a parameter to unit_attack_unit_at_tile_result(). Stop checking the rules for other actions when it is specified. See osdn #42007 --- ai/default/aiair.c | 4 +-- ai/default/aihunt.c | 4 +-- ai/default/aiparatrooper.c | 6 ++-- ai/default/aitools.c | 7 ++-- ai/default/aiunit.c | 21 +++++++----- ai/default/daimilitary.c | 2 +- client/control.c | 2 +- common/actions.c | 4 +-- common/combat.c | 65 +++++++++++++++++++++++++++----------- common/combat.h | 3 ++ server/advisors/advgoto.c | 6 ++-- server/unithand.c | 6 ++-- 12 files changed, 87 insertions(+), 43 deletions(-) diff --git a/ai/default/aiair.c b/ai/default/aiair.c index c36a275ad8..c6fcb77e4a 100644 --- a/ai/default/aiair.c +++ b/ai/default/aiair.c @@ -130,7 +130,7 @@ static int dai_evaluate_tile_for_air_attack(struct unit *punit, int sortie_time; #define PROB_MULTIPLIER 100 /* should unify with those in combat.c */ - if (!can_unit_attack_tile(punit, dst_tile) + if (!can_unit_attack_tile(punit, NULL, dst_tile) || !(pdefender = get_defender(punit, dst_tile))) { return 0; } @@ -231,7 +231,7 @@ static int find_something_to_bomb(struct ai_type *ait, struct unit *punit, if (is_enemy_unit_tile(ptile, pplayer) && dai_should_we_air_attack_tile(ait, punit, ptile) - && can_unit_attack_tile(punit, ptile)) { + && can_unit_attack_tile(punit, NULL, ptile)) { int new_best = dai_evaluate_tile_for_air_attack(punit, ptile); if (new_best > best) { diff --git a/ai/default/aihunt.c b/ai/default/aihunt.c index 6fad940707..8b71fa82c9 100644 --- a/ai/default/aihunt.c +++ b/ai/default/aihunt.c @@ -333,7 +333,7 @@ static void dai_hunter_try_launch(struct ai_type *ait, break; } if (tile_city(ptile) - || !can_unit_attack_tile(punit, ptile)) { + || !can_unit_attack_tile(punit, NULL, ptile)) { continue; } unit_list_iterate(ptile->units, victim) { @@ -468,7 +468,7 @@ int dai_hunter_manage(struct ai_type *ait, struct player *pplayer, } if (tile_city(ptile) - || !can_unit_attack_tile(punit, ptile)) { + || !can_unit_attack_tile(punit, NULL, ptile)) { continue; } diff --git a/ai/default/aiparatrooper.c b/ai/default/aiparatrooper.c index 3011896396..08692f8e68 100644 --- a/ai/default/aiparatrooper.c +++ b/ai/default/aiparatrooper.c @@ -149,7 +149,7 @@ static struct tile *find_best_tile_to_paradrop_to(struct ai_type *ait, /* Iterate over adjacent tile to find good victim */ adjc_iterate(&(wld.map), ptile, target) { if (unit_list_size(target->units) == 0 - || !can_unit_attack_tile(punit, target) + || !can_unit_attack_tile(punit, NULL, target) || is_ocean_tile(target) || (has_handicap(pplayer, H_FOG) && !map_is_known_and_seen(target, pplayer, V_MAIN))) { @@ -160,7 +160,9 @@ static struct tile *find_best_tile_to_paradrop_to(struct ai_type *ait, unit_list_iterate(target->units, victim) { if ((!has_handicap(pplayer, H_FOG) || can_player_see_unit(pplayer, victim)) - && (unit_attack_unit_at_tile_result(punit, victim, target) == ATT_OK)) { + && (unit_attack_unit_at_tile_result(punit, NULL, + victim, target) + == ATT_OK)) { val += victim->hp * 100; } } unit_list_iterate_end; diff --git a/ai/default/aitools.c b/ai/default/aitools.c index a7602e9d27..ef4c21cc55 100644 --- a/ai/default/aitools.c +++ b/ai/default/aitools.c @@ -1273,11 +1273,14 @@ int stack_cost(struct unit *pattacker, struct unit *pdefender) if (is_stack_vulnerable(ptile)) { /* lotsa people die */ unit_list_iterate(ptile->units, aunit) { - if (unit_attack_unit_at_tile_result(pattacker, aunit, ptile) == ATT_OK) { + if (unit_attack_unit_at_tile_result(pattacker, NULL, aunit, ptile) + == ATT_OK) { victim_cost += unit_build_shield_cost_base(aunit); } } unit_list_iterate_end; - } else if (unit_attack_unit_at_tile_result(pattacker, pdefender, ptile) == ATT_OK) { + } else if (unit_attack_unit_at_tile_result(pattacker, NULL, + pdefender, ptile) + == ATT_OK) { /* Only one unit dies if attack is successful */ victim_cost = unit_build_shield_cost_base(pdefender); } diff --git a/ai/default/aiunit.c b/ai/default/aiunit.c index a15f6a5d2b..eaea3dfc57 100644 --- a/ai/default/aiunit.c +++ b/ai/default/aiunit.c @@ -404,8 +404,11 @@ static bool is_my_turn(struct unit *punit, struct unit *pdef) if (aunit == punit || unit_owner(aunit) != unit_owner(punit)) { continue; } - if (unit_attack_units_at_tile_result(aunit, unit_tile(pdef)) != ATT_OK - || unit_attack_unit_at_tile_result(aunit, pdef, unit_tile(pdef)) != ATT_OK) { + if ((unit_attack_units_at_tile_result(aunit, NULL, unit_tile(pdef)) + != ATT_OK) + || (unit_attack_unit_at_tile_result(aunit, NULL, + pdef, unit_tile(pdef)) + != ATT_OK)) { continue; } d = get_virtual_defense_power(unit_type_get(aunit), unit_type_get(pdef), @@ -451,7 +454,7 @@ static int dai_rampage_want(struct unit *punit, struct tile *ptile) CHECK_UNIT(punit); - if (can_unit_attack_tile(punit, ptile) + if (can_unit_attack_tile(punit, NULL, ptile) && (pdef = get_defender(punit, ptile))) { /* See description of kill_desire() about these variables. */ int attack = unit_att_rating_now(punit); @@ -1346,7 +1349,7 @@ int find_something_to_kill(struct ai_type *ait, struct player *pplayer, go_by_boat = TRUE; } - if (can_unit_attack_tile(punit, city_tile(acity)) + if (can_unit_attack_tile(punit, NULL, city_tile(acity)) && (pdefender = get_defender(punit, city_tile(acity)))) { vulnerability = unit_def_rating_squared(punit, pdefender); benefit = unit_build_shield_cost_base(pdefender); @@ -1516,7 +1519,7 @@ int find_something_to_kill(struct ai_type *ait, struct player *pplayer, /* We have to assume the attack is diplomatically ok. * We cannot use can_player_attack_tile, because we might not * be at war with aplayer yet */ - if (!can_unit_attack_tile(punit, atile) + if (!can_unit_attack_tile(punit, NULL, atile) || aunit != get_defender(punit, atile)) { /* We cannot attack it, or it is not the main defender. */ continue; @@ -1744,7 +1747,7 @@ static void dai_military_attack(struct ai_type *ait, struct player *pplayer, NULL, &ferryboat, NULL, NULL); if (!same_pos(unit_tile(punit), dest_tile)) { if (!is_tiles_adjacent(unit_tile(punit), dest_tile) - || !can_unit_attack_tile(punit, dest_tile)) { + || !can_unit_attack_tile(punit, NULL, dest_tile)) { /* Adjacent and can't attack usually means we are not marines * and on a ferry. This fixes the problem (usually). */ @@ -3024,8 +3027,10 @@ void dai_consider_tile_dangerous(struct ai_type *ait, struct tile *ptile, } unit_list_iterate(ptile1->units, enemy) { if (pplayers_at_war(unit_owner(enemy), unit_owner(punit)) - && unit_attack_unit_at_tile_result(enemy, punit, ptile) == ATT_OK - && unit_attack_units_at_tile_result(enemy, ptile) == ATT_OK) { + && (unit_attack_unit_at_tile_result(enemy, NULL, punit, ptile) + == ATT_OK) + && (unit_attack_units_at_tile_result(enemy, NULL, ptile) + == ATT_OK)) { a += adv_unit_att_rating(enemy); if ((a * a * 10) >= d) { /* The enemies combined strength is too big! */ diff --git a/ai/default/daimilitary.c b/ai/default/daimilitary.c index c1ca3656c1..539924731d 100644 --- a/ai/default/daimilitary.c +++ b/ai/default/daimilitary.c @@ -1276,7 +1276,7 @@ static struct adv_choice *kill_something_with(struct ai_type *ait, struct player &boattype, &move_time); if (NULL == ptile || ptile == unit_tile(myunit) - || !can_unit_attack_tile(myunit, ptile)) { + || !can_unit_attack_tile(myunit, NULL, ptile)) { goto cleanup; } diff --git a/client/control.c b/client/control.c index 4c908e98fd..fb19a8f00b 100644 --- a/client/control.c +++ b/client/control.c @@ -1172,7 +1172,7 @@ static bool can_units_attack_at(struct unit_list *punits, { unit_list_iterate(punits, punit) { if (is_attack_unit(punit) - && can_unit_attack_tile(punit, ptile)) { + && can_unit_attack_tile(punit, NULL, ptile)) { return TRUE; } } unit_list_iterate_end; diff --git a/common/actions.c b/common/actions.c index 153b209d41..fcd2604b38 100644 --- a/common/actions.c +++ b/common/actions.c @@ -3947,7 +3947,7 @@ is_action_possible(const action_id wanted_action, case ACTRES_NUKE_UNITS: - if (unit_attack_units_at_tile_result(actor_unit, target_tile) + if (unit_attack_units_at_tile_result(actor_unit, paction, target_tile) != ATT_OK) { /* Unreachable. */ return TRI_NO; @@ -4063,7 +4063,7 @@ is_action_possible(const action_id wanted_action, case ACTRES_ATTACK: /* Reason: Keep the old rules. */ - if (!can_unit_attack_tile(actor_unit, target_tile)) { + if (!can_unit_attack_tile(actor_unit, paction, target_tile)) { return TRI_NO; } break; diff --git a/common/combat.c b/common/combat.c index 95cd1ffd02..770ed18422 100644 --- a/common/combat.c +++ b/common/combat.c @@ -120,26 +120,45 @@ bool is_unit_reachable_at(const struct unit *defender, ***********************************************************************/ enum unit_attack_result unit_attack_unit_at_tile_result(const struct unit *punit, + const struct action *paction, const struct unit *pdefender, const struct tile *dest_tile) { /* 1. Can we attack _anything_ ? */ - if (!(utype_can_do_action(unit_type_get(punit), ACTION_ATTACK) - || utype_can_do_action(unit_type_get(punit), ACTION_SUICIDE_ATTACK) - /* Needed because ACTION_NUKE_UNITS uses this when evaluating its - * hard requirements. */ - || utype_can_do_action(unit_type_get(punit), ACTION_NUKE_UNITS))) { - return ATT_NON_ATTACK; + if (paction == NULL) { + if (!(utype_can_do_action(unit_type_get(punit), ACTION_ATTACK) + || utype_can_do_action(unit_type_get(punit), + ACTION_SUICIDE_ATTACK) + /* Needed because ACTION_NUKE_UNITS uses this when evaluating its + * hard requirements. */ + || utype_can_do_action(unit_type_get(punit), + ACTION_NUKE_UNITS))) { + return ATT_NON_ATTACK; + } + } else { + if (!utype_can_do_action(unit_type_get(punit), paction->id)) { + return ATT_NON_ATTACK; + } } /* 2. Can't attack with ground unit from ocean, except for marines */ - if (!is_native_tile(unit_type_get(punit), unit_tile(punit)) - && !utype_can_do_act_when_ustate(unit_type_get(punit), ACTION_ATTACK, - USP_NATIVE_TILE, FALSE) - && !utype_can_do_act_when_ustate(unit_type_get(punit), - ACTION_SUICIDE_ATTACK, - USP_NATIVE_TILE, FALSE)) { - return ATT_NONNATIVE_SRC; + if (paction == NULL) { + if (!is_native_tile(unit_type_get(punit), unit_tile(punit)) + && !utype_can_do_act_when_ustate(unit_type_get(punit), + ACTION_ATTACK, + USP_NATIVE_TILE, FALSE) + && !utype_can_do_act_when_ustate(unit_type_get(punit), + ACTION_SUICIDE_ATTACK, + USP_NATIVE_TILE, FALSE)) { + return ATT_NONNATIVE_SRC; + } + } else { + if (!is_native_tile(unit_type_get(punit), unit_tile(punit)) + && !utype_can_do_act_when_ustate(unit_type_get(punit), + paction->id, + USP_NATIVE_TILE, FALSE)) { + return ATT_NONNATIVE_SRC; + } } /* 3. Most units can not attack non-native terrain. @@ -167,6 +186,7 @@ unit_attack_unit_at_tile_result(const struct unit *punit, ************************************************************************/ static enum unit_attack_result unit_attack_all_at_tile_result(const struct unit *punit, + const struct action *paction, const struct tile *ptile) { bool any_reachable_unit = FALSE; @@ -181,7 +201,8 @@ unit_attack_all_at_tile_result(const struct unit *punit, if (!unit_transported(aunit)) { enum unit_attack_result result; - result = unit_attack_unit_at_tile_result(punit, aunit, ptile); + result = unit_attack_unit_at_tile_result(punit, paction, + aunit, ptile); if (result == ATT_UNREACHABLE && unit_has_type_flag(aunit, UTYF_NEVER_PROTECTS)) { /* Doesn't prevent us from attacking other units on the tile */ @@ -206,6 +227,7 @@ unit_attack_all_at_tile_result(const struct unit *punit, ************************************************************************/ static enum unit_attack_result unit_attack_any_at_tile_result(const struct unit *punit, + const struct action *paction, const struct tile *ptile) { enum unit_attack_result result = ATT_OK; @@ -214,7 +236,8 @@ unit_attack_any_at_tile_result(const struct unit *punit, /* HACK: we don't count transported units here. This prevents some * bugs like a cargoplane carrying a land unit being vulnerable. */ if (!unit_transported(aunit)) { - result = unit_attack_unit_at_tile_result(punit, aunit, ptile); + result = unit_attack_unit_at_tile_result(punit, paction, + aunit, ptile); if (result == ATT_OK) { return result; } @@ -231,12 +254,13 @@ unit_attack_any_at_tile_result(const struct unit *punit, ***********************************************************************/ enum unit_attack_result unit_attack_units_at_tile_result(const struct unit *punit, + const struct action *paction, const struct tile *ptile) { if (game.info.unreachable_protects) { - return unit_attack_all_at_tile_result(punit, ptile); + return unit_attack_all_at_tile_result(punit, paction, ptile); } else { - return unit_attack_any_at_tile_result(punit, ptile); + return unit_attack_any_at_tile_result(punit, paction, ptile); } } @@ -245,10 +269,12 @@ unit_attack_units_at_tile_result(const struct unit *punit, to do so? ***********************************************************************/ bool can_unit_attack_tile(const struct unit *punit, + const struct action *paction, const struct tile *dest_tile) { return (can_player_attack_tile(unit_owner(punit), dest_tile) - && unit_attack_units_at_tile_result(punit, dest_tile) == ATT_OK); + && (unit_attack_units_at_tile_result(punit, paction, dest_tile) + == ATT_OK)); } /*******************************************************************//** @@ -746,7 +772,8 @@ struct unit *get_defender(const struct unit *attacker, /* We used to skip over allied units, but the logic for that is * complicated and is now handled elsewhere. */ if (unit_can_defend_here(&(wld.map), defender) - && unit_attack_unit_at_tile_result(attacker, defender, ptile) == ATT_OK) { + && (unit_attack_unit_at_tile_result(attacker, NULL, defender, ptile) + == ATT_OK)) { bool change = FALSE; int build_cost = unit_build_shield_cost_base(defender); int defense_rating = get_defense_rating(attacker, defender); diff --git a/common/combat.h b/common/combat.h index 45d8ee1fa9..dd0fc4e89f 100644 --- a/common/combat.h +++ b/common/combat.h @@ -42,12 +42,15 @@ bool is_unit_reachable_at(const struct unit *defender, const struct tile *location); enum unit_attack_result unit_attack_unit_at_tile_result(const struct unit *punit, + const struct action *paction, const struct unit *pdefender, const struct tile *dest_tile); enum unit_attack_result unit_attack_units_at_tile_result(const struct unit *punit, + const struct action *paction, const struct tile *ptile); bool can_unit_attack_tile(const struct unit *punit, + const struct action *paction, const struct tile *ptile); double win_chance(int as, int ahp, int afp, int ds, int dhp, int dfp); diff --git a/server/advisors/advgoto.c b/server/advisors/advgoto.c index 370f3d4b93..c1db96d00f 100644 --- a/server/advisors/advgoto.c +++ b/server/advisors/advgoto.c @@ -439,8 +439,10 @@ bool adv_danger_at(struct unit *punit, struct tile *ptile) } unit_list_iterate(ptile1->units, enemy) { if (pplayers_at_war(unit_owner(enemy), unit_owner(punit)) - && unit_attack_unit_at_tile_result(enemy, punit, ptile) == ATT_OK - && unit_attack_units_at_tile_result(enemy, ptile) == ATT_OK) { + && (unit_attack_unit_at_tile_result(enemy, NULL, punit, ptile) + == ATT_OK) + && (unit_attack_units_at_tile_result(enemy, NULL, ptile) + == ATT_OK)) { a += adv_unit_att_rating(enemy); if ((a * a * 10) >= d) { /* The enemies combined strength is too big! */ diff --git a/server/unithand.c b/server/unithand.c index 731ec2b48e..283f9c0372 100644 --- a/server/unithand.c +++ b/server/unithand.c @@ -1250,10 +1250,12 @@ static struct ane_expl *expl_act_not_enabl(struct unit *punit, action_custom = test_unit_can_airlift_to(NULL, punit, target_city); break; case ACTRES_NUKE_UNITS: - action_custom = unit_attack_units_at_tile_result(punit, target_tile); + action_custom = unit_attack_units_at_tile_result(punit, paction, + target_tile); break; case ACTRES_ATTACK: - action_custom = unit_attack_units_at_tile_result(punit, target_tile); + action_custom = unit_attack_units_at_tile_result(punit, paction, + target_tile); break; case ACTRES_CONQUER_CITY: if (target_city) { -- 2.30.2