From c510e10c697a004858bb54429ad83b80d29e2711 Mon Sep 17 00:00:00 2001 From: Ihnatus Date: Sun, 6 Feb 2022 00:37:21 +0300 Subject: [PATCH] Lua API for unhardcoding autoupgrade. Several functions are enough to replace C do_upgrade_effects() from common/unittools.c, other functions can be used for some different procedures. See OSDN#42666 Signed-off-by: Ihnatus --- common/scriptcore/api_game_methods.c | 82 ++++++++++++++++++++++++++++ common/scriptcore/api_game_methods.h | 9 +++ common/scriptcore/tolua_game.pkg | 12 ++++ common/unit.c | 63 ++++++++++----------- common/unit.h | 3 + server/scripting/api_server_edit.c | 59 ++++++++++++++++++++ server/scripting/api_server_edit.h | 4 ++ server/scripting/tolua_server.pkg | 18 ++++++ 8 files changed, 219 insertions(+), 31 deletions(-) diff --git a/common/scriptcore/api_game_methods.c b/common/scriptcore/api_game_methods.c index 66736c0a99..a604d06836 100644 --- a/common/scriptcore/api_game_methods.c +++ b/common/scriptcore/api_game_methods.c @@ -547,6 +547,88 @@ bool api_methods_player_has_flag(lua_State *L, Player *pplayer, return FALSE; } +/**********************************************************************//** + Return a unit type the player potentially can upgrade utype to, + or nil if the player can't upgrade it +**************************************************************************/ +Unit_Type *api_methods_player_can_upgrade(lua_State *L, Player *pplayer, + Unit_Type *utype) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + LUASCRIPT_CHECK_SELF(L, pplayer, NULL); + LUASCRIPT_CHECK_ARG_NIL(L, utype, 3, Unit_Type, NULL); + + return (Unit_Type *)can_upgrade_unittype(pplayer, utype); +} + +/**********************************************************************//** + Certain tests if pplayer generally can build utype units, maybe after + building a required improvement. Does not consider obsoletion. +**************************************************************************/ +bool api_methods_player_can_build_unit_direct(lua_State *L, Player *pplayer, + Unit_Type *utype) +{ + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, pplayer, FALSE); + LUASCRIPT_CHECK_ARG_NIL(L, utype, 3, Unit_Type, FALSE); + + return can_player_build_unit_direct(pplayer, utype); +} + +/**********************************************************************//** + Certain tests if pplayer generally can build itype buildings. +**************************************************************************/ +bool api_methods_player_can_build_impr_direct(lua_State *L, Player *pplayer, + Building_Type *itype) +{ + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, pplayer, FALSE); + LUASCRIPT_CHECK_ARG_NIL(L, itype, 3, Building_Type, FALSE); + + return can_player_build_improvement_direct(pplayer, itype); +} + + +/**********************************************************************//** + Return if a unit can upgrade considering where it is now. + If is_free is FALSE, considers local city and the owner's treasury. +**************************************************************************/ +bool api_methods_unit_can_upgrade(lua_State *L, Unit *punit, bool is_free) +{ + + return UU_OK == unit_upgrade_test(punit, is_free); +} + +/**********************************************************************//** + Return a name of the problem unit may have being transformed to ptype + where it is now, or nil if no problem seems to exist. +**************************************************************************/ +const char *api_methods_unit_transform_problem(lua_State *L, Unit *punit, + Unit_Type *ptype) +{ + enum unit_upgrade_result uu; + + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, punit, 0); + LUASCRIPT_CHECK_ARG_NIL(L, ptype, 3, Unit_Type, 0); + + uu = unit_transform_result(punit, ptype); + switch (uu) { + case UU_OK: + return NULL; + case UU_NOT_ENOUGH_ROOM: + return "cargo"; + case UU_UNSUITABLE_TRANSPORT: + return "transport"; + case UU_NOT_TERRAIN: + return "terrain"; + default: + fc_assert_msg(FALSE, "Unexpected unit transform result %i", uu); + } + /* should not get here */ + return "\?"; +} + /**********************************************************************//** Return TRUE if players share research. **************************************************************************/ diff --git a/common/scriptcore/api_game_methods.h b/common/scriptcore/api_game_methods.h index bacc9077af..f700dc2caf 100644 --- a/common/scriptcore/api_game_methods.h +++ b/common/scriptcore/api_game_methods.h @@ -99,6 +99,12 @@ City_List_Link *api_methods_private_player_city_list_head(lua_State *L, int api_methods_player_culture_get(lua_State *L, Player *pplayer); bool api_methods_player_has_flag(lua_State *L, Player *pplayer, const char *flag); +Unit_Type *api_methods_player_can_upgrade(lua_State *L, Player *pplayer, + Unit_Type *utype); +bool api_methods_player_can_build_unit_direct(lua_State *L, Player *pplayer, + Unit_Type *utype); +bool api_methods_player_can_build_impr_direct(lua_State *L, Player *pplayer, + Building_Type *itype); /* Tech Type */ const char *api_methods_tech_type_rule_name(lua_State *L, Tech_Type *ptech); @@ -156,6 +162,9 @@ const Direction *api_methods_unit_orientation_get(lua_State *L, Unit *punit); Unit *api_methods_unit_transporter(lua_State *L, Unit *punit); Unit_List_Link *api_methods_private_unit_cargo_list_head(lua_State *L, Unit *punit); +bool api_methods_unit_can_upgrade(lua_State *L, Unit *punit, bool is_free); +const char *api_methods_unit_transform_problem(lua_State *L, Unit *punit, + Unit_Type *ptype); /* Unit Type */ bool api_methods_unit_type_has_flag(lua_State *L, Unit_Type *punit_type, diff --git a/common/scriptcore/tolua_game.pkg b/common/scriptcore/tolua_game.pkg index 216b47984d..11ba35252c 100644 --- a/common/scriptcore/tolua_game.pkg +++ b/common/scriptcore/tolua_game.pkg @@ -57,6 +57,7 @@ struct Unit { /* This used to be @ homecity_id, but it does not work with toluaxx. */ int homecity; + int veteran; const int id; }; @@ -84,6 +85,7 @@ struct Building_Type { struct Unit_Type { int build_cost; + Unit_Type *obsoleted_by; const int item_number @ id; }; @@ -174,6 +176,12 @@ module Player { @ culture(lua_State *L, Player *self); bool api_methods_player_has_flag @ has_flag (lua_State *L, Player *self, const char *flag); + Unit_Type *api_methods_player_can_upgrade + @ can_upgrade(lua_State *L, Player *pplayer, Unit_Type *utype); + bool api_methods_player_can_build_impr_direct + @ can_build_direct (lua_State *L, Player *pplayer, Building_Type *itype); + bool api_methods_player_can_build_unit_direct + @ can_build_direct (lua_State *L, Player *pplayer, Unit_Type *utype); } module methods_private { @@ -258,6 +266,10 @@ module Unit { @ transporter (lua_State *L, Unit *self); bool api_methods_unit_city_can_be_built_here @ is_on_possible_city_tile (lua_State *L, Unit *self); + bool api_methods_unit_can_upgrade + @ can_upgrade (lua_State *L, Unit *punit, bool is_free = TRUE); + const char *api_methods_unit_transform_problem + @ transform_problem (lua_State *L, Unit *punit, Unit_Type *ptype); Direction *api_methods_unit_orientation_get @ facing(lua_State *L, Unit *self); diff --git a/common/unit.c b/common/unit.c index 064b00a8d7..5ae9256948 100644 --- a/common/unit.c +++ b/common/unit.c @@ -1860,6 +1860,34 @@ static bool can_type_transport_units_cargo(const struct unit_type *utype, return TRUE; } +/**********************************************************************//** + Tests if something prevents punit from being transformed to to_unittype + where it is now, presuming its current position is valid. + + FIXME: the transport stack may still fail unit_transport_check in result +**************************************************************************/ +enum unit_upgrade_result +unit_transform_result(const struct unit *punit, + const struct unit_type *to_unittype) +{ + fc_assert_ret_val(NULL != to_unittype, UU_NO_UNITTYPE); + + if (!can_type_transport_units_cargo(to_unittype, punit)) { + return UU_NOT_ENOUGH_ROOM; + } + + if (punit->transporter != NULL) { + if (!can_unit_type_transport(unit_type_get(punit->transporter), + utype_class(to_unittype))) { + return UU_UNSUITABLE_TRANSPORT; + } + } else if (!can_exist_at_tile(&(wld.map), to_unittype, unit_tile(punit))) { + /* The new unit type can't survive on this terrain. */ + return UU_NOT_TERRAIN; + } + return UU_OK; +} + /**********************************************************************//** Tests if the unit could be updated. Returns UU_OK if is this is possible. @@ -1898,23 +1926,9 @@ enum unit_upgrade_result unit_upgrade_test(const struct unit *punit, } } - if (!can_type_transport_units_cargo(to_unittype, punit)) { - /* TODO: allow transported units to be reassigned. Check here - * and make changes to upgrade_unit. */ - return UU_NOT_ENOUGH_ROOM; - } - - if (punit->transporter != NULL) { - if (!can_unit_type_transport(unit_type_get(punit->transporter), - utype_class(to_unittype))) { - return UU_UNSUITABLE_TRANSPORT; - } - } else if (!can_exist_at_tile(&(wld.map), to_unittype, unit_tile(punit))) { - /* The new unit type can't survive on this terrain. */ - return UU_NOT_TERRAIN; - } - - return UU_OK; + /* TODO: allow transported units to be reassigned. Check here + * and make changes to upgrade_unit. */ + return unit_transform_result(punit, to_unittype); } /**********************************************************************//** @@ -1928,20 +1942,7 @@ bool unit_can_convert(const struct unit *punit) return FALSE; } - if (!can_type_transport_units_cargo(tgt, punit)) { - return FALSE; - } - - if (punit->transporter != NULL) { - if (!can_unit_type_transport(unit_type_get(punit->transporter), - utype_class(tgt))) { - return FALSE; - } - } else if (!can_exist_at_tile(&(wld.map), tgt, unit_tile(punit))) { - return FALSE; - } - - return TRUE; + return UU_OK == unit_transform_result(punit, tgt); } /**********************************************************************//** diff --git a/common/unit.h b/common/unit.h index 7e1fd87968..4dbc20f9a9 100644 --- a/common/unit.h +++ b/common/unit.h @@ -415,6 +415,9 @@ struct unit *transporter_for_unit(const struct unit *pcargo); struct unit *transporter_for_unit_at(const struct unit *pcargo, const struct tile *ptile); +enum unit_upgrade_result +unit_transform_result(const struct unit *punit, + const struct unit_type *to_unittype); enum unit_upgrade_result unit_upgrade_test(const struct unit *punit, bool is_free); enum unit_upgrade_result unit_upgrade_info(const struct unit *punit, diff --git a/server/scripting/api_server_edit.c b/server/scripting/api_server_edit.c index 172972024f..4c4cc807f0 100644 --- a/server/scripting/api_server_edit.c +++ b/server/scripting/api_server_edit.c @@ -68,6 +68,33 @@ static void deprecated_semantic_warning(const char *call, const char *aka, } } +/**********************************************************************//** + A wrapper around transform_unit() that correctly processes + some unsafe requests. punit and to_unit must not be NULL. +**************************************************************************/ +static bool +ur_transform_unit(struct unit *punit, const struct unit_type *to_unit, + int vet_loss) +{ + if (UU_OK == unit_transform_result(punit, to_unit)) { + /* Avoid getting overt veteranship if a user requests increasing it */ + if (vet_loss < 0) { + int vl = utype_veteran_levels(to_unit); + + vl = punit->veteran - vl + 1; + if (vl >= 0) { + vet_loss = 0; + } else { + vet_loss = MAX(vet_loss, vl); + } + } + transform_unit(punit, to_unit, vet_loss); + return TRUE; + } else { + return FALSE; + } +} + /**********************************************************************//** Unleash barbarians on a tile, for example from a hut **************************************************************************/ @@ -540,6 +567,38 @@ void api_edit_unit_turn(lua_State *L, Unit *punit, Direction dir) } } +/**********************************************************************//** + Upgrade punit for free in the default manner, lose vet_loss vet levels. + Returns if the upgrade was possible. +**************************************************************************/ +bool api_edit_unit_upgrade(lua_State *L, Unit *punit, int vet_loss) +{ + const struct unit_type *ptype; + + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, punit, FALSE); + + ptype = can_upgrade_unittype(unit_owner(punit), unit_type_get(punit)); + if (!ptype) { + return FALSE; + } + return ur_transform_unit(punit, ptype, vet_loss); +} + +/**********************************************************************//** + Transform punit to ptype, decreasing vet_loss veteranship levels. + Returns if the transformation was possible. +**************************************************************************/ +bool api_edit_unit_transform(lua_State *L, Unit *punit, Unit_Type *ptype, + int vet_loss) +{ + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, punit, FALSE); + LUASCRIPT_CHECK_ARG_NIL(L, ptype, 3, Unit_Type, FALSE); + + return ur_transform_unit(punit, ptype, vet_loss); +} + /**********************************************************************//** Kill the unit. **************************************************************************/ diff --git a/server/scripting/api_server_edit.h b/server/scripting/api_server_edit.h index 68cd6460ae..3d70fda5e8 100644 --- a/server/scripting/api_server_edit.h +++ b/server/scripting/api_server_edit.h @@ -61,6 +61,10 @@ bool api_edit_perform_action_unit_vs_self(lua_State *L, Unit *punit, void api_edit_unit_turn(lua_State *L, Unit *punit, Direction dir); +bool api_edit_unit_upgrade(lua_State *L, Unit *punit, int vet_loss); +bool api_edit_unit_transform(lua_State *L, Unit *punit, Unit_Type *ptype, + int vet_loss); + void api_edit_unit_kill(lua_State *L, Unit *punit, const char *reason, Player *killer); diff --git a/server/scripting/tolua_server.pkg b/server/scripting/tolua_server.pkg index 7172465ac6..fab67a828e 100644 --- a/server/scripting/tolua_server.pkg +++ b/server/scripting/tolua_server.pkg @@ -501,6 +501,14 @@ module Player { @ trait_current_mod (lua_State *L, Player *pplayer, const char *tname); } +/* 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) +module game { + extern const int game_server_autoupgrade_veteran_loss @ autoupgrade_veteran_loss; + extern const int game_server_upgrade_veteran_loss @ upgrade_veteran_loss; +} + /* Additions to common Nation_Type module. */ module Nation_Type { int api_methods_nation_trait_min @@ -511,3 +519,13 @@ module Nation_Type { @ trait_default (lua_State *L, Nation_Type *pnation, const char *tname); } + +/* Additions to common Unit module. */ +module Unit { + bool api_edit_unit_upgrade + @ upgrade (lua_State *L, Unit *punit, int vet_loss = 0); + bool api_edit_unit_transform + @ transform (lua_State *L, Unit *punit, Unit_Type *ptype, + int vet_loss = 0); +} + -- 2.32.0