From 77f28094b77da6813597eb87a09c75d4876c814d Mon Sep 17 00:00:00 2001 From: Ihnatus Date: Fri, 8 Jul 2022 20:49:43 +0300 Subject: [PATCH] Lua: Various research inspection API Provides some ruleset and game data about research costs and progress. Multiresearch data are currently available on server only. game.info object is introduced to get some settings. See OSDN#45068. Signed-off-by: Ihnatus --- common/scriptcore/api_game_methods.c | 111 +++++++++++++++++++++ common/scriptcore/api_game_methods.h | 7 ++ common/scriptcore/api_game_specenum.c | 5 + common/scriptcore/api_specenum.h | 30 ++++++ common/scriptcore/luascript_types.h | 1 + common/scriptcore/tolua_common_z.pkg | 6 +- common/scriptcore/tolua_game.pkg | 61 +++++++++++ server/scripting/api_server_game_methods.c | 21 ++++ server/scripting/api_server_game_methods.h | 2 + server/scripting/tolua_server.pkg | 4 + 10 files changed, 247 insertions(+), 1 deletion(-) diff --git a/common/scriptcore/api_game_methods.c b/common/scriptcore/api_game_methods.c index 08a14a1099..c6d50cafd4 100644 --- a/common/scriptcore/api_game_methods.c +++ b/common/scriptcore/api_game_methods.c @@ -520,6 +520,117 @@ bool api_methods_player_knows_tech(lua_State *L, Player *pplayer, advance_number(ptech)) == TECH_KNOWN; } +/**********************************************************************//** + Return current ptech research (or losing) cost for pplayer + (in clients, calculates some value using known data, + for current researched techs may differ from the actual value + given by :researching_cost()) + pplayer is optional, simplified calculation occurs without it +**************************************************************************/ +int api_methods_player_tech_cost(lua_State *L, Player *pplayer, + Tech_Type *ptech) +{ + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_ARG_NIL(L, ptech, 3, Tech_Type, 0); + + if (!pplayer && TECH_COST_CIV1CIV2 == game.info.tech_cost_style) { + /* Avoid getting error messages and return at least something */ + return ptech->cost * (double) game.info.sciencebox / 100.0; + } + return + research_total_bulbs_required(pplayer ? research_get(pplayer) : NULL, + advance_index(ptech), TRUE); +} + +/**********************************************************************//** + Returns current research target for a player. + If the player researches a future tech, returns a string with + its translated name. If the target is unset, returns false, + if it's unknown, returns nil. + In client, needs at least having an embassy for an informative value. +**************************************************************************/ +lua_Object +api_methods_player_researching(lua_State *L, Player *pplayer) +{ + const struct research *presearch; + int rr; + + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, pplayer, 0); + presearch = research_get(pplayer); + LUASCRIPT_CHECK(L, presearch, "player's research not set", 0); + + rr = presearch->researching; + + switch (rr) { + case A_UNSET: + lua_pushboolean(L, FALSE); + break; + case A_FUTURE: + lua_pushstring(L, research_advance_name_translation(presearch, rr)); + break; + case A_UNKNOWN: + lua_pushnil(L); + break; + default: + /* A regular tech */ + fc_assert(rr >= A_FIRST && rr <= A_LAST); + tolua_pushusertype(L, advance_by_number(rr), "Tech_Type"); + } + + return lua_gettop(L); +} + +/**********************************************************************//** + Number of bulbs on the research stock of pplayer + In clients, needs at least an embassy for nonzero value +**************************************************************************/ +int api_methods_player_bulbs(lua_State *L, Player *pplayer) +{ + const struct research *presearch; + + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, pplayer, 0); + presearch = research_get(pplayer); + LUASCRIPT_CHECK(L, presearch, "player's research not set", 0); + + return presearch->bulbs_researched; +} + +/**********************************************************************//** + Total cost of pplayer's current research target. + In clients, needs at least an embassy for the actual value. +**************************************************************************/ +int api_methods_player_research_cost(lua_State *L, Player *pplayer) +{ + const struct research *presearch; + + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, pplayer, 0); + presearch = research_get(pplayer); + LUASCRIPT_CHECK(L, presearch, "player's research not set", 0); + + return is_server() + ? research_total_bulbs_required(presearch, presearch->researching, FALSE) + : presearch->client.researching_cost; +} + +/**********************************************************************//** + Number of future techs known to pplayer + In clients, needs at least an embassy for nonzero value +**************************************************************************/ +int api_methods_player_future(lua_State *L, Player *pplayer) +{ + const struct research *presearch; + + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, pplayer, 0); + presearch = research_get(pplayer); + LUASCRIPT_CHECK(L, presearch, "player's research not set", 0); + + return presearch->future_tech; +} + /**********************************************************************//** How much culture player has? **************************************************************************/ diff --git a/common/scriptcore/api_game_methods.h b/common/scriptcore/api_game_methods.h index f700dc2caf..bef425097d 100644 --- a/common/scriptcore/api_game_methods.h +++ b/common/scriptcore/api_game_methods.h @@ -87,6 +87,13 @@ int api_methods_player_num_units(lua_State *L, Player *pplayer); int api_methods_player_gold(lua_State *L, Player *pplayer); bool api_methods_player_knows_tech(lua_State *L, Player *pplayer, Tech_Type *ptech); +int api_methods_player_tech_cost(lua_State *L, Player *pplayer, + Tech_Type *ptech); +lua_Object +api_methods_player_researching(lua_State *L, Player *pplayer); +int api_methods_player_bulbs(lua_State *L, Player *pplayer); +int api_methods_player_research_cost(lua_State *L, Player *pplayer); +int api_methods_player_future(lua_State *L, Player *pplayer); bool api_methods_player_shares_research(lua_State *L, Player *pplayer, Player *aplayer); const char *api_methods_research_rule_name(lua_State *L, Player *pplayer); diff --git a/common/scriptcore/api_game_specenum.c b/common/scriptcore/api_game_specenum.c index e041c83b10..3670401fa4 100644 --- a/common/scriptcore/api_game_specenum.c +++ b/common/scriptcore/api_game_specenum.c @@ -27,6 +27,7 @@ /* common */ #include "events.h" +#include "fc_types.h" /* common/scriptcore */ #include "api_specenum.h" @@ -38,6 +39,8 @@ Define the __index function for each exported specenum type. **************************************************************************/ API_SPECENUM_DEFINE_INDEX(event_type, "E_") +API_SPECENUM_DEFINE_INDEX_REV(tech_cost_style); +API_SPECENUM_DEFINE_INDEX_REV(tech_leakage_style); /**********************************************************************//** Load the specenum modules into Lua state L. @@ -45,6 +48,8 @@ API_SPECENUM_DEFINE_INDEX(event_type, "E_") int api_game_specenum_open(lua_State *L) { API_SPECENUM_CREATE_TABLE(L, event_type, "E"); + API_SPECENUM_CREATE_TABLE_REV(L, tech_cost_style, "TECH_COST"); + API_SPECENUM_CREATE_TABLE_REV(L, tech_leakage_style, "TECH_LEAKAGE"); return 0; } diff --git a/common/scriptcore/api_specenum.h b/common/scriptcore/api_specenum.h index 0012e9553e..eb8a1550c2 100644 --- a/common/scriptcore/api_specenum.h +++ b/common/scriptcore/api_specenum.h @@ -18,6 +18,7 @@ extern "C" { #endif /* __cplusplus */ #define API_SPECENUM_INDEX_NAME(type) api_specenum_##type##_index +#define API_SPECENUM_NAME_NAME(type) api_specenum_##type##_name /**********************************************************************//** Define a the __index (table, key) -> value metamethod @@ -47,11 +48,40 @@ extern "C" { return 1; \ } +/**********************************************************************//** + Define the __index (table, key) -> value metamethod + Return the enum name whose value is supplied. + The fetched value is written back to the lua table, and further accesses + will resolve there instead of this function. + Note that the indices usually go from 0, not 1. +**************************************************************************/ +#define API_SPECENUM_DEFINE_INDEX_REV(type_name) \ + static int (API_SPECENUM_NAME_NAME(type_name))(lua_State *L) \ + { \ + enum type_name _key; \ + const char *_value; \ + luaL_checktype(L, 1, LUA_TTABLE); \ + _key = luaL_checkinteger(L, 2); \ + if (type_name##_is_valid(_key)) { \ + _value = type_name##_name(_key); \ + /* T[_key] = _value */ \ + lua_pushinteger(L, _key); \ + lua_pushstring(L, _value); \ + lua_rawset(L, 1); \ + lua_pushstring(L, _value); \ + } else { \ + lua_pushnil(L); \ + } \ + return 1; \ + } + void api_specenum_create_table(lua_State *L, const char *name, lua_CFunction findex); #define API_SPECENUM_CREATE_TABLE(L, type, name) \ api_specenum_create_table((L), (name), API_SPECENUM_INDEX_NAME(type)) +#define API_SPECENUM_CREATE_TABLE_REV(L, type, name) \ + api_specenum_create_table((L), (name), API_SPECENUM_NAME_NAME(type)) diff --git a/common/scriptcore/luascript_types.h b/common/scriptcore/luascript_types.h index d226e425b0..a87aecfbee 100644 --- a/common/scriptcore/luascript_types.h +++ b/common/scriptcore/luascript_types.h @@ -58,6 +58,7 @@ typedef enum direction8 Direction; typedef struct disaster_type Disaster; typedef struct achievement Achievement; typedef struct action Action; +typedef struct packet_game_info Game_Info; typedef void Nonexistent; diff --git a/common/scriptcore/tolua_common_z.pkg b/common/scriptcore/tolua_common_z.pkg index b1341e30d4..1369f67b30 100644 --- a/common/scriptcore/tolua_common_z.pkg +++ b/common/scriptcore/tolua_common_z.pkg @@ -52,7 +52,8 @@ do "Disaster", "Achievement", "Action", - "Direction" + "Direction", + "Game_Info" } local function id_eq (o1, o2) @@ -124,6 +125,9 @@ tolua = { type=tolua.type, } +-- Hide some unwanted API +game[".set"] = nil + -- Hide all private methods methods_private = nil diff --git a/common/scriptcore/tolua_game.pkg b/common/scriptcore/tolua_game.pkg index 981fb0dcf6..0da9265642 100644 --- a/common/scriptcore/tolua_game.pkg +++ b/common/scriptcore/tolua_game.pkg @@ -92,6 +92,7 @@ struct Unit_Type { struct Tech_Type { const int item_number @ id; + const int cost @ cost_base; }; struct Terrain { @@ -120,8 +121,26 @@ struct Unit_List_Link { struct City_List_Link { }; +/* Declaring all fields as const, readonly */ +struct Game_Info { + const int base_tech_cost; + const int min_tech_cost; + const int tech_leak_pct; + const bool tech_steal_allow_holes; + const bool tech_trade_allow_holes; + const bool tech_trade_loss_allow_holes; + const bool tech_parasite_allow_holes; + const bool tech_loss_allow_holes; + const int sciencebox; + const char tech_cost_style @ tech_cost_style_id; + const char tech_leakage @ tech_leakage_style_id; +}; + /* Module Game */ +$#define game_info_substructure game.info module game { + extern Game_Info game_info_substructure @ info; + int api_methods_game_turn @ current_turn (lua_State *L); @@ -150,6 +169,8 @@ module Player { module properties { int api_methods_player_number @ id (lua_State *L, Player *self); + int api_methods_player_bulbs + @ bulbs (lua_State *L, Player *pplayer); } const char *api_methods_player_controlling_gui @@ -159,12 +180,20 @@ module Player { @ num_cities (lua_State *L, Player *self); int api_methods_player_num_units @ num_units (lua_State *L, Player *self); + int api_methods_player_future + @ num_future_techs (lua_State *L, Player *pplayer); bool api_methods_player_has_wonder @ has_wonder (lua_State *L, Player *self, Building_Type *building); int api_methods_player_gold @ gold (lua_State *L, Player *self); bool api_methods_player_knows_tech @ knows_tech (lua_State *L, Player *self, Tech_Type *ptech); + int api_methods_player_tech_cost + @ tech_cost (lua_State *L, Player *pplayer, Tech_Type *ptech); + lua_Object api_methods_player_researching + @ researching (lua_State *L, Player *pplayer); + int api_methods_player_research_cost + @ researching_cost (lua_State *L, Player *pplayer); bool api_methods_player_shares_research @ shares_research (lua_State *L, Player *self, Player *other); const char *api_methods_research_rule_name @@ -433,6 +462,14 @@ module Tech_Type { @ name_translation (lua_State *L, Tech_Type *self); } +$[ +local ptcost = Player.tech_cost +-- Unpersonalized tech cost method (without "Tech_Cost_Factor" effect) +function Tech_Type:cost() + return ptcost(nil, self) +end +$] + /* Module Terrain. */ module Terrain { const char *api_methods_terrain_rule_name @@ -555,6 +592,13 @@ module find { @ nonexistent (lua_State *L); } +$[ +local game_info = game.info +function find.game_info() + return game_info +end +$] + module E { /* Notify events module is exported by api_specenum */ } @@ -610,7 +654,24 @@ Direction.properties.next_cw = direction.next_cw Direction.properties.opposite = direction.opposite $] +module Game_Info { + +} + $[ +-- Getting enums from game.info as rule names +-- The tables are defined and filled in api_game_specenum.c +local tcs, tls = TECH_COST, TECH_LEAKAGE +-- Remove the tables from _G, likely no more use for them +TECH_COST, TECH_LEAKAGE = nil, nil +Game_Info.properties = { + tech_cost_style = function(self) + return tcs[self.tech_cost_style_id]; + end, + tech_leakage_style = function(self) + return tls[self.tech_leakage_style_id]; + end +} -- *************************************************************************** -- Player and Tile: cities_iterate and units_iterate methods diff --git a/server/scripting/api_server_game_methods.c b/server/scripting/api_server_game_methods.c index 15df33c442..528da762a3 100644 --- a/server/scripting/api_server_game_methods.c +++ b/server/scripting/api_server_game_methods.c @@ -15,6 +15,9 @@ #include #endif +/* common */ +#include "research.h" + /* common/scriptcore */ #include "luascript.h" @@ -162,3 +165,21 @@ int api_methods_nation_trait_default(lua_State *L, Nation_Type *pnation, return pnation->server.traits[tr].fixed; } + +/**********************************************************************//** + In multiresearch mode, returns bulbs saved for a specific tech + in pplayer's research. In other modes, result is undefined. +**************************************************************************/ +int api_methods_player_tech_bulbs(lua_State *L, Player *pplayer, + Tech_Type *tech) +{ + const struct research *presearch; + + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, pplayer, 0); + LUASCRIPT_CHECK_ARG_NIL(L, tech, 3, Tech_Type, 0) + presearch = research_get(pplayer); + LUASCRIPT_CHECK(L, presearch, "player's research not set", 0); + + return presearch->inventions[advance_number(tech)].bulbs_researched_saved; +} diff --git a/server/scripting/api_server_game_methods.h b/server/scripting/api_server_game_methods.h index 404ec1bc69..f59b3eb2d7 100644 --- a/server/scripting/api_server_game_methods.h +++ b/server/scripting/api_server_game_methods.h @@ -37,5 +37,7 @@ int api_methods_nation_trait_max(lua_State *L, Nation_Type *pnation, const char *tname); 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); #endif /* FC__API_SERVER_GAME_METHODS_H */ diff --git a/server/scripting/tolua_server.pkg b/server/scripting/tolua_server.pkg index 219d0883fd..18bc991885 100644 --- a/server/scripting/tolua_server.pkg +++ b/server/scripting/tolua_server.pkg @@ -505,6 +505,8 @@ module Player { @ trait_current_mod (lua_State *L, Player *pplayer, const char *tname); void api_methods_player_lose @ lose (lua_State *L, Player *pplayer, Player *looter = NULL); + int api_methods_player_tech_bulbs + @ bulbs_saved (lua_State *L, Player *pplayer, Tech_Type *tech); } $[ @@ -514,9 +516,11 @@ $] /* server game parameters */ $#define game_server_autoupgrade_veteran_loss (game.server.autoupgrade_veteran_loss) $#define game_server_upgrade_veteran_loss (game.server.upgrade_veteran_loss) +$#define game_server_multiresearch (game.server.multiresearch) module game { extern const int game_server_autoupgrade_veteran_loss @ autoupgrade_veteran_loss; extern const int game_server_upgrade_veteran_loss @ upgrade_veteran_loss; + extern const bool game_server_multiresearch @ multiresearch; } /* Additions to common Nation_Type module. */ -- 2.34.1