diff -Nur -X../diff_ignore ./client/client_main.c ../LT2_5-my/client/client_main.c --- ./client/client_main.c 2019-09-22 19:25:34.018168568 +0300 +++ ../LT2_5-my/client/client_main.c 2019-08-01 23:37:42.793397996 +0300 @@ -147,6 +147,7 @@ bool auto_spawn = FALSE; /* TRUE = skip main menu, start local server */ bool in_ggz = FALSE; enum announce_type announce; +enum fc_tristate got_tech = TRI_MAYBE; struct civclient client; @@ -875,6 +876,7 @@ can_slide = FALSE; unit_focus_update(); can_slide = TRUE; + got_tech = TRI_MAYBE; /* No way know if the player has just got a tech */ set_client_page(PAGE_GAME); /* Find something sensible to display instead of the intro gfx. */ center_on_something(); diff -Nur -X../diff_ignore ./client/client_main.h ../LT2_5-my/client/client_main.h --- ./client/client_main.h 2019-09-22 19:25:34.018168568 +0300 +++ ../LT2_5-my/client/client_main.h 2019-08-01 23:37:42.885398827 +0300 @@ -37,7 +37,7 @@ * to a server. In this state, neither game nor ruleset * is in effect. * C_S_PREPARING: Connected in pregame. Game and ruleset are done. - * C_S_RUNNING: Connected ith game in progress. + * C_S_RUNNING: Connected with game in progress. * C_S_OVER: Connected with game over. */ enum client_states { @@ -80,6 +80,7 @@ extern bool auto_spawn; extern bool waiting_for_end_turn; extern bool in_ggz; +extern enum fc_tristate got_tech; struct global_worklist_list; /* Defined in global_worklist.[ch]. */ Двоичные файлы ./client/freeciv-gtk2 и ../LT2_5-my/client/freeciv-gtk2 различаются Двоичные файлы ./client/freeciv-sdl и ../LT2_5-my/client/freeciv-sdl различаются diff -Nur -X../diff_ignore ./client/gui-gtk-2.0/repodlgs.c ../LT2_5-my/client/gui-gtk-2.0/repodlgs.c --- ./client/gui-gtk-2.0/repodlgs.c 2019-09-22 19:25:34.058175192 +0300 +++ ../LT2_5-my/client/gui-gtk-2.0/repodlgs.c 2019-08-02 23:19:36.421363530 +0300 @@ -74,6 +74,8 @@ GtkProgressBar *progress_bar; GtkLabel *goal_label; GtkLayout *drawing_area; + GtkWidget *ch_now; + Tech_type_id target_tech; /* for ch_now dialog */ }; static GtkListStore *science_report_store_new(void); @@ -105,6 +107,9 @@ static struct science_report science_report = { NULL, }; static bool science_report_no_combo_callback = FALSE; +static void science_report_confirm_research(Tech_type_id tech); +static void confirm_change_response(GtkWidget *w, gint response, + gpointer data); /* Those values must match the function science_report_store_new(). */ enum science_report_columns { @@ -187,6 +192,143 @@ log_error("%s(): Tech %d not found in the combo.", __FUNCTION__, tech); } + +/************************************************************************//** + Handles changing research: if changing will have effect immediately, + asks to confirm and warns about bulb loss. +****************************************************************************/ +static void science_report_confirm_research(Tech_type_id tech) +{ + struct player_research *research = + (client_has_player() ? player_research_get(client_player()) : NULL);; + GtkWidget *chpop; + int bulbs, loss; + int cost = base_total_bulbs_required(client_player(), tech, FALSE); + int min_cost = (int) (cost * min_leakage_ratio(client_player(), tech)); + struct advance *vap = valid_advance_by_number(tech); + char buf[511]; + struct option *tpo; + + fc_assert(NULL != vap); + fc_assert(0 != cost); + + bulbs = (tech == research->researching_saved) + ? research->bulbs_researching_saved + : research->bulbs_researched; + + /* In some cases you have more bulbs than your current tech costs... */ + if (!(TRI_YES == got_tech) && (tech != research->researching_saved)) { + tpo = optset_option_by_name(server_optset, "techpenalty"); + + if (!tpo) { + log_error("techpenalty server option unknown"); + loss = 0; + } else { + loss = option_int_get(tpo) * bulbs / 100; + } + } else { + loss = 0; + } + fc_assert(bulbs >= loss); + if(!min_cost) { + min_cost = 1; + } + log_debug("Consider switching to %s with %d bulbs (%d|%d), %d losing, " + "%d..%d expecting to spend", + advance_rule_name(advance_by_number(tech)), bulbs, + research->bulbs_researching_saved, research->bulbs_researched, + loss, min_cost, cost); + + if ((TRI_NO == got_tech ? bulbs - loss : bulbs) >= min_cost) { + if (min_cost != cost) { + fc_snprintf(buf, sizeof(buf), + PL_("(The cost might be down to %d bulb" + " due to tech leakage.)\n", + "(The cost might be down to %d bulbs" + " due to tech leakage.)\n", + min_cost), + min_cost); + } else { + buf[0] = '\0'; + } + if (loss > 0) { + if (TRI_NO == got_tech) { + cat_snprintf(buf, sizeof(buf), + PL_("%d bulb will be lost.\n", + "%d bulbs will be lost.\n", loss), loss); + } else { + cat_snprintf(buf, sizeof(buf), + PL_("%d bulb may be lost " + "if you have got no advance this turn.\n", + "%d bulbs may be lost " + "if you have got no advance this turn.\n", loss), + loss); + } + } + + if ((TRI_MAYBE != got_tech) && (min_cost == cost)) { + cat_snprintf(buf, sizeof(buf), + PL_("%d bulb will remain.\n", + "%d bulbs will remain.\n", bulbs - cost - loss), + bulbs - cost - loss); + } else { + int max_rem = (TRI_NO == got_tech + ? bulbs - min_cost - loss + : bulbs - min_cost); + int min_rem = (TRI_YES == got_tech + ? bulbs - cost + : bulbs - cost - loss); + cat_snprintf(buf, sizeof(buf), + /* FIXME: in some languages does PL_ act good here? */ + PL_("%d to %d bulb will remain.\n", + "%d to %d bulbs will remain.\n", + max_rem), + MAX(0, min_rem), + max_rem + ); + } + + chpop = gtk_message_dialog_new(NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_OK_CANCEL, + PL_("Research %s for %d bulb?\n%s", + "Research %s for %d bulbs?\n%s", cost), + advance_name_translation(vap), cost, buf); + setup_dialog(chpop, gui_dialog_get_toplevel(science_report.shell)); + + gtk_window_set_title(GTK_WINDOW(chpop), + (bulbs - loss >= cost) ? _("Research now!") + : _("Immediate research might happen!")); + gtk_window_set_position(GTK_WINDOW(chpop), GTK_WIN_POS_CENTER_ON_PARENT); + + science_report.ch_now = chpop; + science_report.target_tech = tech; + g_signal_connect(chpop, "response", + G_CALLBACK(confirm_change_response), &science_report); + + gtk_window_present(GTK_WINDOW(chpop)); + } else { + dsend_packet_player_research(&client.conn, tech); + } +} + +/**********************************************************************//** + User has responded to change research confirmation +**************************************************************************/ +static void confirm_change_response(GtkWidget *w, gint response, + gpointer data) +{ + struct science_report *pdialog = data; + + if (response == GTK_RESPONSE_OK) { + dsend_packet_player_research(&client.conn, pdialog->target_tech); + } + gtk_widget_destroy(w); + + pdialog->ch_now = NULL; +} + /**************************************************************************** Change tech goal, research or open help dialog. ****************************************************************************/ @@ -209,7 +351,8 @@ /* LMB: set research or research goal */ switch (player_invention_state(client_player(), tech)) { case TECH_PREREQS_KNOWN: - dsend_packet_player_research(&client.conn, tech); + /* If we can research target tech immediately, popup a dialog */ + science_report_confirm_research(tech); break; case TECH_UNKNOWN: dsend_packet_player_tech_goal(&client.conn, tech); @@ -473,7 +616,7 @@ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data))) { popup_help_dialog_typed(tech_name, HELP_TECH); } else if (can_client_issue_orders()) { - dsend_packet_player_research(&client.conn, tech); + science_report_confirm_research(tech); } /* Revert, or we will be not synchron with the server. */ science_report_combo_set_active(combo, player_research_get @@ -620,6 +763,8 @@ science_report_update(preport); gui_dialog_show_all(preport->shell); + preport->ch_now = NULL; + preport->target_tech = A_LAST; /* This must be _after_ the dialog is drawn to really center it ... */ science_report_redraw(preport); @@ -632,6 +777,9 @@ { fc_assert_ret(NULL != preport); + if (preport->ch_now) { + gtk_widget_destroy(preport->ch_now); + } gui_dialog_destroy(preport->shell); fc_assert(NULL == preport->shell); diff -Nur -X../diff_ignore ./client/luascript/api_client_base.c ../LT2_5-my/client/luascript/api_client_base.c --- ./client/luascript/api_client_base.c 2019-09-22 19:25:34.106183141 +0300 +++ ../LT2_5-my/client/luascript/api_client_base.c 2019-09-22 15:57:51.759181229 +0300 @@ -16,9 +16,14 @@ #endif /* common */ +#include "fc_types.h" + #include "connection.h" #include "featured_text.h" #include "unit.h" +#include "road.h" +#include "base.h" +#include "packets.h" /*struct packet_unit_orders*/ /* common/scriptcore */ #include "luascript.h" @@ -26,10 +31,50 @@ /* client */ #include "chatline_common.h" #include "client_main.h" +#include "control.h" #include "goto.h" #include "api_client_base.h" +/* some definitions for older lua */ +#if LUA_VERSION_NUM < 503 +#include /* isfinite */ +static bool lua_isinteger(lua_State *L, int idx) +{ + if (lua_isnumber(L, idx)) { + lua_Number n = lua_tonumber(L, idx); + return isfinite(n); + } + return FALSE; +} + +static int lua_geti(lua_State *L, int idx, int tidx) +{ + idx = lua_absindex(L, idx); /* just mminusing the negative does it bad */ + lua_pushinteger(L, tidx); + fc_assert_msg(LUA_TTABLE == lua_type(L, idx), + "Not a table but %s at [%d]", tolua_typename(L, idx), idx); + lua_gettable(L, idx); + return lua_type(L, -1); +} +#endif + +/* Move this to some header files [[*/ +/* NB! If you return from it, the current index is still in L! */ +#define sequence_iterate(__luastate__, __seq__) \ + for (int _seqno = 1; LUA_TNIL != lua_geti(__luastate__, __seq__, _seqno);\ + _seqno++) +#define sequence_iterate_end(__luastate__) lua_pop(__luastate__, 1) +#define macro2str_base(__macro__) #__macro__ +#define macro2str(__macro__) macro2str_base(__macro__) +/* ]] */ + +static enum unit_orders pchar2order(const char *order); +static bool add_top(struct packet_unit_orders *p, lua_State *L); +/* static void orders_append_path(struct packet_unit_orders *p, + struct pf_path *path); */ +enum direction8 top2dir8(lua_State *L); + /***************************************************************************** Return the player the client is connected to. *****************************************************************************/ @@ -109,3 +154,668 @@ output_window_printf(ftc_chat_luaconsole, "%s", msg); } + +/**********************************************************************//*** + Add unit to the selection +***************************************************************************/ +void api_client_unit_focus_add(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L); + LUASCRIPT_CHECK_ARG_NIL(L, punit, 2, Unit); + + unit_focus_add(punit); +} + + +/**********************************************************************//*** + Remove unit from the selection +***************************************************************************/ +void api_client_unit_focus_remove(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L); + LUASCRIPT_CHECK_ARG_NIL(L, punit, 2, Unit); + + unit_focus_remove(punit); +} + +/**********************************************************************//*** + Is a unit in focus +***************************************************************************/ +bool api_client_unit_is_in_focus(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, punit, NULL); + + return unit_is_in_focus(punit); +} + +/**********************************************************************//*** + Number of units in focus +***************************************************************************/ +int api_client_num_units_in_focus(lua_State *L) +{ + LUASCRIPT_CHECK_STATE(L, -1); + return get_num_units_in_focus(); +} + +/**********************************************************************//*** + units in focus (for use in iterators) +***************************************************************************/ +Unit_List_Link *api_client_private_focus_head(lua_State *L) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + return unit_list_head(get_units_in_focus()); +} + +/**********************************************************************//*** + Tries to found a city on the current tile with punit. + If no name is specified, it will be requested in the standard way. +***************************************************************************/ +void api_client_unit_build_city(lua_State *L, Unit *punit, + const char *name) +{ + LUASCRIPT_CHECK_STATE(L); + LUASCRIPT_CHECK_SELF(L, punit); + + if (name) { + dsend_packet_unit_build_city(&client.conn, punit->id, name); + } else { + request_unit_build_city(punit); + } +} + +/**********************************************************************//*** + Build a road road on current tile, returns true if it's possible. + If no name is specified, builds next possible road +***************************************************************************/ +bool api_client_unit_road(lua_State *L, Unit *punit, const char *road) +{ + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, punit, FALSE); + + struct road_type *proad = road ? road_type_by_rule_name(road) : NULL; + + if (NULL != proad) { + struct act_tgt tgt = {.type = ATT_ROAD, .obj.road = road_number(proad)}; + + if (can_unit_do_activity_targeted(punit, ACTIVITY_GEN_ROAD, &tgt)) { + request_new_unit_activity_road(punit, proad); + return TRUE; + } + } + return FALSE; +} + +/**********************************************************************//*** + Build next possible road on current tile, returns true if it's possible. +***************************************************************************/ +bool api_client_unit_next_road(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, punit, FALSE); + + struct road_type *proad = + next_road_for_tile(unit_tile(punit), unit_owner(punit), punit); + if (NULL != proad) { + struct act_tgt tgt = {.type = ATT_ROAD, .obj.road = road_number(proad)}; + + if (can_unit_do_activity_targeted(punit, ACTIVITY_GEN_ROAD, &tgt)) { + request_new_unit_activity_road(punit, proad); + return TRUE; + } + } + return FALSE; +} + +/**********************************************************************//*** + Build a base base (required) on current tile, returns true if it's valid. +***************************************************************************/ +bool api_client_unit_base(lua_State *L, Unit *punit, const char *base) +{ + struct base_type *pbase; + + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, punit, FALSE); + if (base) { + pbase = base_type_by_rule_name(base); + if ((NULL != pbase) + && can_unit_do_activity_base(punit, pbase->item_number)) { + request_new_unit_activity_base(punit, pbase); + return TRUE; + } + } + return FALSE; +} + +/**********************************************************************//*** + Mines current tile, returns true if it's valid. +***************************************************************************/ +bool api_client_unit_mine(lua_State *L, Unit *punit) +{ + if (can_unit_do_activity(punit, ACTIVITY_MINE)) { + request_new_unit_activity(punit, ACTIVITY_MINE); + return TRUE; + } + return FALSE; +} + +/**********************************************************************//*** + Irrigates current tile, returns true if it's valid. +***************************************************************************/ +bool api_client_unit_irrigate(lua_State *L, Unit *punit) +{ + if (can_unit_do_activity(punit, ACTIVITY_IRRIGATE)) { + request_new_unit_activity(punit, ACTIVITY_IRRIGATE); + return TRUE; + } + return FALSE; +} + +/**********************************************************************//*** + Transforms current tile, returns true if it's valid. +***************************************************************************/ +bool api_client_unit_transform(lua_State *L, Unit *punit) +{ + if (can_unit_do_activity(punit, ACTIVITY_TRANSFORM)) { + request_new_unit_activity(punit, ACTIVITY_TRANSFORM); + return TRUE; + } + return FALSE; +} + +/**********************************************************************//*** + Checks if a unit is transporting any cargo (works for foreign ones) +***************************************************************************/ +bool api_client_unit_occupied(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_SELF(L, punit, FALSE); + return punit->client.occupied; +} + +/**********************************************************************//*** + Requests an untargeted activity. Does not do targeted ones. +***************************************************************************/ +void api_client_unit_request_activity(lua_State *L, Unit *punit, + char *activity_name) +{ + enum unit_activity act; + + LUASCRIPT_CHECK_STATE(L); + LUASCRIPT_CHECK_SELF(L, punit); + LUASCRIPT_CHECK_ARG_NIL(L, activity_name, 3, string); + act = unit_activity_by_name(activity_name, strcmp); + if ((act != unit_activity_invalid()) + && can_unit_do_activity(punit, act)) { + request_new_unit_activity(punit, act); + } +} + +/**********************************************************************//*** + Helper function modified from savegame.c + Returns order from a string, cares only first byte except for '[hH]'. +***************************************************************************/ +static enum unit_orders pchar2order(const char *order) +{ + fc_assert(order); + + switch (*order) { + case 'm': + case 'M': + return ORDER_MOVE; + case 'w': + case 'W': + return ORDER_FULL_MP; + case 'b': + case 'B': + return ORDER_BUILD_CITY; + case 'a': + case 'A': + return ORDER_ACTIVITY; + case 'd': + case 'D': + return ORDER_DISBAND; + case 'u': + case 'U': + return ORDER_BUILD_WONDER; + case 't': + case 'T': + case 'e': + case 'E': + return ORDER_TRADE_ROUTE; + case 'h': + case 'H': + if(!(fc_strcasecmp(order, "HELPWONDER") + || fc_strcasecmp(order, "HELP WONDER"))) { + return ORDER_BUILD_WONDER; + } else { + return ORDER_HOMECITY; + } + } + + return ORDER_LAST; +} + +/**********************************************************************//*** + Helper function, tries to interpret lua top as a direction. +***************************************************************************/ +enum direction8 top2dir8(lua_State *L) +{ + enum direction8 dir; + switch (lua_type(L, -1)) { + case LUA_TNUMBER: + dir = (enum direction8) lua_tointeger(L, -1); + break; + case LUA_TSTRING: + dir = direction8_by_name(lua_tostring(L, -1), strcmp); + if (!is_valid_dir(dir)) { + dir = (enum direction8) (lua_tostring(L, -1)[0] - '0'); + if (dir < 0 || dir > direction8_invalid()) { + dir = direction8_invalid(); + } + } + break; + case LUA_TUSERDATA: + if (!strcmp(tolua_typename(L, -1), "Direction")) { + dir = *((enum direction8 *) lua_touserdata(L, -1)); + } + default: + dir = direction8_invalid(); + } + return dir; +} + +/**********************************************************************//*** + Helper function, gets another order from top stack value and pops it. + Returns false iff the value is invalid (performs minimal checks). +***************************************************************************/ +static bool add_top(struct packet_unit_orders *p, lua_State *L) +{ + enum unit_orders order = ORDER_LAST; + enum direction8 dir = -1; + enum unit_activity activity = ACTIVITY_LAST; + Base_type_id base = BASE_NONE; + Road_type_id road = ROAD_NONE; + union { + struct road_type *rtype; + struct base_type *btype; + } ptr; + int times; + struct unit *pu; /* obtained only for auto-filling a road in */ + + LUASCRIPT_CHECK_STATE(L, FALSE); + /* stack: ordval */ + log_verbose("Trying to add %s to orders from L top", tolua_typename(L, -1)); + switch (lua_type(L, -1)) { + case LUA_TNUMBER: + order = lua_tointeger(L, -1); /* 0 if bad integer */ + break; + case LUA_TSTRING: + /* Road name, base name, action name, direction or order specifier */ + ptr.rtype = road_type_by_rule_name(lua_tostring(L, -1)); + if (NULL != ptr.rtype) { + road = road_number(ptr.rtype); + break; + } + ptr.btype = base_type_by_rule_name(lua_tostring(L, -1)); + if (NULL != ptr.btype) { + base = base_number(ptr.btype); + break; + } + activity = unit_activity_by_name(lua_tostring(L, -1), strcmp); + if (unit_activity_is_valid(activity)) { + break; + } + dir = direction8_by_name(lua_tostring(L, -1), strcmp); + if (is_valid_dir(dir)) { + break; + } else { /* raw dir8 in a character */ + dir = (enum direction8) (lua_tostring(L, -1)[0] - '0'); + if (dir < direction8_invalid() && is_valid_dir(dir)) { + break; + } + } + order = pchar2order(lua_tostring(L, -1)); + if (ORDER_LAST != order) { + break; + } + /* All checks and autofilling go later, but something must it be */ + luaL_error(L, "Unrecognized string '%s' supplied as an order", + lua_tostring(L, -1)); + return FALSE; + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: /* FIXME: One seems to not work */ + /* Direction (TODO: or Tile) */ + if (!strcmp(tolua_typename(L, -1), "Direction")) { + dir = *((enum direction8 *) lua_touserdata(L, -1)); + /* } else if (!strcmp(lua_tostring(L, -1), "Tile")) { */ + /* TODO: goto tile */ + } else { + luaL_error(L, "Invalid user type %s supplied as an order", + tolua_typename(L, -1)); + lua_pop(L, 1); /* stack: */ + return FALSE; + } + break; + case LUA_TTABLE: + /*TODO: to_tile = Tile => execute on all tiles along auto path */ + lua_getfield(L, -1, "times"); /* stack: ordval times */ + if (lua_isinteger(L, -1)) { + /* sequence of orders, repeat it times */ + times = lua_tointeger(L, -1); + lua_pop(L, 1); /* stack: ordval */ + log_verbose("Processing subsequence (%d times) in L[%d]", times, + lua_gettop(L)); + if ((0 <= times) && (times <= MAX_LEN_ROUTE)) { + int ptnn = p->length, ptl; + + if (!times) { + lua_pop(L, 1);/* stack: */ + return TRUE; + } + sequence_iterate(L, -1) { + /* stack: ordval ordval[i]*/ + if (p->length == MAX_LEN_ROUTE) { + luaL_error(L, "Orders package overflow"); + lua_pop(L, 2); /* stack: */ + return FALSE; + } /*stack: ordval ordval[i] */ + if (!add_top(p, L)) { /* stack: ordval */ + /* Invalid order in inner list. + * Pop value and errmsg are done by the called function. */ + lua_pop(L, 1); /* stack: */ + return FALSE; + }/* stack: ordval */ + } sequence_iterate_end(L);/* stack: ordval */ + ptl = p->length - ptnn; + + if ((p->length = ptnn + ptl * times) >= MAX_LEN_ROUTE) { + luaL_error(L, "Orders package overflow"); + lua_pop(L, 1); /* stack: */ + return FALSE; + } + + while (times--) { + for (int i = ptnn; i < p->length; i += ptl) { + p->orders[i + ptl] = p->orders[i]; + p->dir[i + ptl] = p->dir[i]; + p->activity[i + ptl] = p->activity[i]; + p->road[i + ptl] = p->road[i]; + p->base[i + ptl] = p->base[i]; + } + } + + return TRUE; + } else { + luaL_error(L, "times specifier %d out of diapazone (0-" + macro2str(MAX_LEN_ROUTE)")", times); + lua_pop(L, 1);/* stack: ordval */ + return FALSE; + } + lua_pop(L, 1); /* stack: */ + } else if (!lua_isnil(L, -1)) { + luaL_error(L, "Wrong times specifier (%s, must be integer)", + tolua_typename(L, -1)); + lua_pop(L, 2);/* stack: */ + return FALSE; + } else { + lua_pop(L, 1);/* stack: ordval */ + } + /* single order */ + lua_getfield(L, -1, "order");/* stack: ordval ordval.order */ + if (lua_isinteger(L, -1)) { + order = lua_tointeger(L, -1); + } else if (lua_isstring(L, -1)) { + order = pchar2order(lua_tostring(L, -1)); + } else if (!lua_isnil(L, -1)) { + luaL_error(L, "Invalid order specifier type %s", + tolua_typename(L, -1)); + lua_pop(L, 2); + return FALSE; + } + lua_pop(L, 1);/* stack: ordval */ + + lua_getfield(L, -1, "dir");/* stack: ordval ordval.dir */ + dir = top2dir8(L); + if (!lua_isnil(L, -1) && !is_valid_dir(dir)) { + luaL_error(L, "Invalid direction specifier of type %s", + tolua_typename(L, -1)); + lua_pop(L, 2); + return FALSE; + } + lua_pop(L, 1);/* stack: ordval */ + + lua_getfield(L, -1, "activity");/* stack: ordval ordval.activity */ + if (lua_isstring(L, -1)) { + activity = unit_activity_by_name(lua_tostring(L, -1), strcmp); + if (!unit_activity_is_valid(activity)) { + luaL_error(L, "Invalid activity specifier '%s'", + lua_tostring(L, -1)); + lua_pop(L, 2); + return FALSE; + } + } else if (!lua_isnil(L, -1)) { + luaL_error(L, "Invalid activity specifier type %s", + tolua_typename(L, -1)); + lua_pop(L, 2); + return FALSE; + } + lua_pop(L, 1);/* stack: ordval */ + + lua_getfield(L, -1, "base");/* stack: ordval ordval.base */ + if (lua_isstring(L, -1)) { + ptr.btype = base_type_by_rule_name(lua_tostring(L, -1)); + if (NULL != ptr.btype) { + base = base_number(ptr.btype); + } else { + luaL_error(L, "Invalid base specifier '%s'", + lua_tostring(L, -1)); + lua_pop(L, 2); + return FALSE; + } + } else if (!lua_isnil(L, -1)) { + luaL_error(L, "Invalid base specifier type %s", + tolua_typename(L, -1)); + lua_pop(L, 2); + return FALSE; + } + lua_pop(L, 1);/* stack: ordval */ + + lua_getfield(L, -1, "road");/* stack: ordval ordval.road */ + if (lua_isstring(L, -1)) { + ptr.rtype = road_type_by_rule_name(lua_tostring(L, -1)); + if (NULL != ptr.rtype) { + road = road_number(ptr.rtype); + } else { + luaL_error(L, "Invalid road specifier '%s'", + lua_tostring(L, -1)); + lua_pop(L, 2); + return FALSE; + } + } else if (!lua_isnil(L, -1)) { + luaL_error(L, "Invalid road specifier type %s", + tolua_typename(L, -1)); + lua_pop(L, 2); + return FALSE; + } + lua_pop(L, 1);/* stack: ordval */ + /* Maybe, somehow interpret sequences here? */ + break; + default: + luaL_error(L, "Invalid type %s supplied as an order", + lua_typename(L, lua_type(L, -1))); + lua_pop(L, 1);/* stack: */ + return FALSE; + } + + + /* Autofilling and checking */ + if (!unit_activity_is_valid(activity)) { + if (base != BASE_NONE) { + if (road != ROAD_NONE) { + luaL_error(L, "Can not build base and road on the same step"); + lua_pop(L, 1);/* stack: */ + return FALSE; + } + activity = ACTIVITY_BASE; + } else if (road != ROAD_NONE) { + activity = ACTIVITY_GEN_ROAD; + } + } else if (ACTIVITY_GEN_ROAD == activity && ROAD_NONE == road) { + pu = game_unit_by_number(p->unit_id); + road + = next_road_for_tile(index_to_tile(p->dest_tile), pu->owner, pu)->id; + } /* no such a handy base selector exists yet */ + if (order < ORDER_MOVE || order >= ORDER_LAST) { + if (dir <= direction8_invalid() && is_valid_dir(dir)) { + order = ORDER_MOVE; + } else { + dir = direction8_invalid(); + } + /* Activity suppresses direction, just for if we need it for a hack */ + if (unit_activity_is_valid(activity)) { + order = ORDER_ACTIVITY; + } + } + + /* Writing it to the package's next line */ + times = p->length; + fc_assert_ret_val(0 <= times && times < MAX_LEN_ROUTE, FALSE); + + p->orders[times] = order; + p->dir[times] = dir; + p->activity[times] = activity; + p->road[times] = road; + p->base[times] = base; + p->length++; + + /* Shifting destintion tile. */ + if ((ORDER_MOVE == order) && is_valid_dir(dir)) { + p->dest_tile = tile_index(mapstep(index_to_tile(p->dest_tile), dir)); + } + + lua_pop(L, 1);/*stack: */ + return TRUE; +} + +/**********************************************************************//*** + Requests a sequence of actions recorded in Lua table or a string. + Examples of usage: + * u:give_orders{"Mine", "South", "South", "Road", {base = "Fortress"}} + * u:give_orders({"Southwest", + {"Road", "Irrigate", "South", times = 3}}, true) + Raises Lua error if not set up properly. +***************************************************************************/ +void api_client_unit_order_sequence(lua_State *L, Unit *punit, + lua_Object seq, + bool vigilant, bool rep) +{ + struct packet_unit_orders p; + struct tile *curr = unit_tile(punit); + + LUASCRIPT_CHECK_STATE(L); + LUASCRIPT_CHECK_SELF(L, punit); + + p.unit_id = punit->id; + p.dest_tile = (p.src_tile = tile_index(curr)); /* shifted by add_top() */ + p.length = 0; + p.repeat = rep; + p.vigilant = vigilant; + + switch (lua_type(L, seq)) { + case LUA_TTABLE: + sequence_iterate(L, seq) {/* push seq[i] */ + if (p.length < MAX_LEN_ROUTE) { + if (!add_top(&p, L)) { /* pop seq[i] */ + break; /* for */ + } + } else { + lua_pop(L, 1); + luaL_error(L, "Orders package overflow"); + return; + } + } sequence_iterate_end(L); + lua_pop(L, 1); /* pop final nil */ + break; /* case */ + default: + luaL_error(L, "Unsupported type %s passed as an order sequence", + tolua_typename(L, seq)); + return; + } + clear_unit_orders(punit); + send_packet_unit_orders(&client.conn, &p); +} + +/**********************************************************************//*** + Implements the form + * u:give_orders"01w117t" + +***************************************************************************/ +void api_client_unit_order_string(lua_State *L, Unit *punit, + const char* seq, bool vigilant, bool rep) +{ + struct packet_unit_orders p; + struct tile *curr = unit_tile(punit); + int i; + + LUASCRIPT_CHECK_STATE(L); + LUASCRIPT_CHECK_SELF(L, punit); + + p.unit_id = punit->id; + p.dest_tile = (p.src_tile = tile_index(curr)); + p.repeat = rep; + p.vigilant = vigilant; + + for (i = 0; seq[i]; i++) { + if (is_valid_dir((enum direction8) (seq[i] - '0'))) { + p.orders[i] = ORDER_MOVE; + p.dir[i] = seq[i] - '0'; + } else { + switch (seq[i]) { + case 'h': + case 'H':/* Build wonders with 'U', homecity is more necessary */ + p.orders[i] = ORDER_HOMECITY; + break; + case 'a': + case 'A': /* Activity requires specification, not implemented */ + luaL_error(L, "Activities not supported in an order string"); + return; + case 'm': + case 'M': /* Move requires direction, use '0'...'9' */ + luaL_error(L, "Describe movement orders in a string with '0'..'7'" + " (map Northwest..Southeast)"); + return; + default: + if (ORDER_LAST == (p.orders[i] = pchar2order(&seq[i]))) { + luaL_error(L, "Wrong character '%c' in order string", seq[i]); + return; + } + } + p.dir[i] = -1; + } + p.activity[i] = ACTIVITY_LAST; + p.base[i] = BASE_NONE; + p.road[i] = ROAD_NONE; + } /* for */ + p.length = i; + + clear_unit_orders(punit); + send_packet_unit_orders(&client.conn, &p); +} + +/**********************************************************************//*** + Checks if a city is not empty (works for foreign ones) +***************************************************************************/ +bool api_client_city_occupied(lua_State *L, City *pcity) +{ + LUASCRIPT_CHECK_SELF(L, pcity, FALSE); + return pcity->client.occupied; +} + +/**********************************************************************//*** + Checks if a city has visible walls (works for foreign ones) +***************************************************************************/ +bool api_client_city_walled(lua_State *L, City *pcity) +{ + LUASCRIPT_CHECK_SELF(L, pcity, FALSE); + return pcity->client.walls; +} diff -Nur -X../diff_ignore ./client/luascript/api_client_base.h ../LT2_5-my/client/luascript/api_client_base.h --- ./client/luascript/api_client_base.h 2019-01-19 17:40:00.495912862 +0300 +++ ../LT2_5-my/client/luascript/api_client_base.h 2019-09-22 00:55:05.204587629 +0300 @@ -25,12 +25,36 @@ Player *api_client_player(lua_State *L); void api_client_chat_base(lua_State *L, const char *msg); +bool api_client_city_occupied(lua_State *L, City *pcity); +bool api_client_city_walled(lua_State *L, City *pcity); void api_client_unit_airlift(lua_State *L, Unit *punit, City *pcity); void api_client_unit_load(lua_State *L, Unit *pcargo, Unit *ptransport); void api_client_unit_move(lua_State *L, Unit *punit, Tile *ptile); void api_client_unit_upgrade(lua_State *L, Unit *punit); void api_client_diplomat_action(lua_State *L, Unit *pdiplo, int target_id, int value, int action); +void api_client_unit_focus_add(lua_State *L, Unit *punit); +void api_client_unit_focus_remove(lua_State *L, Unit *punit); +bool api_client_unit_is_in_focus(lua_State *L, Unit *punit); +int api_client_num_units_in_focus(lua_State *L); +Unit_List_Link *api_client_private_focus_head(lua_State *L); + +void api_client_unit_build_city(lua_State *L, Unit *punit, + const char *name); +bool api_client_unit_road(lua_State *L, Unit *punit, const char *road); +bool api_client_unit_next_road(lua_State *L, Unit *punit); +bool api_client_unit_base(lua_State *L, Unit *punit, const char *base); +bool api_client_unit_mine(lua_State *L, Unit *punit); +bool api_client_unit_irrigate(lua_State *L, Unit *punit); +bool api_client_unit_occupied(lua_State *L, Unit *punit); +bool api_client_unit_transform(lua_State *L, Unit *punit); +void api_client_unit_request_activity(lua_State *L, Unit *punit, + char *activity_name); +void api_client_unit_order_sequence(lua_State *L, Unit *punit, + lua_Object seq, + bool vigilant, bool rep); +void api_client_unit_order_string(lua_State *L, Unit *punit, + const char* seq, bool vigilant, bool rep); #ifdef __cplusplus diff -Nur -X../diff_ignore ./client/luascript/tolua_client.pkg ../LT2_5-my/client/luascript/tolua_client.pkg --- ./client/luascript/tolua_client.pkg 2019-01-19 17:40:00.495912862 +0300 +++ ../LT2_5-my/client/luascript/tolua_client.pkg 2019-09-22 00:55:05.132590188 +0300 @@ -60,4 +60,119 @@ @ unit_upgrade (lua_State *L, Unit *punit); void api_client_diplomat_action @ diplomat_action (lua_State *L, Unit *pdiplo, int target_id, int value, int action); + void api_client_unit_order_sequence + @ unit_orders(lua_State *L, Unit *punit, lua_Object seq, + bool vigilant, bool rep); +} + +$[ + control.da = {-- cheat sheet for diplomat_action + --TODO: upgrade automatically from enum diplomat_actions @ unit.h ? + DIPLOMAT_MOVE = 0, + DIPLOMAT_EMBASSY = 1, + DIPLOMAT_BRIBE = 2, + DIPLOMAT_INCITE = 3, + DIPLOMAT_INVESTIGATE = 4, + DIPLOMAT_SABOTAGE = 5, + DIPLOMAT_STEAL = 6, + SPY_POISON = 7, + SPY_SABOTAGE_UNIT = 8 + } +$] + +/* Focus module */ +module focus { + void api_client_unit_focus_add @ add(lua_State *L, Unit *punit); + void api_client_unit_focus_remove @ remove (lua_State *L, Unit *punit); + bool api_client_unit_is_in_focus @ contains (lua_State *L, Unit *punit); + int api_client_num_units_in_focus @ number(lua_State *L); +} + +/* hidden for inner use */ +module private_methods{ + Unit_List_Link *api_client_private_focus_head @ focus_head(lua_State *L); +} + +$[ +do + local focus_head = private_methods.focus_head + + function focus.list() -- Note: units can be destroyed afterwards + local list, link = {}, focus_head() + while link do + list[#list + 1] = link:data() + link = link:next() + end + + return list + end + + -- The iterator can be called between pakets processings that will + -- remove the units, so make it safe (paranoid?) + function focus.iterate() + local idlist, i, n, link = {}, 1, focus.number(), focus_head(); + for j = 1, n do + idlist[j] = link:data().id + link = link:next() + end + return function() + local u + while i <= n do + u = find.unit(nil, idlist[i]) + i = i + 1 + if u then break end + end + return u + end + end +end +$] + +module Unit { + void api_client_unit_airlift + @ airlift (lua_State *L, Unit *punit, City *pcity); + void api_client_unit_load + @ load (lua_State *L, Unit *pcargo, Unit *ptransport); + void api_client_unit_move + @ move (lua_State *L, Unit *punit, Tile *ptile); + void api_client_unit_upgrade + @ upgrade (lua_State *L, Unit *punit); + void api_client_unit_focus_add + @ select (lua_State *L, Unit *punit); + void api_client_unit_focus_remove + @ unselect (lua_State *L, Unit *punit); + bool api_client_unit_is_in_focus + @ selected (lua_State *L, Unit *punit); + void api_client_unit_build_city + @ build_city (lua_State *L, Unit *punit, const char *name); + bool api_client_unit_road + @ road (lua_State *L, Unit *punit, const char *road); + bool api_client_unit_next_road + @ road (lua_State *L, Unit *punit); + bool api_client_unit_base + @ base (lua_State *L, Unit *punit, const char *base); + bool api_client_unit_mine + @ mine (lua_State *L, Unit *punit); + bool api_client_unit_irrigate + @ irrigate (lua_State *L, Unit *punit); + bool api_client_unit_transform + @ transform (lua_State *L, Unit *punit); + void api_client_unit_request_activity + @ request_activity (lua_State *L, Unit *punit, char *activity_name); + /* Declare lua_Object polymorphic method first */ + void api_client_unit_order_sequence + @ give_orders(lua_State *L, Unit *punit, lua_Object seq, + bool vigilant = FALSE, bool rep = FALSE); + void api_client_unit_order_string + @ give_orders(lua_State *L, Unit *punit, const char* seq, + bool vigilant = FALSE, bool rep = FALSE); + bool api_client_unit_occupied + @ occupied (lua_State *L, Unit *punit); +} + +module City { + bool api_client_city_occupied + @ occupied (lua_State *L, City *pcity); + bool api_client_city_walled + @ has_walls (lua_State *L, City *pcity); } diff -Nur -X../diff_ignore ./client/packhand.c ../LT2_5-my/client/packhand.c --- ./client/packhand.c 2019-09-22 19:25:34.110183804 +0300 +++ ../LT2_5-my/client/packhand.c 2019-08-01 23:37:42.665396840 +0300 @@ -1211,6 +1211,9 @@ } unit_list_iterate_end; update_map_canvas_visible(); + + /* We likely have not researched anything before we're told about it */ + got_tech = TRI_NO; } update_info_label(); @@ -2307,6 +2310,8 @@ log_error("Received illegal gained tech %d", tech); return; } + + got_tech = TRI_YES; show_tech_gained_dialog(tech); } diff -Nur -X../diff_ignore ./common/scriptcore/api_game_find.c ../LT2_5-my/common/scriptcore/api_game_find.c --- ./common/scriptcore/api_game_find.c 2019-09-22 19:25:34.126186453 +0300 +++ ../LT2_5-my/common/scriptcore/api_game_find.c 2019-08-05 22:22:24.301676664 +0300 @@ -35,6 +35,38 @@ } /***************************************************************************** + Return a player with the given name +*****************************************************************************/ +Player *api_find_player_by_name(lua_State *L, const char* plrname) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + LUASCRIPT_CHECK_ARG_NIL(L, plrname, 2, string, NULL); + + return player_by_name(plrname); +} + +/***************************************************************************** + Return any city with the given name (case-insensitive). +*****************************************************************************/ +City *api_find_city_by_name(lua_State *L, Player *pplayer, const char *name) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + LUASCRIPT_CHECK_ARG_NIL(L, name, 3, string, NULL); + + if (pplayer) { + return city_list_find_name(pplayer->cities, name); + } else { + cities_iterate (pcity) { + if (!fc_strcasecmp(name, city_name(pcity))) { + return pcity; + } + } cities_iterate_end; + } + return NULL; +} + + +/***************************************************************************** Return a player city with the given city_id. *****************************************************************************/ City *api_find_city(lua_State *L, Player *pplayer, int city_id) @@ -258,6 +290,27 @@ } /***************************************************************************** + Return the resource type with the given terrain_id index. +*****************************************************************************/ +Resource *api_find_resource(lua_State *L, int resource_id) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + + return resource_by_number(resource_id); +} + +/***************************************************************************** + Return the resource with the given name_orig. +*****************************************************************************/ +Resource *api_find_resource_by_name(lua_State *L, const char *name_orig) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + LUASCRIPT_CHECK_ARG_NIL(L, name_orig, 2, string, NULL); + + return resource_by_rule_name(name_orig); +} + +/***************************************************************************** Return a dummy pointer. *****************************************************************************/ Nonexistent *api_find_nonexistent(lua_State *L) diff -Nur -X../diff_ignore ./common/scriptcore/api_game_find.h ../LT2_5-my/common/scriptcore/api_game_find.h --- ./common/scriptcore/api_game_find.h 2019-09-22 19:25:34.126186453 +0300 +++ ../LT2_5-my/common/scriptcore/api_game_find.h 2019-08-05 22:40:41.287188996 +0300 @@ -25,8 +25,10 @@ #include "luascript_types.h" /* Object find module. */ +Player *api_find_player_by_name(lua_State *L, const char* plrname); Player *api_find_player(lua_State *L, int player_id); +City *api_find_city_by_name(lua_State *L, Player *pplayer, const char *name); City *api_find_city(lua_State *L, Player *pplayer, int city_id); Unit *api_find_unit(lua_State *L, Player *pplayer, int unit_id); @@ -54,6 +56,9 @@ Terrain *api_find_terrain(lua_State *L, int terrain_id); Terrain *api_find_terrain_by_name(lua_State *L, const char *name_orig); +Resource *api_find_resource(lua_State *L, int resource_id); +Resource *api_find_resource_by_name(lua_State *L, const char *name_orig); + Nonexistent *api_find_nonexistent(lua_State *L); #ifdef __cplusplus diff -Nur -X../diff_ignore ./common/scriptcore/api_game_methods.c ../LT2_5-my/common/scriptcore/api_game_methods.c --- ./common/scriptcore/api_game_methods.c 2019-09-22 19:25:34.126186453 +0300 +++ ../LT2_5-my/common/scriptcore/api_game_methods.c 2019-09-22 15:57:51.819183655 +0300 @@ -17,6 +17,7 @@ /* common */ #include "citizens.h" +#include "combat.h" #include "game.h" #include "government.h" #include "improvement.h" @@ -24,6 +25,7 @@ #include "movement.h" #include "nation.h" #include "research.h" +#include "spaceship.h" #include "tech.h" #include "terrain.h" #include "tile.h" @@ -35,6 +37,37 @@ #include "api_game_methods.h" +/* FIXME: Move this to some header files [[ */ +#define macro2str_base(__macro__) #__macro__ +#define macro2str(__macro__) macro2str_base(__macro__) + +static int tile_gcdist(const struct tile *ptile, const struct player *plr); +/* ]] */ + +static /* FIXME: Move this to some common C file [[ */ +int tile_gcdist(const struct tile *ptile, const struct player *plr) +{ + int res = FC_INFINITY; + struct city *gc = tile_city(ptile); + + if (gc && city_owner(gc) == plr && is_gov_center(gc)) { + return 0; + } else { + city_list_iterate(plr->cities, pc) { + /* Do not recheck current city */ + if (gc != pc && is_gov_center(pc)) { + int dist = real_map_distance(ptile, pc->tile); + + if (dist < res) { + res = dist; + } + } + } city_list_iterate_end; + } + return FC_INFINITY == res ? -1 : res; +} +/* ]] */ + /***************************************************************************** Return the current turn. @@ -57,7 +90,6 @@ return is_wonder(pbuilding); } - /***************************************************************************** Return TRUE if pbuilding is a great wonder. *****************************************************************************/ @@ -121,6 +153,29 @@ /***************************************************************************** + Return link to the list of city supported units +*****************************************************************************/ +Unit_List_Link +*api_methods_private_city_supported_units_link(lua_State *L, City *pcity) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + LUASCRIPT_CHECK_SELF(L, pcity, NULL); + + return unit_list_head(pcity->units_supported); +} + +/***************************************************************************** + Return number of city supported units +*****************************************************************************/ +int api_methods_city_supported_units_number(lua_State *L, City *pcity) +{ + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, pcity, 0); + + return unit_list_size(pcity->units_supported); +} + +/***************************************************************************** Return TRUE iff city has building *****************************************************************************/ bool api_methods_city_has_building(lua_State *L, City *pcity, @@ -134,6 +189,34 @@ } /***************************************************************************** + Returns a building or a unit that the city is working on now, or nil +*****************************************************************************/ +lua_Object api_methods_city_production(lua_State *L, City *pcity) +{ + LUASCRIPT_CHECK_STATE(L, 0); /* no state, no top */ + /* LUASCRIPT_CHECK_SELF(L, lua_gettop(L)<-nil); */ + if (!pcity) { + luascript_arg_error(L, 2, "got 'nil' for self"); + lua_pushnil(L); + } else { + switch (pcity->production.kind) { + case VUT_IMPROVEMENT: + tolua_pushusertype(L, pcity->production.value.building, + "Building_Type"); + break; + case VUT_UTYPE: + tolua_pushusertype(L, pcity->production.value.utype, "Unit_Type"); + break; + default: + /* should not be here, except very strange cases */ + log_error("City builds some wrong kind %d", pcity->production.kind); + lua_pushnil(L); + } + } + return lua_gettop(L); +} + +/***************************************************************************** Return the square raduis of the city map. *****************************************************************************/ int api_methods_city_map_sq_radius(lua_State *L, City *pcity) @@ -166,6 +249,100 @@ return pcity->tile; } +/*********************************************************************//*** + Pushes a table of pcity nationalities {[Player] = int}, + or nil if nationalities are off +**************************************************************************/ +lua_Object api_methods_city_nationality(lua_State *L, City *pcity) +{ + int count = 0, t; + + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, pcity, 0); + + if (!game.info.citizen_nationality) { + lua_pushnil(L); + return lua_gettop(L); + } + + lua_newtable(L); + t = lua_gettop(L); + citizens_iterate(pcity, psl, num) { + count++; + tolua_pushusertype(L, player_slot_get_player(psl), "Player"); + lua_pushinteger(L, num); + lua_settable(L, t); + } citizens_iterate_end; + + return t; +} + +/*********************************************************************//*** + Returns how much of production of otn the city wastes. + If gcdist is specified, considers this govcener distance, otherwise + tries to calculate by available map data. + Does not consider trade min. size or unhappy state. +**************************************************************************/ +double api_methods_city_waste_level(lua_State *L, City *pcity, + int otype, lua_Object gcd) +{ + int gcdist; + + LUASCRIPT_CHECK_STATE(L, 0.); + LUASCRIPT_CHECK_SELF(L, pcity, 0.); + LUASCRIPT_CHECK_ARG(L, otype >= O_FOOD && otype < O_LAST, + 3, "Wrong output type", 0.); + + if (gcd && lua_isnumber(L, gcd)) {/*FIXME: TOLUA_NIL in newer tolua*/ + gcdist = lua_tointeger(L, gcd); + } else { + gcdist = tile_gcdist(city_tile(pcity), city_owner(pcity)); + } + + if (gcdist < 0) { /* Waste all if no capital */ + return 1.; + } + + return (double) + (get_city_output_bonus(pcity, get_output_type(otype), EFT_OUTPUT_WASTE) + + gcdist + * get_city_output_bonus(pcity, get_output_type(otype), + EFT_OUTPUT_WASTE_BY_DISTANCE) + ) * 0.0001 + * (100 - get_city_output_bonus(pcity, get_output_type(otype), + EFT_OUTPUT_WASTE_PCT)); +} + +/*********************************************************************//*** + Returns how much of production of otn the city wastes. + Wrapper for the numeric otype function that understands output type names +**************************************************************************/ +double +api_methods_city_waste_level_ostr(lua_State *L, City *pcity, + const char* otn, lua_Object gcd) +{ + return api_methods_city_waste_level(L, pcity, + output_type_by_identifier(otn), gcd); +} + +/*********************************************************************//*** + Tells how many citizens are as happy as cat at given level +**************************************************************************/ +int api_methods_city_happy_count(lua_State *L, City *pcity, + int cat, int level) +{ + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, pcity, 0); + LUASCRIPT_CHECK_ARG(L, cat >= CITIZEN_HAPPY && cat <= CITIZEN_SPECIALIST, + 3, "Wrong citizens category %d (max. " + macro2str(CITIZEN_SPECIALIST) ")", 0); + LUASCRIPT_CHECK_ARG(L, level >= FEELING_BASE && level < FEELING_LAST, 4, + "Wrong feeling level %d (max. " + macro2str(FEELING_FINAL) ")", 0); + return + pcity->feel[(enum citizen_category) cat][(enum citizen_feeling) level]; +} + /************************************************************************** How much city inspires partisans for a player. **************************************************************************/ @@ -316,6 +493,17 @@ } /***************************************************************************** + Return the total known population (in "thousands") of pplayer's nation. +*****************************************************************************/ +int api_methods_player_population(lua_State *L, Player *pplayer) +{ + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, pplayer, 0); + + return civ_population(pplayer); +} + +/***************************************************************************** Return the number of units pplayer has. *****************************************************************************/ int api_methods_player_num_units(lua_State *L, Player *pplayer) @@ -375,6 +563,39 @@ } /***************************************************************************** + Return the spaceship state string: nil, "Building", "Launched" or "Arrived" +*****************************************************************************/ +const char* api_methods_spaceship_state(lua_State *L, Spaceship *sh) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + LUASCRIPT_CHECK_SELF(L, sh, NULL); + + switch (sh->state) { + case SSHIP_NONE: + return NULL; + case SSHIP_STARTED: + return "Building"; + case SSHIP_LAUNCHED: + return "Launched"; + case SSHIP_ARRIVED: + return "Arrived"; + } + luaL_error(L, "Wrong spaceship state"); + return NULL; +} + +/***************************************************************************** + Return the spaceship owner +*****************************************************************************/ +Player *api_methods_spaceship_owner(lua_State *L, Spaceship *sh) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + LUASCRIPT_CHECK_SELF(L, sh, NULL); + + return (Player*) ((char*) sh - (char*) &((Player*) 0)->spaceship); +} + +/***************************************************************************** Return list head for unit list for Player *****************************************************************************/ Unit_List_Link *api_methods_private_player_unit_list_head(lua_State *L, @@ -500,6 +721,19 @@ } /***************************************************************************** + Return the native x and y coordinates of the tile. +*****************************************************************************/ +void api_methods_tile_nat_coords(lua_State *L, Tile *ptile, + int *x, int *y) +{ + LUASCRIPT_CHECK_STATE(L); + LUASCRIPT_CHECK_SELF(L, ptile); + + *x = index_to_native_pos_x(tile_index(ptile)); + *y = index_to_native_pos_y(tile_index(ptile)); +} + +/***************************************************************************** Return the map x coordinate of the tile. *****************************************************************************/ int api_methods_tile_map_x(lua_State *L, Tile *ptile) @@ -522,6 +756,19 @@ } /***************************************************************************** + Return the map x and y coordinates of the tile. +*****************************************************************************/ +void api_methods_tile_map_coords(lua_State *L, Tile *ptile, + int *x, int *y) +{ + LUASCRIPT_CHECK_STATE(L); + LUASCRIPT_CHECK_SELF(L, ptile); + + *x = index_to_map_pos_x(tile_index(ptile)); + *y = index_to_map_pos_y(tile_index(ptile)); +} + +/***************************************************************************** Return Player owning ptile, else NULL *****************************************************************************/ Player *api_methods_tile_owner(lua_State *L, Tile *ptile) @@ -533,6 +780,19 @@ } /***************************************************************************** + Return the distance to the nearest gov. center of plr, or -1 if none. + FIXME: move the calculator to a common header file +*****************************************************************************/ +int api_methods_tile_gcdist(lua_State *L, Tile *ptile, Player *plr) +{ + LUASCRIPT_CHECK_STATE(L, -1); + LUASCRIPT_CHECK_SELF(L, ptile, -1); + LUASCRIPT_CHECK_ARG_NIL(L, plr, 3, Player, -1); + + return tile_gcdist(ptile, plr); +} + +/***************************************************************************** Return Resource on ptile, else NULL *****************************************************************************/ Resource *api_methods_tile_resource(lua_State *L, Tile *ptile) @@ -699,12 +959,24 @@ { LUASCRIPT_CHECK_STATE(L, 0); LUASCRIPT_CHECK_SELF(L, ptile1, 0); LUASCRIPT_CHECK_ARG_NIL(L, ptile2, 3, Tile, 0); return sq_map_distance(ptile1, ptile2); } /***************************************************************************** + Return real map distance between tiles 1 and 2 +*****************************************************************************/ +int api_methods_tile_map_distance(lua_State *L, Tile *ptile1, Tile *ptile2) +{ + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, ptile1, 0); + LUASCRIPT_CHECK_ARG_NIL(L, ptile2, 2, Tile, 0); + + return real_map_distance(ptile1, ptile2); +} + +/***************************************************************************** Can punit found a city on its tile? *****************************************************************************/ bool api_methods_unit_city_can_be_built_here(lua_State *L, Unit *punit) @@ -738,6 +1010,139 @@ } /***************************************************************************** + Get direction name +*****************************************************************************/ +const char *api_methods_dir2str(lua_State *L, Direction dir) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + LUASCRIPT_CHECK(L, is_valid_dir(dir), "Direction is invalid", NULL); + + return direction8_name(dir); +} + +/***************************************************************************** + Get unit health +*****************************************************************************/ +int api_methods_unit_hp_get(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L, -1); + LUASCRIPT_CHECK_SELF(L, punit, -1); + + return punit->hp; +} + +/***************************************************************************** + Get punit full mp from all known bonuses +*****************************************************************************/ +int api_methods_unit_move_rate(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L, -1); + LUASCRIPT_CHECK_SELF(L, punit, -1); + + return unit_move_rate(punit); +} + +/***************************************************************************** + Get punit full work speed (does not check if it has any mp) +*****************************************************************************/ +int api_methods_unit_activity_rate(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L, -1); + LUASCRIPT_CHECK_SELF(L, punit, -1); + + return get_activity_rate(punit); +} + +/***************************************************************************** + Get unit moves +*****************************************************************************/ +int api_methods_unit_moves_left_get(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L, -1); + LUASCRIPT_CHECK_SELF(L, punit, -1); + + return punit->moves_left; +} + +/************************************************************************//*** + Get punit attacking tunit power (in current state) +*****************************************************************************/ +int api_methods_unit_attack_power(lua_State *L, Unit *punit, Unit *tunit) +{ + LUASCRIPT_CHECK_STATE(L, 0.); + LUASCRIPT_CHECK_SELF(L, punit, 0.); + LUASCRIPT_CHECK_ARG_NIL(L, tunit, 2, Unit, 0.); + + return get_total_attack_power(punit, tunit); +} + +/************************************************************************//*** + Get punit defending from tunit power (in current state) +*****************************************************************************/ +int api_methods_unit_defense_power(lua_State *L, Unit *punit, Unit *tunit) +{ + LUASCRIPT_CHECK_STATE(L, 0.); + LUASCRIPT_CHECK_SELF(L, punit, 0.); + LUASCRIPT_CHECK_ARG_NIL(L, tunit, 2, Unit, 0.); + + return get_total_defense_power(tunit, punit); +} + +/************************************************************************//*** + Get punit attacking tunit win chance (in current state) +*****************************************************************************/ +double api_methods_unit_win_chance(lua_State *L, Unit *punit, Unit *tunit) +{ + LUASCRIPT_CHECK_STATE(L, 0.); + LUASCRIPT_CHECK_SELF(L, punit, 0.); + LUASCRIPT_CHECK_ARG_NIL(L, tunit, 2, Unit, 0.); + + return unit_win_chance(punit, tunit); +} + +/***************************************************************************** + Get unit veteranship rank +*****************************************************************************/ +int api_methods_unit_vet_get(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L, -1); + LUASCRIPT_CHECK_ARG_NIL(L, punit, 2, Unit, -1); + + return punit->veteran; +} + +/********************************************************************//******* + Check if unit can do activity activity_name (by specenum unit_activity) + Returns false for activities that have targets + TODO: write for with a target +*****************************************************************************/ +bool api_methods_unit_can_do_activity(lua_State *L, Unit *punit, + char *activity_name) +{ + enum unit_activity act; + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, punit, FALSE); + LUASCRIPT_CHECK_ARG_NIL(L, activity_name, 3, string, FALSE); + + act = unit_activity_by_name(activity_name, strcmp); + if ((act != unit_activity_invalid()) && !activity_requires_target(act)) { + return can_unit_do_activity(punit, act); + } + return FALSE; +} + +/********************************************************************//******* + Returns current unit activity name +*****************************************************************************/ +const char *api_methods_unit_activity(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + LUASCRIPT_CHECK_SELF(L, punit, NULL); + + return unit_activity_name(punit->activity); +} + +/***************************************************************************** Return TRUE if punit_type has flag. *****************************************************************************/ bool api_methods_unit_type_has_flag(lua_State *L, Unit_Type *punit_type, diff -Nur -X../diff_ignore ./common/scriptcore/api_game_methods.h ../LT2_5-my/common/scriptcore/api_game_methods.h --- ./common/scriptcore/api_game_methods.h 2019-09-22 19:25:34.126186453 +0300 +++ ../LT2_5-my/common/scriptcore/api_game_methods.h 2019-09-22 15:57:51.915187536 +0300 @@ -48,6 +48,18 @@ int api_methods_city_size_get(lua_State *L, City *pcity); Tile *api_methods_city_tile_get(lua_State *L, City *pcity); int api_methods_city_inspire_partisans(lua_State *L, City *self, Player *inspirer); +Unit_List_Link +*api_methods_private_city_supported_units_link(lua_State *L, City *pcity); +int api_methods_city_supported_units_number(lua_State *L, City *pcity); +int api_methods_city_happy_count(lua_State *L, City *pcity, + int cat, int level); +lua_Object api_methods_city_production(lua_State *L, City *pcity); +lua_Object api_methods_city_nationality(lua_State *L, City *pcity); +double api_methods_city_waste_level(lua_State *L, City *pcity, + int otype, lua_Object gcd); +double +api_methods_city_waste_level_ostr(lua_State *L, City *pcity, + const char* otn, lua_Object gcd); /* Government */ const char *api_methods_government_rule_name(lua_State *L, @@ -72,6 +84,7 @@ int api_methods_player_land_area(lua_State *L, Player *pplayer); int api_methods_player_number(lua_State *L, Player *pplayer); int api_methods_player_num_cities(lua_State *L, Player *pplayer); +int api_methods_player_population(lua_State *L, Player *pplayer); 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_shares_research(lua_State *L, Player *pplayer, @@ -102,6 +115,10 @@ int api_methods_tile_nat_y(lua_State *L, Tile *ptile); int api_methods_tile_map_x(lua_State *L, Tile *ptile); int api_methods_tile_map_y(lua_State *L, Tile *ptile); +void api_methods_tile_nat_coords(lua_State *L, Tile *ptile, + int *x, int *y); +void api_methods_tile_map_coords(lua_State *L, Tile *ptile, + int *x, int *y); Resource *api_methods_tile_resource(lua_State *L, Tile *ptile); Player *api_methods_tile_owner(lua_State *L, Tile *ptile); City *api_methods_tile_city(lua_State *L, Tile *ptile); @@ -112,6 +129,8 @@ bool api_methods_tile_has_road(lua_State *L, Tile *ptile, const char *name); int api_methods_tile_num_units(lua_State *L, Tile *ptile); int api_methods_tile_sq_distance(lua_State *L, Tile *ptile1, Tile *ptile2); +int api_methods_tile_map_distance(lua_State *L, Tile *ptile1, Tile *ptile2); +int api_methods_tile_gcdist(lua_State *L, Tile *ptile, Player *plr); int api_methods_private_tile_next_outward_index(lua_State *L, Tile *pstart, int index, int max_dist); Tile *api_methods_private_tile_for_outward_index(lua_State *L, Tile *pstart, @@ -122,10 +141,24 @@ /* Resource */ const char *api_methods_resource_rule_name(lua_State *L, Resource *presource); +/* Direction */ +const char *api_methods_dir2str(lua_State *L, Direction dir); + /* Unit */ bool api_methods_unit_city_can_be_built_here(lua_State *L, Unit *punit); Tile *api_methods_unit_tile_get(lua_State *L, Unit * punit); Direction api_methods_unit_orientation_get(lua_State *L, Unit *punit); +bool api_methods_unit_can_do_activity(lua_State *L, Unit *punit, + char *activity_name); +int api_methods_unit_hp_get(lua_State *L, Unit *punit); +int api_methods_unit_moves_left_get(lua_State *L, Unit *punit); +int api_methods_unit_vet_get(lua_State *L, Unit *punit); +const char *api_methods_unit_activity(lua_State *L, Unit *punit); +int api_methods_unit_attack_power(lua_State *L, Unit *punit, Unit *tunit); +int api_methods_unit_defense_power(lua_State *L, Unit *punit, Unit *tunit); +double api_methods_unit_win_chance(lua_State *L, Unit *punit, Unit *tunit); +int api_methods_unit_move_rate(lua_State *L, Unit *punit); +int api_methods_unit_activity_rate(lua_State *L, Unit *punit); /* Unit Type */ bool api_methods_unit_type_has_flag(lua_State *L, Unit_Type *punit_type, @@ -140,6 +173,10 @@ const char *api_methods_unit_type_name_translation(lua_State *L, Unit_Type *punit_type); +/* Spaceship */ +const char* api_methods_spaceship_state(lua_State *L, Spaceship *sh); +Player *api_methods_spaceship_owner(lua_State *L, Spaceship *sh); + /* Unit_List_Link Type */ Unit *api_methods_unit_list_link_data(lua_State *L, Unit_List_Link *link); Unit_List_Link *api_methods_unit_list_next_link(lua_State *L, diff -Nur -X../diff_ignore ./common/scriptcore/luascript_types.h ../LT2_5-my/common/scriptcore/luascript_types.h --- ./common/scriptcore/luascript_types.h 2019-09-22 19:25:34.126186453 +0300 +++ ../LT2_5-my/common/scriptcore/luascript_types.h 2019-08-22 21:07:12.180420450 +0300 @@ -39,7 +39,7 @@ /* Classes. */ /* If a new class is defined, an entry should be added to the enum api_types * below and the class name should be added to the api_types list in - * tolua_common_a.pkg. */ + * tolua_common_z.pkg. */ typedef struct player Player; typedef struct player_ai Player_ai; typedef struct city City; @@ -54,6 +54,8 @@ typedef struct connection Connection; typedef enum direction8 Direction; typedef struct disaster_type Disaster; +typedef struct resource Resource; +typedef struct player_spaceship Spaceship; typedef void Nonexistent; @@ -97,6 +99,10 @@ #define SPECENUM_VALUE14NAME "Direction" #define SPECENUM_VALUE15 API_TYPE_DISASTER #define SPECENUM_VALUE15NAME "Disaster" +#define SPECENUM_VALUE16 API_TYPE_RESOURCE +#define SPECENUM_VALUE16NAME "Resource" +#define SPECENUM_VALUE17 API_TYPE_SPACESHIP +#define SPECENUM_VALUE17NAME "Spaceship" #include "specenum_gen.h" #ifdef __cplusplus diff -Nur -X../diff_ignore ./common/scriptcore/tolua_common_a.pkg ../LT2_5-my/common/scriptcore/tolua_common_a.pkg --- ./common/scriptcore/tolua_common_a.pkg 2019-09-22 19:25:34.126186453 +0300 +++ ../LT2_5-my/common/scriptcore/tolua_common_a.pkg 2019-09-22 00:55:05.392580946 +0300 @@ -133,13 +133,17 @@ elseif type(v) == 'userdata' then local method = string.lower(tolua.type(v)) - res = res .. k .. '=find.' .. method - if method == 'city' or method == 'unit' then - res = res .. '(nil,' .. v.id .. ')' - elseif v.id then - res = res .. '(' .. v.id .. ')' + if method == 'spaceship' then + res = ('%s=find.player(%d).spaceship'):format(res, v.owner.id) else - res = res .. '()' + res = res .. k .. '=find.' .. method + if method == 'city' or method == 'unit' then + res = res .. '(nil,' .. v.id .. ')' + elseif v.id then + res = res .. '(' .. v.id .. ')' + else + res = res .. '()' + end end res = res .. '\n' end diff -Nur -X../diff_ignore ./common/scriptcore/tolua_common_z.pkg ../LT2_5-my/common/scriptcore/tolua_common_z.pkg --- ./common/scriptcore/tolua_common_z.pkg 2019-09-22 19:25:34.126186453 +0300 +++ ../LT2_5-my/common/scriptcore/tolua_common_z.pkg 2019-08-22 21:07:12.236419801 +0300 @@ -47,7 +47,9 @@ "Unit", "Unit_List_Link", "Unit_Type", - "Disaster" + "Disaster", + "Resource", + "Spaceship" } local function id_eq (o1, o2) diff -Nur -X../diff_ignore ./common/scriptcore/tolua_game.pkg ../LT2_5-my/common/scriptcore/tolua_game.pkg --- ./common/scriptcore/tolua_game.pkg 2019-09-22 19:25:34.126186453 +0300 +++ ../LT2_5-my/common/scriptcore/tolua_game.pkg 2019-09-22 15:57:51.863185433 +0300 @@ -30,11 +30,21 @@ $#include "luascript_types.h" /* Classes. */ +struct Spaceship { + int structurals; + int components; + int modules; + double success_rate; + double travel_time; +}; + struct Player { const char *name; Nation_Type *nation; bool ai_controlled; bool is_alive; + bool is_male; + Spaceship spaceship; }; struct City { @@ -43,6 +53,22 @@ Player *original; const int id; + int food_stock; + int shield_stock; + int airlift; + bool did_buy; + bool did_sell; + int surplus[O_LAST]; /* Final surplus in each category. */ + int waste[O_LAST]; /* Waste/corruption in each category. */ + int unhappy_penalty[O_LAST]; /* Penalty from unhappy cities. */ + int prod[O_LAST]; /* Production is total minus waste and penalty. */ + int citizen_base[O_LAST]; /* Base production from citizens. */ + int usage[O_LAST]; /* Amount of each resource being used. */ + + int before_change_shields; + int caravan_shields; + int disbanded_shields; + int last_turns_shield_surplus; }; struct Connection { @@ -55,6 +81,8 @@ /* This used to be @ homecity_id, but it does not work with toluaxx. */ int homecity; + int fuel; + int activity_count; const int id; }; @@ -85,6 +113,9 @@ struct Unit_Type { int build_cost; + int move_rate; + int fuel; + int happy_cost; const int item_number @ id; }; @@ -107,6 +138,51 @@ struct City_List_Link { }; +module FEELING { + enum { /* citizen_feeling */ + FEELING_BASE @ BASE, /* before any of the modifiers below */ + FEELING_LUXURY @ LUXURY, /* after luxury */ + FEELING_EFFECT @ FEELING_FINAL, /* after building effects */ + FEELING_NATIONALITY @ NATIONALITY, /* after citizen nationality effects */ + FEELING_MARTIAL @ MARTIAL, /* after units enforce martial order */ + FEELING_FINAL @ FINAL /* after wonders (final result) */ + }; +} + +module CITIZEN { + enum { /* citizen_category */ + CITIZEN_HAPPY @ HAPPY, + CITIZEN_CONTENT @ CONTENT, + CITIZEN_UNHAPPY @ UNHAPPY, + CITIZEN_ANGRY @ ANGRY, + CITIZEN_SPECIALIST @ SPECIALIST + }; +} + +module OUTPUT{ + enum {/* output_type_id */ + O_FOOD @ FOOD, + O_SHIELD @ SHIELD, + O_TRADE @ TRADE, + O_GOLD @ GOLD, + O_LUXURY @ LUXURY, + O_SCIENCE @ SCIENCE + }; +} + +module ORDER { + enum /* unit_orders */ { + ORDER_MOVE @ MOVE = 0, + ORDER_ACTIVITY @ ACTIVITY = 1, + ORDER_FULL_MP @ FULL_MP = 2, + ORDER_BUILD_CITY @ BUILD_CITY = 3, + ORDER_DISBAND @ DISBAND = 4, + ORDER_BUILD_WONDER @ BUILD_WONDER = 5, + ORDER_TRADE_ROUTE @ TRADE_ROUTE = 6, + ORDER_HOMECITY @ HOMECITY = 7 + }; +} + /* Module Game */ module game { int api_methods_game_turn @@ -136,6 +212,8 @@ @ shares_research (lua_State *L, Player *self, Player *other); int api_methods_player_turns_idle @ turns_idle (lua_State *L, Player *self); + int api_methods_player_population + @ civ_population (lua_State *L, Player *pplayer); } module methods_private { @@ -157,9 +235,18 @@ function Player:exists() return true end - $] +/* Module Spaceship. */ +module Spaceship { + module properties { + const char *api_methods_spaceship_state + @ state (lua_State *L, Spaceship *sh); + Player *api_methods_spaceship_owner + @ owner (lua_State *L, Spaceship *sh); + } +} + /* Module City. */ module City { module properties { @@ -167,6 +254,12 @@ @ size(lua_State *L, City *self); Tile *api_methods_city_tile_get @ tile(lua_State *L, City *self); + int api_methods_city_supported_units_number + @ supported_count(lua_State *L, City *pcity); + lua_Object api_methods_city_production + @ production (lua_State *L, City *pcity); + lua_Object api_methods_city_nationality + @ nationalities (lua_State *L, City *pcity); } bool api_methods_city_has_building @@ -175,9 +268,26 @@ @ map_sq_radius(lua_State *L, City *self); int api_methods_city_inspire_partisans @ inspire_partisans(lua_State *L, City *self, Player *inspirer); + double api_methods_city_waste_level_ostr + @ waste_level(lua_State *L, City *pcity, + const char* otn, lua_Object gcd = 0); + double api_methods_city_waste_level + @ waste_level (lua_State *L, City *pcity, + int otype = O_SHIELD, lua_Object gcd = 0); + int api_methods_city_happy_count + @ happy_count(lua_State *L, City *pcity, + int cat = CITIZEN_HAPPY, int level = FEELING_FINAL); +} + +module methods_private { + module City { + Unit_List_Link *api_methods_private_city_supported_units_link + @ supported_head(lua_State *L, City *pcity); + } } $[ +Spaceship.exists = Player.exists --exists even if is not started -- City methods. function City:exists() @@ -191,12 +301,32 @@ module properties { Tile *api_methods_unit_tile_get @ tile(lua_State *L, Unit *self); + int api_methods_unit_hp_get + @ hp(lua_State *L, Unit *punit); + int api_methods_unit_moves_left_get + @ moves_left(lua_State *L, Unit *punit); + int api_methods_unit_vet_get + @ veteran(lua_State *L, Unit *punit); + const char *api_methods_unit_activity + @ activity(lua_State *L, Unit *punit); } bool api_methods_unit_city_can_be_built_here @ is_on_possible_city_tile (lua_State *L, Unit *self); Direction api_methods_unit_orientation_get @ facing(lua_State *L, Unit *self); + bool api_methods_unit_can_do_activity + @ can_do_activity(lua_State *L, Unit *punit, char *activity_name); + int api_methods_unit_attack_power + @ attack_power (lua_State *L, Unit *punit, Unit *tunit); + int api_methods_unit_defense_power + @ defense_power (lua_State *L, Unit *punit, Unit *tunit); + double api_methods_unit_win_chance + @ win_chance(lua_State *L, Unit *punit, Unit *tunit); + int api_methods_unit_move_rate + @ move_rate(lua_State *L, Unit *punit); + int api_methods_unit_activity_rate + @ activity_rate(lua_State *L, Unit *punit); } $[ @@ -225,6 +355,12 @@ @ y (lua_State *L, Tile *self); } + void api_methods_tile_nat_coords + @ nat_coords (lua_State *L, Tile *ptile, + int *x = 0, int *y = 0); + void api_methods_tile_map_coords + @ coords (lua_State *L, Tile *ptile, + int *x = 0, int *y = 0); Resource *api_methods_tile_resource @ resource (lua_State *L, Tile *self); City *api_methods_tile_city @@ -241,6 +377,10 @@ @ num_units (lua_State *L, Tile *self); int api_methods_tile_sq_distance @ sq_distance (lua_State *L, Tile *self, Tile *other); + int api_methods_tile_map_distance + @ map_distance (lua_State *L, Tile *ptile1, Tile *ptile2); + int api_methods_tile_gcdist + @ govcenter_dist (lua_State *L, Tile *ptile, Player *plr); } module Resource { @@ -270,6 +410,8 @@ @ tile_for_outward_index (lua_State *L, Tile *pcenter, int index); Unit_List_Link *api_methods_private_tile_unit_list_head @ unit_list_head (lua_State *L, Tile *self); + int api_methods_tile_gcdist + @ govcenter_dist (lua_State *L, Tile *ptile, Player *plr); } } @@ -380,9 +522,15 @@ } /* Module find. */ +/* NOTE: For overloading to work correctly, the string function + * must be before the integer function for each case below. */ module find { + Player *api_find_player_by_name + @ player (lua_State *L, const char* plrname); Player *api_find_player @ player (lua_State *L, int player_id); + City *api_find_city_by_name + @ city (lua_State *L, Player *pplayer, const char *name); City *api_find_city @ city (lua_State *L, Player *pplayer, int city_id); Unit *api_find_unit @@ -394,9 +542,6 @@ @ tile (lua_State *L, int nat_x, int nat_y); Tile *api_find_tile_by_index @ tile (lua_State *L, int index); - - /* NOTE: For overloading to work correctly, the string function - * must be before the integer function for each case below. */ Government *api_find_government_by_name @ government (lua_State *L, const char *name_orig); Government *api_find_government @@ -423,6 +568,10 @@ @ terrain (lua_State *L, const char *name_orig); Terrain *api_find_terrain @ terrain (lua_State *L, int terrain_id); + Resource *api_find_resource_by_name + @ resource (lua_State *L, const char *name_orig); + Resource *api_find_resource + @ resource (lua_State *L, int resource_id); Nonexistent *api_find_nonexistent @ nonexistent (lua_State *L); } @@ -445,6 +594,8 @@ module direction { Direction api_utilities_str2dir @ str2dir (lua_State *L, const char *str); + const char *api_methods_dir2str + @ name (lua_State *L, Direction dir); } $[ @@ -454,6 +605,9 @@ function str2direction(str) return direction.str2dir(str) end + +-- Use directions as English names in strings +Direction.__tostring = Direction.name $] $[ @@ -499,6 +653,15 @@ function Tile:units_iterate() return safe_iterate_list(private.Tile.unit_list_head(self)) end + + -- Safe iteration over supported units + function City:supported_iterate() + return safe_iterate_list(private.City.supported_head(self)) + end + + function City:govcenter_dist() + return self.tile:govcenter_dist(self.owner) + end end -- *************************************************************************** diff -Nur -X../diff_ignore ./common/tech.c ../LT2_5-my/common/tech.c --- ./common/tech.c 2019-09-22 19:25:34.126186453 +0300 +++ ../LT2_5-my/common/tech.c 2019-08-02 00:22:06.229454963 +0300 @@ -662,6 +662,63 @@ FALSE); } +/**********************************************************************//** + If leakage style is 2 or 3, it counts players that pplayer does not have + embassy to and returns how much cheaper the tech would be if they all + know it rather than if they all don't know +**************************************************************************/ +double min_leakage_ratio(const struct player *pplayer, + Tech_type_id tech) +{ + const struct player_research *presearch = player_research_get(pplayer); + fc_assert(presearch); + + switch (game.info.tech_leakage) { + case 0: + case 1: + return 1.0; + break; + case 2: + case 3: + { + int players = 0, players_with_tech = 0, players_obscure = 0; + + players_iterate_alive(aplayer) { + if ((3 == game.info.tech_leakage) && is_barbarian(aplayer)) { + continue; + } + players++; + if (player_has_embassy(pplayer, aplayer)) { + if (A_FUTURE == tech + ? (player_research_get(aplayer)->future_tech + > presearch->future_tech) + : TECH_KNOWN == player_invention_state(aplayer, tech)) { + players_with_tech++; + } + } else { + players_obscure++; + } + + } players_iterate_alive_end; + + fc_assert_ret_val(0 < players, 1.0); + fc_assert(players >= players_with_tech + players_obscure); + + if (0 == players_obscure) { + return 1.0; + } + return (double) (players - players_with_tech - players_obscure) + / (players - players_with_tech); + } + break; + + default: + log_error("Invalid tech_leakage %d", game.info.tech_leakage); + } + + return 1.0; +} + /**************************************************************************** Function to determine cost for technology. The equation is determined from game.info.tech_cost_style and game.info.tech_leakage. diff -Nur -X../diff_ignore ./common/tech.h ../LT2_5-my/common/tech.h --- ./common/tech.h 2019-09-22 19:25:34.126186453 +0300 +++ ../LT2_5-my/common/tech.h 2019-08-01 23:37:42.929399224 +0300 @@ -225,6 +225,8 @@ bool is_future_tech(Tech_type_id tech); void precalc_tech_data(void); +double min_leakage_ratio(const struct player *pplayer, + Tech_type_id tech); /* Initialization and iteration */ void techs_init(void); diff -Nur -X../diff_ignore ./server/civserver.c ../LT2_5-my/server/civserver.c --- ./server/civserver.c 2019-09-22 19:25:34.682278532 +0300 +++ ../LT2_5-my/server/civserver.c 2019-09-06 20:53:19.441638040 +0300 @@ -286,6 +286,8 @@ srvarg.auth_allow_guests = TRUE; } else if (is_option("--Newusers", argv[inx])) { srvarg.auth_allow_newusers = TRUE; + } else if ((option = get_option_malloc("--Luadbscript", argv, &inx, argc))) { + srvarg.fcdb_luafile = option; #endif /* HAVE_FCDB */ } else if ((option = get_option_malloc("--Serverid", argv, &inx, argc))) { sz_strlcpy(srvarg.serverid, option); @@ -393,6 +395,10 @@ /* TRANS: "log" is exactly what user must type, do not translate. */ _("log FILE"), _("Use FILE as logfile")); + cmdhelp_add(help, "L", + /* TRANS: "Luadbscript" is exactly what user must type, do not translate. */ + _("Luadbscript FILE"), + _("Use FILE as fcdb script")); cmdhelp_add(help, "m", "meta", _("Notify metaserver and send server's info")); cmdhelp_add(help, "M", diff -Nur -X../diff_ignore ./server/fcdb.c ../LT2_5-my/server/fcdb.c --- ./server/fcdb.c 2019-09-22 19:25:34.682278532 +0300 +++ ../LT2_5-my/server/fcdb.c 2019-09-06 20:45:26.181780042 +0300 @@ -171,7 +171,7 @@ log_debug("No fcdb config file."); } - return script_fcdb_init(NULL); + return script_fcdb_init(srvarg.fcdb_luafile); } /**************************************************************************** diff -Nur -X../diff_ignore ./server/plrhand.c ../LT2_5-my/server/plrhand.c --- ./server/plrhand.c 2019-09-22 19:25:34.702281844 +0300 +++ ../LT2_5-my/server/plrhand.c 2019-09-04 23:08:11.555116298 +0300 @@ -1560,6 +1560,7 @@ } script_server_remove_exported_object(pplayer); + script_server_remove_exported_object(&(pplayer->spaceship)); /* Clear data saved in the other player structs. */ players_iterate(aplayer) { BV_CLR(aplayer->real_embassy, player_index(pplayer)); diff -Nur -X../diff_ignore ./server/srv_main.c ../LT2_5-my/server/srv_main.c --- ./server/srv_main.c 2019-09-22 19:25:34.710283169 +0300 +++ ../LT2_5-my/server/srv_main.c 2019-09-06 20:48:10.718273825 +0300 @@ -254,6 +254,7 @@ srvarg.fcdb_enabled = FALSE; srvarg.fcdb_conf = NULL; + srvarg.fcdb_luafile = NULL; srvarg.auth_enabled = FALSE; srvarg.auth_allow_guests = FALSE; srvarg.auth_allow_newusers = FALSE; diff -Nur -X../diff_ignore ./server/srv_main.h ../LT2_5-my/server/srv_main.h --- ./server/srv_main.h 2019-09-22 19:25:34.710283169 +0300 +++ ../LT2_5-my/server/srv_main.h 2019-09-06 20:45:26.249782139 +0300 @@ -53,6 +53,7 @@ /* authentication options */ bool fcdb_enabled; /* defaults to FALSE */ char *fcdb_conf; /* freeciv database configuration file */ + char *fcdb_luafile; /* freeciv database script */ bool auth_enabled; /* defaults to FALSE */ bool auth_allow_guests; /* defaults to FALSE */ bool auth_allow_newusers; /* defaults to FALSE */ diff -Nur -X../diff_ignore ./server/stdinhand.c ../LT2_5-my/server/stdinhand.c --- ./server/stdinhand.c 2019-09-22 19:25:34.714283832 +0300 +++ ../LT2_5-my/server/stdinhand.c 2019-09-06 20:45:26.121778192 +0300 @@ -5790,7 +5790,7 @@ case FCDB_RELOAD: /* Reload database lua script. */ script_fcdb_free(); - script_fcdb_init(NULL); + script_fcdb_init(srvarg.fcdb_luafile); break; case FCDB_LUA: