diff -Nur -X diff_ignore ./LT2_5/client/client_main.c ./LT2_5-my/client/client_main.c --- ./LT2_5/client/client_main.c 2019-10-20 23:27:32.650946756 +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 ./LT2_5/client/client_main.h ./LT2_5-my/client/client_main.h --- ./LT2_5/client/client_main.h 2019-10-20 23:27:32.650946756 +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]. */ diff -Nur -X diff_ignore ./LT2_5/client/control.c ./LT2_5-my/client/control.c --- ./LT2_5/client/control.c 2019-10-20 23:27:32.650946756 +0300 +++ ./LT2_5-my/client/control.c 2019-10-13 18:32:52.811991425 +0300 @@ -38,6 +38,9 @@ #include "mapview_g.h" #include "menu_g.h" +/* client/luascript */ +#include "script_client.h" + /* client */ #include "audio.h" #include "client_main.h" @@ -2360,6 +2363,12 @@ refresh_unit_mapcanvas(punit, dst_tile, TRUE, FALSE); } + + script_client_signal_emit("unit_moved", 3, + API_TYPE_UNIT, punit, + API_TYPE_TILE, src_tile, + API_TYPE_TILE, dst_tile); + /* FIXME: can unit ever be destroyed by the callback? */ /* With the "full" city bar we have to update the city bar when units move * into or out of a city. For foreign cities this is handled separately, Двоичные файлы ./LT2_5/client/freeciv-gtk2 и ./LT2_5-my/client/freeciv-gtk2 различаются Двоичные файлы ./LT2_5/client/freeciv-sdl и ./LT2_5-my/client/freeciv-sdl различаются diff -Nur -X diff_ignore ./LT2_5/client/gui-gtk-2.0/luaconsole.c ./LT2_5-my/client/gui-gtk-2.0/luaconsole.c --- ./LT2_5/client/gui-gtk-2.0/luaconsole.c 2019-10-20 23:27:32.662946554 +0300 +++ ./LT2_5-my/client/gui-gtk-2.0/luaconsole.c 2019-10-18 23:43:18.581099824 +0300 @@ -53,6 +53,7 @@ struct genlist *history_list; int history_pos; + gchar *filename; }; static struct luaconsole_data *luaconsole = NULL; @@ -91,6 +92,7 @@ luaconsole->history_list = genlist_new(); luaconsole->history_pos = -1; + luaconsole->filename = NULL; luaconsole_welcome_message(); } @@ -104,6 +106,9 @@ if (luaconsole->history_list) { genlist_destroy(luaconsole->history_list); } + if (luaconsole->filename) { + g_free(luaconsole->filename); + } luaconsole_dialog_popdown(); free(luaconsole); @@ -307,6 +312,10 @@ /* Create the selector */ filesel = gtk_file_selection_new("Load Lua file"); + if (luaconsole->filename) { + gtk_file_selection_set_filename(GTK_FILE_SELECTION(filesel), + luaconsole->filename); + } setup_dialog(filesel, toplevel); gtk_window_set_position(GTK_WINDOW(filesel), GTK_WIN_POS_MOUSE); @@ -329,9 +338,12 @@ -1, NULL, NULL, NULL); if (NULL != filename) { + if (NULL != luaconsole->filename) { + g_free(luaconsole->filename); + } + luaconsole->filename = filename; luaconsole_printf(ftc_luaconsole_input, "(file)> %s", filename); script_client_do_file(filename); - g_free(filename); } } gtk_widget_destroy(widget); diff -Nur -X diff_ignore ./LT2_5/client/gui-gtk-2.0/repodlgs.c ./LT2_5-my/client/gui-gtk-2.0/repodlgs.c --- ./LT2_5/client/gui-gtk-2.0/repodlgs.c 2019-10-20 23:27:32.662946554 +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 ./LT2_5/client/luascript/api_client_base.c ./LT2_5-my/client/luascript/api_client_base.c --- ./LT2_5/client/luascript/api_client_base.c 2019-10-20 23:27:32.710945748 +0300 +++ ./LT2_5-my/client/luascript/api_client_base.c 2019-10-20 23:09:28.001079523 +0300 @@ -16,20 +16,93 @@ #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" +/* client/agents */ +#include "agents/cma_core.h" +#include "agents/cma_fec.h" + /* client */ #include "chatline_common.h" #include "client_main.h" +#include "control.h" #include "goto.h" +#include "options.h" +#include "tilespec.h" #include "api_client_base.h" +/* FIXME: don't know how to include climap.h with each its include/?_g.h */ +extern void center_tile_mapcanvas(struct tile* ptile); + +/* 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); */ +static enum direction8 top2dir8(lua_State *L); +static int lua_mtostring(lua_State *L); + +/***************************************************************************** + Function to put into metatables of objects with a string representation + stored in 'string' field of the metatable. + Probably should be transferred to some header. +*****************************************************************************/ +static int lua_mtostring(lua_State *L){ + LUASCRIPT_CHECK_STATE(L, 0); + + if (lua_gettop(L) != 1) { + luaL_error(L, "Wrong stringification of an enum value"); + return 0; + } + lua_getmetatable(L, -1); + lua_getfield(L, -1, "string"); + fc_assert(LUA_TSTRING == lua_type(L, -1)); + return 1; +} + /***************************************************************************** Return the player the client is connected to. *****************************************************************************/ @@ -52,7 +125,7 @@ dsend_packet_unit_airlift(&client.conn, punit->id, pcity->id); } /***************************************************************************** - Load unit in transport. + Load unit in transport. You must own pcargo. *****************************************************************************/ void api_client_unit_load(lua_State *L, Unit *pcargo, Unit *ptransport) { @@ -60,7 +133,30 @@ LUASCRIPT_CHECK_ARG_NIL(L, pcargo, 2, Unit); LUASCRIPT_CHECK_ARG_NIL(L, ptransport, 3, Unit); - dsend_packet_unit_load(&client.conn, pcargo->id, ptransport->id); + if (can_client_issue_orders()){ + dsend_packet_unit_load(&client.conn, pcargo->id, ptransport->id); + } +} + +/***************************************************************************** + Unoad unit from a transport. You must own one or another. + Does not check if pcargo is currently in ptransport or can survive here. + Sets sentrying pcargo to idle. +*****************************************************************************/ +void api_client_unit_unload(lua_State *L, Unit *pcargo, Unit *ptransport) +{ + LUASCRIPT_CHECK_STATE(L); + LUASCRIPT_CHECK_ARG_NIL(L, pcargo, 2, Unit); + LUASCRIPT_CHECK_ARG_NIL(L, ptransport, 3, Unit); + if (can_client_issue_orders()){ + dsend_packet_unit_unload(&client.conn, pcargo->id, ptransport->id); + if (unit_owner(pcargo) == client.conn.playing + && pcargo->activity == ACTIVITY_SENTRY) { + /* Activate the unit. */ + dsend_packet_unit_change_activity(&client.conn, pcargo->id, + ACTIVITY_IDLE, S_LAST); + } + } } /***************************************************************************** @@ -109,3 +205,880 @@ 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); + } +} + +/**********************************************************************//*** + Requests pillage without specifying the target. If multiple ones are + possible, a dialog appears. +***************************************************************************/ +void api_client_unit_request_pillage(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L); + LUASCRIPT_CHECK_SELF(L, punit); + request_unit_pillage(punit); +} + +/**********************************************************************//*** + Requests pillage specifying the target (road or base) in a string. + Never displays dialogs. Accepts nil target only if pillage target + selection is off. + If you happen to have a base and a road with the same name, the + base is pillaged. Then, a special is tested. Does not check the tile. +***************************************************************************/ +void api_client_unit_request_pillage_tgt(lua_State *L, Unit *punit, + const char *tgt) +{ + struct act_tgt target; + + LUASCRIPT_CHECK_STATE(L); + LUASCRIPT_CHECK_SELF(L, punit); + if (!game.info.pillage_select) { + /* FIXME: do we need to set it so if pillage is not selective? */ + target.type = ATT_SPECIAL; + target.obj.spe = S_LAST; + } else { + union { + struct base_type *b; + struct road_type *r; + } p; + + LUASCRIPT_CHECK_ARG_NIL(L, tgt, 3, string); + if ((p.b = base_type_by_rule_name(tgt))) { + target.obj.base = base_number(p.b); + target.type = ATT_BASE; + } else if ((p.r = road_type_by_rule_name(tgt))) { + target.obj.road = road_number(p.r); + target.type = ATT_ROAD; + } else if ((target.obj.spe = special_by_rule_name(tgt)) != S_LAST) { + target.type = ATT_SPECIAL; + } else { + luaL_argerror(L, 3, "Unknown extra type"); + return; + } + } + request_new_unit_activity_targeted(punit, ACTIVITY_PILLAGE, &target); +} + +/**********************************************************************//*** + 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. +***************************************************************************/ +static 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 = direction8_invalid(); + 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; + } + + for (int i = ptnn; i < p->length - ptl; i++) { + p->orders[i + ptl] = (order = p->orders[i]); + p->dir[i + ptl] = (dir = p->dir[i]); + p->activity[i + ptl] = p->activity[i]; + p->road[i + ptl] = p->road[i]; + p->base[i + ptl] = p->base[i]; + + /* Shifting destintion tile. */ + if ((ORDER_MOVE == order) && is_valid_dir(dir)) { + p->dest_tile = + tile_index(mapstep(index_to_tile(p->dest_tile), dir)); + } + } + + 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_STATE(L, FALSE); + 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_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, pcity, FALSE); + return pcity->client.walls; +} + +/**********************************************************************//*** + Returns CMA preset name, "custom" for custom governor, nil for none +***************************************************************************/ +const char *api_client_city_cma_name(lua_State *L, City *pcity) +{ + struct cm_parameter cmpar; + + LUASCRIPT_CHECK_STATE(L, NULL); + LUASCRIPT_CHECK_SELF(L, pcity, NULL); + + if (cma_is_city_under_agent(pcity, &cmpar)) { + return cmafec_get_short_descr(&cmpar); + } else { + return NULL; + } +} + +/**********************************************************************//*** + Requests city production change to the given item +***************************************************************************/ +void api_client_city_change_production(lua_State *L, City *pcity, + lua_Object prod) +{ + const char *argt = tolua_typename(L, prod); + enum universals_n kind; + + LUASCRIPT_CHECK_STATE(L); + LUASCRIPT_CHECK_SELF(L, pcity); + + if (strcmp(argt, "Unit_Type")) { + kind = VUT_UTYPE; + } else if (strcmp(argt, "Building_Type")) { + kind = VUT_IMPROVEMENT; + } else { + luascript_arg_error(L, 3, "Wrong production type (must be Unit_Type" + " or Building_Type"); + return; + } + lua_getfield(L, prod, "id"); + dsend_packet_city_change(&client.conn, pcity->id, kind, + lua_tointeger(L, -1)); /* cleaned by wrap code */ +} + +/**********************************************************************//*** + Gets a server option value available for the client +***************************************************************************/ +lua_Object api_client_option_get(lua_State *L, const char *name, + bool is_server_opt) +{ + struct option *opt + = optset_option_by_name(is_server_opt ? server_optset : client_optset, + name); + + struct ft_color col; + int t; + + LUASCRIPT_CHECK_STATE(L, 0); /* hope it works */ + + if (!opt) { + lua_pushnil(L); + return lua_gettop(L); + } + + switch (option_type(opt)) { + case OT_BOOLEAN: + lua_pushboolean(L, option_bool_get(opt)); + break; + case OT_INTEGER: + lua_pushinteger(L, option_int_get(opt)); + break; + case OT_STRING: + tolua_pushstring(L, option_str_get(opt)); + break; + case OT_ENUM: /* Push a number that has a string representation */ + lua_pushinteger(L, option_enum_get_int(opt)); /* stack: int */ + lua_createtable(L, 0, 2); /* int {} */ + t = lua_gettop(L); + tolua_pushstring(L, option_enum_get_str(opt));/* int {} str */ + lua_setfield(L, t, "string"); /* int {string = str} */ + lua_pushcfunction(L, &lua_mtostring); /* int {string = str} fn */ + lua_setfield(L, t, "__tostring"); /* int {string=str, __tostring=fn} */ + lua_setmetatable(L, t - 1); /* int~string */ + break; + case OT_BITWISE: /* FIXME: want a pretty table here */ + lua_pushunsigned(L, option_bitwise_get(opt)); + break; + case OT_FONT: + tolua_pushstring(L, option_font_get(opt)); + break; + case OT_COLOR: + col = option_color_get(opt); + lua_createtable(L, 0, 2); + t = lua_gettop(L); + tolua_pushstring(L, col.foreground); + lua_setfield(L, t, "foreground"); + tolua_pushstring(L, col.background); + lua_setfield(L, t, "background"); + break; + case OT_VIDEO_MODE: /* FIXME: not yet supported */ + luaL_error(L, "Video mode options like %s not yet supported by Lua", + name); + lua_pushnil(L); + break; + default: /* should not be here */ + fc_assert(FALSE); + luaL_error(L, "Unknown option type"); + lua_pushnil(L); + } + return lua_gettop(L); +} + +/**********************************************************************//*** + Centers the view at given tile +***************************************************************************/ +void api_client_center(lua_State *L, Tile* tile) +{ + LUASCRIPT_CHECK_STATE(L); + + center_tile_mapcanvas(tile); +} + +/**********************************************************************//*** + Gets a string describing the current state of the client +***************************************************************************/ +const char *api_client_state(lua_State *L) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + + switch (client_state()) { + case C_S_INITIAL: return "Initial"; + case C_S_DISCONNECTED: return "Disconnected"; + case C_S_PREPARING: return "Preparing"; + case C_S_RUNNING: + if (client_is_observer()) { + return "Observing"; + } else { + return "Playing"; + } + case C_S_OVER: return "Gameover"; + default: + /* should not get here */ + fc_assert(FALSE); + return NULL; + } +} +const char *api_client_tileset_name(lua_State *L) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + + return tileset_get_name(tileset); +} + diff -Nur -X diff_ignore ./LT2_5/client/luascript/api_client_base.h ./LT2_5-my/client/luascript/api_client_base.h --- ./LT2_5/client/luascript/api_client_base.h 2019-01-19 17:40:00.495912862 +0300 +++ ./LT2_5-my/client/luascript/api_client_base.h 2019-10-18 22:54:00.440227633 +0300 @@ -24,14 +24,51 @@ struct lua_State; Player *api_client_player(lua_State *L); +void api_client_center(lua_State *L, Tile* tile); +const char *api_client_state(lua_State *L); +const char *api_client_tileset_name(lua_State *L); +lua_Object api_client_option_get(lua_State *L, const char *name, + bool is_server_opt); 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); +const char *api_client_city_cma_name(lua_State *L, City *pcity); +void api_client_city_change_production(lua_State *L, City *pcity, + lua_Object prod); + 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_unload(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_request_pillage(lua_State *L, Unit *punit); +void api_client_unit_request_pillage_tgt(lua_State *L, Unit *punit, + const char *tgt); +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 ./LT2_5/client/luascript/script_client.c ./LT2_5-my/client/luascript/script_client.c --- ./LT2_5/client/luascript/script_client.c 2019-10-20 23:28:39.529904495 +0300 +++ ./LT2_5-my/client/luascript/script_client.c 2019-10-13 18:54:12.615968506 +0300 @@ -318,8 +318,8 @@ static void script_client_signal_create(void) { luascript_signal_create(fcl, "new_tech", 0); - luascript_signal_create(fcl, "unit_packet", 1, API_TYPE_UNIT); - luascript_signal_create(fcl, "unit_remove", 1, API_TYPE_INT); - luascript_signal_create(fcl, "combat_info", 4, API_TYPE_INT, API_TYPE_INT, - API_TYPE_INT, API_TYPE_INT); + luascript_signal_create(fcl, "unit_moved", 3, + API_TYPE_UNIT, API_TYPE_TILE, API_TYPE_TILE); + luascript_signal_create(fcl, "unit_removed", 1, API_TYPE_UNIT); + luascript_signal_create(fcl, "unit_created", 1, API_TYPE_UNIT); } diff -Nur -X diff_ignore ./LT2_5/client/luascript/tolua_client.pkg ./LT2_5-my/client/luascript/tolua_client.pkg --- ./LT2_5/client/luascript/tolua_client.pkg 2019-01-19 17:40:00.495912862 +0300 +++ ./LT2_5-my/client/luascript/tolua_client.pkg 2019-10-18 22:54:00.484229337 +0300 @@ -46,7 +46,14 @@ $] -Player *api_client_player @ client_player(lua_State *L); +module client { + Player *api_client_player @ player(lua_State *L); + void api_client_center @ center(lua_State *L, Tile* tile); + const char *api_client_state @ state(lua_State *L); + const char *api_client_tileset_name @ tileset_name(lua_State *L); + lua_Object api_client_option_get + @ option (lua_State *L, const char *name, bool is_server_opt = FALSE); +} /* Control module. */ module control { @@ -60,4 +67,140 @@ @ 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_unload + @ unload_from (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); + void api_client_unit_request_pillage + @ request_pillage (lua_State *L, Unit *punit); + void api_client_unit_request_pillage_tgt + @ request_pillage (lua_State *L, Unit *punit, const char *tgt); + /* 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); + const char *api_client_city_cma_name + @ cma_name (lua_State *L, City *pcity); + void api_client_city_change_production + @ change_production (lua_State *L, City *pcity, lua_Object prod); } + +$[ + -- Unloads something from something appropriate + function Unit:unload(uother) + if uother:transporter() == self then + uother:unload_from(self) + else + self:unload_from(uother) + end + end +$] diff -Nur -X diff_ignore ./LT2_5/client/packhand.c ./LT2_5-my/client/packhand.c --- ./LT2_5/client/packhand.c 2019-10-20 23:28:39.553904149 +0300 +++ ./LT2_5-my/client/packhand.c 2019-10-13 19:13:49.421790954 +0300 @@ -69,6 +69,9 @@ #include "voteinfo_bar_g.h" #include "wldlg_g.h" +/* client/luascript */ +#include "script_client.h" + /* client */ #include "agents.h" #include "attribute.h" @@ -89,9 +92,6 @@ #include "update_queue.h" #include "voteinfo.h" -/* client/luascript */ -#include "script_client.h" - #include "packhand.h" /* Define this macro to get additional debug output about the transport @@ -401,8 +401,6 @@ struct player *powner; bool need_economy_report_update; - script_client_signal_emit("unit_remove", 1, API_TYPE_INT, unit_id); - if (!punit) { log_error("Server wants us to remove unit id %d, " "but we don't know about this unit!", @@ -417,6 +415,8 @@ process_diplomat_arrival(NULL, 0); } + script_client_signal_emit("unit_removed", 1, API_TYPE_UNIT, punit); + /* FIXME: any chance to destroy things by the callback? */ need_economy_report_update = (0 < punit->upkeep[O_GOLD]); powner = unit_owner(punit); @@ -480,14 +480,13 @@ struct unit *punit0 = game_unit_by_number(attacker_unit_id); struct unit *punit1 = game_unit_by_number(defender_unit_id); - script_client_signal_emit("combat_info", 4, API_TYPE_INT, attacker_unit_id, - API_TYPE_INT, defender_unit_id, - API_TYPE_INT, attacker_hp, - API_TYPE_INT, defender_hp); - create_event(unit_tile(punit1), E_UNIT_LOST_ATT, ftc_client, - _("Combat result: defending %s (%d) %d hp, attacking %s" - "(%d) %d hp."), unit_name_translation(punit1), + make_winner_veteran + ? _("Combat result: defending %s (%d) %d hp, attacking %s" + "(%d) %d hp.") + : _("Combat result: defending %s (%d) %d hp, attacking %s" + "(%d) %d hp (veterancy achieved)."), + unit_name_translation(punit1), defender_unit_id, defender_hp, unit_name_translation(punit0), attacker_unit_id, attacker_hp); @@ -1221,6 +1220,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(); @@ -1729,6 +1731,8 @@ /* The unit is in a city - obviously it's occupied. */ pcity->client.occupied = TRUE; } + script_client_signal_emit("unit_created", 1, API_TYPE_UNIT, punit); + /* FIXME: any chance to destroy things by the callback? */ need_units_report_update = TRUE; } /*** End of Create new unit ***/ @@ -1794,8 +1798,6 @@ } } - script_client_signal_emit("unit_packet", 1, API_TYPE_UNIT, punit); - return ret; } @@ -2319,6 +2321,8 @@ log_error("Received illegal gained tech %d", tech); return; } + + got_tech = TRI_YES; show_tech_gained_dialog(tech); } diff -Nur -X diff_ignore ./LT2_5/common/scriptcore/api_game_find.c ./LT2_5-my/common/scriptcore/api_game_find.c --- ./LT2_5/common/scriptcore/api_game_find.c 2019-10-20 23:27:32.726945480 +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 ./LT2_5/common/scriptcore/api_game_find.h ./LT2_5-my/common/scriptcore/api_game_find.h --- ./LT2_5/common/scriptcore/api_game_find.h 2019-10-20 23:27:32.726945480 +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 ./LT2_5/common/scriptcore/api_game_methods.c ./LT2_5-my/common/scriptcore/api_game_methods.c --- ./LT2_5/common/scriptcore/api_game_methods.c 2019-10-20 23:27:32.726945480 +0300 +++ ./LT2_5-my/common/scriptcore/api_game_methods.c 2019-10-18 23:44:26.508968035 +0300 @@ -17,6 +17,8 @@ /* common */ #include "citizens.h" +#include "combat.h" +#include "fc_interface.h" #include "game.h" #include "government.h" #include "improvement.h" @@ -24,17 +26,52 @@ #include "movement.h" #include "nation.h" #include "research.h" +#include "spaceship.h" +#include "specialist.h" #include "tech.h" #include "terrain.h" #include "tile.h" +#include "traderoutes.h" #include "unitlist.h" #include "unittype.h" +#include "vision.h" /* common/scriptcore */ #include "luascript.h" #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 +94,6 @@ return is_wonder(pbuilding); } - /***************************************************************************** Return TRUE if pbuilding is a great wonder. *****************************************************************************/ @@ -121,6 +157,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 +193,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) @@ -156,6 +243,21 @@ } /************************************************************************** + Return number of specialists by rule name +**************************************************************************/ +int api_methods_city_specialists(lua_State *L, City *pcity, + const char *spec) +{ + struct specialist *specs = specialist_by_rule_name(spec); + + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, pcity, 0); + LUASCRIPT_CHECK_ARG(L, NULL != specs, 3, "Wrong specialist rule name", 0); + + return pcity->specialists[specs->item_number]; +} + +/************************************************************************** Return the tile of the city. **************************************************************************/ Tile *api_methods_city_tile_get(lua_State *L, City *pcity) @@ -166,6 +268,144 @@ 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 if a city is virtual (can be got from Tile:worked etc. + in the client). Such virtual cities contain few useful info. +**************************************************************************/ +bool api_methods_city_is_virtual(lua_State *L, City *pcity) +{ + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, pcity, FALSE); + + return city_is_virtual(pcity); +} + +/*********************************************************************//*** + Returns current number of established trade routes. +**************************************************************************/ +int api_methods_city_traderoutes_number(lua_State *L, City *pcity){ + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, pcity, FALSE); + + return city_num_trade_routes(pcity); +} + +/*********************************************************************//*** + Returns table {[City partner] = int trade}. + Some partners may be unseen and thus virtual in the client. +**************************************************************************/ +lua_Object api_methods_city_traderoutes(lua_State *L, City *pcity) +{ + int t; + + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, pcity, 0); + + lua_createtable(L, 0, city_num_trade_routes(pcity)); + t = lua_gettop(L); + trade_routes_iterate(pcity, tcity) { + tolua_pushusertype(L, tcity, "City"); + lua_pushinteger(L, pcity->trade_value[_itcity]); /* FIXME: macro... */ + lua_settable(L, t); + } trade_routes_iterate_end; + + return t; +} + +/*********************************************************************//*** + Returns how much of production of otype 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 +556,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) @@ -364,6 +615,118 @@ } /***************************************************************************** + Return player team number +*****************************************************************************/ +int api_methods_player_team_number(lua_State *L, Player *pplayer) +{ + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, pplayer, FALSE); + + return team_number(pplayer->team); +} + +/***************************************************************************** + Return player team rule name +*****************************************************************************/ +const char *api_methods_player_team_rule_name(lua_State *L, Player *pplayer) +{ + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, pplayer, FALSE); + + return team_rule_name(pplayer->team); +} + +/***************************************************************************** + Return player team translated name +*****************************************************************************/ +const char + *api_methods_player_team_name_translation(lua_State *L, Player *pplayer) +{ + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, pplayer, FALSE); + + return team_name_translation(pplayer->team); +} + +/***************************************************************************** + Tests diplomatic state between players (DiplRel req in v.2.6) +*****************************************************************************/ +bool api_methods_player_dipl_rel(lua_State *L, Player *pplayer, + Player *aplayer, const char *ds) +{ + static const char *ds_names[DS_LAST] = /* FIXME: use specenum since 2.6 */ + { + N_("?diplomatic_state:Armistice"), + N_("?diplomatic_state:War"), + N_("?diplomatic_state:Cease-fire"), + N_("?diplomatic_state:Peace"), + N_("?diplomatic_state:Alliance"), + N_("?diplomatic_state:Never met"), + N_("?diplomatic_state:Team") + }; + enum diplstate_type dpa; + + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, pplayer, FALSE); + LUASCRIPT_CHECK_ARG_NIL(L, aplayer, 3, Player, FALSE); + + /* Any player is considered allied and teamed with themself */ + if (pplayer == aplayer) { + if (!fc_strcasecmp(skip_intl_qualifier_prefix(ds_names[DS_ALLIANCE]), ds) + || !fc_strcasecmp(skip_intl_qualifier_prefix(ds_names[DS_TEAM]), ds)) { + return TRUE; + } + } else { + dpa = player_diplstate_get(pplayer, aplayer)->type; + + /* Team means alliance */ + if (!fc_strcasecmp(skip_intl_qualifier_prefix(ds_names[dpa]), ds) + || (dpa == DS_TEAM + && !fc_strcasecmp(skip_intl_qualifier_prefix(ds_names[DS_ALLIANCE]), + ds))) { + return TRUE; + } + } + /* specenum diplrel_other in future v.v. */ + if (!fc_strcasecmp(ds, "Gives shared vision")) { + return gives_shared_vision(pplayer, aplayer); + } + if (!fc_strcasecmp(ds, "Receives shared vision")) { + return gives_shared_vision(aplayer, pplayer); + } + if (!fc_strcasecmp(ds, "Hosts embassy")) { + return player_has_embassy(aplayer, pplayer); + } + if (!fc_strcasecmp(ds, "Has embassy")) { + return player_has_embassy(pplayer, aplayer); + } + if (!fc_strcasecmp(ds, "Hosts real embassy")) { + return player_has_real_embassy(aplayer, pplayer); + } + if (!fc_strcasecmp(ds, "Has real embassy")) { + return player_has_real_embassy(pplayer, aplayer); + } + if (!fc_strcasecmp(ds, "Has Casus Belli")) { + return 0 < player_diplstate_get(pplayer, aplayer)->has_reason_to_cancel; + } + if (!fc_strcasecmp(ds, "Provided Casus Belli")) { + return 0 < player_diplstate_get(aplayer, pplayer)->has_reason_to_cancel; + } + if (!fc_strcasecmp(ds, "Foreign")) { + return pplayer != aplayer; + } + + /* Check if the string is valid at all */ + for (dpa = DS_ARMISTICE; dpa <= DS_LAST; dpa++) { + if (!fc_strcasecmp(skip_intl_qualifier_prefix(ds_names[dpa]), ds)) { + return FALSE; + } + } + luaL_argerror(L, 4, "Wrong diplomatic state"); + return FALSE; +} + +/***************************************************************************** Return player turns idle *****************************************************************************/ int api_methods_player_turns_idle(lua_State *L, Player *pplayer) @@ -375,6 +738,54 @@ } /***************************************************************************** + Return if player can see the tile at given vision layer +*****************************************************************************/ +bool api_methods_player_can_see_tile(lua_State *L, Player *pplayer, + Tile *tile, int vlayer) +{ + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, pplayer, FALSE); + LUASCRIPT_CHECK_ARG_NIL(L, tile, 3, Tile, FALSE); + LUASCRIPT_CHECK_ARG(L, vlayer >= V_MAIN && vlayer < V_COUNT, 4, + "Wrong vision layer", FALSE) + + return fc_funcs->player_tile_vision_get(tile, pplayer, V_INVIS); +} + +/***************************************************************************** + 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 +911,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 +946,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) @@ -532,6 +969,60 @@ return tile_owner(ptile); } +/****************************************************************//********* + Return a city working ptile (in client it may be virtual if is not seen), + NULL if none +***************************************************************************/ +City *api_methods_tile_worked(lua_State *L, Tile *self) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + LUASCRIPT_CHECK_SELF(L, self, NULL); + + return tile_worked(self); +} + +/****************************************************************//********* + Return tile's output of otype, for the city if it is specified. +***************************************************************************/ +int api_methods_tile_output(lua_State *L, Tile *self, int otype, City *city) +{ + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, self, 0); + LUASCRIPT_CHECK_ARG(L, otype >= O_FOOD && otype < O_LAST, + 3, "Wrong output type", 0); + + return city ? city_tile_output_now(city, self, otype) + : city_tile_output(NULL, self, FALSE, otype); +} + +/****************************************************************//********* + Return tile's output of otype, for the city if it is specified and + for the case it is (not) celebrating +***************************************************************************/ +int api_methods_tile_output_full(lua_State *L, Tile *self, int otype, + City *city, bool celeb) +{ + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, self, 0); + LUASCRIPT_CHECK_ARG(L, otype >= O_FOOD && otype < O_LAST, + 3, "Wrong output type", 0); + + return city_tile_output(city, self, celeb, otype); +} + +/***************************************************************************** + 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 *****************************************************************************/ @@ -699,12 +1190,24 @@ { LUASCRIPT_CHECK_STATE(L, 0); LUASCRIPT_CHECK_SELF(L, ptile1, 0); - LUASCRIPT_CHECK_ARG_NIL(L, ptile2, 3, Tile, 0); + LUASCRIPT_CHECK_ARG_NIL(L, ptile2, 2, 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) @@ -737,6 +1240,161 @@ return punit->facing; } +/*************************************************************************//** + Return list head for cargo list for Unit +*****************************************************************************/ +Unit_List_Link *api_methods_private_unit_cargo_list_head(lua_State *L, + Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + LUASCRIPT_CHECK_SELF(L, punit, NULL); + return unit_list_head(punit->transporting); +} + +/*************************************************************************//** + Return Unit that transports punit, if any. +*****************************************************************************/ +Unit *api_methods_unit_transporter(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L, NULL); + LUASCRIPT_CHECK_SELF(L, punit, NULL); + + return punit->transporter; +} + +/***************************************************************************** + 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. *****************************************************************************/ @@ -794,6 +1452,92 @@ } /***************************************************************************** + Return current vision radius for a unit, if ptile is specified - on it +*****************************************************************************/ +int api_methods_unit_vision_radius_sq(lua_State *L, Unit *punit, Tile *ptile) +{ + int base; + + LUASCRIPT_CHECK_STATE(L, FALSE); + LUASCRIPT_CHECK_SELF(L, punit, FALSE); + + base = unit_type(punit)->vision_radius_sq + + get_unittype_bonus(unit_owner(punit), + ptile ? ptile : unit_tile(punit), + unit_type(punit), + EFT_UNIT_VISION_RADIUS_SQ); + return MAX(base,0); +} + +/***************************************************************************** + Return three values: orders, bool repeat and bool vigilant + {{order=int, dir=Direction, activity=string, base=string, road=string}} +*****************************************************************************/ +lua_Object api_methods_unit_orders(lua_State *L, Unit *punit, + bool *repeat, bool *vigilant) +{ + int t, o; + void* tolua_obj; + + LUASCRIPT_CHECK_STATE(L, 0); + LUASCRIPT_CHECK_SELF(L, punit, 0); + + if (!unit_has_orders(punit)) { + lua_pushnil(L); + return lua_gettop(L); /* boolean values are FALSE by tolua_game.pkg */ + } + *repeat = punit->orders.repeat; + *vigilant = punit->orders.vigilant; + lua_createtable(L, punit->orders.length, 0); + t = lua_gettop(L); + for (int i = 0; i < punit->orders.length; i++) { + lua_pushinteger(L, i + 1); + lua_createtable(L, 0, 2); /* We mostly just move around */ + o = lua_gettop(L); + lua_pushinteger(L, punit->orders.list[i].order); + lua_setfield(L, o, "order"); + if (is_valid_dir(punit->orders.list[i].dir)) { + tolua_obj = tolua_copy(L, (void*) &punit->orders.list[i].dir, + sizeof(Direction)); + tolua_pushusertype(L, tolua_clone(L, tolua_obj, NULL), "Direction"); + lua_setfield(L, o, "dir"); + } + if (unit_activity_is_valid(punit->orders.list[i].activity)) { + lua_pushstring(L, unit_activity_name(punit->orders.list[i].activity)); + lua_setfield(L, o, "activity"); + } + /* We hope that base and road are not set together */ + tolua_obj = (void *) base_by_number(punit->orders.list[i].base); + if (tolua_obj) { + lua_pushstring(L, base_rule_name((struct base_type *) tolua_obj) ); + lua_setfield(L, o, "base"); + } + tolua_obj = (void *) road_by_number(punit->orders.list[i].road); + if (tolua_obj) { + lua_pushstring(L, road_rule_name((struct road_type *) tolua_obj)); + lua_setfield(L, o, "road"); + } + lua_settable(L, t); + } + return t; +} + +/***************************************************************************** + Return current index of punit's orders, -1 if no orders +*****************************************************************************/ +int api_methods_unit_orders_index(lua_State *L, Unit *punit) +{ + LUASCRIPT_CHECK_STATE(L, -1); + LUASCRIPT_CHECK_SELF(L, punit, -1); + + if (unit_has_orders(punit)) { + return punit->orders.index + 1; + } else { + return -1; + } +} + +/***************************************************************************** Return rule name for Unit_Type *****************************************************************************/ const char *api_methods_unit_type_rule_name(lua_State *L, @@ -818,6 +1562,19 @@ } +/**********************************************************************//*** + Gets graphic tags (main and alternative) for a unit type +***************************************************************************/ +void api_methods_utype_gfx(lua_State *L, Unit_Type *self, + const char **graphic, const char** graphic_alt) +{ + LUASCRIPT_CHECK_STATE(L); + LUASCRIPT_CHECK_SELF(L, self); + + *graphic = self->graphic_str; + *graphic_alt = self->graphic_alt; +} + /***************************************************************************** Return Unit for list link *****************************************************************************/ diff -Nur -X diff_ignore ./LT2_5/common/scriptcore/api_game_methods.h ./LT2_5-my/common/scriptcore/api_game_methods.h --- ./LT2_5/common/scriptcore/api_game_methods.h 2019-10-20 23:27:32.726945480 +0300 +++ ./LT2_5-my/common/scriptcore/api_game_methods.h 2019-10-18 23:43:18.533102925 +0300 @@ -48,6 +48,23 @@ 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); +bool api_methods_city_is_virtual(lua_State *L, City *pcity); +int api_methods_city_traderoutes_number(lua_State *L, City *pcity); +lua_Object api_methods_city_traderoutes(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); +int api_methods_city_specialists(lua_State *L, City *pcity, + const char *spec); /* Government */ const char *api_methods_government_rule_name(lua_State *L, @@ -72,11 +89,21 @@ 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, Player *aplayer); + +int api_methods_player_team_number(lua_State *L, Player *pplayer); +const char *api_methods_player_team_rule_name(lua_State *L, Player *pplayer); +const char + *api_methods_player_team_name_translation(lua_State *L, Player *pplayer); +bool api_methods_player_dipl_rel(lua_State *L, Player *pplayer, + Player *aplayer, const char *ds); int api_methods_player_turns_idle(lua_State *L, Player *pplayer); +bool api_methods_player_can_see_tile(lua_State *L, Player *pplayer, + Tile *tile, int vlayer); Unit_List_Link *api_methods_private_player_unit_list_head(lua_State *L, Player *pplayer); City_List_Link *api_methods_private_player_city_list_head(lua_State *L, @@ -102,6 +129,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 +143,12 @@ 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); +City *api_methods_tile_worked(lua_State *L, Tile *self); +int api_methods_tile_output(lua_State *L, Tile *self, int otype, City *city); +int api_methods_tile_output_full(lua_State *L, Tile *self, int otype, + City *city, bool celeb); 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 +159,31 @@ /* 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 *api_methods_unit_transporter(lua_State *L, Unit *punit); +Unit_List_Link *api_methods_private_unit_cargo_list_head(lua_State *L, + Unit *punit); +int api_methods_unit_vision_radius_sq(lua_State *L, Unit *punit, Tile *ptile); +lua_Object api_methods_unit_orders(lua_State *L, Unit *punit, + bool *repeat, bool *vigilant); +int api_methods_unit_orders_index(lua_State *L, Unit *punit); /* Unit Type */ bool api_methods_unit_type_has_flag(lua_State *L, Unit_Type *punit_type, @@ -139,6 +197,14 @@ Unit_Type *punit_type); const char *api_methods_unit_type_name_translation(lua_State *L, Unit_Type *punit_type); +void api_methods_utype_gfx(lua_State *L, Unit_Type *self, + const char **graphic, const char** graphic_alt); +int api_methods_unit_vision_radius_sq(lua_State *L, Unit *punit, + Tile *ptile); + +/* 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); diff -Nur -X diff_ignore ./LT2_5/common/scriptcore/luascript_types.h ./LT2_5-my/common/scriptcore/luascript_types.h --- ./LT2_5/common/scriptcore/luascript_types.h 2019-10-20 23:27:32.726945480 +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 ./LT2_5/common/scriptcore/tolua_common_a.pkg ./LT2_5-my/common/scriptcore/tolua_common_a.pkg --- ./LT2_5/common/scriptcore/tolua_common_a.pkg 2019-10-20 23:27:32.726945480 +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 ./LT2_5/common/scriptcore/tolua_common_z.pkg ./LT2_5-my/common/scriptcore/tolua_common_z.pkg --- ./LT2_5/common/scriptcore/tolua_common_z.pkg 2019-10-20 23:27:32.726945480 +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 ./LT2_5/common/scriptcore/tolua_game.pkg ./LT2_5-my/common/scriptcore/tolua_game.pkg --- ./LT2_5/common/scriptcore/tolua_game.pkg 2019-10-20 23:27:32.726945480 +0300 +++ ./LT2_5-my/common/scriptcore/tolua_game.pkg 2019-10-18 23:43:18.637096206 +0300 @@ -21,6 +21,8 @@ $#ifdef HAVE_CONFIG_H $#include $#endif +/* common */ +$#include "vision.h" /* enum vision_layer */ /* common/scriptcore */ $#include "api_common_utilities.h" @@ -30,11 +32,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 +55,24 @@ Player *original; const int id; + unsigned char martial_law; + unsigned char unit_happy_upkeep; + 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 +85,8 @@ /* This used to be @ homecity_id, but it does not work with toluaxx. */ int homecity; + int fuel; + int activity_count; const int id; }; @@ -63,6 +95,7 @@ Terrain *terrain; const int index @ id; + signed short continent; }; struct Resource { @@ -85,6 +118,9 @@ struct Unit_Type { int build_cost; + int move_rate; + int fuel; + int happy_cost; const int item_number @ id; }; @@ -107,6 +143,58 @@ 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 VISION { + enum /* vision_layer */ { + V_MAIN @ MAIN, + V_INVIS @ INVIS + } +} + /* Module Game */ module game { int api_methods_game_turn @@ -118,6 +206,12 @@ module properties { int api_methods_player_number @ id (lua_State *L, Player *self); + int api_methods_player_team_number + @ team_number (lua_State *L, Player *pplayer); + const char *api_methods_player_team_rule_name + @ team_rule_name (lua_State *L, Player *pplayer); + const char *api_methods_player_team_name_translation + @ team_name_translated (lua_State *L, Player *pplayer); } int api_methods_player_num_cities @@ -134,8 +228,16 @@ @ land_area (lua_State *L, Player *self); bool api_methods_player_shares_research @ shares_research (lua_State *L, Player *self, Player *other); + bool api_methods_player_dipl_rel + @ dipl_rel(lua_State *L, Player *pplayer, Player *aplayer, + const char *ds); 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); + bool api_methods_player_can_see_tile + @ can_see_tile (lua_State *L, Player *pplayer, Tile *tile, + int vlayer = V_MAIN); } module methods_private { @@ -157,9 +259,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 +278,16 @@ @ 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_is_virtual + @ is_virtual (lua_State *L, City *pcity); + int api_methods_city_traderoutes_number + @ trade_routes_count (lua_State *L, City *pcity); } bool api_methods_city_has_building @@ -175,9 +296,30 @@ @ 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); + lua_Object api_methods_city_traderoutes + @ trade_routes(lua_State *L, City *pcity); + int api_methods_city_specialists + @ specialists(lua_State *L, City *pcity, const char *spec); +} + +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 +333,48 @@ 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); + int api_methods_unit_orders_index + @ orders_index(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); + Unit *api_methods_unit_transporter + @ transporter (lua_State *L, Unit *self); + int api_methods_unit_vision_radius_sq + @ vision_radius_sq(lua_State *L, Unit *punit, Tile *ptile = NULL); + lua_Object api_methods_unit_orders + @ orders (lua_State *L, Unit *punit, + bool *repeat = FALSE, bool *vigilant = FALSE); +} + +module methods_private { + module Unit { + Unit_List_Link *api_methods_private_unit_cargo_list_head + @ cargo_list_head (lua_State *L, Unit *self); + } } $[ @@ -225,10 +403,24 @@ @ 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 @ city (lua_State *L, Tile *self); + City *api_methods_tile_worked + @ worked (lua_State *L, Tile *self); + int api_methods_tile_output + @ output (lua_State *L, Tile *self, int otype, + City *city = NULL); + int api_methods_tile_output_full + @ output (lua_State *L, Tile *self, int otype, + City *city, bool celeb); Player *api_methods_tile_owner @ owner (lua_State *L, Tile *self); bool api_methods_tile_city_exists_within_max_city_map @@ -241,6 +433,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 +466,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); } } @@ -328,6 +526,10 @@ @ name_translation (lua_State *L, Unit_Type *self); bool api_methods_unit_type_can_exist_at_tile @ can_exist_at_tile(lua_State *L, Unit_Type *self, Tile *ptile); + void api_methods_utype_gfx + @ graphic_tags (lua_State *L, Unit_Type *self, + const char **graphic = NULL, + const char **graphic_alt = NULL); } $[ @@ -380,9 +582,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 +602,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 +628,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 +654,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 +665,9 @@ function str2direction(str) return direction.str2dir(str) end + +-- Use directions as English names in strings +Direction.__tostring = Direction.name $] $[ @@ -499,6 +713,21 @@ 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 + + + -- Safe iteration over the units transported by Unit + function Unit:cargo_iterate() + return safe_iterate_list(private.Unit.cargo_list_head(self)) + end end -- *************************************************************************** diff -Nur -X diff_ignore ./LT2_5/common/tech.c ./LT2_5-my/common/tech.c --- ./LT2_5/common/tech.c 2019-10-20 23:27:32.726945480 +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 ./LT2_5/common/tech.h ./LT2_5-my/common/tech.h --- ./LT2_5/common/tech.h 2019-10-20 23:27:32.730945412 +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); Двоичные файлы ./LT2_5/freeciv.sqlite и ./LT2_5-my/freeciv.sqlite различаются diff -Nur -X diff_ignore ./LT2_5/.geanyprj ./LT2_5-my/.geanyprj --- ./LT2_5/.geanyprj 1970-01-01 03:00:00.000000000 +0300 +++ ./LT2_5-my/.geanyprj 2019-01-19 23:02:11.902533452 +0300 @@ -0,0 +1,7 @@ +[project] +name=LT2_5 +description= +base_path=./ +run_cmd= +regenerate=true +type=C/C++ diff -Nur -X diff_ignore ./LT2_5/server/civserver.c ./LT2_5-my/server/civserver.c --- ./LT2_5/server/civserver.c 2019-10-20 23:27:33.150938364 +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 ./LT2_5/server/fcdb.c ./LT2_5-my/server/fcdb.c --- ./LT2_5/server/fcdb.c 2019-10-20 23:27:33.150938364 +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 ./LT2_5/server/plrhand.c ./LT2_5-my/server/plrhand.c --- ./LT2_5/server/plrhand.c 2019-10-20 23:27:33.170938029 +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 ./LT2_5/server/srv_main.c ./LT2_5-my/server/srv_main.c --- ./LT2_5/server/srv_main.c 2019-10-20 23:27:33.174937961 +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 ./LT2_5/server/srv_main.h ./LT2_5-my/server/srv_main.h --- ./LT2_5/server/srv_main.h 2019-10-20 23:27:33.174937961 +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 ./LT2_5/server/stdinhand.c ./LT2_5-my/server/stdinhand.c --- ./LT2_5/server/stdinhand.c 2019-10-20 23:27:33.178937895 +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: