From 75aa9dbc819a74f16f6689fb4822a269eb4a6578 Mon Sep 17 00:00:00 2001 From: Sveinung Kvilhaugsvik Date: Tue, 11 May 2021 23:28:58 +0200 Subject: [PATCH] Introduce "Bombard Lethal". "Bombard Lethal" is like the other bombard actions except that it kills its target units if they run out of hit points. See osdn #42312 --- ai/default/aitools.c | 5 +++ client/gui-qt/dialogs.cpp | 14 ++++++++ client/packhand.c | 1 + common/actions.c | 23 +++++++++++++ common/actions.h | 2 ++ data/alien/game.ruleset | 7 ++++ data/civ1/game.ruleset | 7 ++++ data/civ2/game.ruleset | 7 ++++ data/civ2civ3/game.ruleset | 7 ++++ data/classic/game.ruleset | 7 ++++ data/goldkeep/game.ruleset | 7 ++++ data/granularity/game.ruleset | 7 ++++ data/multiplayer/game.ruleset | 7 ++++ data/sandbox/game.ruleset | 7 ++++ data/stub/game.ruleset | 7 ++++ data/webperimental/game.ruleset | 7 ++++ doc/README.actions | 11 ++++++ server/unithand.c | 59 +++++++++++++++++++++++---------- server/unittools.c | 8 +++-- 19 files changed, 181 insertions(+), 19 deletions(-) diff --git a/ai/default/aitools.c b/ai/default/aitools.c index 95da35a55f..cfdd77d7c6 100644 --- a/ai/default/aitools.c +++ b/ai/default/aitools.c @@ -855,6 +855,11 @@ bool dai_unit_attack(struct ai_type *ait, struct unit *punit, struct tile *ptile /* Choose capture. */ unit_do_action(unit_owner(punit), punit->id, tile_index(ptile), 0, "", ACTION_CAPTURE_UNITS); + } else if (is_action_enabled_unit_on_units(ACTION_BOMBARD_LETHAL, + punit, ptile)) { + /* Choose "Bombard Lethal". */ + unit_do_action(unit_owner(punit), punit->id, tile_index(ptile), + 0, "", ACTION_BOMBARD_LETHAL); } else if (is_action_enabled_unit_on_units(ACTION_BOMBARD, punit, ptile)) { /* Choose "Bombard". */ diff --git a/client/gui-qt/dialogs.cpp b/client/gui-qt/dialogs.cpp index f776f507b7..5fb726346e 100644 --- a/client/gui-qt/dialogs.cpp +++ b/client/gui-qt/dialogs.cpp @@ -116,6 +116,7 @@ static void expel_unit(QVariant data1, QVariant data2); static void bombard(QVariant data1, QVariant data2); static void bombard2(QVariant data1, QVariant data2); static void bombard3(QVariant data1, QVariant data2); +static void bombard_lethal(QVariant data1, QVariant data2); static void found_city(QVariant data1, QVariant data2); static void transform_terrain(QVariant data1, QVariant data2); static void cultivate(QVariant data1, QVariant data2); @@ -250,6 +251,7 @@ static const QHash af_map_init(void) action_function[ACTION_BOMBARD] = bombard; action_function[ACTION_BOMBARD2] = bombard2; action_function[ACTION_BOMBARD3] = bombard3; + action_function[ACTION_BOMBARD_LETHAL] = bombard_lethal; action_function[ACTION_NUKE_UNITS] = nuke_units; /* Unit acting against a tile. */ @@ -2666,6 +2668,18 @@ static void bombard3(QVariant data1, QVariant data2) target_id, 0, ""); } +/***********************************************************************//** + Action "Bombard Lethal" for choice dialog +***************************************************************************/ +static void bombard_lethal(QVariant data1, QVariant data2) +{ + int actor_id = data1.toInt(); + int target_id = data2.toInt(); + + request_do_action(ACTION_BOMBARD_LETHAL, actor_id, + target_id, 0, ""); +} + /***********************************************************************//** Action build city for choice dialog ***************************************************************************/ diff --git a/client/packhand.c b/client/packhand.c index 77fd081b25..c1746b6005 100644 --- a/client/packhand.c +++ b/client/packhand.c @@ -4782,6 +4782,7 @@ static action_id auto_attack_act(const struct act_prob *act_probs) case ACTION_BOMBARD: case ACTION_BOMBARD2: case ACTION_BOMBARD3: + case ACTION_BOMBARD_LETHAL: case ACTION_NUKE: case ACTION_NUKE_CITY: case ACTION_NUKE_UNITS: diff --git a/common/actions.c b/common/actions.c index 2fe4eba544..cdcef5dc38 100644 --- a/common/actions.c +++ b/common/actions.c @@ -1093,6 +1093,16 @@ static void hard_code_actions(void) /* Overwritten by the ruleset's bombard_3_max_range */ 1, FALSE); + actions[ACTION_BOMBARD_LETHAL] = + unit_action_new(ACTION_BOMBARD_LETHAL, ACTRES_BOMBARD, + FALSE, TRUE, MAK_STAYS, + /* A single domestic unit at the target tile will make + * the action illegal. It must therefore be performed + * from another tile. */ + 1, + /* Overwritten by the ruleset's bombard_lethal_max_range */ + 1, + FALSE); actions[ACTION_SPY_NUKE] = unit_action_new(ACTION_SPY_NUKE, ACTRES_SPY_NUKE, FALSE, TRUE, @@ -7449,6 +7459,8 @@ const char *action_ui_name_ruleset_var_name(int act) return "ui_name_bombard_2"; case ACTION_BOMBARD3: return "ui_name_bombard_3"; + case ACTION_BOMBARD_LETHAL: + return "ui_name_bombard_lethal"; case ACTION_SPY_NUKE: return "ui_name_suitcase_nuke"; case ACTION_SPY_NUKE_ESC: @@ -7712,6 +7724,9 @@ const char *action_ui_name_default(int act) case ACTION_BOMBARD3: /* TRANS: B_ombard 3 (100% chance of success). */ return N_("B%sombard 3%s"); + case ACTION_BOMBARD_LETHAL: + /* TRANS: Lethal B_ombard (100% chance of success). */ + return N_("Lethal B%sombard%s"); case ACTION_SPY_NUKE: /* TRANS: Suitcase _Nuke (100% chance of success). */ return N_("Suitcase %sNuke%s"); @@ -7986,6 +8001,7 @@ const char *action_min_range_ruleset_var_name(int act) case ACTION_BOMBARD: case ACTION_BOMBARD2: case ACTION_BOMBARD3: + case ACTION_BOMBARD_LETHAL: case ACTION_SPY_ATTACK: case ACTION_CONQUER_EXTRAS: case ACTION_CONQUER_EXTRAS2: @@ -8219,6 +8235,8 @@ const char *action_max_range_ruleset_var_name(int act) return "bombard_2_max_range"; case ACTION_BOMBARD3: return "bombard_3_max_range"; + case ACTION_BOMBARD_LETHAL: + return "bombard_lethal_max_range"; case ACTION_NUKE: return "explode_nuclear_max_range"; case ACTION_NUKE_CITY: @@ -8418,6 +8436,7 @@ const char *action_target_kind_ruleset_var_name(int act) case ACTION_BOMBARD: case ACTION_BOMBARD2: case ACTION_BOMBARD3: + case ACTION_BOMBARD_LETHAL: case ACTION_SPY_ATTACK: case ACTION_CONQUER_EXTRAS: case ACTION_CONQUER_EXTRAS2: @@ -8926,6 +8945,7 @@ const char *action_actor_consuming_always_ruleset_var_name(action_id act) case ACTION_BOMBARD: case ACTION_BOMBARD2: case ACTION_BOMBARD3: + case ACTION_BOMBARD_LETHAL: case ACTION_SPY_ATTACK: case ACTION_CONQUER_EXTRAS: case ACTION_CONQUER_EXTRAS2: @@ -8986,6 +9006,8 @@ const char *action_blocked_by_ruleset_var_name(const struct action *act) return "bombard_2_blocked_by"; case ACTION_BOMBARD3: return "bombard_3_blocked_by"; + case ACTION_BOMBARD_LETHAL: + return "bombard_lethal_blocked_by"; case ACTION_NUKE: return "explode_nuclear_blocked_by"; case ACTION_NUKE_CITY: @@ -9138,6 +9160,7 @@ action_post_success_forced_ruleset_var_name(const struct action *act) case ACTION_BOMBARD: case ACTION_BOMBARD2: case ACTION_BOMBARD3: + case ACTION_BOMBARD_LETHAL: case ACTION_NUKE: case ACTION_NUKE_CITY: case ACTION_NUKE_UNITS: diff --git a/common/actions.h b/common/actions.h index 7b9a6f4f88..25293e1942 100644 --- a/common/actions.h +++ b/common/actions.h @@ -276,6 +276,8 @@ extern "C" { #define SPECENUM_VALUE104NAME "Unit Make Homeless" #define SPECENUM_VALUE105 ACTION_WIPE_UNITS #define SPECENUM_VALUE105NAME "Wipe Units" +#define SPECENUM_VALUE106 ACTION_BOMBARD_LETHAL +#define SPECENUM_VALUE106NAME "Bombard Lethal" #define SPECENUM_BITVECTOR bv_actions #define SPECENUM_COUNT ACTION_COUNT #include "specenum_gen.h" diff --git a/data/alien/game.ruleset b/data/alien/game.ruleset index 2846b952e7..e135cfeb64 100644 --- a/data/alien/game.ruleset +++ b/data/alien/game.ruleset @@ -359,6 +359,13 @@ bombard_2_max_range = 1 ; distance. bombard_3_max_range = 1 +; The maximum distance from the actor unit to the target of the +; "Bombard Lethal" action. The value 1 means that the targets must be on a +; tile adjacent to the actor unit. The special value "unlimited" lifts the +; maximum distance restriction. The maximum distance can`t be smaller than +; the minimum distance. +bombard_lethal_max_range = 1 + ; The minimal distance from the actor unit to the target. The value 0 means ; that the target must be at the tile of the actor unit. The value 1 means ; that the tile must be a tile adjacent to the actor unit. diff --git a/data/civ1/game.ruleset b/data/civ1/game.ruleset index 1b01e0482e..f38e953004 100644 --- a/data/civ1/game.ruleset +++ b/data/civ1/game.ruleset @@ -345,6 +345,13 @@ bombard_2_max_range = 1 ; distance. bombard_3_max_range = 1 +; The maximum distance from the actor unit to the target of the +; "Bombard Lethal" action. The value 1 means that the targets must be on a +; tile adjacent to the actor unit. The special value "unlimited" lifts the +; maximum distance restriction. The maximum distance can`t be smaller than +; the minimum distance. +bombard_lethal_max_range = 1 + ; The minimal distance from the actor unit to the target. The value 0 means ; that the target must be at the tile of the actor unit. The value 1 means ; that the tile must be a tile adjacent to the actor unit. diff --git a/data/civ2/game.ruleset b/data/civ2/game.ruleset index 572efef409..c69d9b86d7 100644 --- a/data/civ2/game.ruleset +++ b/data/civ2/game.ruleset @@ -344,6 +344,13 @@ bombard_2_max_range = 1 ; distance. bombard_3_max_range = 1 +; The maximum distance from the actor unit to the target of the +; "Bombard Lethal" action. The value 1 means that the targets must be on a +; tile adjacent to the actor unit. The special value "unlimited" lifts the +; maximum distance restriction. The maximum distance can`t be smaller than +; the minimum distance. +bombard_lethal_max_range = 1 + ; The minimal distance from the actor unit to the target. The value 0 means ; that the target must be at the tile of the actor unit. The value 1 means ; that the tile must be a tile adjacent to the actor unit. diff --git a/data/civ2civ3/game.ruleset b/data/civ2civ3/game.ruleset index cf3331ebdb..488d1ace71 100644 --- a/data/civ2civ3/game.ruleset +++ b/data/civ2civ3/game.ruleset @@ -386,6 +386,13 @@ bombard_2_max_range = 1 ; distance. bombard_3_max_range = 1 +; The maximum distance from the actor unit to the target of the +; "Bombard Lethal" action. The value 1 means that the targets must be on a +; tile adjacent to the actor unit. The special value "unlimited" lifts the +; maximum distance restriction. The maximum distance can`t be smaller than +; the minimum distance. +bombard_lethal_max_range = 1 + ; The minimal distance from the actor unit to the target. The value 0 means ; that the target must be at the tile of the actor unit. The value 1 means ; that the tile must be a tile adjacent to the actor unit. diff --git a/data/classic/game.ruleset b/data/classic/game.ruleset index e212352e4d..52d37d2383 100644 --- a/data/classic/game.ruleset +++ b/data/classic/game.ruleset @@ -370,6 +370,13 @@ bombard_2_max_range = 1 ; distance. bombard_3_max_range = 1 +; The maximum distance from the actor unit to the target of the +; "Bombard Lethal" action. The value 1 means that the targets must be on a +; tile adjacent to the actor unit. The special value "unlimited" lifts the +; maximum distance restriction. The maximum distance can`t be smaller than +; the minimum distance. +bombard_lethal_max_range = 1 + ; The minimal distance from the actor unit to the target. The value 0 means ; that the target must be at the tile of the actor unit. The value 1 means ; that the tile must be a tile adjacent to the actor unit. diff --git a/data/goldkeep/game.ruleset b/data/goldkeep/game.ruleset index 661ebd5ffa..38830352d5 100644 --- a/data/goldkeep/game.ruleset +++ b/data/goldkeep/game.ruleset @@ -370,6 +370,13 @@ bombard_2_max_range = 1 ; distance. bombard_3_max_range = 1 +; The maximum distance from the actor unit to the target of the +; "Bombard Lethal" action. The value 1 means that the targets must be on a +; tile adjacent to the actor unit. The special value "unlimited" lifts the +; maximum distance restriction. The maximum distance can`t be smaller than +; the minimum distance. +bombard_lethal_max_range = 1 + ; The minimal distance from the actor unit to the target. The value 0 means ; that the target must be at the tile of the actor unit. The value 1 means ; that the tile must be a tile adjacent to the actor unit. diff --git a/data/granularity/game.ruleset b/data/granularity/game.ruleset index 1505ca26c8..68722ddca0 100644 --- a/data/granularity/game.ruleset +++ b/data/granularity/game.ruleset @@ -310,6 +310,13 @@ bombard_2_max_range = 1 ; distance. bombard_3_max_range = 1 +; The maximum distance from the actor unit to the target of the +; "Bombard Lethal" action. The value 1 means that the targets must be on a +; tile adjacent to the actor unit. The special value "unlimited" lifts the +; maximum distance restriction. The maximum distance can`t be smaller than +; the minimum distance. +bombard_lethal_max_range = 1 + ; The minimal distance from the actor unit to the target. The value 0 means ; that the target must be at the tile of the actor unit. The value 1 means ; that the tile must be a tile adjacent to the actor unit. diff --git a/data/multiplayer/game.ruleset b/data/multiplayer/game.ruleset index 72ded9610b..07795d7250 100644 --- a/data/multiplayer/game.ruleset +++ b/data/multiplayer/game.ruleset @@ -370,6 +370,13 @@ bombard_2_max_range = 1 ; distance. bombard_3_max_range = 1 +; The maximum distance from the actor unit to the target of the +; "Bombard Lethal" action. The value 1 means that the targets must be on a +; tile adjacent to the actor unit. The special value "unlimited" lifts the +; maximum distance restriction. The maximum distance can`t be smaller than +; the minimum distance. +bombard_lethal_max_range = 1 + ; The minimal distance from the actor unit to the target. The value 0 means ; that the target must be at the tile of the actor unit. The value 1 means ; that the tile must be a tile adjacent to the actor unit. diff --git a/data/sandbox/game.ruleset b/data/sandbox/game.ruleset index 365cd888bd..691bce4eb8 100644 --- a/data/sandbox/game.ruleset +++ b/data/sandbox/game.ruleset @@ -397,6 +397,13 @@ bombard_2_max_range = 1 ; distance. bombard_3_max_range = 1 +; The maximum distance from the actor unit to the target of the +; "Bombard Lethal" action. The value 1 means that the targets must be on a +; tile adjacent to the actor unit. The special value "unlimited" lifts the +; maximum distance restriction. The maximum distance can`t be smaller than +; the minimum distance. +bombard_lethal_max_range = 1 + ; The minimal distance from the actor unit to the target. The value 0 means ; that the target must be at the tile of the actor unit. The value 1 means ; that the tile must be a tile adjacent to the actor unit. diff --git a/data/stub/game.ruleset b/data/stub/game.ruleset index 790e7f4e8b..9f6ca6bdc3 100644 --- a/data/stub/game.ruleset +++ b/data/stub/game.ruleset @@ -301,6 +301,13 @@ bombard_2_max_range = 1 ; distance. bombard_3_max_range = 1 +; The maximum distance from the actor unit to the target of the +; "Bombard Lethal" action. The value 1 means that the targets must be on a +; tile adjacent to the actor unit. The special value "unlimited" lifts the +; maximum distance restriction. The maximum distance can`t be smaller than +; the minimum distance. +bombard_lethal_max_range = 1 + ; The minimal distance from the actor unit to the target. The value 0 means ; that the target must be at the tile of the actor unit. The value 1 means ; that the tile must be a tile adjacent to the actor unit. diff --git a/data/webperimental/game.ruleset b/data/webperimental/game.ruleset index b4740c11ce..91f229dd87 100644 --- a/data/webperimental/game.ruleset +++ b/data/webperimental/game.ruleset @@ -343,6 +343,13 @@ bombard_2_max_range = 1 ; distance. bombard_3_max_range = 1 +; The maximum distance from the actor unit to the target of the +; "Bombard Lethal" action. The value 1 means that the targets must be on a +; tile adjacent to the actor unit. The special value "unlimited" lifts the +; maximum distance restriction. The maximum distance can`t be smaller than +; the minimum distance. +bombard_lethal_max_range = 1 + ; The minimal distance from the actor unit to the target. The value 0 means ; that the target must be at the tile of the actor unit. The value 1 means ; that the tile must be a tile adjacent to the actor unit. diff --git a/doc/README.actions b/doc/README.actions index dc570d6c48..ed9ded7f74 100644 --- a/doc/README.actions +++ b/doc/README.actions @@ -733,6 +733,17 @@ Actions done by a unit against all units at a tile * A copy of "Bombard". * See "Bombard" for everything else. +"Bombard Lethal" - bombard the units (and city) at the tile. + * UI name can be set using ui_name_bombard_lethal + * can kill target units + * any action listed in bombard_lethal_blocked_by must be impossible + * actor must have a bombard_rate > 0 + * actor must have an attack > 0 + * actor must be on a tile next to the target or, if + bombard_lethal_max_range allows it, futher away. + * target can't be in a city the actor player isn't at war with. + * target owner must be at war with actor. (!) + "Attack" * UI name can be set using ui_name_attack * any action listed in attack_blocked_by must be impossible diff --git a/server/unithand.c b/server/unithand.c index 5904087ec4..dce4186d7f 100644 --- a/server/unithand.c +++ b/server/unithand.c @@ -4265,21 +4265,42 @@ static bool unit_bombard(struct unit *punit, struct tile *ptile, unit_bombs_unit(punit, pdefender, &att_hp, &def_hp, paction); - notify_player(pplayer, ptile, - E_UNIT_WIN_ATT, ftc_server, - /* TRANS: Your Bomber bombards the English Rifleman.*/ - _("Your %s bombards the %s %s."), - unit_name_translation(punit), - nation_adjective_for_player(unit_owner(pdefender)), - unit_name_translation(pdefender)); - - notify_player(unit_owner(pdefender), ptile, - E_UNIT_WIN_DEF, ftc_server, - /* TRANS: Your Rifleman is bombarded by the French Bomber.*/ - _("Your %s is bombarded by the %s %s."), - unit_name_translation(pdefender), - nation_adjective_for_player(pplayer), - unit_name_translation(punit)); + if (def_hp <= 0) { + notify_player(pplayer, ptile, + E_UNIT_WIN_ATT, ftc_server, + /* TRANS: Your Bomber killed the English Rifleman by + * doing Bombard. */ + _("Your %s killed the %s %s by doing %s."), + unit_name_translation(punit), + nation_adjective_for_player(unit_owner(pdefender)), + unit_name_translation(pdefender), + action_name_translation(paction)); + notify_player(unit_owner(pdefender), ptile, + E_UNIT_WIN_DEF, ftc_server, + /* TRANS: Your Rifleman was killed by the French + * Bomber doing Bombard. */ + _("Your %s was killed by the %s %s doing %s."), + unit_name_translation(pdefender), + nation_adjective_for_player(pplayer), + unit_name_translation(punit), + action_name_translation(paction)); + } else { + notify_player(pplayer, ptile, + E_UNIT_WIN_ATT, ftc_server, + /* TRANS: Your Bomber bombards the English Rifleman. */ + _("Your %s bombards the %s %s."), + unit_name_translation(punit), + nation_adjective_for_player(unit_owner(pdefender)), + unit_name_translation(pdefender)); + notify_player(unit_owner(pdefender), ptile, + E_UNIT_WIN_DEF, ftc_server, + /* TRANS: Your Rifleman is bombarded by the French + * Bomber. */ + _("Your %s is bombarded by the %s %s."), + unit_name_translation(pdefender), + nation_adjective_for_player(pplayer), + unit_name_translation(punit)); + } see_combat(punit, pdefender); @@ -4287,8 +4308,6 @@ static bool unit_bombard(struct unit *punit, struct tile *ptile, pdefender->hp = def_hp; send_combat(punit, pdefender, 0, 0, 1); - - send_unit_info(NULL, pdefender); /* May cause an incident */ action_consequence_success(paction, @@ -4296,6 +4315,12 @@ static bool unit_bombard(struct unit *punit, struct tile *ptile, unit_owner(pdefender), unit_tile(pdefender), unit_link(pdefender)); + + if (def_hp <= 0) { + wipe_unit(pdefender, ULR_KILLED, unit_owner(pdefender)); + } else { + send_unit_info(NULL, pdefender); + } } } unit_list_iterate_safe_end; diff --git a/server/unittools.c b/server/unittools.c index a2cb9c8d2e..49b9224e33 100644 --- a/server/unittools.c +++ b/server/unittools.c @@ -357,9 +357,13 @@ void unit_bombs_unit(struct unit *attacker, struct unit *defender, } } - /* Don't kill the target. */ if (*def_hp <= 0) { - *def_hp = 1; + if (BV_ISSET(paction->sub_results, ACT_SUB_RES_NON_LETHAL)) { + /* Don't kill the target. */ + *def_hp = 1; + } else { + *def_hp = 0; + } } } -- 2.30.2