From 8189e37e5ae6919f3bddbcb7ee12708e4d9080f1 Mon Sep 17 00:00:00 2001 From: Sveinung Kvilhaugsvik Date: Fri, 12 Mar 2021 21:36:02 +0100 Subject: [PATCH 15/15] New action "Unit Make Homeless". See osdn #41622 --- ai/default/daicity.c | 1 + ai/default/daidiplomacy.c | 1 + client/gui-qt/dialogs.cpp | 16 +++++++++++++++ client/packhand.c | 1 + common/actions.c | 40 +++++++++++++++++++++++++++++++++++++ common/actions.h | 2 ++ common/fc_types.h | 2 ++ common/unittype.c | 1 + doc/README.actions | 4 ++++ server/advisors/advdata.c | 1 + server/unithand.c | 42 ++++++++++++++++++++++++++++++--------- 11 files changed, 102 insertions(+), 9 deletions(-) diff --git a/ai/default/daicity.c b/ai/default/daicity.c index bf16eebd96..1f1641e02a 100644 --- a/ai/default/daicity.c +++ b/ai/default/daicity.c @@ -1304,6 +1304,7 @@ static int action_target_neg_util(action_id act_id, case ACTRES_HUT_ENTER: case ACTRES_HUT_FRIGHTEN: case ACTRES_UNIT_MOVE: + case ACTRES_HOMELESS: fc_assert_msg(action_id_get_target_kind(act_id) == ATK_CITY, "Action not aimed at cities"); } diff --git a/ai/default/daidiplomacy.c b/ai/default/daidiplomacy.c index ca4b93698a..11cdfa5f6d 100644 --- a/ai/default/daidiplomacy.c +++ b/ai/default/daidiplomacy.c @@ -1985,6 +1985,7 @@ void dai_incident(struct ai_type *ait, enum incident_type type, case ACTRES_RECYCLE_UNIT: case ACTRES_DISBAND_UNIT: case ACTRES_HOME_CITY: + case ACTRES_HOMELESS: case ACTRES_UPGRADE_UNIT: case ACTRES_PARADROP: case ACTRES_PARADROP_CONQUER: /* TODO: bigger incident */ diff --git a/client/gui-qt/dialogs.cpp b/client/gui-qt/dialogs.cpp index 5a7e38f764..843ef3cf06 100644 --- a/client/gui-qt/dialogs.cpp +++ b/client/gui-qt/dialogs.cpp @@ -145,6 +145,7 @@ static void regular_move(QVariant data1, QVariant data2); static void convert_unit(QVariant data1, QVariant data2); static void fortify(QVariant data1, QVariant data2); static void disband_unit(QVariant data1, QVariant data2); +static void homeless(QVariant data1, QVariant data2); static void join_city(QVariant data1, QVariant data2); static void unit_home_city(QVariant data1, QVariant data2); static void unit_upgrade(QVariant data1, QVariant data2); @@ -288,6 +289,7 @@ static const QHash af_map_init(void) action_function[ACTION_DISBAND_UNIT] = disband_unit; action_function[ACTION_FORTIFY] = fortify; action_function[ACTION_CONVERT] = convert_unit; + action_function[ACTION_HOMELESS] = homeless; return action_function; } @@ -2366,6 +2368,20 @@ static void convert_unit(QVariant data1, QVariant data2) target_id, 0, ""); } +/***********************************************************************//** + Action "Unit Make Homeless" for choice dialog +***************************************************************************/ +static void homeless(QVariant data1, QVariant data2) +{ + int actor_id = data1.toInt(); + int target_id = data2.toInt(); + + if (NULL != game_unit_by_number(actor_id)) { + request_do_action(ACTION_HOMELESS, actor_id, + target_id, 0, ""); + } +} + /***********************************************************************//** Action bribe unit for choice dialog ***************************************************************************/ diff --git a/client/packhand.c b/client/packhand.c index d0c7c31a76..66b5749c1b 100644 --- a/client/packhand.c +++ b/client/packhand.c @@ -4840,6 +4840,7 @@ static action_id auto_attack_act(const struct act_prob *act_probs) case ACTION_EXPEL_UNIT: case ACTION_RECYCLE_UNIT: case ACTION_HOME_CITY: + case ACTION_HOMELESS: case ACTION_UPGRADE_UNIT: case ACTION_PARADROP: case ACTION_PARADROP_CONQUER: diff --git a/common/actions.c b/common/actions.c index c045ed83db..46da2e283f 100644 --- a/common/actions.c +++ b/common/actions.c @@ -654,6 +654,15 @@ static void hard_code_oblig_hard_reqs(void) ACTRES_AIRLIFT, ACTRES_NONE); + /* Why this is a hard requirement: sanity. */ + oblig_hard_req_register(req_from_values(VUT_UNITSTATE, REQ_RANGE_LOCAL, + FALSE, FALSE, FALSE, + USP_HAS_HOME_CITY), + FALSE, + N_("All action enablers for %s must require" + " that the actor has a home city."), + ACTRES_HOMELESS, ACTRES_NONE); + /* Why this is a hard requirement: Assumed in the code. */ oblig_hard_req_register(req_from_values(VUT_UNITSTATE, REQ_RANGE_LOCAL, FALSE, TRUE, TRUE, @@ -1017,6 +1026,10 @@ static void hard_code_actions(void) /* Illegal to perform to a target on another tile to * keep the rules exactly as they were for now. */ MAK_STAYS, 0, 0, FALSE); + actions[ACTION_HOMELESS] = + unit_action_new(ACTION_HOMELESS, ACTRES_HOMELESS, + TRUE, FALSE, + MAK_STAYS, 0, 0, FALSE); actions[ACTION_UPGRADE_UNIT] = unit_action_new(ACTION_UPGRADE_UNIT, ACTRES_UPGRADE_UNIT, TRUE, TRUE, MAK_STAYS, @@ -2116,6 +2129,7 @@ bool action_creates_extra(const struct action *paction, case ACTRES_RECYCLE_UNIT: case ACTRES_DISBAND_UNIT: case ACTRES_HOME_CITY: + case ACTRES_HOMELESS: case ACTRES_UPGRADE_UNIT: case ACTRES_PARADROP: case ACTRES_PARADROP_CONQUER: @@ -2200,6 +2214,7 @@ bool action_removes_extra(const struct action *paction, case ACTRES_RECYCLE_UNIT: case ACTRES_DISBAND_UNIT: case ACTRES_HOME_CITY: + case ACTRES_HOMELESS: case ACTRES_UPGRADE_UNIT: case ACTRES_PARADROP: case ACTRES_PARADROP_CONQUER: @@ -3247,6 +3262,7 @@ action_actor_utype_hard_reqs_ok_full(const struct action *paction, case ACTRES_RECYCLE_UNIT: case ACTRES_DISBAND_UNIT: case ACTRES_HOME_CITY: + case ACTRES_HOMELESS: case ACTRES_AIRLIFT: case ACTRES_STRIKE_BUILDING: case ACTRES_STRIKE_PRODUCTION: @@ -3399,6 +3415,7 @@ action_hard_reqs_actor(const struct action *paction, } break; + case ACTRES_HOMELESS: case ACTRES_UNIT_MOVE: case ACTRES_ESTABLISH_EMBASSY: case ACTRES_SPY_INVESTIGATE_CITY: @@ -4370,6 +4387,7 @@ is_action_possible(const action_id wanted_action, case ACTRES_STRIKE_BUILDING: case ACTRES_STRIKE_PRODUCTION: case ACTRES_FORTIFY: + case ACTRES_HOMELESS: case ACTRES_NONE: /* No known hard coded requirements. */ break; @@ -5384,6 +5402,10 @@ action_prob(const action_id wanted_action, /* No battle is fought first. */ chance = ACTPROB_CERTAIN; break; + case ACTRES_HOMELESS: + /* No battle is fought first. */ + chance = ACTPROB_CERTAIN; + break; case ACTRES_UPGRADE_UNIT: /* No battle is fought first. */ chance = ACTPROB_CERTAIN; @@ -6611,6 +6633,7 @@ int action_dice_roll_initial_odds(const struct action *paction) case ACTRES_RECYCLE_UNIT: case ACTRES_DISBAND_UNIT: case ACTRES_HOME_CITY: + case ACTRES_HOMELESS: case ACTRES_UPGRADE_UNIT: case ACTRES_PARADROP: case ACTRES_PARADROP_CONQUER: @@ -7119,6 +7142,8 @@ const char *action_ui_name_ruleset_var_name(int act) return "ui_name_disband_unit"; case ACTION_HOME_CITY: return "ui_name_home_city"; + case ACTION_HOMELESS: + return "ui_name_homeless"; case ACTION_UPGRADE_UNIT: return "ui_name_upgrade_unit"; case ACTION_PARADROP: @@ -7387,6 +7412,9 @@ const char *action_ui_name_default(int act) case ACTION_HOME_CITY: /* TRANS: Set _Home City (100% chance of success). */ return N_("Set %sHome City%s"); + case ACTION_HOMELESS: + /* TRANS: Make _Homeless (100% chance of success). */ + return N_("Make %sHomeless%s"); case ACTION_UPGRADE_UNIT: /* TRANS: _Upgrade Unit (100% chance of success). */ return N_("%sUpgrade Unit%s"); @@ -7583,6 +7611,7 @@ const char *action_min_range_ruleset_var_name(int act) case ACTION_RECYCLE_UNIT: case ACTION_DISBAND_UNIT: case ACTION_HOME_CITY: + case ACTION_HOMELESS: case ACTION_UPGRADE_UNIT: case ACTION_PARADROP: case ACTION_PARADROP_CONQUER: @@ -7696,6 +7725,7 @@ int action_min_range_default(enum action_result result) case ACTRES_RECYCLE_UNIT: case ACTRES_DISBAND_UNIT: case ACTRES_HOME_CITY: + case ACTRES_HOMELESS: case ACTRES_UPGRADE_UNIT: case ACTRES_PARADROP: case ACTRES_PARADROP_CONQUER: @@ -7788,6 +7818,7 @@ const char *action_max_range_ruleset_var_name(int act) case ACTION_DESTROY_CITY: case ACTION_DISBAND_UNIT: case ACTION_HOME_CITY: + case ACTION_HOMELESS: case ACTION_UPGRADE_UNIT: case ACTION_PARADROP: case ACTION_PARADROP_CONQUER: @@ -7906,6 +7937,7 @@ int action_max_range_default(enum action_result result) case ACTRES_EXPEL_UNIT: case ACTRES_DISBAND_UNIT: case ACTRES_HOME_CITY: + case ACTRES_HOMELESS: case ACTRES_UPGRADE_UNIT: case ACTRES_PARADROP: case ACTRES_PARADROP_CONQUER: @@ -8008,6 +8040,7 @@ const char *action_target_kind_ruleset_var_name(int act) case ACTION_RECYCLE_UNIT: case ACTION_DISBAND_UNIT: case ACTION_HOME_CITY: + case ACTION_HOMELESS: case ACTION_UPGRADE_UNIT: case ACTION_PARADROP: case ACTION_PARADROP_CONQUER: @@ -8168,6 +8201,7 @@ action_target_kind_default(enum action_result result) case ACTRES_DISBAND_UNIT: case ACTRES_CONVERT: case ACTRES_FORTIFY: + case ACTRES_HOMELESS: return ATK_SELF; case ACTRES_NONE: return RS_DEFAULT_USER_ACTION_TARGET_KIND; @@ -8253,6 +8287,7 @@ bool action_result_legal_target_kind(enum action_result result, case ACTRES_DISBAND_UNIT: case ACTRES_CONVERT: case ACTRES_FORTIFY: + case ACTRES_HOMELESS: return tgt_kind == ATK_SELF; case ACTRES_PILLAGE: return (tgt_kind == ATK_TILE || tgt_kind == ATK_EXTRAS); @@ -8306,6 +8341,7 @@ action_sub_target_kind_default(enum action_result result) case ACTRES_DESTROY_CITY: case ACTRES_RECYCLE_UNIT: case ACTRES_HOME_CITY: + case ACTRES_HOMELESS: case ACTRES_UPGRADE_UNIT: case ACTRES_AIRLIFT: case ACTRES_STRIKE_PRODUCTION: @@ -8397,6 +8433,7 @@ action_target_compl_calc(enum action_result result, case ACTRES_DESTROY_CITY: case ACTRES_RECYCLE_UNIT: case ACTRES_HOME_CITY: + case ACTRES_HOMELESS: case ACTRES_UPGRADE_UNIT: case ACTRES_AIRLIFT: case ACTRES_STRIKE_PRODUCTION: @@ -8505,6 +8542,7 @@ const char *action_actor_consuming_always_ruleset_var_name(action_id act) case ACTION_RECYCLE_UNIT: case ACTION_DISBAND_UNIT: case ACTION_HOME_CITY: + case ACTION_HOMELESS: case ACTION_UPGRADE_UNIT: case ACTION_PARADROP: case ACTION_PARADROP_CONQUER: @@ -8669,6 +8707,7 @@ const char *action_blocked_by_ruleset_var_name(const struct action *act) case ACTION_RECYCLE_UNIT: case ACTION_DISBAND_UNIT: case ACTION_HOME_CITY: + case ACTION_HOMELESS: case ACTION_UPGRADE_UNIT: case ACTION_PARADROP: case ACTION_PARADROP_CONQUER: @@ -8798,6 +8837,7 @@ action_post_success_forced_ruleset_var_name(const struct action *act) case ACTION_RECYCLE_UNIT: case ACTION_DISBAND_UNIT: case ACTION_HOME_CITY: + case ACTION_HOMELESS: case ACTION_UPGRADE_UNIT: case ACTION_PARADROP: case ACTION_PARADROP_CONQUER: diff --git a/common/actions.h b/common/actions.h index b430925c1c..bd10579e5d 100644 --- a/common/actions.h +++ b/common/actions.h @@ -272,6 +272,8 @@ extern "C" { #define SPECENUM_VALUE102NAME "Paradrop Unit Enter" #define SPECENUM_VALUE103 ACTION_PARADROP_ENTER_CONQUER #define SPECENUM_VALUE103NAME "Paradrop Unit Enter Conquer" +#define SPECENUM_VALUE104 ACTION_HOMELESS +#define SPECENUM_VALUE104NAME "Unit Make Homeless" #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 214ac471cd..7b6345eac7 100644 --- a/common/fc_types.h +++ b/common/fc_types.h @@ -293,6 +293,8 @@ enum output_type_id { #define SPECENUM_VALUE58NAME "Unit Move" #define SPECENUM_VALUE59 ACTRES_PARADROP_CONQUER #define SPECENUM_VALUE59NAME "Unit Paradrop Conquer" +#define SPECENUM_VALUE60 ACTRES_HOMELESS +#define SPECENUM_VALUE60NAME "Unit Make Homeless" /* 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 b06e08f8c4..26d9b36efe 100644 --- a/common/unittype.c +++ b/common/unittype.c @@ -343,6 +343,7 @@ static bool action_is_hostile(action_id act_id) case ACTRES_RECYCLE_UNIT: case ACTRES_DISBAND_UNIT: case ACTRES_HOME_CITY: + case ACTRES_HOMELESS: case ACTRES_UPGRADE_UNIT: case ACTRES_PARADROP: case ACTRES_PARADROP_CONQUER: /* TODO: should this be hostile? */ diff --git a/doc/README.actions b/doc/README.actions index 646a5f6509..1017673cb6 100644 --- a/doc/README.actions +++ b/doc/README.actions @@ -1328,6 +1328,10 @@ Actions done by a unit to it self location. * actor unit's converted form must have room for its current cargo. +"Unit Make Homeless" - unhome a unit + * UI name can be set using ui_name_homeless + * the actor unit must have a home city. (!) + Ruleset defined actions ======================= User actions are "blank". The ruleset does everything they do. The following diff --git a/server/advisors/advdata.c b/server/advisors/advdata.c index f75f274344..b809d3e66d 100644 --- a/server/advisors/advdata.c +++ b/server/advisors/advdata.c @@ -954,6 +954,7 @@ void adv_best_government(struct player *pplayer) case ACTRES_HELP_WONDER: case ACTRES_RECYCLE_UNIT: case ACTRES_HOME_CITY: + case ACTRES_HOMELESS: case ACTRES_UPGRADE_UNIT: case ACTRES_AIRLIFT: case ACTRES_HEAL_UNIT: diff --git a/server/unithand.c b/server/unithand.c index de5efc1830..74e0ff0cbb 100644 --- a/server/unithand.c +++ b/server/unithand.c @@ -751,6 +751,19 @@ static bool do_unit_embark(struct player *act_player, return TRUE; } +/**********************************************************************//** + Deletes a unit's home city making it unhomed. + + Returns TRUE iff the action could be done, FALSE if it couldn't. +**************************************************************************/ +static bool do_unit_make_homeless(struct unit *punit, + const struct action *paction) +{ + unit_change_homecity_handling(punit, NULL, TRUE); + + return punit->homecity == IDENTITY_NUMBER_ZERO; +} + /**********************************************************************//** Returns TRUE iff the player is able to change his diplomatic relationship to the other player to war. @@ -870,6 +883,7 @@ static struct player *need_war_player_hlp(const struct unit *actor, case ACTRES_RECYCLE_UNIT: case ACTRES_DISBAND_UNIT: case ACTRES_HOME_CITY: + case ACTRES_HOMELESS: case ACTRES_UPGRADE_UNIT: case ACTRES_AIRLIFT: case ACTRES_HEAL_UNIT: @@ -3284,6 +3298,10 @@ bool unit_perform_action(struct player *pplayer, ACTION_STARTED_UNIT_SELF(action_type, actor_unit, do_action_activity(actor_unit, paction)); break; + case ACTRES_HOMELESS: + ACTION_STARTED_UNIT_SELF(action_type, actor_unit, + do_unit_make_homeless(actor_unit, 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, @@ -3574,7 +3592,8 @@ void unit_change_homecity_handling(struct unit *punit, struct city *new_pcity, { struct city *old_pcity = game_city_by_number(punit->homecity); struct player *old_owner = unit_owner(punit); - struct player *new_owner = city_owner(new_pcity); + struct player *new_owner = (new_pcity == NULL ? old_owner + : city_owner(new_pcity)); /* Calling this function when new_pcity is same as old_pcity should * be safe with current implementation, but it is not meant to @@ -3627,12 +3646,16 @@ void unit_change_homecity_handling(struct unit *punit, struct city *new_pcity, city_units_upkeep(old_pcity); } - unit_list_prepend(new_pcity->units_supported, punit); + if (new_pcity != NULL) { + unit_list_prepend(new_pcity->units_supported, punit); - /* update unit upkeep */ - city_units_upkeep(new_pcity); + /* update unit upkeep */ + city_units_upkeep(new_pcity); - punit->homecity = new_pcity->id; + punit->homecity = new_pcity->id; + } else { + punit->homecity = IDENTITY_NUMBER_ZERO; + } } if (!can_unit_continue_current_activity(punit)) { @@ -3644,8 +3667,11 @@ void unit_change_homecity_handling(struct unit *punit, struct city *new_pcity, /* Send info to players and observers. */ send_unit_info(NULL, punit); - city_refresh(new_pcity); - send_city_info(new_owner, new_pcity); + if (new_pcity != NULL) { + city_refresh(new_pcity); + send_city_info(new_owner, new_pcity); + fc_assert(unit_owner(punit) == city_owner(new_pcity)); + } if (old_pcity) { fc_assert(city_owner(old_pcity) == old_owner); @@ -3654,8 +3680,6 @@ void unit_change_homecity_handling(struct unit *punit, struct city *new_pcity, } unit_get_goods(punit); - - fc_assert(unit_owner(punit) == city_owner(new_pcity)); } /**********************************************************************//** -- 2.20.1