diff --git a/simutrans/trunk/Makefile b/simutrans/trunk/Makefile index c4dd72b05..eb89f9b95 100644 --- a/simutrans/trunk/Makefile +++ b/simutrans/trunk/Makefile @@ -232,6 +232,7 @@ SOURCES += bauer/fabrikbauer.cc SOURCES += bauer/goods_manager.cc SOURCES += bauer/hausbauer.cc SOURCES += bauer/tunnelbauer.cc +SOURCES += bauer/script_tool_manager.cc SOURCES += bauer/vehikelbauer.cc SOURCES += bauer/wegbauer.cc SOURCES += boden/boden.cc @@ -405,6 +406,7 @@ SOURCES += gui/scenario_frame.cc SOURCES += gui/scenario_info.cc SOURCES += gui/schedule_gui.cc SOURCES += gui/schedule_list.cc +SOURCES += gui/script_tool_frame.cc SOURCES += gui/server_frame.cc SOURCES += gui/settings_frame.cc SOURCES += gui/settings_stats.cc @@ -506,6 +508,7 @@ SOURCES += simskin.cc SOURCES += simsound.cc SOURCES += simticker.cc SOURCES += simtool.cc +SOURCES += simtool-scripted.cc SOURCES += simware.cc SOURCES += simworld.cc SOURCES += squirrel/sq_extensions.cc diff --git a/simutrans/trunk/bauer/script_tool_manager.cc b/simutrans/trunk/bauer/script_tool_manager.cc new file mode 100644 index 000000000..515f3d0b7 --- /dev/null +++ b/simutrans/trunk/bauer/script_tool_manager.cc @@ -0,0 +1,132 @@ +/* + * This file is part of the Simutrans project under the Artistic License. + * (see LICENSE.txt) + */ + +#include "script_tool_manager.h" + +#include "../dataobj/tabfile.h" + +#include "../simdebug.h" +#include "../simtool-scripted.h" +#include "../simskin.h" +#include "../descriptor/skin_desc.h" +#include "../sys/simsys.h" +#include "../gui/tool_selector.h" +#include "../utils/cbuffer_t.h" +#include "../utils/searchfolder.h" + + +vector_tpl script_tool_manager_t::one_click_script_tools; +vector_tpl script_tool_manager_t::two_click_script_tools; + + +bool script_tool_manager_t::check_file( const char *path ) +{ + cbuffer_t buf; + buf.printf("%s/tool.nut", path ); + if (FILE* const f = dr_fopen(buf, "r")) { + fclose(f); + return true; + } + return false; +} + + +const scripted_tool_info_t* script_tool_manager_t::get_script_info(const char* path) +{ + scripted_tool_info_t* info = new scripted_tool_info_t(); + info->path = path; + + // read description.tab + cbuffer_t buf; + buf.printf("%s/description.tab", path); + tabfile_t file; + + if ( file.open(buf) ) { + tabfileobj_t contents; + file.read( contents ); + info->title = contents.get_string("title", path); + info->menu_arg = contents.get_string("menu", ""); + + printf("Path %s Title %s\n", path, info->title.c_str()); + + const char* skin_name = contents.get_string("icon", ""); + info->desc = skinverwaltung_t::get_extra(skin_name, strlen(skin_name), skinverwaltung_t::cursor); + info->is_one_click = !( strcmp(contents.get_string("type", "one_click"), "two_click")==0 ); + } + else { + // no description.tab, use default values + info->title = path; + } + return info; +} + + +tool_t* script_tool_manager_t::load_tool(char const* path, tool_t* tool) +{ + if (!check_file(path)) { + return tool; + } + const scripted_tool_info_t* info = get_script_info(path); + + if (tool) { + // reinitialize existing tool + exec_script_base_t* esb = dynamic_cast(tool); + assert(esb); + esb->set_info(info); + } + else { + // create new tool + if (info->is_one_click) { + tool = new tool_exec_script_t(info); + } + else { + tool = new tool_exec_two_click_script_t(info); + } + } + // set icon and cursor + if (info->desc) { + tool->cursor = info->desc->get_image_id(0); + tool->set_icon(info->desc->get_image_id(1)); + } + // TODO set default cursor... + return tool; +} + + +void script_tool_manager_t::load_scripts(char const* path) +{ + searchfolder_t find; + find.search(path, "", true, false); + FOR(searchfolder_t, const &name, find) { + cbuffer_t fullname; + fullname.printf("%s%s",path,name); + + tool_t *tool = load_tool(fullname); + + if (tool_exec_script_t* ot = dynamic_cast(tool)) { + one_click_script_tools.append(ot); + } + else if (tool_exec_two_click_script_t* tt = dynamic_cast(tool)) { + two_click_script_tools.append(tt); + } + } +} + + +void script_tool_manager_t::fill_menu(tool_selector_t* tool_selector, char const* arg, sint16 /*sound_ok*/) +{ + for(uint32 i=0; iget_menu_arg(), arg)==0 ) { + tool_selector->add_tool_selector(tool); + } + } + for(uint32 i=0; iget_menu_arg(), arg)==0 ) { + tool_selector->add_tool_selector(tool); + } + } +} diff --git a/simutrans/trunk/bauer/script_tool_manager.h b/simutrans/trunk/bauer/script_tool_manager.h new file mode 100644 index 000000000..a3c40192c --- /dev/null +++ b/simutrans/trunk/bauer/script_tool_manager.h @@ -0,0 +1,61 @@ +/* + * This file is part of the Simutrans project under the Artistic License. + * (see LICENSE.txt) + */ + +#ifndef BAUER_SCRIPT_TOOL_MANAGER_H +#define BAUER_SCRIPT_TOOL_MANAGER_H + + +#include "../tpl/vector_tpl.h" + +class exec_script_base_t; +class scripted_tool_info_t; +class tool_t; +class tool_selector_t; +class tool_exec_script_t; +class tool_exec_two_click_script_t; + +/** + * There's no need to construct an instance since everything is static here. + */ +class script_tool_manager_t +{ +private: + /// All one-click script tools + static vector_tpl one_click_script_tools; + /// All two-click script tools + static vector_tpl two_click_script_tools; + +public: + + /// Looks for tool.nut in path + static bool check_file(char const* path); + + /** + * Reads description.tab at path. + * If no description is found, use default values. + * @returns filled info structure. + */ + static const scripted_tool_info_t* get_script_info(const char* path); + + /** + * Loads tool from path. + * If @p tool is not NULL it is reinitialized with the scripted tool. + * @returns tool + */ + static tool_t* load_tool(char const* path, tool_t* tool = NULL); + + /** + * Reads all tools from directory @p path. + */ + static void load_scripts(char const* path); + + /** + * Fills toolbar with scripted tools. + * Select only tools with menu entry equal to @p arg. + */ + static void fill_menu(tool_selector_t* tool_selector, char const* arg, sint16 sound_ok); +}; + +#endif diff --git a/simutrans/trunk/gui/script_tool_frame.cc b/simutrans/trunk/gui/script_tool_frame.cc new file mode 100644 index 000000000..1181dd991 --- /dev/null +++ b/simutrans/trunk/gui/script_tool_frame.cc @@ -0,0 +1,74 @@ +/* + * This file is part of the Simutrans project under the Artistic License. + * (see LICENSE.txt) + */ + +#include "script_tool_frame.h" + +#include "../bauer/script_tool_manager.h" +#include "../dataobj/environment.h" +#include "../dataobj/tabfile.h" +#include "../dataobj/translator.h" +#include "../simdebug.h" +#include "../simworld.h" +#include "../simtool-scripted.h" +#include "../sys/simsys.h" +#include "../utils/cbuffer_t.h" +#include "../utils/simstring.h" + + +script_tool_frame_t::~script_tool_frame_t() +{ + clear_ptr_vector(infos); +} + + +script_tool_frame_t::script_tool_frame_t() : savegame_frame_t(NULL, true, NULL, false) +{ + cbuffer_t pakset_script_tool; + cbuffer_t addons_script_tool; + + pakset_script_tool.printf("%s%stool/", env_t::program_dir, env_t::objfilename.c_str()); + addons_script_tool.printf("addons/%stool/", env_t::objfilename.c_str()); + + if (env_t::default_settings.get_with_private_paks()) { + this->add_path(addons_script_tool); + } + this->add_path(pakset_script_tool); + + set_name(translator::translate("Load script tool")); + set_focus(NULL); +} + + +/** + * Action, started after button pressing. + */ +bool script_tool_frame_t::item_action(const char *fullpath) +{ + const scripted_tool_info_t* info = script_tool_manager_t::get_script_info(fullpath); + bool is_one_click = info->is_one_click; + delete info; + + tool_t* tool = script_tool_manager_t::load_tool(fullpath, tool_t::general_tool[is_one_click ? TOOL_EXEC_SCRIPT : TOOL_EXEC_TWO_CLICK_SCRIPT]); + assert(tool); + + welt->set_tool(tool, welt->get_active_player()); + return true; +} + + +// calls tool manager to read description.tab +const char *script_tool_frame_t::get_info(const char *path) +{ + const scripted_tool_info_t* info = script_tool_manager_t::get_script_info(path); + infos.append(info); + + return info->title.c_str(); +} + + +bool script_tool_frame_t::check_file( const char *filename, const char * ) +{ + return script_tool_manager_t::check_file(filename); +} diff --git a/simutrans/trunk/gui/script_tool_frame.h b/simutrans/trunk/gui/script_tool_frame.h new file mode 100644 index 000000000..204d70002 --- /dev/null +++ b/simutrans/trunk/gui/script_tool_frame.h @@ -0,0 +1,48 @@ +/* + * This file is part of the Simutrans project under the Artistic License. + * (see LICENSE.txt) + */ + +#ifndef GUI_SCRIPT_TOOL_INFO_H +#define GUI_SCRIPT_TOOL_INFO_H + + +#include "savegame_frame.h" +#include "../tpl/vector_tpl.h" + +class scripted_tool_info_t; + + +class script_tool_frame_t : public savegame_frame_t +{ +private: + // pointers to info structures + vector_tpl infos; + +protected: + /** + * Action that's started by the press of a button. + */ + bool item_action(const char *fullpath) OVERRIDE; + + /** + * Returns extra file info: title of tool from description.tab + */ + const char *get_info(const char *path) OVERRIDE; + + // true, if valid + bool check_file( const char *filename, const char *suffix ) OVERRIDE; + +public: + script_tool_frame_t(); + + ~script_tool_frame_t(); + + /** + * Set the window associated helptext + * @return the filename for the helptext, or NULL + */ + const char * get_help_filename() const OVERRIDE { return "script_tool.txt"; } +}; + +#endif diff --git a/simutrans/trunk/network/network_cmd_ingame.cc b/simutrans/trunk/network/network_cmd_ingame.cc index a20f0e3b7..48fccea5a 100644 --- a/simutrans/trunk/network/network_cmd_ingame.cc +++ b/simutrans/trunk/network/network_cmd_ingame.cc @@ -1215,6 +1215,7 @@ void nwc_tool_t::do_command(karte_t *welt) // callback to script here if (local && callback_id != 0) { + printf("calling back %d\n", callback_id); if (init) { suspended_scripts_t::tell_return_value(callback_id, res); } diff --git a/simutrans/trunk/script/api/api_command.cc b/simutrans/trunk/script/api/api_command.cc index 129f63554..4e77e73d7 100644 --- a/simutrans/trunk/script/api/api_command.cc +++ b/simutrans/trunk/script/api/api_command.cc @@ -91,6 +91,8 @@ SQInteger param::push(HSQUIRRELVM vm, call_tool_init v) // register for wakeup suspended_scripts_t::register_suspended_script(callback_id, vm); // suspend vm for now, after wakeup it returns to the script + + printf("work suspended %d\n", callback_id); return sq_suspendvm(vm); } else { diff --git a/simutrans/trunk/script/script.cc b/simutrans/trunk/script/script.cc index 55ede3e8f..6f9fcf13a 100644 --- a/simutrans/trunk/script/script.cc +++ b/simutrans/trunk/script/script.cc @@ -27,6 +27,7 @@ // debug: store stack pointer #define BEGIN_STACK_WATCH(v) int stack_top = sq_gettop(v); +// printf(".. %s[%d]\n", __FUNCTION__, __LINE__); // debug: compare stack pointer with expected stack pointer, raise warning if failure #define END_STACK_WATCH(v, delta) if ( (stack_top+(delta)) != sq_gettop(v)) { dbg->warning( __FUNCTION__, "(%d) stack in %d expected %d out %d", __LINE__,stack_top,stack_top+(delta),sq_gettop(v)); } @@ -349,6 +350,7 @@ void script_vm_t::intern_resume_call(HSQUIRRELVM job) return; } if (!sq_canresumevm(job)) { + printf(".. waiting\n"); // vm waits for return value to suspended call dbg->message("script_vm_t::intern_resume_call", "waiting for return value"); return; @@ -357,6 +359,7 @@ void script_vm_t::intern_resume_call(HSQUIRRELVM job) if (nparams < 0) { retvalue = false; } + printf("..resuming..\n"); // resume v.m. if (!SQ_SUCCEEDED(sq_resumevm(job, retvalue, 10000))) { @@ -458,6 +461,7 @@ void script_vm_t::intern_queue_call(HSQUIRRELVM job, int nparams, bool retvalue) // found identic call // add callback to queue if (equal) { + printf("..swallowed call\n"); sq_pushstring(job, "queued_callbacks", -1); sq_get(job, -4); sq_pushinteger(job, i); diff --git a/simutrans/trunk/simmain.cc b/simutrans/trunk/simmain.cc index 987515986..51470a3f0 100644 --- a/simutrans/trunk/simmain.cc +++ b/simutrans/trunk/simmain.cc @@ -81,6 +81,7 @@ #include "utils/simrandom.h" #include "bauer/vehikelbauer.h" +#include "bauer/script_tool_manager.h" #include "vehicle/simvehicle.h" #include "vehicle/simroadtraffic.h" @@ -1106,12 +1107,19 @@ int simu_main(int argc, char** argv) pakset_info_t::calculate_checksum(); pakset_info_t::debug(); - if( !overlaid_warning.empty() ) { - overlaid_warning.append( "

