From 5d77f2a825372be4df6cc1786df1ae60dab9eb5b Mon Sep 17 00:00:00 2001 From: Ihnatus Date: Sun, 16 Oct 2022 21:49:42 +0300 Subject: [PATCH] Split researched bulbs on free and bound This abandons got_tech flags. Caravan or Lua bonuses now go to free bulbs (except in multiresearch mode) and are not lost or penalized on switch between the saved and a newly researched tech. See OSDN#45685 Signed-off-by: Ihnatus --- ai/default/aitech.c | 4 +- common/research.h | 8 ++-- server/cityturn.c | 2 +- server/plrhand.c | 2 +- server/savegame/savecompat.c | 48 +++++++++++++++++----- server/savegame/savegame2.c | 6 ++- server/savegame/savegame3.c | 15 +++---- server/scripting/api_server_edit.c | 2 +- server/scripting/api_server_game_methods.c | 23 ++++++----- server/scripting/api_server_game_methods.h | 2 +- server/scripting/tolua_server.pkg | 4 +- server/srv_main.c | 7 ++-- server/techtools.c | 43 ++++++++++++------- server/techtools.h | 3 +- server/unithand.c | 2 +- 15 files changed, 107 insertions(+), 64 deletions(-) diff --git a/ai/default/aitech.c b/ai/default/aitech.c index 3da1e7597c..4f1119ea6e 100644 --- a/ai/default/aitech.c +++ b/ai/default/aitech.c @@ -335,7 +335,9 @@ void dai_manage_tech(struct ai_type *ait, struct player *pplayer) struct ai_tech_choice choice, goal; struct research *research = research_get(pplayer); /* Penalty for switching research */ - int penalty = (research->got_tech ? 0 : research->bulbs_researched); + /* FIXME: get real penalty with game.server.techpenalty and multiresearch */ + int penalty = research->bulbs_researched - research->free_bulbs; + penalty = MAX(penalty, 0); /* Even when we let human to do the final decision, we keep our * wants correctly calculated. Add effect values in */ diff --git a/common/research.h b/common/research.h index bb5898f847..b7f609b2ae 100644 --- a/common/research.h +++ b/common/research.h @@ -62,11 +62,9 @@ struct research { Tech_type_id researching_saved; int bulbs_researching_saved; - /* If the player completed a research this turn, this value is turned on - * and changing targets may be done without penalty. */ - bool got_tech; - /* The same as got_tech but flipped back in choose_tech() */ - bool got_tech_multi; + /* For this amount of bulbs, changing targets this turn + * may be done without penalty. */ + int free_bulbs; struct research_invention { /* One of TECH_UNKNOWN, TECH_KNOWN or TECH_PREREQS_KNOWN. */ diff --git a/server/cityturn.c b/server/cityturn.c index 9f6531f581..9f91ed108d 100644 --- a/server/cityturn.c +++ b/server/cityturn.c @@ -3358,7 +3358,7 @@ static void update_city_activity(struct city *pcity) pcity->did_sell = FALSE; pcity->did_buy = FALSE; pcity->airlift = city_airlift_max(pcity); - update_bulbs(pplayer, pcity->prod[O_SCIENCE], FALSE); + update_bulbs(pplayer, pcity->prod[O_SCIENCE], FALSE, FALSE); pplayer->economic.infra_points += get_city_bonus(pcity, EFT_INFRA_POINTS); diff --git a/server/plrhand.c b/server/plrhand.c index 514f3da58e..dff2154f0d 100644 --- a/server/plrhand.c +++ b/server/plrhand.c @@ -3458,5 +3458,5 @@ void update_national_activities(struct player *pplayer, int old_gold) research_get(pplayer)->researching_saved = A_UNKNOWN; /* Reduce the number of bulbs by the amount needed for tech upkeep and * check for finished research */ - update_bulbs(pplayer, -player_tech_upkeep(pplayer), TRUE); + update_bulbs(pplayer, -player_tech_upkeep(pplayer), TRUE, FALSE); } diff --git a/server/savegame/savecompat.c b/server/savegame/savecompat.c index 183cc9ae8d..1a59db581b 100644 --- a/server/savegame/savecompat.c +++ b/server/savegame/savecompat.c @@ -2113,18 +2113,24 @@ static void compat_load_030200(struct loaddata *loading, secfile_replace_int(loading->file, set_count, "settings.set_count"); } - if (format_class == SAVEGAME_3) { - /* Older savegames had a bug that got_tech_multi was not saved. - * Insert the entry to such savegames */ - - /* May be unsaved (e.g. scenario case). */ + { + /* Replace got_tech[_multi] bools on free_bulbs integers. */ + /* Older savegames had a bug that got_tech_multi was not saved. */ count = secfile_lookup_int_default(loading->file, 0, "research.count"); for (i = 0; i < count; i++) { - if (secfile_entry_lookup(loading->file, - "research.r%d.got_tech_multi", i) == NULL) { - /* Default to FALSE */ - secfile_insert_bool(loading->file, FALSE, - "research.r%d.got_tech_multi", i); + bool got_tech = FALSE; + int bulbs = 0; + bool got_tech_multi + = secfile_lookup_bool_default(loading->file, FALSE, + "research.r%d.got_tech_multi", i); + + if (secfile_lookup_bool(loading->file, &got_tech, + "research.r%d.got_tech", i) + && secfile_lookup_int(loading->file, &bulbs, + "research.r%d.bulbs", i)) { + secfile_insert_int(loading->file, + got_tech || got_tech_multi ? bulbs : 0, + "research.r%d.free_bulbs", i); } } } @@ -2813,6 +2819,28 @@ static void compat_load_dev(struct loaddata *loading) } player_slots_iterate_end; } + + /* Replace got_tech[_multi] bools on free_bulbs integers. */ + { + int count = secfile_lookup_int_default(loading->file, 0, "research.count"); + + for (int i = 0; i < count; i++) { + bool got_tech = FALSE; + int bulbs = 0; + bool got_tech_multi + = secfile_lookup_bool_default(loading->file, FALSE, + "research.r%d.got_tech_multi", i); + + if (secfile_lookup_bool(loading->file, &got_tech, + "research.r%d.got_tech", i) + && secfile_lookup_int(loading->file, &bulbs, + "research.r%d.bulbs", i)) { + secfile_insert_int(loading->file, + got_tech || got_tech_multi ? bulbs : 0, + "research.r%d.free_bulbs", i); + } + } + } } /* Version < 3.1.93 */ #endif /* FREECIV_DEV_SAVE_COMPAT_3_2 */ diff --git a/server/savegame/savegame2.c b/server/savegame/savegame2.c index 3dc1710b31..49026ea49e 100644 --- a/server/savegame/savegame2.c +++ b/server/savegame/savegame2.c @@ -4938,6 +4938,7 @@ static void sg_load_researches(struct loaddata *loading) int number; const char *str; int i, j; + bool got_tech; /* Check status and return if not OK (sg_success FALSE). */ sg_check_ret(); @@ -4981,9 +4982,12 @@ static void sg_load_researches(struct loaddata *loading) presearch->researching = technology_load(loading->file, "research.r%d.now", i); sg_failure_ret(secfile_lookup_bool(loading->file, - &presearch->got_tech, + &got_tech, "research.r%d.got_tech", i), "%s", secfile_error()); + if (got_tech) { + presearch->free_bulbs = presearch->bulbs_researched; + } str = secfile_lookup_str(loading->file, "research.r%d.done", i); sg_failure_ret(str != NULL, "%s", secfile_error()); diff --git a/server/savegame/savegame3.c b/server/savegame/savegame3.c index dd662759e2..35ef5994e8 100644 --- a/server/savegame/savegame3.c +++ b/server/savegame/savegame3.c @@ -7263,12 +7263,9 @@ static void sg_load_researches(struct loaddata *loading) "research.r%d.saved", i); presearch->researching = technology_load(loading->file, "research.r%d.now", i); - sg_failure_ret(secfile_lookup_bool(loading->file, - &presearch->got_tech, - "research.r%d.got_tech", i), - "%s", secfile_error()); - sg_failure_ret(secfile_lookup_bool(loading->file, &presearch->got_tech_multi, - "research.r%d.got_tech_multi", i), + sg_failure_ret(!secfile_lookup_int(loading->file, + &presearch->free_bulbs, + "research.r%d.free_bulbs", i), "%s", secfile_error()); str = secfile_lookup_str(loading->file, "research.r%d.done", i); @@ -7361,10 +7358,8 @@ static void sg_save_researches(struct savedata *saving) "research.r%d.bulbs", i); technology_save(saving->file, "research.r%d.now", i, presearch->researching); - secfile_insert_bool(saving->file, presearch->got_tech, - "research.r%d.got_tech", i); - secfile_insert_bool(saving->file, presearch->got_tech_multi, - "research.r%d.got_tech_multi", i); + secfile_insert_int(saving->file, presearch->free_bulbs, + "research.r%d.free_bulbs", i); /* Save technology lists as bytevector. Note that technology order is * saved in savefile.technology.order */ advance_index_iterate(A_NONE, tech_id) { diff --git a/server/scripting/api_server_edit.c b/server/scripting/api_server_edit.c index 49c6a67eb6..623a72ce39 100644 --- a/server/scripting/api_server_edit.c +++ b/server/scripting/api_server_edit.c @@ -1094,7 +1094,7 @@ void api_edit_player_give_bulbs(lua_State *L, Player *pplayer, int amount) LUASCRIPT_CHECK_STATE(L); LUASCRIPT_CHECK_SELF(L, pplayer); - update_bulbs(pplayer, amount, TRUE); + update_bulbs(pplayer, amount, TRUE, TRUE); send_research_info(research_get(pplayer), NULL); } diff --git a/server/scripting/api_server_game_methods.c b/server/scripting/api_server_game_methods.c index cfc562de68..895b521687 100644 --- a/server/scripting/api_server_game_methods.c +++ b/server/scripting/api_server_game_methods.c @@ -191,9 +191,17 @@ int api_methods_player_tech_bulbs(lua_State *L, Player *pplayer, if (presearch->researching_saved == tn) { return presearch->bulbs_researching_saved - presearch->bulbs_researched; - } else if (!presearch->got_tech && tn != presearch->researching + } else if (tn != presearch->researching && presearch->bulbs_researched > 0) { - return -presearch->bulbs_researched * game.server.techpenalty / 100; + int bound_bulbs = presearch->bulbs_researched - presearch->free_bulbs; + int penalty; + + if (bound_bulbs <= 0) { + return 0; + } + penalty = bound_bulbs * game.server.techpenalty / 100; + + return -MIN(penalty, presearch->bulbs_researched); } else { return 0; } @@ -201,10 +209,9 @@ int api_methods_player_tech_bulbs(lua_State *L, Player *pplayer, } /**********************************************************************//** - Returns whether pplayer is in "got tech" state and can invest their - remaining rsearched bulbs freely. + Returns bulbs that can be freely transferred to a new research target. **************************************************************************/ -bool api_methods_player_got_tech(lua_State *L, Player *pplayer) +int api_methods_player_free_bulbs(lua_State *L, Player *pplayer) { const struct research *presearch; @@ -213,9 +220,5 @@ bool api_methods_player_got_tech(lua_State *L, Player *pplayer) presearch = research_get(pplayer); LUASCRIPT_CHECK(L, presearch, "player's research not set", 0); - if (game.server.multiresearch) { - return presearch->got_tech_multi; - } else { - return presearch->got_tech; - } + return presearch->free_bulbs; } diff --git a/server/scripting/api_server_game_methods.h b/server/scripting/api_server_game_methods.h index c74a0bd61b..4a6e3d5148 100644 --- a/server/scripting/api_server_game_methods.h +++ b/server/scripting/api_server_game_methods.h @@ -39,6 +39,6 @@ int api_methods_nation_trait_default(lua_State *L, Nation_Type *pnation, const char *tname); int api_methods_player_tech_bulbs(lua_State *L, Player *pplayer, Tech_Type *tech); -bool api_methods_player_got_tech(lua_State *L, Player *pplayer); +int api_methods_player_free_bulbs(lua_State *L, Player *pplayer); #endif /* FC__API_SERVER_GAME_METHODS_H */ diff --git a/server/scripting/tolua_server.pkg b/server/scripting/tolua_server.pkg index ac62a1a426..204c82aeff 100644 --- a/server/scripting/tolua_server.pkg +++ b/server/scripting/tolua_server.pkg @@ -498,8 +498,8 @@ $] /* Additions to common Player module. */ module Player { module properties { - bool api_methods_player_got_tech - @ got_tech (lua_State *L, Player *pplayer); + int api_methods_player_free_bulbs + @ free_bulbs (lua_State *L, Player *pplayer); } int api_methods_player_trait @ trait (lua_State *L, Player *pplayer, const char *tname); diff --git a/server/srv_main.c b/server/srv_main.c index 3c6819f31f..716e71da4e 100644 --- a/server/srv_main.c +++ b/server/srv_main.c @@ -1416,7 +1416,7 @@ static void end_phase(void) } /* Add the researched bulbs to the pool; do *NOT* check for finished * research */ - update_bulbs(pplayer, 0, FALSE); + update_bulbs(pplayer, 0, FALSE, FALSE); } } phase_players_iterate_end; @@ -1438,8 +1438,7 @@ static void end_phase(void) /* Refresh cities */ phase_players_iterate(pplayer) { - research_get(pplayer)->got_tech = FALSE; - research_get(pplayer)->got_tech_multi = FALSE; + research_get(pplayer)->free_bulbs = 0; } phase_players_iterate_end; alive_phase_players_iterate(pplayer) { @@ -3382,7 +3381,7 @@ static void srv_ready(void) players_iterate(pplayer) { /* Check for finished research. */ - update_bulbs(pplayer, 0, TRUE); + update_bulbs(pplayer, 0, TRUE, FALSE); } players_iterate_end; } diff --git a/server/techtools.c b/server/techtools.c index 8b4e65a38d..62d810554e 100644 --- a/server/techtools.c +++ b/server/techtools.c @@ -408,11 +408,10 @@ void found_new_tech(struct research *presearch, Tech_type_id tech_found, could_switch = create_current_governments_data(presearch); - /* got_tech allows us to change research without applying techpenalty + /* getting tech allows us to change research without applying techpenalty * (without losing bulbs) */ if (tech_found == presearch->researching) { - presearch->got_tech = TRUE; - presearch->got_tech_multi = TRUE; + presearch->free_bulbs = presearch->bulbs_researched; } presearch->researching_saved = A_UNKNOWN; presearch->techs_researched++; @@ -640,14 +639,17 @@ static bool lose_tech(struct research *research) /************************************************************************//** Adds the given number of bulbs into the player's tech and (if necessary and 'check_tech' is TRUE) completes the research. If the total number of bulbs - is negative due to tech upkeep, one (randomly chosen) tech is lost. + is negative due to tech upkeep, one (randomly chosen) tech may be lost. + free_bulbs allows to set the bulbs free for player to invest into any + tech (in multiresearch mode it happens iff the tech is not selected). The caller is responsible for sending updated player information. This is called from each city every turn, from caravan revenue, at the - end of the phase, and from lua API. + end of the phase, and from Lua API. ****************************************************************************/ -void update_bulbs(struct player *pplayer, int bulbs, bool check_tech) +void update_bulbs(struct player *pplayer, int bulbs, bool check_tech, + bool free_bulbs) { struct research *research = research_get(pplayer); @@ -655,10 +657,19 @@ void update_bulbs(struct player *pplayer, int bulbs, bool check_tech) /* Dead players do not produce research */ return; } + fc_assert_ret(research); /* count our research contribution this turn */ pplayer->server.bulbs_last_turn += bulbs; research->bulbs_researched += bulbs; + if (A_UNKNOWN != research->researching_saved) { + fc_assert(research->researching_saved != research->researching); + research->bulbs_researching_saved += bulbs; + } + if ((free_bulbs && !game.server.multiresearch) + || A_UNSET == research->researching) { + research->free_bulbs += bulbs; + } advance_index_iterate(A_FIRST, j) { if (j == research->researching) { research->inventions[j].bulbs_researched_saved = research->bulbs_researched; @@ -670,7 +681,7 @@ void update_bulbs(struct player *pplayer, int bulbs, bool check_tech) /* If we have a negative number of bulbs we do try to: * - reduce the number of future techs; * - or lose one random tech. - * After that the number of bulbs available is incresed based on the + * After that the number of bulbs available is increased based on the * value of the lost tech. */ if (lose_tech(research)) { Tech_type_id tech = (research->future_tech > 0 @@ -1019,11 +1030,8 @@ void choose_tech(struct research *research, Tech_type_id tech) } } advance_index_iterate_end; research->researching = tech; - if (!research->got_tech_multi) { - research->bulbs_researched = 0; - } - research->bulbs_researched = research->bulbs_researched + bulbs_res; - research->got_tech_multi = FALSE; + research->bulbs_researched = research->free_bulbs + bulbs_res; + research->free_bulbs = 0; if (research->bulbs_researched >= research_total_bulbs_required(research, tech, FALSE)) { tech_researched(research); @@ -1031,14 +1039,19 @@ void choose_tech(struct research *research, Tech_type_id tech) return; } - if (!research->got_tech && research->researching_saved == A_UNKNOWN) { + /* The first check implies we have NOT recently got a technology + * and are changing from one we were researching at tc. */ + if (research->bulbs_researched - research->free_bulbs > 0 + && research->researching_saved == A_UNKNOWN) { research->bulbs_researching_saved = research->bulbs_researched; research->researching_saved = research->researching; /* Subtract a penalty because we changed subject. */ if (research->bulbs_researched > 0) { research->bulbs_researched - -= ((research->bulbs_researched * game.server.techpenalty) / 100); - fc_assert(research->bulbs_researched >= 0); + -= ((research->bulbs_researched - research->free_bulbs) + * game.server.techpenalty) / 100; + /* If free_bulbs were for some reason negative... */ + research->bulbs_researched = MAX(research->bulbs_researched, 0); } } else if (tech == research->researching_saved) { research->bulbs_researched = research->bulbs_researching_saved; diff --git a/server/techtools.h b/server/techtools.h index 74c5e6195b..52e155ba79 100644 --- a/server/techtools.h +++ b/server/techtools.h @@ -38,7 +38,8 @@ void script_tech_learned(struct research *presearch, const char *reason); void found_new_tech(struct research *presearch, Tech_type_id tech_found, bool was_discovery, bool saving_bulbs); -void update_bulbs(struct player *pplayer, int bulbs, bool check_tech); +void update_bulbs(struct player *pplayer, int bulbs, bool check_tech, + bool free_bulbs); void init_tech(struct research *presearch, bool update); void choose_tech(struct research *presearch, Tech_type_id tech); void choose_random_tech(struct research *presearch); diff --git a/server/unithand.c b/server/unithand.c index 408cf00815..f8fe084a06 100644 --- a/server/unithand.c +++ b/server/unithand.c @@ -5970,7 +5970,7 @@ static bool do_unit_establish_trade(struct player *pplayer, if (bonus_type == TBONUS_SCIENCE || bonus_type == TBONUS_BOTH) { /* add bulbs and check for finished research */ - update_bulbs(pplayer, revenue, TRUE); + update_bulbs(pplayer, revenue, TRUE, TRUE); /* Inform everyone about tech changes */ send_research_info(research_get(pplayer), NULL); -- 2.34.1