From b00642e7e9e4eea5a3137eac76c1fd4a068ffee2 Mon Sep 17 00:00:00 2001 From: Sveinung Kvilhaugsvik Date: Fri, 19 Feb 2021 09:37:43 +0100 Subject: [PATCH 01/12] Action sub results. Add the field sub_results to the action struct. It is for side effects that happens on top of the action result. Add "Unit Enter Hut" and "Unit Frighten Hut" as the first action sub results. Move the parts of the corresponding action results that can be moved to the action sub results. See osdn ##41610 --- client/packhand.c | 1 + common/actions.c | 62 +++++++++++++++++++---------------- common/actions.h | 15 ++++++++- common/networking/packets.def | 2 ++ common/unittype.c | 2 +- fc_version | 2 +- server/rssanity.c | 8 +++++ server/ruleset.c | 9 +++++ server/unithand.c | 4 +-- server/unittools.c | 2 ++ 10 files changed, 74 insertions(+), 33 deletions(-) diff --git a/client/packhand.c b/client/packhand.c index 2cdf079c63..fd3dca36be 100644 --- a/client/packhand.c +++ b/client/packhand.c @@ -4156,6 +4156,7 @@ void handle_ruleset_action(const struct packet_ruleset_action *p) act->quiet = p->quiet; act->result = p->result; + act->sub_results = p->sub_results; act->actor_consuming_always = p->actor_consuming_always; act->actor_kind = p->act_kind; diff --git a/common/actions.c b/common/actions.c index c51ca40737..75fbf57b97 100644 --- a/common/actions.c +++ b/common/actions.c @@ -1339,6 +1339,8 @@ static struct action *action_new(action_id id, action->id = id; action->result = result; + /* Not set here */ + BV_CLR_ALL(action->sub_results); action->actor_kind = AAK_UNIT; action->target_kind = action_target_kind_default(result); @@ -2587,7 +2589,7 @@ action_enabler_suggest_improvement(const struct action_enabler *enabler) bool has_user = FALSE; unit_type_iterate(pactor) { - if (action_actor_utype_hard_reqs_ok(paction->result, pactor) + if (action_actor_utype_hard_reqs_ok(paction, pactor) && requirement_fulfilled_by_unit_type(pactor, &(enabler->actor_reqs))) { has_user = TRUE; @@ -2951,8 +2953,8 @@ struct action *action_is_blocked_by(const struct action *act, } /**********************************************************************//** - Returns TRUE if the specified unit type can perform an action with the - wanted result given that an action enabler later will enable it. + Returns TRUE if the specified unit type can perform the specified action + given that an action enabler later will enable it. This is done by checking the action result's hard requirements. Hard requirements must be TRUE before an action can be done. The reason why @@ -2971,11 +2973,25 @@ struct action *action_is_blocked_by(const struct action *act, given that an action enabler later will enable it. **************************************************************************/ static bool -action_actor_utype_hard_reqs_ok_full(enum action_result result, +action_actor_utype_hard_reqs_ok_full(const struct action *paction, const struct unit_type *actor_unittype, bool ignore_third_party) { - switch (result) { + if (BV_ISSET(paction->sub_results, ACT_SUB_RES_HUT_ENTER)) { + if (utype_class(actor_unittype)->hut_behavior != HUT_NORMAL) { + /* Reason: Keep hut_behavior working. */ + return FALSE; + } + } + + if (BV_ISSET(paction->sub_results, ACT_SUB_RES_HUT_FRIGHTEN)) { + if (utype_class(actor_unittype)->hut_behavior != HUT_FRIGHTEN) { + /* Reason: Keep hut_behavior working. */ + return FALSE; + } + } + + switch (paction->result) { case ACTRES_JOIN_CITY: if (actor_unittype->pop_cost <= 0) { /* Reason: Must have population to add. */ @@ -3072,20 +3088,6 @@ action_actor_utype_hard_reqs_ok_full(enum action_result result, } break; - case ACTRES_HUT_ENTER: - if (utype_class(actor_unittype)->hut_behavior != HUT_NORMAL) { - /* Reason: Keep hut_behavior working. */ - return FALSE; - } - break; - - case ACTRES_HUT_FRIGHTEN: - if (utype_class(actor_unittype)->hut_behavior != HUT_FRIGHTEN) { - /* Reason: Keep hut_behavior working. */ - return FALSE; - } - break; - case ACTRES_PARADROP: if (actor_unittype->paratroopers_range <= 0) { /* Reason: Can't pardrop 0 tiles. */ @@ -3093,6 +3095,8 @@ action_actor_utype_hard_reqs_ok_full(enum action_result result, } break; + case ACTRES_HUT_ENTER: + case ACTRES_HUT_FRIGHTEN: case ACTRES_ESTABLISH_EMBASSY: case ACTRES_SPY_INVESTIGATE_CITY: case ACTRES_SPY_POISON: @@ -3147,8 +3151,8 @@ action_actor_utype_hard_reqs_ok_full(enum action_result result, } /**********************************************************************//** - Returns TRUE if the specified unit type can perform an action with the - wanted result given that an action enabler later will enable it. + Returns TRUE if the specified unit type can perform the specified action + given that an action enabler later will enable it. This is done by checking the action result's hard requirements. Hard requirements must be TRUE before an action can be done. The reason why @@ -3163,11 +3167,11 @@ action_actor_utype_hard_reqs_ok_full(enum action_result result, given that an action enabler later will enable it. **************************************************************************/ bool -action_actor_utype_hard_reqs_ok(enum action_result result, +action_actor_utype_hard_reqs_ok(const struct action *paction, const struct unit_type *actor_unittype) { - return action_actor_utype_hard_reqs_ok_full(result, actor_unittype, - FALSE); + return action_actor_utype_hard_reqs_ok_full(paction, + actor_unittype, FALSE); } /**********************************************************************//** @@ -3179,7 +3183,7 @@ action_actor_utype_hard_reqs_ok(enum action_result result, omniscient. **************************************************************************/ static enum fc_tristate -action_hard_reqs_actor(enum action_result result, +action_hard_reqs_actor(const struct action *paction, const struct player *actor_player, const struct city *actor_city, const struct impr_type *actor_building, @@ -3191,7 +3195,9 @@ action_hard_reqs_actor(enum action_result result, const bool omniscient, const struct city *homecity) { - if (!action_actor_utype_hard_reqs_ok_full(result, actor_unittype, TRUE)) { + enum action_result result = paction->result; + + if (!action_actor_utype_hard_reqs_ok_full(paction, actor_unittype, TRUE)) { /* Info leak: The actor player knows the type of his unit. */ /* The actor unit type can't perform the action because of hard * unit type requirements. */ @@ -3460,7 +3466,7 @@ is_action_possible(const action_id wanted_action, } /* Actor specific hard requirements. */ - out = action_hard_reqs_actor(paction->result, + out = action_hard_reqs_actor(paction, actor_player, actor_city, actor_building, actor_tile, actor_unit, actor_unittype, actor_output, actor_specialist, @@ -6604,7 +6610,7 @@ bool action_maybe_possible_actor_unit(const action_id act_id, return FALSE; } - result = action_hard_reqs_actor(paction->result, + result = action_hard_reqs_actor(paction, actor_player, actor_city, NULL, actor_tile, actor_unit, actor_unittype, NULL, NULL, FALSE, diff --git a/common/actions.h b/common/actions.h index eb97c16774..6bf738b69e 100644 --- a/common/actions.h +++ b/common/actions.h @@ -276,6 +276,18 @@ extern "C" { #define MAX_NUM_ACTIONS ACTION_COUNT #define NUM_ACTIONS MAX_NUM_ACTIONS +/* Used in the network protocol. */ +#define SPECENUM_NAME action_sub_result +/* Will enter each enterable hut at the target tile */ +#define SPECENUM_VALUE0 ACT_SUB_RES_HUT_ENTER +#define SPECENUM_VALUE0NAME "Unit Enter Hut" +/* Will frighten each frightenable hut at the target tile */ +#define SPECENUM_VALUE1 ACT_SUB_RES_HUT_FRIGHTEN +#define SPECENUM_VALUE1NAME "Unit Frighten Hut" +#define SPECENUM_BITVECTOR bv_action_sub_results +#define SPECENUM_COUNT ACT_SUB_RES_COUNT +#include "specenum_gen.h" + /* A battle is against a defender that tries to stop the action where the * defender is in danger. A dice roll without a defender risking anything, * like the roll controlled by EFT_ACTION_ODDS_PCT, isn't a battle. */ @@ -360,6 +372,7 @@ struct action action_id id; enum action_result result; + bv_action_sub_results sub_results; enum action_actor_kind actor_kind; enum action_target_kind target_kind; @@ -865,7 +878,7 @@ int action_dice_roll_odds(const struct player *act_player, const struct action *paction); bool -action_actor_utype_hard_reqs_ok(enum action_result result, +action_actor_utype_hard_reqs_ok(const struct action *result, const struct unit_type *actor_unittype); /* Reasoning about actions */ diff --git a/common/networking/packets.def b/common/networking/packets.def index 0e2b34cf20..3a87fc1bed 100644 --- a/common/networking/packets.def +++ b/common/networking/packets.def @@ -268,6 +268,7 @@ type CAPITAL = uint8(enum capital_type) # typedefs for bit vectors type BV_ACTIONS = bitvector(bv_actions) +type BV_ACTION_SUB_RES = bitvector(bv_action_sub_results) type BV_EXTRA_FLAGS = bitvector(bv_extra_flags) type BV_GOODS_FLAGS = bitvector(bv_goods_flags) type BV_EXTRAS = bitvector(bv_extras) @@ -1823,6 +1824,7 @@ PACKET_RULESET_ACTION = 246; sc, lsend BOOL quiet; ACTION_RESULT result; + BV_ACTION_SUB_RES sub_results; BOOL actor_consuming_always; ACTOR_KIND act_kind; diff --git a/common/unittype.c b/common/unittype.c index 20a9b947f2..50a882a198 100644 --- a/common/unittype.c +++ b/common/unittype.c @@ -407,7 +407,7 @@ static void unit_can_act_cache_set(struct unit_type *putype) action_enablers_iterate(enabler) { const struct action *paction = action_by_number(enabler->action); if (action_id_get_actor_kind(enabler->action) == AAK_UNIT - && action_actor_utype_hard_reqs_ok(paction->result, putype) + && action_actor_utype_hard_reqs_ok(paction, putype) && requirement_fulfilled_by_unit_type(putype, &(enabler->actor_reqs))) { log_debug("act_cache: %s can %s", diff --git a/fc_version b/fc_version index 17ac8a312b..2cd9459ab7 100755 --- a/fc_version +++ b/fc_version @@ -56,7 +56,7 @@ DEFAULT_FOLLOW_TAG=S3_1 # - No new mandatory capabilities can be added to the release branch; doing # so would break network capability of supposedly "compatible" releases. # -NETWORK_CAPSTRING="+Freeciv.Devel-3.1-2021.Feb.20b" +NETWORK_CAPSTRING="+Freeciv.Devel-3.1-2021.Feb.21" FREECIV_DISTRIBUTOR="" diff --git a/server/rssanity.c b/server/rssanity.c index 6ca180b47c..a5f4744287 100644 --- a/server/rssanity.c +++ b/server/rssanity.c @@ -1188,6 +1188,14 @@ bool sanity_check_ruleset_data(bool ignore_retired) action_rule_name(paction)); ok = FALSE; } + + if (BV_ISSET(paction->sub_results, ACT_SUB_RES_HUT_ENTER) + && BV_ISSET(paction->sub_results, ACT_SUB_RES_HUT_FRIGHTEN)) { + ruleset_error(LOG_ERROR, + "%s both enters and frightens a hut at the same time.", + action_rule_name(paction)); + ok = FALSE; + } } action_iterate_end; /* Auto attack */ diff --git a/server/ruleset.c b/server/ruleset.c index 7c329a82a5..e3aaa4edec 100644 --- a/server/ruleset.c +++ b/server/ruleset.c @@ -6799,6 +6799,14 @@ static bool load_ruleset_game(struct section_file *file, bool act, free(quiet_actions); } + + /* Hard code action sub results for now. */ + action_by_result_iterate(paction, act_id, ACTRES_HUT_ENTER) { + BV_SET(paction->sub_results, ACT_SUB_RES_HUT_ENTER); + } action_by_result_iterate_end; + action_by_result_iterate(paction, act_id, ACTRES_HUT_FRIGHTEN) { + BV_SET(paction->sub_results, ACT_SUB_RES_HUT_FRIGHTEN); + } action_by_result_iterate_end; } if (ok) { @@ -8170,6 +8178,7 @@ static void send_ruleset_actions(struct conn_list *dest) packet.quiet = action_by_number(act)->quiet; packet.result = paction->result; + packet.sub_results = paction->sub_results; packet.actor_consuming_always = paction->actor_consuming_always; packet.act_kind = action_by_number(act)->actor_kind; diff --git a/server/unithand.c b/server/unithand.c index a5c385dcf0..53e1fdc6b5 100644 --- a/server/unithand.c +++ b/server/unithand.c @@ -1430,7 +1430,7 @@ static struct ane_expl *expl_act_not_enabl(struct unit *punit, explnat->distance = paction->min_distance; } else if (target_city && (action_has_result_safe(paction, ACTRES_JOIN_CITY) - && action_actor_utype_hard_reqs_ok(ACTRES_JOIN_CITY, + && action_actor_utype_hard_reqs_ok(paction, unit_type_get(punit)) && (city_size_get(target_city) + unit_pop_value(punit) > game.info.add_to_size_limit))) { @@ -1439,7 +1439,7 @@ static struct ane_expl *expl_act_not_enabl(struct unit *punit, explnat->kind = ANEK_CITY_TOO_BIG; } else if (target_city && (action_has_result_safe(paction, ACTRES_JOIN_CITY) - && action_actor_utype_hard_reqs_ok(ACTRES_JOIN_CITY, + && action_actor_utype_hard_reqs_ok(paction, unit_type_get(punit)) && (!city_can_grow_to(target_city, city_size_get(target_city) diff --git a/server/unittools.c b/server/unittools.c index fc025f7f74..292668dd97 100644 --- a/server/unittools.c +++ b/server/unittools.c @@ -2928,6 +2928,8 @@ bool do_paradrop(struct unit *punit, struct tile *ptile, (extra_owner(ptile) == NULL || pplayers_at_war(extra_owner(ptile), unit_owner(punit))) && tile_has_claimable_base(ptile, unit_type_get(punit)), + /* TODO: Split "Paradrop Unit" so hut entry / frightening + * action sub results can be set for it. */ unit_class_get(punit)->hut_behavior != HUT_NOTHING)) { /* Ensure we finished on valid state. */ fc_assert(can_unit_exist_at_tile(&(wld.map), punit, unit_tile(punit)) -- 2.20.1