Continue by ESC, SPACE, or BACKSPACE.
" ); - help_frame_t *win = new help_frame_t(); - win->set_text( overlaid_warning.c_str() ); - modal_dialogue( win, magic_pakset_info_t, NULL, wait_for_key ); - destroy_all_win(true); +// if( !overlaid_warning.empty() ) { +// overlaid_warning.append( "

Continue by ESC, SPACE, or BACKSPACE.
" ); +// help_frame_t *win = new help_frame_t(); +// win->set_text( overlaid_warning.c_str() ); +// modal_dialogue( win, magic_pakset_info_t, NULL, wait_for_key ); +// destroy_all_win(true); +// } + + // load tool scripts + dbg->message("simmain()","Reading tool scripts ..."); + script_tool_manager_t::load_scripts((env_t::objfilename + "tool/").c_str()); + if( env_t::default_settings.get_with_private_paks() ) { + script_tool_manager_t::load_scripts(("addons/" + env_t::objfilename + "tool/").c_str()); } dbg->message("simmain()","Reading menu configuration ..."); diff --git a/simutrans/trunk/simmenu.cc b/simutrans/trunk/simmenu.cc index 2126a6e47..2bbe08482 100644 --- a/simutrans/trunk/simmenu.cc +++ b/simutrans/trunk/simmenu.cc @@ -15,6 +15,7 @@ #include "simmenu.h" #include "simtool.h" #include "simtool-dialogs.h" +#include "simtool-scripted.h" #include "simskin.h" #include "simsound.h" @@ -22,6 +23,7 @@ #include "bauer/wegbauer.h" #include "bauer/brueckenbauer.h" #include "bauer/tunnelbauer.h" +#include "bauer/script_tool_manager.h" #include "descriptor/building_desc.h" #include "descriptor/bridge_desc.h" @@ -118,6 +120,8 @@ tool_t *create_general_tool(int toolnr) case TOOL_SET_CLIMATE: tool = new tool_set_climate_t(); break; case TOOL_ROTATE_BUILDING: tool = new tool_rotate_building_t(); break; case TOOL_MERGE_STOP: tool = new tool_merge_stop_t(); break; + case TOOL_EXEC_SCRIPT: tool = new tool_exec_script_t(); break; + case TOOL_EXEC_TWO_CLICK_SCRIPT: tool = new tool_exec_two_click_script_t(); break; default: dbg->error("create_general_tool()","cannot satisfy request for general_tool[%i]!",toolnr); return NULL; @@ -219,6 +223,7 @@ tool_t *create_dialog_tool(int toolnr) case DIALOG_SCENARIO_INFO: tool = new dialog_scenario_info_t(); break; case DIALOG_LIST_DEPOT: tool = new dialog_list_depot_t(); break; case DIALOG_LIST_VEHICLE: tool = new dialog_list_vehicle_t(); break; + case DIALOG_SCRIPT_TOOL: tool = new dialog_script_tool_t(); break; default: dbg->error("create_dialog_tool()","cannot satisfy request for dialog_tool[%i]!",toolnr); return NULL; @@ -895,6 +900,13 @@ void toolbar_t::update(player_t *player) waytype_t way = (waytype_t)(*c!=0 ? atoi(++c) : 0); hausbauer_t::fill_menu( tool_selector, utype, way, get_sound(c)); } + else if (char const* const c = strstart(param, "scripts(")) { + const char* end = strchr(c, ')'); + char buf[1000]; + size_t len = end ? min(lengthof(buf), end-c) : lengthof(buf); + tstrncpy(buf, c, len); + script_tool_manager_t::fill_menu(tool_selector, buf, get_sound(c)); + } else if (param[0] == '-') { // add dummy tool_t as seperator tool_selector->add_tool_selector( dummy ); diff --git a/simutrans/trunk/simmenu.h b/simutrans/trunk/simmenu.h index 104824643..5690b7aad 100644 --- a/simutrans/trunk/simmenu.h +++ b/simutrans/trunk/simmenu.h @@ -74,6 +74,8 @@ enum { TOOL_SET_CLIMATE, TOOL_ROTATE_BUILDING, TOOL_MERGE_STOP, + TOOL_EXEC_SCRIPT, + TOOL_EXEC_TWO_CLICK_SCRIPT, GENERAL_TOOL_COUNT, GENERAL_TOOL = 0x1000 }; @@ -156,6 +158,7 @@ enum { DIALOG_SCENARIO_INFO, DIALOG_LIST_DEPOT, DIALOG_LIST_VEHICLE, + DIALOG_SCRIPT_TOOL, DIALOGE_TOOL_COUNT, DIALOGE_TOOL = 0x4000 }; diff --git a/simutrans/trunk/simskin.cc b/simutrans/trunk/simskin.cc index 5111fe624..69ffc0ecd 100644 --- a/simutrans/trunk/simskin.cc +++ b/simutrans/trunk/simskin.cc @@ -78,7 +78,8 @@ const skin_desc_t* skinverwaltung_t::pumpe = NULL; const skin_desc_t* skinverwaltung_t::senke = NULL; const skin_desc_t* skinverwaltung_t::tunnel_texture = NULL; -slist_tplskinverwaltung_t::extra_obj; +slist_tplskinverwaltung_t::extra_menu_obj; +slist_tplskinverwaltung_t::extra_cursor_obj; static special_obj_tpl const misc_objekte[] = { @@ -191,20 +192,26 @@ bool skinverwaltung_t::register_desc(skintyp_t type, const skin_desc_t* desc) case nothing: return true; default: return false; } - if( !::register_desc(sd, desc) ) { - // currently no misc objects allowed ... - if( !(type==cursor || type==symbol) ) { - if( type==menu ) { - extra_obj.insert( desc ); - dbg->message( "skinverwaltung_t::register_desc()","Extra object %s added.", desc->get_name() ); - } - else { - dbg->warning("skinverwaltung_t::register_desc()","Spurious object '%s' loaded (will not be referenced anyway)!", desc->get_name() ); - } + if( ::register_desc(sd, desc) ) { + return true; + } + else if( type==cursor || type==symbol ) { + if( ::register_desc( fakultative_objekte, desc ) ) { + return true; + } + } + // currently no misc objects allowed ... + if( type==cursor || type==menu ) { + if( type==cursor ) { + extra_cursor_obj.insert( desc ); } else { - return ::register_desc( fakultative_objekte, desc ); + extra_menu_obj.insert( desc ); } + dbg->message( "skinverwaltung_t::register_desc()","Extra object %s added.", desc->get_name() ); + } + else { + dbg->warning("skinverwaltung_t::register_desc()","Spurious object '%s' loaded (will not be referenced anyway)!", desc->get_name() ); } return true; } @@ -212,10 +219,15 @@ bool skinverwaltung_t::register_desc(skintyp_t type, const skin_desc_t* desc) // return the extra_obj with this name -const skin_desc_t *skinverwaltung_t::get_extra( const char *str, int len ) +const skin_desc_t *skinverwaltung_t::get_extra( const char *str, int len, skintyp_t type ) { - FOR(slist_tpl, const s, skinverwaltung_t::extra_obj) { - if (strncmp(str, s->get_name(), len) == 0) { + if( type!=menu && type!=cursor ) { + // illegal type + return NULL; + } + FOR(slist_tpl, const s, + (type==menu ? skinverwaltung_t::extra_menu_obj : skinverwaltung_t::extra_cursor_obj)) { + if ( strncmp(str, s->get_name(), len) == 0 ) { return s; } } diff --git a/simutrans/trunk/simskin.h b/simutrans/trunk/simskin.h index 54789d202..b8ef09ede 100644 --- a/simutrans/trunk/simskin.h +++ b/simutrans/trunk/simskin.h @@ -152,11 +152,12 @@ public: * @param len length of string * @return pointer to skin object or NULL if nothing found */ - static const skin_desc_t *get_extra( const char *str, int len ); + static const skin_desc_t *get_extra( const char *str, int len, skintyp_t type = menu ); private: - /// holds objects from paks with type 'menu' - static slist_tplextra_obj; + /// holds objects from paks with type 'menu' and 'cursor' + static slist_tplextra_menu_obj; + static slist_tplextra_cursor_obj; }; #endif diff --git a/simutrans/trunk/simtool-dialogs.h b/simutrans/trunk/simtool-dialogs.h index a282bc6cc..6767d06d7 100644 --- a/simutrans/trunk/simtool-dialogs.h +++ b/simutrans/trunk/simtool-dialogs.h @@ -48,6 +48,7 @@ #include "gui/scenario_info.h" #include "gui/depotlist_frame.h" #include "gui/vehiclelist_frame.h" +#include "gui/script_tool_frame.h" class player_t; @@ -273,6 +274,21 @@ public: bool is_init_network_save() const OVERRIDE{ return true; } }; +// open scripted-tools dialog +class dialog_script_tool_t : public tool_t { +public: + dialog_script_tool_t() : tool_t(DIALOG_SCRIPT_TOOL | DIALOGE_TOOL) {} + char const* get_tooltip(player_t const*) const OVERRIDE{ return translator::translate("Load tool script"); } + bool is_selected() const OVERRIDE{ return win_get_magic(magic_load_t); } + bool init(player_t*) OVERRIDE{ + destroy_win(magic_save_t); + create_win( new script_tool_frame_t(), w_info, magic_load_t ); + return false; + } + bool exit(player_t*) OVERRIDE{ destroy_win(magic_load_t); return false; } + bool is_init_network_save() const OVERRIDE{ return true; } +}; + // open scenario dialog class dialog_scenario_t : public tool_t { public: diff --git a/simutrans/trunk/simtool-scripted.cc b/simutrans/trunk/simtool-scripted.cc new file mode 100644 index 000000000..a400ed783 --- /dev/null +++ b/simutrans/trunk/simtool-scripted.cc @@ -0,0 +1,278 @@ +/* + * This file is part of the Simutrans project under the Artistic License. + * (see LICENSE.txt) + */ + +#include "simtool-scripted.h" +#include "player/simplay.h" + +#include "bauer/script_tool_manager.h" +#include "script/script.h" +#include "script/script_loader.h" +#include "script/api/api.h" +#include "script/api_class.h" +#include "script/api_param.h" + + +void export_scripted_tools(HSQUIRRELVM vm); + +// callback to receive error messages from work/do_work calls +static bool exec_script_base_work_callback(exec_script_base_t *esb, player_t* player, const char* err) +{ + tool_t *tool = esb ? dynamic_cast(esb) : NULL; + if (tool && player) { + player->tell_tool_result(tool, koord3d::invalid, err); + } + if (tool_exec_two_click_script_t* tct = dynamic_cast(esb)) { + tct->waiting_for_do_work = false; + tct->init(player); + } + printf(">> callback %p %p %s\n", tool, player, err); + return true; +} + + +// -- basic script handling -- + +exec_script_base_t::~exec_script_base_t() +{ + delete info; + delete script; +} + + +void exec_script_base_t::set_info(const scripted_tool_info_t *i) +{ + delete info; + info = i; +} + + +bool exec_script_base_t::init_vm(player_t* player) +{ + if (get_info() == NULL) { + // tool probably read from menuconf.tab + // path in default_param, initialize + if (tool_t *tool = dynamic_cast(this)) { + script_tool_manager_t::load_tool(tool->get_default_param(), tool); + } + } + if( script==NULL || (info && info->restart) ) { + load_script(info->path, player); + } + else { + // TODO script->reset(); // clear queues, remove from suspended scripts list, clear wait_external, delete slots in registry + } + return script != NULL; +} + + +void exec_script_base_t::load_script(const char* path, player_t* player) +{ + cbuffer_t buf; + buf.printf("script-exec-%d.log", player->get_player_nr()); + if( script ) { + // if vm already exists, delete it. + delete script; + script = NULL; + } + // start vm + script = script_loader_t::start_vm("tool_base.nut", buf, path, false); + if (script == NULL) { + return; + } + // set my player number + script->set_my_player(player->get_player_nr()); + // export tool-pointer handling + export_scripted_tools(script->get_vm()); + // callback + script->register_callback(exec_script_base_work_callback, "exec_script_base_work_callback"); + // call script to initialize it + buf.clear(); + buf.printf( "%s/tool.nut", path ); + if (const char* err = script->call_script(buf)) { + if (strcmp(err, "suspended")) { + dbg->error("tool_exec_script_t::load_script", "error [%s] calling %s", err, (const char*)buf); + delete script; + script = NULL; + } + } +} + + +/** + * Mechanic to transfer exec_script_base_t* pointers between squirrel and c++ + */ +namespace script_api { + + declare_specialized_param(exec_script_base_t*, "x", "scripted_tool_t"); + + SQInteger param::push(HSQUIRRELVM vm, exec_script_base_t* const& v) + { + push_instance(vm, param::squirrel_type()); + sq_setinstanceup(vm, -1, v); + return 1; + } + exec_script_base_t* param::get(HSQUIRRELVM vm, SQInteger index) + { + return get_attached_instance(vm, index, param::tag()); + } + void* param::tag() { return (void*)¶m::get; } +}; + +void export_scripted_tools(HSQUIRRELVM vm) +{ + script_api::create_class(vm, script_api::param::squirrel_type()); + script_api::end_class(vm); + + // TODO export a function to mark tiles. +} + + +#define check_script() \ + if (!script) { \ + dbg->warning("tool_exec_script_t::call_function", "script vm is not available."); \ + return ""; \ + } +#define check_error() \ + if (err && strcmp(err, "suspended")==0) {\ + dbg->warning("tool_exec_script_t::call_function", "error calling %s: %s", function, err);\ + } + +template +const char* exec_script_base_t::call_function(script_vm_t::call_type_t ct, const char* function, player_t* player, R& ret) +{ + check_script(); + printf("called %s\n", function); + const char* err = script->call_function(ct, function, ret, player); + printf("-- returned(%s) from %s\n", err, function); + check_error(); + return err; +} + +template +const char* exec_script_base_t::call_function(script_vm_t::call_type_t ct, const char* function, player_t* player, R& ret, A1 arg1) +{ + check_script(); + printf("called %s\n", function); + const char* err = script->call_function(ct, function, ret, player, arg1); + printf("-- returned(%s) from %s\n", err, function); + check_error(); + return err; +} + +template +const char* exec_script_base_t::call_function(script_vm_t::call_type_t ct, const char* function, player_t* player, R& ret, A1 arg1, A2 arg2) +{ + check_script(); + printf("called %s\n", function); + const char* err = script->call_function(ct, function, ret, player, arg1, arg2); + printf("-- returned(%s) from %s\n", err, function); + check_error(); + return err; +} + + +void exec_script_base_t::step() +{ + if (script) { +// printf("called %s\n", "step"); + /*const char* err = */script->call_function(script_vm_t::QUEUE, "step"); +// printf("-- returned(%s) from %s\n", err, "step"); + } +} + + +bool tool_exec_script_t::init(player_t* player) +{ + bool res = false; + return init_vm(player) && call_function(script_vm_t::FORCE, "init", player, res)== NULL && res; +} + + +bool tool_exec_script_t::exit(player_t* player) +{ + bool res = false; + return call_function(script_vm_t::FORCE, "exit", player, res)== NULL && res; +} + + +const char* tool_exec_script_t::work(player_t* player, koord3d pos) +{ + static plainstring res; + // callback + script->prepare_callback("exec_script_base_work_callback", 3, (exec_script_base_t*)this, player, ""); + // now call + const char* err = call_function(script_vm_t::QUEUE, "work", player, res, pos); + if (err && strcmp(err, "suspended")==0) { + // suspended + } + else { + // no callback necessary + script->clear_pending_callback(); + } + return res.c_str(); +} + + +bool tool_exec_two_click_script_t::init(player_t* player) +{ + if (waiting_for_do_work) { + return two_click_tool_t::init(player); + } + bool res = false; + return two_click_tool_t::init(player) && init_vm(player) && call_function(script_vm_t::FORCE, "init", player, res)== NULL && res; +} + + +bool tool_exec_two_click_script_t::exit(player_t* player) +{ + bool res = false; + return two_click_tool_t::exit(player) && call_function(script_vm_t::FORCE, "exit", player, res)== NULL && res; +} + + +const char* tool_exec_two_click_script_t::do_work(player_t* player, const koord3d &start, const koord3d &end) +{ + if (waiting_for_do_work) { + return ""; + } + static plainstring res; + // callback + script->prepare_callback("exec_script_base_work_callback", 3, (exec_script_base_t*)this, player, ""); + // now call + const char* err = call_function(script_vm_t::QUEUE, "do_work", player, res, start, end); + if (err && strcmp(err, "suspended")==0) { + // suspended + waiting_for_do_work = true; + } + else { + // no callback necessary + script->clear_pending_callback(); + } + return res.c_str(); +} + + +void tool_exec_two_click_script_t::mark_tiles(player_t* player, const koord3d &start, const koord3d &end) +{ + if (waiting_for_do_work) { + return; + } + bool dummy; + // try to mark; if script is busy, do nothing + call_function(script_vm_t::TRY, "mark_tiles", player, dummy, start, end); +} + + +uint8 tool_exec_two_click_script_t::is_valid_pos(player_t* player, const koord3d &pos, const char *&error, const koord3d &start) +{ + error = NULL; + uint8 res = 2; // allow dragging + const char* err = call_function(script_vm_t::FORCEX, "is_valid_pos", player, res, pos, start); + // script error? signal 'start dragging is allowed here' + if (err) { + res = 2; // allow dragging + } + return res; +} diff --git a/simutrans/trunk/simtool-scripted.h b/simutrans/trunk/simtool-scripted.h new file mode 100644 index 000000000..f5bba6bdc --- /dev/null +++ b/simutrans/trunk/simtool-scripted.h @@ -0,0 +1,116 @@ +/* + * This file is part of the Simutrans project under the Artistic License. + * (see LICENSE.txt) + */ + +#ifndef SIMTOOL_SCRIPTED_H +#define SIMTOOL_SCRIPTED_H + +#include "simmenu.h" +#include "script/script.h" +#include "utils/plainstring.h" + +class script_vm_t; +class skin_desc_t; + + +/** + * Information about scripted tools, extracted from description.tab. + * - data from description.tab: + * + * plainstring path + * plainstring title=tool for test 2 + * bool type=one_click or two_click + * bool restart=1 + * plainstring menu=my_script + * skin_desc_t* icon=one_click_test: cursor, icon, marker + * + */ +struct scripted_tool_info_t { + plainstring path; ///< path to files + plainstring title; ///< name of tool (used for dialog and tooltip) + plainstring menu_arg; ///< menu name, where this tool should appear (used in menuconf.tab) + const skin_desc_t* desc; ///< skin object used for cursor (0), icon (1), and maybe marker (2) + bool restart; ///< true, if script vm has to be restarted after work() + bool is_one_click; ///< true, if tool is one-click (otherwise needs two clicks/coordinates to work) + /// sets default values + scripted_tool_info_t() + { + desc = NULL; + restart = true; + is_one_click = true; + } +}; + +class exec_script_base_t { +private: + /// information about tool, take ownership of pointer + const scripted_tool_info_t *info; + + void load_script(const char* path, player_t* player); +protected: + /// the vm, will be initialized in init() + script_vm_t *script; + /// starts vm, sets our_player, returns true if successful + bool init_vm(player_t* player); + + template + const char* call_function(script_vm_t::call_type_t ct, const char* function, player_t* player, R& ret); + template + const char* call_function(script_vm_t::call_type_t ct, const char* function, player_t* player, R& ret, A1 arg1); + template + const char* call_function(script_vm_t::call_type_t ct, const char* function, player_t* player, R& ret, A1 arg1, A2 arg2); +public: + exec_script_base_t(const scripted_tool_info_t *i) : info(i), script(NULL) {} + virtual ~exec_script_base_t(); + + void set_info(const scripted_tool_info_t *i); + const scripted_tool_info_t* get_info() const { return info; }; + + const char* get_menu_arg() const { return info ? info->menu_arg : ""; } + + /// has to be called if the tool is active, to resume script if a work-command gets suspended + void step(); +}; + + +class tool_exec_script_t : public tool_t, public exec_script_base_t { +protected: + +public: + tool_exec_script_t(const scripted_tool_info_t *i = NULL) : tool_t(TOOL_EXEC_SCRIPT | GENERAL_TOOL), exec_script_base_t(i) {} + /// is network-safe, as calls to work-commands will be properly handled in network mode + bool is_init_network_save() const OVERRIDE { return true; } + bool is_work_network_save() const OVERRIDE { return true; } + + bool init(player_t* player) OVERRIDE; + bool exit(player_t* player) OVERRIDE; + + const char *work(player_t* player, koord3d pos) OVERRIDE; + +// const char *get_tooltip(const player_t *) const OVERRIDE { return title; } +}; + +class tool_exec_two_click_script_t : public two_click_tool_t, public exec_script_base_t { +public: + tool_exec_two_click_script_t(const scripted_tool_info_t *i = NULL) : two_click_tool_t(TOOL_EXEC_TWO_CLICK_SCRIPT | GENERAL_TOOL), exec_script_base_t(i), waiting_for_do_work(false) {} + /// is network-safe, as calls to work-commands will be properly handled in network mode + bool is_work_network_save() const OVERRIDE { return true; } + bool is_init_network_save() const OVERRIDE { return true; } + + bool init(player_t* player) OVERRIDE; + bool exit(player_t* player) OVERRIDE; + + uint8 is_valid_pos(player_t* player, const koord3d &pos, const char *&error, const koord3d &start) OVERRIDE; + const char *do_work(player_t* player, const koord3d &start, const koord3d &end) OVERRIDE; + void mark_tiles(player_t* player, const koord3d &start, const koord3d &end) OVERRIDE; + + // two_click_tool_t calls init() after do_work(), + // because it assumes all work is done. + // We have to block this. + bool waiting_for_do_work; + +// const char *get_tooltip(const player_t *) const OVERRIDE { return title; } +}; + +#endif diff --git a/simutrans/trunk/simtool.cc b/simutrans/trunk/simtool.cc index 1da64ada6..6bf29b3d7 100644 --- a/simutrans/trunk/simtool.cc +++ b/simutrans/trunk/simtool.cc @@ -92,7 +92,6 @@ #include "simtool.h" #include "player/finance.h" - #define is_scenario() welt->get_scenario()->is_scripted() #define CHECK_FUNDS() \ @@ -6443,6 +6442,7 @@ const char *tool_merge_stop_t::do_work( player_t *player, const koord3d &last_po return NULL; } + bool tool_show_trees_t::init( player_t * ) { env_t::hide_trees = !env_t::hide_trees; diff --git a/simutrans/trunk/simtool.h b/simutrans/trunk/simtool.h index 5f2539c04..e0d48ea95 100644 --- a/simutrans/trunk/simtool.h +++ b/simutrans/trunk/simtool.h @@ -645,6 +645,7 @@ public: char const* work(player_t*, koord3d) OVERRIDE { return default_param ? default_param : ""; } }; + /********************* one click tools ****************************/ class tool_pause_t : public tool_t { diff --git a/simutrans/trunk/simutrans/script/tool_base.nut b/simutrans/trunk/simutrans/script/tool_base.nut new file mode 100644 index 000000000..5bdf09274 --- /dev/null +++ b/simutrans/trunk/simutrans/script/tool_base.nut @@ -0,0 +1,45 @@ +// stubs for functions needed by scripted tools +function init(player) +{ + return true +} + +function exit(player) +{ + return true +} +// one-click tools +function work(player, pos) +{ + return null +} + +// two-click tools +function do_work(player, start, end) +{ + return null +} + +function mark_tiles(player, start, end) +{ +} + +/** + * Can the tool start/end on pos? If it is the second click, start is the position of the first click + * 0 = no + * 1 = This tool can work on this tile (with single click) + * 2 = On this tile can dragging start/end + * 3 = Both (1 and 2) + * + * TODO: how to propagate error message + */ +function is_valid_pos(player, start, pos) +{ + return 2 +} + +// dummy auxiliary function, will be regularly called +function step() +{ + // do not implement +} diff --git a/simutrans/trunk/simworld.cc b/simutrans/trunk/simworld.cc index c040502da..d898f65ec 100644 --- a/simutrans/trunk/simworld.cc +++ b/simutrans/trunk/simworld.cc @@ -34,7 +34,7 @@ #include "simunits.h" #include "simversion.h" #include "display/simview.h" -#include "simtool.h" +#include "simtool-scripted.h" #include "gui/simwin.h" #include "simworld.h" #include "sys/simsys.h" @@ -4181,6 +4181,13 @@ void karte_t::step() if( get_scenario()->is_scripted() ) { get_scenario()->step(); } + + if (selected_tool[active_player_nr]) { + if (exec_script_base_t* esb = dynamic_cast(selected_tool[active_player_nr])) { + esb->step(); + } + } + DBG_DEBUG4("karte_t::step", "end"); }