From 0c20fc972b7b9684505c7aaaf14d6174eacdc0b2 Mon Sep 17 00:00:00 2001 From: Ihnatus Date: Wed, 9 Nov 2022 01:14:38 +0300 Subject: [PATCH] Add "Trade_Revenue_Rel_Pct" effect Scales caravan one-time bonus in simple percentage way. Calculated from caravan owner and home city but relative to the target tile. Intended use: connectivity bonuses like in CivII. See OSDN#45930 Signed-off-by: Ihnatus --- ai/default/daidomestic.c | 71 +++++++++++++++++++++++++++++++++++++++- ai/default/daidomestic.h | 4 +++ ai/default/daieffects.c | 20 +++++++++-- common/effects.h | 18 +++++----- common/improvement.c | 2 +- common/improvement.h | 2 +- common/traderoutes.c | 19 ++++++++++- doc/README.effects | 5 +++ 8 files changed, 126 insertions(+), 15 deletions(-) diff --git a/ai/default/daidomestic.c b/ai/default/daidomestic.c index fd875cdffb..59f2b6e13d 100644 --- a/ai/default/daidomestic.c +++ b/ai/default/daidomestic.c @@ -64,6 +64,40 @@ #include "daidomestic.h" +/**********************************************************************//** + A callback for testing connectivity requirements for expectable trade. + We believe that partner players are basically equal. + For most reqs, data and data_sz are passed to default_tester_cb() +**************************************************************************/ +enum fc_tristate dai_conn_city_cb(const struct req_context *context, + const struct player *other_player, + const struct requirement *req, + void *data, int data_sz) +{ + fc_assert_ret_val(context->city && context->player, TRI_NO); + + if (VUT_IMPROVEMENT == req->source.kind + && REQ_RANGE_TILE == req->range + && req->present) { + const struct impr_type *impr = req->source.value.building; + + if (!is_great_wonder(impr)) { + if (city_has_building(context->city, impr) + || can_player_build_improvement_now(context->player, impr)) { + return TRI_MAYBE; + } else { + return TRI_NO; + } + } else { + /* Too many obstacles may we meet in a way to a single city */ + return great_wonder_owner(impr) == context->player + && !city_has_building(context->city, impr) ? TRI_MAYBE : TRI_NO; + } + } + + return default_tester_cb(context, other_player, req, data, data_sz); +} + /***********************************************************************//** Evaluate the need for units (like caravans) that aid wonder construction. If another city is building wonder and needs help but pplayer is not @@ -190,6 +224,15 @@ static void dai_choose_trade_route(struct ai_type *ait, struct city *pcity, bool dest_city_in_different_cont = FALSE; bool dest_city_in_same_cont = FALSE; bool prefer_different_cont; + int ipctl, ipcth; + struct requirement same_cont ={ + .source = { + .kind = VUT_CITYTILE, + .value = {.citytile = CITYT_SAME_CONTINENT}, + }, + .range = REQ_RANGE_TILE + }; + bool def_cont; int pct = 0; int trader_trait; bool can_move_ic = FALSE, has_boats = TRUE; @@ -362,11 +405,32 @@ static void dai_choose_trade_route(struct ai_type *ait, struct city *pcity, income = (10 + 10) * (1.75 * pcity->surplus[O_TRADE]) / 24; } + /* Put connectivity bonus in scope. */ + /* Look, can we assume will the trade be to other or to same continent */ + def_cont = (dest_city_nat_different_cont || dest_city_in_different_cont) + && (prefer_different_cont || + !(dest_city_in_same_cont || dest_city_nat_same_cont)); + if (def_cont) { + same_cont.present = TRUE; + } else if (dest_city_in_same_cont || dest_city_nat_same_cont) { + def_cont = TRUE; + same_cont.present = FALSE; + } + /* A ruleset may use the Trade_Revenue_Bonus effect to reduce the one * time bonus if no trade route is established. Make sure it gets the * correct action. */ trade_action = utype_can_do_action(unit_type, ACTION_TRADE_ROUTE) ? ACTION_TRADE_ROUTE : ACTION_MARKETPLACE; + get_target_effects_limits(&ipctl, &ipcth, + &(const struct req_context) { + .player = city_owner(pcity), + .city = pcity, + .unittype = unit_type, + .action = action_by_number(trade_action), + }, NULL, + EFT_TRADE_REVENUE_REL_PCT, + dai_conn_city_cb, &same_cont, def_cont ? 1 : 0); bonus = get_target_bonus_effects(NULL, &(const struct req_context) { .player = pplayer, @@ -379,8 +443,13 @@ static void dai_choose_trade_route(struct ai_type *ait, struct city *pcity, NULL, EFT_TRADE_REVENUE_BONUS); + if (ipcth > 100 + ipctl) { + /* Too much luck? */ + ipcth = (ipcth + 100 + ipctl) / 2; + } /* Be mercy full to players with small amounts. Round up. */ - income = ceil((float)income * pow(2.0, (double)bonus / 1000.0)); + income = ceil((float)income * (200 + MAX(ipctl, -50) + ipcth) / 200.0 + * pow(2.0, (double)bonus / 1000.0)); if (dest_city_nat_same_cont) { pct = trade_route_type_trade_pct(TRT_NATIONAL); diff --git a/ai/default/daidomestic.h b/ai/default/daidomestic.h index c43237711c..8424fc0ac3 100644 --- a/ai/default/daidomestic.h +++ b/ai/default/daidomestic.h @@ -16,6 +16,10 @@ /* common */ #include "fc_types.h" +enum fc_tristate dai_conn_city_cb(const struct req_context *context, + const struct player *other_player, + const struct requirement *req, + void *data, int data_sz); struct adv_choice *domestic_advisor_choose_build(struct ai_type *ait, struct player *pplayer, struct city *pcity); diff --git a/ai/default/daieffects.c b/ai/default/daieffects.c index f06e81ea87..c59d56c4f1 100644 --- a/ai/default/daieffects.c +++ b/ai/default/daieffects.c @@ -39,7 +39,7 @@ /* ai */ #include "aitraits.h" #include "handicaps.h" - +#include "daidomestic.h" #include "daieffects.h" @@ -568,6 +568,7 @@ adv_want dai_effect_value(struct player *pplayer, case EFT_OUTPUT_PENALTY_TILE: case EFT_OUTPUT_INC_TILE_CELEBRATE: case EFT_TRADE_REVENUE_BONUS: + case EFT_TRADE_REVENUE_REL_PCT: case EFT_TILE_WORKABLE: case EFT_COMBAT_ROUNDS: case EFT_ILLEGAL_ACTION_MOVE_COST: @@ -638,7 +639,7 @@ adv_want dai_effect_value(struct player *pplayer, * can establish trade routes) */ int trr = action_id_get_role(ACTION_TRADE_ROUTE); const struct unit_type *van = best_role_unit(pcity, trr); - int bonus; + int bonus, pctl, pcth; if (NULL == van){ if (0 < num_role_units(trr)) { @@ -654,11 +655,24 @@ adv_want dai_effect_value(struct player *pplayer, .tile = city_tile(pcity), .unittype = van }, NULL, EFT_TRADE_REVENUE_BONUS); - + get_target_effects_limits(&pctl, &pcth, + &(const struct req_context) { + .player = city_owner(pcity), + .city = pcity, + .unittype = van + }, NULL, + EFT_TRADE_REVENUE_REL_PCT, + dai_conn_city_cb, NULL, 0); + + if (pcth > 100 + pctl) { + /* Too much luck? */ + pcth = (pcth + 100 + pctl) / 2; + } trait = ai_trait_get_value(TRAIT_TRADER, pplayer); v += amount * (pow(2.0, (double) bonus / 1000.0) + * (200 + MAX(pctl, -50) + pcth) / 200.0 + c) * trait / TRAIT_DEFAULT_VALUE; diff --git a/common/effects.h b/common/effects.h index 3f85c29888..9747eb7bb4 100644 --- a/common/effects.h +++ b/common/effects.h @@ -331,15 +331,17 @@ struct multiplier; #define SPECENUM_VALUE133NAME "HP_Regen_2" #define SPECENUM_VALUE134 EFT_TECH_PARASITE_PCT_MAX #define SPECENUM_VALUE134NAME "Tech_Parasite_Pct_Max" +#define SPECENUM_VALUE135 EFT_TRADE_REVENUE_REL_PCT +#define SPECENUM_VALUE135NAME "Trade_Revenue_Rel_Pct" /* Ruleset specific effects for use from Lua scripts */ -#define SPECENUM_VALUE135 EFT_USER_EFFECT_1 -#define SPECENUM_VALUE135NAME "User_Effect_1" -#define SPECENUM_VALUE136 EFT_USER_EFFECT_2 -#define SPECENUM_VALUE136NAME "User_Effect_2" -#define SPECENUM_VALUE137 EFT_USER_EFFECT_3 -#define SPECENUM_VALUE137NAME "User_Effect_3" -#define SPECENUM_VALUE138 EFT_USER_EFFECT_4 -#define SPECENUM_VALUE138NAME "User_Effect_4" +#define SPECENUM_VALUE136 EFT_USER_EFFECT_1 +#define SPECENUM_VALUE136NAME "User_Effect_1" +#define SPECENUM_VALUE137 EFT_USER_EFFECT_2 +#define SPECENUM_VALUE137NAME "User_Effect_2" +#define SPECENUM_VALUE138 EFT_USER_EFFECT_3 +#define SPECENUM_VALUE138NAME "User_Effect_3" +#define SPECENUM_VALUE139 EFT_USER_EFFECT_4 +#define SPECENUM_VALUE139NAME "User_Effect_4" /* Keep this last */ #define SPECENUM_COUNT EFT_COUNT #include "specenum_gen.h" diff --git a/common/improvement.c b/common/improvement.c index e86e5acf07..d341001cf8 100644 --- a/common/improvement.c +++ b/common/improvement.c @@ -714,7 +714,7 @@ bool can_player_build_improvement_direct(const struct player *p, Returns FALSE if building is obsolete. **************************************************************************/ bool can_player_build_improvement_now(const struct player *p, - struct impr_type *pimprove) + const struct impr_type *pimprove) { if (!can_player_build_improvement_direct(p, pimprove)) { return FALSE; diff --git a/common/improvement.h b/common/improvement.h index 889917adba..a3db639490 100644 --- a/common/improvement.h +++ b/common/improvement.h @@ -194,7 +194,7 @@ bool can_player_build_improvement_direct(const struct player *p, bool can_player_build_improvement_later(const struct player *p, const struct impr_type *pimprove); bool can_player_build_improvement_now(const struct player *p, - struct impr_type *pimprove); + const struct impr_type *pimprove); /* Initialization and iteration */ void improvements_init(void); diff --git a/common/traderoutes.c b/common/traderoutes.c index fc2e8d6ee0..c28a2f8014 100644 --- a/common/traderoutes.c +++ b/common/traderoutes.c @@ -469,7 +469,7 @@ int get_caravan_enter_city_trade_bonus(const struct city *pc1, struct goods_type *pgood, const bool establish_trade) { - int tb = 0, bonus = 0; + int tb = 0, bonus = 0, pct; enum trade_route_type trtype = cities_trade_route_type(pc1, pc2); if (trtss[trtype].bonus_type == TBONUS_NONE) { @@ -496,6 +496,23 @@ int get_caravan_enter_city_trade_bonus(const struct city *pc1, + max_trade_prod(pc1) + max_trade_prod(pc2)) * 2, 2); } + /* Scaling the bonus for the connectivity of the target to pc1 */ + pct = get_target_bonus_effects + (NULL, + &(const struct req_context) { + .player = city_owner(pc2), + .city = pc1, + .tile = city_tile(pc2), + .unittype = ut, + .action = action_by_number( + establish_trade + ? ACTION_TRADE_ROUTE + : ACTION_MARKETPLACE + ), + }, city_owner(pc1), + EFT_TRADE_REVENUE_REL_PCT); + tb = tb * (100 + pct) / 100; + if (pgood != NULL) { tb = tb * pgood->onetime_pct / 100; } diff --git a/doc/README.effects b/doc/README.effects index fc143417f3..01bc38d386 100644 --- a/doc/README.effects +++ b/doc/README.effects @@ -640,6 +640,11 @@ Trade_Revenue_Bonus One time trade revenue bonus is multiplied by pow(2, amount/1000). The amount value is taken from the caravan's home city. +Trade_Revenue_Rel_Pct + One time trade revenue bonus is increased on amount percents. + The amount value is taken from the caravan's home city + and owner but the destination city tile. + Traderoute_Pct Percentage bonus for trade from traderoutes. This bonus applies after the value of the traderoute is already calculated. It affects one end -- 2.34.1