diff --git a/library/Core.cpp b/library/Core.cpp index df11ca5b2..ad9fdc61a 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -204,7 +204,7 @@ struct sortable }; }; -static std::string getLuaHelp(std::string path) +static std::string getScriptHelp(std::string path, std::string helpprefix) { ifstream script(path.c_str()); @@ -212,14 +212,14 @@ static std::string getLuaHelp(std::string path) { std::string help; if (getline(script, help) && - help.substr(0,3) == "-- ") - return help.substr(3); + help.substr(0,helpprefix.length()) == helpprefix) + return help.substr(helpprefix.length()); } - return "Lua script."; + return "No help available."; } -static std::map listLuaScripts(std::string path) +static std::map listScripts(PluginManager *plug_mgr, std::string path) { std::vector files; getdir(path, files); @@ -229,10 +229,16 @@ static std::map listLuaScripts(std::string path) { if (hasEnding(files[i], ".lua")) { - std::string help = getLuaHelp(path + files[i]); + std::string help = getScriptHelp(path + files[i], "-- "); pset[files[i].substr(0, files[i].size()-4)] = help; } + else if (plug_mgr->eval_ruby && hasEnding(files[i], ".rb")) + { + std::string help = getScriptHelp(path + files[i], "# "); + + pset[files[i].substr(0, files[i].size()-3)] = help; + } } return pset; } @@ -275,6 +281,18 @@ static command_result runLuaScript(color_ostream &out, std::string name, vector< return ok ? CR_OK : CR_FAILURE; } +static command_result runRubyScript(PluginManager *plug_mgr, std::string name, vector &args) +{ + std::string rbcmd = "$script_args = ["; + for (size_t i = 0; i < args.size(); i++) + rbcmd += "'" + args[i] + "', "; + rbcmd += "]\n"; + + rbcmd += "load './hack/scripts/" + name + ".rb'"; + + return plug_mgr->eval_ruby(rbcmd.c_str()); +} + command_result Core::runCommand(color_ostream &out, const std::string &command) { if (!command.empty()) @@ -348,10 +366,16 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve return CR_OK; } } - auto filename = getHackPath() + "scripts/" + parts[0] + ".lua"; - if (fileExists(filename)) + auto filename = getHackPath() + "scripts/" + parts[0]; + if (fileExists(filename + ".lua")) + { + string help = getScriptHelp(filename + ".lua", "-- "); + con.print("%s: %s\n", parts[0].c_str(), help.c_str()); + return CR_OK; + } + if (plug_mgr->eval_ruby && fileExists(filename + ".rb")) { - string help = getLuaHelp(filename); + string help = getScriptHelp(filename + ".rb", "# "); con.print("%s: %s\n", parts[0].c_str(), help.c_str()); return CR_OK; } @@ -499,7 +523,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve con.print(" %-22s- %s\n",(*iter).name.c_str(), (*iter).description.c_str()); con.reset_color(); } - auto scripts = listLuaScripts(getHackPath() + "scripts/"); + auto scripts = listScripts(plug_mgr, getHackPath() + "scripts/"); if (!scripts.empty()) { con.print("\nscripts:\n"); @@ -604,9 +628,11 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve command_result res = plug_mgr->InvokeCommand(con, first, parts); if(res == CR_NOT_IMPLEMENTED) { - auto filename = getHackPath() + "scripts/" + first + ".lua"; - if (fileExists(filename)) + auto filename = getHackPath() + "scripts/" + first; + if (fileExists(filename + ".lua")) res = runLuaScript(con, first, parts); + else if (plug_mgr->eval_ruby && fileExists(filename + ".rb")) + res = runRubyScript(plug_mgr, first, parts); else con.printerr("%s is not a recognized command.\n", first.c_str()); } diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index ae8cc755f..a314883e1 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -188,6 +188,7 @@ bool Plugin::load(color_ostream &con) plugin_shutdown = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_shutdown"); plugin_onstatechange = (command_result (*)(color_ostream &, state_change_event)) LookupPlugin(plug, "plugin_onstatechange"); plugin_rpcconnect = (RPCService* (*)(color_ostream &)) LookupPlugin(plug, "plugin_rpcconnect"); + plugin_eval_ruby = (command_result (*)(const char*)) LookupPlugin(plug, "plugin_eval_ruby"); index_lua(plug); this->name = *plug_name; plugin_lib = plug; @@ -538,6 +539,7 @@ PluginManager::PluginManager(Core * core) const string searchstr = ".plug.dll"; #endif cmdlist_mutex = new mutex(); + eval_ruby = NULL; vector filez; getdir(path, filez); for(size_t i = 0; i < filez.size();i++) @@ -620,6 +622,8 @@ void PluginManager::registerCommands( Plugin * p ) { belongs[cmds[i].name] = p; } + if (p->plugin_eval_ruby) + eval_ruby = p->plugin_eval_ruby; cmdlist_mutex->unlock(); } @@ -632,5 +636,7 @@ void PluginManager::unregisterCommands( Plugin * p ) { belongs.erase(cmds[i].name); } + if (p->plugin_eval_ruby) + eval_ruby = NULL; cmdlist_mutex->unlock(); -} \ No newline at end of file +} diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index b76df437d..5da9fc92f 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -209,6 +209,7 @@ namespace DFHack command_result (*plugin_onupdate)(color_ostream &); command_result (*plugin_onstatechange)(color_ostream &, state_change_event); RPCService* (*plugin_rpcconnect)(color_ostream &); + command_result (*plugin_eval_ruby)(const char*); }; class DFHACK_EXPORT PluginManager { @@ -237,6 +238,7 @@ namespace DFHack { return all_plugins.size(); } + command_result (*eval_ruby)(const char*); // DATA private: tthread::mutex * cmdlist_mutex; diff --git a/plugins/ruby/README b/plugins/ruby/README index 4bfc20957..619d85cf2 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -33,6 +33,20 @@ df/hack/ on linux), the library should be named 'libruby.dll' (.so on linux). You can download a tested version at http://github.com/jjyg/dfhack/downloads/ +Ruby scripts +------------ + +The ruby plugin allows the creation of '.rb' scripts in df/hack/scripts/. + +If you create such a script, e.g. 'test.rb', that will add a new dfhack console +command 'test'. +The script can access the console command arguments through the global variable +'$script_args', which is an array of ruby Strings. + +The help string displayed in dfhack 'ls' command is the first line of the +script, if it is a comment (starts with '# '). + + DFHack callbacks ---------------- diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index 482cf24ff..64da12ff9 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -353,6 +353,7 @@ module DFHack end def empty? ; length == 0 ; end def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end + def index(elem=nil, &b) ; (0...length).find { |i| b ? b[self[i]] : self[i] == elem } ; end end class StaticArray < MemStruct attr_accessor :_tglen, :_length, :_indexenum, :_tg diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index e3bed78b7..f4a553380 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -96,8 +96,11 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) } // send a single ruby line to be evaluated by the ruby thread -static command_result plugin_eval_rb(const char *command) +DFhackCExport command_result plugin_eval_ruby(const char *command) { + if (!r_thread) + return CR_FAILURE; + command_result ret; // serialize 'accesses' to the ruby thread @@ -123,11 +126,6 @@ static command_result plugin_eval_rb(const char *command) return ret; } -static command_result plugin_eval_rb(std::string &command) -{ - return plugin_eval_rb(command.c_str()); -} - DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { if (!r_thread) @@ -136,7 +134,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (!onupdate_active) return CR_OK; - return plugin_eval_rb("DFHack.onupdate"); + return plugin_eval_ruby("DFHack.onupdate"); } DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event e) @@ -157,7 +155,7 @@ DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_ch #undef SCASE } - return plugin_eval_rb(cmd); + return plugin_eval_ruby(cmd.c_str()); } static command_result df_rubyeval(color_ostream &out, std::vector & parameters) @@ -178,7 +176,7 @@ static command_result df_rubyeval(color_ostream &out, std::vector full += " "; } - return plugin_eval_rb(full); + return plugin_eval_ruby(full.c_str()); } diff --git a/scripts/slayrace.rb b/scripts/slayrace.rb new file mode 100644 index 000000000..b58835257 --- /dev/null +++ b/scripts/slayrace.rb @@ -0,0 +1,21 @@ +# slay all creatures of a given race (default = goblins) + +race = $script_args[0] || 'GOBLIN' + +all_races = df.world.raws.creatures.all.map { |cr| cr.creature_id } +raw_race = df.match_rawname(race, all_races) +raise 'invalid race' if not raw_race + +race_nr = df.world.raws.creatures.all.index { |cr| cr.creature_id == raw_race } +count = 0 + +df.suspend { + df.world.units.active.each { |u| + if u.race == race_nr and u.body.blood_count != 0 + u.body.blood_count = 0 + count += 1 + end + } +} + +puts "slain #{count} #{raw_race}"