From 3f1ba15c0ba30a61a3ab4e676a5516469680f1e8 Mon Sep 17 00:00:00 2001 From: Sveinung Kvilhaugsvik Date: Mon, 7 Jun 2021 00:03:11 +0200 Subject: [PATCH] Introduce the new action "Spy Escape". See osdn #41800 --- ai/default/daicity.c | 1 + ai/default/daidiplomacy.c | 1 + client/gui-qt/dialogs.cpp | 17 ++++++++++++++++ client/packhand.c | 1 + common/actions.c | 39 ++++++++++++++++++++++++++++++++++++ common/actions.h | 2 ++ common/fc_types.h | 2 ++ common/unittype.c | 1 + doc/README.actions | 7 +++++++ server/advisors/advdata.c | 1 + server/diplomats.c | 42 ++++++++++++++++++++++++++++++++++++--- server/diplomats.h | 5 +++++ server/unithand.c | 6 ++++++ 13 files changed, 122 insertions(+), 3 deletions(-) diff --git a/ai/default/daicity.c b/ai/default/daicity.c index fd957c5278..d0aa83e4d1 100644 --- a/ai/default/daicity.c +++ b/ai/default/daicity.c @@ -1250,6 +1250,7 @@ static int action_target_neg_util(action_id act_id, case ACTRES_ESTABLISH_EMBASSY: case ACTRES_SPY_INVESTIGATE_CITY: case ACTRES_MARKETPLACE: + case ACTRES_SPY_ESCAPE: /* TODO: Individual and well balanced values */ return 1; diff --git a/ai/default/daidiplomacy.c b/ai/default/daidiplomacy.c index a45e5a44ea..bfa0e9cf29 100644 --- a/ai/default/daidiplomacy.c +++ b/ai/default/daidiplomacy.c @@ -1987,6 +1987,7 @@ void dai_incident(struct ai_type *ait, enum incident_type type, dai_incident_simple(receiver, violator, victim, scope, 2); break; case ACTRES_UNIT_MOVE: + case ACTRES_SPY_ESCAPE: case ACTRES_TRADE_ROUTE: case ACTRES_MARKETPLACE: case ACTRES_HELP_WONDER: diff --git a/client/gui-qt/dialogs.cpp b/client/gui-qt/dialogs.cpp index 5fb726346e..ffc64c661a 100644 --- a/client/gui-qt/dialogs.cpp +++ b/client/gui-qt/dialogs.cpp @@ -92,6 +92,7 @@ static void spy_steal_gold(QVariant data1, QVariant data2); static void spy_steal_gold_esc(QVariant data1, QVariant data2); static void spy_steal_maps(QVariant data1, QVariant data2); static void spy_steal_maps_esc(QVariant data1, QVariant data2); +static void spy_escape(QVariant data1, QVariant data2); static void spy_nuke_city(QVariant data1, QVariant data2); static void spy_nuke_city_esc(QVariant data1, QVariant data2); static void nuke_city(QVariant data1, QVariant data2); @@ -224,6 +225,7 @@ static const QHash af_map_init(void) action_function[ACTION_STEAL_MAPS_ESC] = spy_steal_maps_esc; action_function[ACTION_SPY_NUKE] = spy_nuke_city; action_function[ACTION_SPY_NUKE_ESC] = spy_nuke_city_esc; + action_function[ACTION_SPY_ESCAPE] = spy_escape; action_function[ACTION_DESTROY_CITY] = destroy_city; action_function[ACTION_RECYCLE_UNIT] = unit_recycle; action_function[ACTION_HOME_CITY] = unit_home_city; @@ -3344,6 +3346,21 @@ static void spy_steal_maps_esc(QVariant data1, QVariant data2) } } +/***********************************************************************//** + Action Spy Escape for choice dialog +***************************************************************************/ +static void spy_escape(QVariant data1, QVariant data2) +{ + int diplomat_id = data1.toInt(); + int diplomat_target_id = data2.toInt(); + + if (NULL != game_unit_by_number(diplomat_id) + && NULL != game_city_by_number(diplomat_target_id)) { + request_do_action(ACTION_SPY_ESCAPE, + diplomat_id, diplomat_target_id, 0, ""); + } +} + /***********************************************************************//** Action establish embassy for choice dialog ***************************************************************************/ diff --git a/client/packhand.c b/client/packhand.c index 3e9c7c6e3e..ff50860f6d 100644 --- a/client/packhand.c +++ b/client/packhand.c @@ -4773,6 +4773,7 @@ static action_id auto_attack_act(const struct act_prob *act_probs) case ACTION_UNIT_MOVE: case ACTION_UNIT_MOVE2: case ACTION_UNIT_MOVE3: + case ACTION_SPY_ESCAPE: /* Not interesting. */ break; case ACTION_USER_ACTION1: diff --git a/common/actions.c b/common/actions.c index 986096b404..4e4b22b9ef 100644 --- a/common/actions.c +++ b/common/actions.c @@ -379,6 +379,7 @@ static void hard_code_oblig_hard_reqs(void) ACTRES_SPY_BRIBE_UNIT, ACTRES_CAPTURE_UNITS, ACTRES_CONQUER_CITY, + ACTRES_SPY_ESCAPE, ACTRES_NONE); /* The same for tile targeted actions that also can be done to unclaimed * tiles. */ @@ -1021,6 +1022,11 @@ static void hard_code_actions(void) ACTRES_SPY_SPREAD_PLAGUE, FALSE, TRUE, MAK_ESCAPE, 0, 1, FALSE); + actions[ACTION_SPY_ESCAPE] = + unit_action_new(ACTION_SPY_ESCAPE, + ACTRES_SPY_ESCAPE, + FALSE, TRUE, + MAK_ESCAPE, 0, 1, FALSE); actions[ACTION_TRADE_ROUTE] = unit_action_new(ACTION_TRADE_ROUTE, ACTRES_TRADE_ROUTE, FALSE, TRUE, @@ -2258,6 +2264,7 @@ bool action_creates_extra(const struct action *paction, case ACTRES_SPY_STEAL_TECH: case ACTRES_SPY_TARGETED_STEAL_TECH: case ACTRES_SPY_INCITE_CITY: + case ACTRES_SPY_ESCAPE: case ACTRES_TRADE_ROUTE: case ACTRES_MARKETPLACE: case ACTRES_HELP_WONDER: @@ -2344,6 +2351,7 @@ bool action_removes_extra(const struct action *paction, case ACTRES_SPY_STEAL_TECH: case ACTRES_SPY_TARGETED_STEAL_TECH: case ACTRES_SPY_INCITE_CITY: + case ACTRES_SPY_ESCAPE: case ACTRES_TRADE_ROUTE: case ACTRES_MARKETPLACE: case ACTRES_HELP_WONDER: @@ -3442,6 +3450,7 @@ action_actor_utype_hard_reqs_ok_full(const struct action *paction, case ACTRES_SPY_STEAL_TECH: case ACTRES_SPY_TARGETED_STEAL_TECH: case ACTRES_SPY_INCITE_CITY: + case ACTRES_SPY_ESCAPE: case ACTRES_TRADE_ROUTE: case ACTRES_MARKETPLACE: case ACTRES_HELP_WONDER: @@ -3623,6 +3632,7 @@ action_hard_reqs_actor(const struct action *paction, case ACTRES_SPY_STEAL_TECH: case ACTRES_SPY_TARGETED_STEAL_TECH: case ACTRES_SPY_INCITE_CITY: + case ACTRES_SPY_ESCAPE: case ACTRES_TRADE_ROUTE: case ACTRES_MARKETPLACE: case ACTRES_HELP_WONDER: @@ -4563,6 +4573,14 @@ is_action_possible(const action_id wanted_action, } break; + case ACTRES_SPY_ESCAPE: + /* Reason: Be merciful. */ + /* Info leak: The player know if he has any cities. */ + if (city_list_size(actor_player->cities) < 1) { + return TRI_NO; + } + break; + case ACTRES_SPY_SPREAD_PLAGUE: /* Enabling this action with illness_on = FALSE prevents spread. */ break; @@ -5590,6 +5608,9 @@ action_prob(const action_id wanted_action, /* There is no risk that the city won't get investigated. */ chance = ACTPROB_CERTAIN; break; + case ACTRES_SPY_ESCAPE: + /* TODO */ + break; case ACTRES_TRADE_ROUTE: /* TODO */ break; @@ -6998,6 +7019,7 @@ int action_dice_roll_initial_odds(const struct action *paction) case ACTRES_HUT_ENTER: case ACTRES_HUT_FRIGHTEN: case ACTRES_UNIT_MOVE: + case ACTRES_SPY_ESCAPE: case ACTRES_NONE: /* No additional dice roll. */ break; @@ -7594,6 +7616,8 @@ const char *action_ui_name_ruleset_var_name(int act) return "ui_name_unit_move_2"; case ACTION_UNIT_MOVE3: return "ui_name_unit_move_3"; + case ACTION_SPY_ESCAPE: + return "ui_name_escape"; case ACTION_USER_ACTION1: return "ui_name_user_action_1"; case ACTION_USER_ACTION2: @@ -7893,6 +7917,9 @@ const char *action_ui_name_default(int act) case ACTION_UNIT_MOVE3: /* TRANS: Regular _Move (100% chance of success). */ return N_("Regular %sMove%s"); + case ACTION_SPY_ESCAPE: + /* TRANS: _Escape To Nearest City (100% chance of success). */ + return N_("%sEscape To Nearest City%s"); case ACTION_USER_ACTION1: /* TRANS: _User Action 1 (100% chance of success). */ return N_("%sUser Action 1%s"); @@ -8017,6 +8044,7 @@ const char *action_min_range_ruleset_var_name(int act) case ACTION_UNIT_MOVE: case ACTION_UNIT_MOVE2: case ACTION_UNIT_MOVE3: + case ACTION_SPY_ESCAPE: /* Min range is not ruleset changeable */ return NULL; case ACTION_NUKE: @@ -8105,6 +8133,7 @@ int action_min_range_default(enum action_result result) case ACTRES_HUT_ENTER: case ACTRES_HUT_FRIGHTEN: case ACTRES_UNIT_MOVE: + case ACTRES_SPY_ESCAPE: /* Non ruleset defined action min range not supported here */ fc_assert_msg(FALSE, "Probably wrong value."); return RS_DEFAULT_ACTION_MIN_RANGE; @@ -8222,6 +8251,7 @@ const char *action_max_range_ruleset_var_name(int act) case ACTION_UNIT_MOVE: case ACTION_UNIT_MOVE2: case ACTION_UNIT_MOVE3: + case ACTION_SPY_ESCAPE: /* Max range is not ruleset changeable */ return NULL; case ACTION_HELP_WONDER: @@ -8320,6 +8350,7 @@ int action_max_range_default(enum action_result result) case ACTRES_HUT_ENTER: case ACTRES_HUT_FRIGHTEN: case ACTRES_UNIT_MOVE: + case ACTRES_SPY_ESCAPE: /* Non ruleset defined action max range not supported here */ fc_assert_msg(FALSE, "Probably wrong value."); return RS_DEFAULT_ACTION_MAX_RANGE; @@ -8452,6 +8483,7 @@ const char *action_target_kind_ruleset_var_name(int act) case ACTION_UNIT_MOVE: case ACTION_UNIT_MOVE2: case ACTION_UNIT_MOVE3: + case ACTION_SPY_ESCAPE: /* Target kind is not ruleset changeable */ return NULL; case ACTION_NUKE: @@ -8509,6 +8541,7 @@ action_target_kind_default(enum action_result result) case ACTRES_STRIKE_PRODUCTION: case ACTRES_CONQUER_CITY: case ACTRES_SPY_SPREAD_PLAGUE: + case ACTRES_SPY_ESCAPE: return ATK_CITY; case ACTRES_SPY_BRIBE_UNIT: case ACTRES_SPY_SABOTAGE_UNIT: @@ -8603,6 +8636,7 @@ bool action_result_legal_target_kind(enum action_result result, case ACTRES_STRIKE_PRODUCTION: case ACTRES_CONQUER_CITY: case ACTRES_SPY_SPREAD_PLAGUE: + case ACTRES_SPY_ESCAPE: return tgt_kind == ATK_CITY; case ACTRES_SPY_BRIBE_UNIT: case ACTRES_SPY_SABOTAGE_UNIT: @@ -8735,6 +8769,7 @@ action_sub_target_kind_default(enum action_result result) case ACTRES_HUT_ENTER: case ACTRES_HUT_FRIGHTEN: case ACTRES_UNIT_MOVE: + case ACTRES_SPY_ESCAPE: return ASTK_NONE; case ACTRES_PILLAGE: case ACTRES_CLEAN_POLLUTION: @@ -8827,6 +8862,7 @@ action_target_compl_calc(enum action_result result, case ACTRES_HUT_ENTER: case ACTRES_HUT_FRIGHTEN: case ACTRES_UNIT_MOVE: + case ACTRES_SPY_ESCAPE: return ACT_TGT_COMPL_SIMPLE; case ACTRES_PILLAGE: case ACTRES_CLEAN_POLLUTION: @@ -8961,6 +8997,7 @@ const char *action_actor_consuming_always_ruleset_var_name(action_id act) case ACTION_UNIT_MOVE: case ACTION_UNIT_MOVE2: case ACTION_UNIT_MOVE3: + case ACTION_SPY_ESCAPE: /* actor consuming always is not ruleset changeable */ return NULL; case ACTION_NUKE: @@ -9033,6 +9070,7 @@ const char *action_blocked_by_ruleset_var_name(const struct action *act) return "move_2_is_blocked_by"; case ACTION_UNIT_MOVE3: return "move_3_is_blocked_by"; + case ACTION_SPY_ESCAPE: case ACTION_SPY_POISON: case ACTION_SPY_POISON_ESC: case ACTION_SPY_SABOTAGE_UNIT: @@ -9256,6 +9294,7 @@ action_post_success_forced_ruleset_var_name(const struct action *act) case ACTION_UNIT_MOVE: case ACTION_UNIT_MOVE2: case ACTION_UNIT_MOVE3: + case ACTION_SPY_ESCAPE: case ACTION_USER_ACTION1: case ACTION_USER_ACTION2: case ACTION_USER_ACTION3: diff --git a/common/actions.h b/common/actions.h index 1b6ce10827..c4245fc8ba 100644 --- a/common/actions.h +++ b/common/actions.h @@ -278,6 +278,8 @@ extern "C" { #define SPECENUM_VALUE105NAME "Wipe Units" #define SPECENUM_VALUE106 ACTION_BOMBARD_LETHAL #define SPECENUM_VALUE106NAME "Bombard Lethal" +#define SPECENUM_VALUE107 ACTION_SPY_ESCAPE +#define SPECENUM_VALUE107NAME "Spy Escape" #define SPECENUM_BITVECTOR bv_actions #define SPECENUM_COUNT ACTION_COUNT #include "specenum_gen.h" diff --git a/common/fc_types.h b/common/fc_types.h index 363f19afb7..79be199602 100644 --- a/common/fc_types.h +++ b/common/fc_types.h @@ -297,6 +297,8 @@ enum output_type_id { #define SPECENUM_VALUE60NAME "Unit Make Homeless" #define SPECENUM_VALUE61 ACTRES_WIPE_UNITS #define SPECENUM_VALUE61NAME "Wipe Units" +#define SPECENUM_VALUE62 ACTRES_SPY_ESCAPE +#define SPECENUM_VALUE62NAME "Unit Spy Escape" /* All consequences are handled as (ruleset) action data. */ #define SPECENUM_COUNT ACTRES_NONE #include "specenum_gen.h" diff --git a/common/unittype.c b/common/unittype.c index dddd896805..2bd1533c2e 100644 --- a/common/unittype.c +++ b/common/unittype.c @@ -369,6 +369,7 @@ static bool action_is_hostile(action_id act_id) case ACTRES_HUT_ENTER: case ACTRES_HUT_FRIGHTEN: case ACTRES_UNIT_MOVE: + case ACTRES_SPY_ESCAPE: return FALSE; case ACTRES_NONE: /* Assume they are up to something. */ diff --git a/doc/README.actions b/doc/README.actions index ed9ded7f74..b9d5630c33 100644 --- a/doc/README.actions +++ b/doc/README.actions @@ -408,6 +408,13 @@ Actions done by a unit against a city * actor must be on the same tile as the target or on the tile next to it. * target must be foreign. (!) +"Spy Escape" - Just escape without doing anything else first. + * UI name can be set using ui_name_escape + * actor must be aware that the target exists + * actor must be on the same tile as the target or on the tile next to it. + * the actor player must have a city to escape to. + * target must be foreign. (!) + "Suitcase Nuke" - Cause a nuclear explosion in the target city. * UI name can be set using ui_name_suitcase_nuke * spends the actor unit diff --git a/server/advisors/advdata.c b/server/advisors/advdata.c index 4ceba4f556..d56b05283e 100644 --- a/server/advisors/advdata.c +++ b/server/advisors/advdata.c @@ -882,6 +882,7 @@ adv_want adv_gov_action_immunity_want(struct government *gov) case ACTRES_PARADROP: case ACTRES_PARADROP_CONQUER: case ACTRES_FORTIFY: + case ACTRES_SPY_ESCAPE: /* Wants the ability to do this to it self. Don't want others * to target it. Do nothing since action_immune_government() * doesn't separate based on who the actor is. */ diff --git a/server/diplomats.c b/server/diplomats.c index 3cc84306c5..b1a405e4e1 100644 --- a/server/diplomats.c +++ b/server/diplomats.c @@ -2161,9 +2161,9 @@ static bool diplomat_infiltrate_tile(struct player *pplayer, - Escapes to home city. - Escapee may become a veteran. ****************************************************************************/ -static void diplomat_escape(struct player *pplayer, struct unit *pdiplomat, - const struct city *pcity, - const struct action *paction) +void diplomat_escape(struct player *pplayer, struct unit *pdiplomat, + const struct city *pcity, + const struct action *paction) { struct tile *ptile; const char *vlink; @@ -2263,6 +2263,42 @@ static void diplomat_escape_full(struct player *pplayer, } } +/************************************************************************//** + Attempt to escape without doing anything else first. + + May be captured and executed, or escape to the nearest domeestic city. + + Returns TRUE iff action could be done, FALSE if it couldn't. Even if + this returns TRUE, unit may have died during the action. +****************************************************************************/ +bool spy_escape(struct player *pplayer, + struct unit *actor_unit, + struct city *target_city, + struct tile *target_tile, + const struct action *paction) +{ + const char *vlink; + const struct unit_type *act_utype = unit_type_get(actor_unit); + + if (target_city != NULL) { + fc_assert(city_tile(target_city) == target_tile); + vlink = city_link(target_city); + } else { + vlink = tile_link(target_tile); + } + + /* this may cause a diplomatic incident */ + action_consequence_success(paction, pplayer, act_utype, + tile_owner(target_tile), + target_tile, vlink); + + /* Try to escape. */ + diplomat_escape_full(pplayer, actor_unit, target_city != NULL, + target_tile, vlink, paction); + + return TRUE; +} + /************************************************************************//** Return number of diplomats on this square. AJS 20000130 ****************************************************************************/ diff --git a/server/diplomats.h b/server/diplomats.h index d50dbd4ce8..30e81a030a 100644 --- a/server/diplomats.h +++ b/server/diplomats.h @@ -53,6 +53,11 @@ bool spy_steal_some_maps(struct player *act_player, struct unit *act_unit, const struct action *paction); bool spy_nuke_city(struct player *act_player, struct unit *act_unit, struct city *tgt_city, const struct action *paction); +bool spy_escape(struct player *pplayer, + struct unit *actor_unit, + struct city *target_city, + struct tile *target_tile, + const struct action *paction); int count_diplomats_on_tile(struct tile *ptile); diff --git a/server/unithand.c b/server/unithand.c index 84523c9c90..92dec39535 100644 --- a/server/unithand.c +++ b/server/unithand.c @@ -1000,6 +1000,7 @@ static struct player *need_war_player_hlp(const struct unit *actor, case ACTRES_HUT_ENTER: case ACTRES_HUT_FRIGHTEN: case ACTRES_UNIT_MOVE: + case ACTRES_SPY_ESCAPE: case ACTRES_NONE: /* No special help. */ break; @@ -3483,6 +3484,11 @@ bool unit_perform_action(struct player *pplayer, ACTION_STARTED_UNIT_SELF(action_type, actor_unit, do_unit_make_homeless(actor_unit, paction)); break; + case ACTRES_SPY_ESCAPE: + ACTION_STARTED_UNIT_CITY(action_type, actor_unit, pcity, + spy_escape(pplayer, actor_unit, + pcity, target_tile, paction)); + break; case ACTRES_SPY_SABOTAGE_CITY: /* Difference is caused by data in the action structure. */ ACTION_STARTED_UNIT_CITY(action_type, actor_unit, pcity, -- 2.30.2