Merge branch 'develop' of https://github.com/DFHack/dfhack into NEWS-cleanup

develop
PeridexisErrant 2015-10-28 10:07:13 +11:00
commit aee2655886
22 changed files with 463 additions and 41 deletions

@ -789,11 +789,12 @@ can be omitted.
Returns the DF version string from ``symbols.xml``.
* ``getDFHackVersion()``
* ``getDFHackRelease()``
* ``getCompiledDFVersion()``
* ``getGitDescription()``
* ``getGitCommit()``
* ``dfhack.getDFHackVersion()``
* ``dfhack.getDFHackRelease()``
* ``dfhack.getCompiledDFVersion()``
* ``dfhack.getGitDescription()``
* ``dfhack.getGitCommit()``
* ``dfhack.isRelease()``
Return information about the DFHack build in use.
@ -835,10 +836,20 @@ can be omitted.
Convert a string from DF's CP437 encoding to UTF-8.
* ``dfhack.df2console()``
Convert a string from DF's CP437 encoding to the correct encoding for the
DFHack console.
* ``dfhack.utf2df(string)``
Convert a string from UTF-8 to DF's CP437 encoding.
**Note:** When printing CP437-encoded text to the console (for example, names
returned from TranslateName()), use ``print(dfhack.df2console(text)`` to ensure
proper display on all platforms.
Gui module
----------
@ -856,6 +867,12 @@ Gui module
Returns the focus string of the current viewscreen.
* ``dfhack.gui.getViewscreenByType(type [, depth])``
Returns the topmost viewscreen out of the top ``depth`` viewscreens with
the specified type (e.g. ``df.viewscreen_titlest``), or ``nil`` if none match.
If ``depth`` is not specified or is less than 1, all viewscreens are checked.
* ``dfhack.gui.getSelectedWorkshopJob([silent])``
When a job is selected in *'q'* mode, returns the job, else

@ -127,6 +127,7 @@ include/modules/MapCache.h
include/modules/Materials.h
include/modules/Notes.h
include/modules/Random.h
include/modules/Renderer.h
include/modules/Screen.h
include/modules/Translation.h
include/modules/Vermin.h
@ -152,6 +153,7 @@ modules/Maps.cpp
modules/Materials.cpp
modules/Notes.cpp
modules/Random.cpp
modules/Renderer.cpp
modules/Screen.cpp
modules/Translation.cpp
modules/Vermin.cpp

@ -241,6 +241,17 @@ struct sortable
};
};
static string dfhack_version_desc()
{
stringstream s;
s << Version::dfhack_version() << " ";
if (Version::is_release())
s << "(release)";
else
s << "(development build " << Version::git_description() << ")";
return s.str();
}
static std::string getScriptHelp(std::string path, std::string helpprefix)
{
ifstream script(path.c_str());
@ -611,7 +622,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
" reload PLUGIN|-all - Reload a plugin or all loaded plugins.\n"
);
con.print("\nDFHack version %s.\n", Version::dfhack_version());
con.print("\nDFHack version %s\n", dfhack_version_desc().c_str());
}
else if (parts.size() == 1)
{
@ -908,29 +919,34 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
}
con << parts[0];
string builtin_cmd = getBuiltinCommand(parts[0]);
string lua_path = findScript(parts[0] + ".lua");
string ruby_path = findScript(parts[0] + ".rb");
Plugin *plug = plug_mgr->getPluginByCommand(parts[0]);
if (builtin_cmd.size())
{
con << " is a built-in command";
if (builtin_cmd != parts[0])
con << " (aliased to " << builtin_cmd << ")";
con << "." << std::endl;
con << std::endl;
}
else if (plug)
{
con << " is part of plugin " << plug->getName() << "." << std::endl;
con << " is a command implemented by the plugin " << plug->getName() << std::endl;
}
else if (findScript(parts[0] + ".lua").size())
else if (lua_path.size())
{
con << " is a Lua script." << std::endl;
con << " is a Lua script: " << lua_path << std::endl;
}
else if (findScript(parts[0] + ".rb").size())
else if (ruby_path.size())
{
con << " is a Ruby script." << std::endl;
con << " is a Ruby script: " << ruby_path << std::endl;
}
else
{
con << " is not recognized." << std::endl;
con << " is not a recognized command." << std::endl;
plug = plug_mgr->getPluginByName(parts[0]);
if (plug)
con << "Plugin " << parts[0] << " exists and implements " << plug->size() << " commands." << std::endl;
return CR_FAILURE;
}
}
@ -1258,7 +1274,7 @@ void fIOthread(void * iodata)
con.print("DFHack is ready. Have a nice day!\n"
"DFHack version %s\n"
"Type in '?' or 'help' for general help, 'ls' to see all commands.\n",
Version::dfhack_version());
dfhack_version_desc().c_str());
int clueless_counter = 0;
while (true)
@ -1325,15 +1341,19 @@ Core::Core()
script_path_mutex = new mutex();
};
void Core::fatal (std::string output, bool deactivate)
void Core::fatal (std::string output)
{
errorstate = true;
stringstream out;
out << output ;
if(deactivate)
if (output[output.size() - 1] != '\n')
out << '\n';
out << "DFHack will now deactivate.\n";
if(con.isInited())
{
con.printerr("%s", out.str().c_str());
con.reset_color();
con.print("\n");
}
fprintf(stderr, "%s\n", out.str().c_str());
#ifndef LINUX_BUILD
@ -1384,7 +1404,7 @@ bool Core::Init()
delete vif;
vif = NULL;
errorstate = true;
fatal(out.str(), true);
fatal(out.str());
return false;
}
p = new DFHack::Process(vif);
@ -1392,7 +1412,7 @@ bool Core::Init()
if(!vinfo || !p->isIdentified())
{
fatal ("Not a known DF version.\n", true);
fatal("Not a known DF version.\n");
errorstate = true;
delete p;
p = NULL;
@ -1418,7 +1438,7 @@ bool Core::Init()
else if(con.init(false))
cerr << "Console is running.\n";
else
fatal ("Console has failed to initialize!\n", false);
cerr << "Console has failed to initialize!\n";
/*
// dump offsets to a file
std::ofstream dump("offsets.log");
@ -1465,7 +1485,11 @@ bool Core::Init()
}
// initialize common lua context
Lua::Core::Init(con);
if (!Lua::Core::Init(con))
{
fatal("Lua failed to initialize");
return false;
}
// create mutex for syncing with interactive tasks
misc_data_mutex=new mutex();

@ -24,5 +24,14 @@ namespace DFHack {
{
return DFHACK_GIT_COMMIT;
}
bool is_release()
{
#ifdef DFHACK_GIT_TAGGED
return true;
#else
return false;
#endif
}
}
}

@ -1417,6 +1417,7 @@ static const LuaWrapper::FunctionReg dfhack_module[] = {
WRAP_VERSION_FUNC(getCompiledDFVersion, df_version),
WRAP_VERSION_FUNC(getGitDescription, git_description),
WRAP_VERSION_FUNC(getGitCommit, git_commit),
WRAP_VERSION_FUNC(isRelease, is_release),
{ NULL, NULL }
};

@ -1701,7 +1701,11 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
Lua::Core::InitCoreContext();
// load dfhack.lua
Require(out, state, "dfhack");
if (!Require(out, state, "dfhack"))
{
out.printerr("Could not load dfhack.lua\n");
return NULL;
}
lua_settop(state, 0);
if (!lua_checkstack(state, 64))
@ -1868,15 +1872,17 @@ void DFHack::Lua::Core::onUpdate(color_ostream &out)
run_timers(out, State, tick_timers, frame[1], world->frame_counter);
}
void DFHack::Lua::Core::Init(color_ostream &out)
bool DFHack::Lua::Core::Init(color_ostream &out)
{
if (State)
return;
if (State) {
out.printerr("state already exists\n");
return false;
}
State = luaL_newstate();
// Calls InitCoreContext after checking IsCoreContext
Lua::Open(out, State);
return (Lua::Open(out, State) != NULL);
}
static void Lua::Core::InitCoreContext()

@ -4,12 +4,20 @@ execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --long
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_COMMIT)
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --exact-match
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
RESULT_VARIABLE DFHACK_GIT_TAGGED_RESULT
OUTPUT_QUIET ERROR_QUIET)
string(STRIP ${DFHACK_GIT_DESCRIPTION} DFHACK_GIT_DESCRIPTION)
file(WRITE ${dfhack_SOURCE_DIR}/library/include/git-describe.tmp.h
"#define DFHACK_GIT_DESCRIPTION \"${DFHACK_GIT_DESCRIPTION}\"\n")
string(STRIP ${DFHACK_GIT_COMMIT} DFHACK_GIT_COMMIT)
file(APPEND ${dfhack_SOURCE_DIR}/library/include/git-describe.tmp.h
"#define DFHACK_GIT_COMMIT \"${DFHACK_GIT_COMMIT}\"")
"#define DFHACK_GIT_COMMIT \"${DFHACK_GIT_COMMIT}\"\n")
if(${DFHACK_GIT_TAGGED_RESULT} EQUAL 0)
file(APPEND ${dfhack_SOURCE_DIR}/library/include/git-describe.tmp.h
"#define DFHACK_GIT_TAGGED\n")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
${dfhack_SOURCE_DIR}/library/include/git-describe.tmp.h
${dfhack_SOURCE_DIR}/library/include/git-describe.h)

@ -217,7 +217,7 @@ namespace DFHack
void operator=(Core const&); // Don't implement
// report error to user while failing
void fatal (std::string output, bool will_deactivate);
void fatal (std::string output);
// 1 = fatal failure
bool errorstate;

@ -6,13 +6,15 @@ namespace DFHack {
const char *dfhack_release();
const char *git_description();
const char *git_commit();
bool is_release();
}
}
#ifndef NO_DFHACK_VERSION_MACROS
#define DF_VERSION DFHack::Version::df_version()
#define DFHACK_RELEASE DFHack::Version::dfhack_release()
#define DFHACK_VERSION DFHack::Version::dfhack_version()
#define DFHACK_GIT_DESCRIPTION DFHack::Version::git_description()
#define DFHACK_GIT_COMMIT DFHack::Version::git_commit()
#define DF_VERSION (DFHack::Version::df_version())
#define DFHACK_RELEASE (DFHack::Version::dfhack_release())
#define DFHACK_VERSION (DFHack::Version::dfhack_version())
#define DFHACK_GIT_DESCRIPTION (DFHack::Version::git_description())
#define DFHACK_GIT_COMMIT (DFHack::Version::git_commit())
#define DFHACK_IS_RELEASE (DFHack::Version::is_release())
#endif

@ -380,7 +380,7 @@ namespace DFHack {namespace Lua {
DFHACK_EXPORT extern lua_State *State;
// Not exported; for use by the Core class
void Init(color_ostream &out);
bool Init(color_ostream &out);
void Reset(color_ostream &out, const char *where);
// Events signalled by the core

@ -0,0 +1,38 @@
#include "Export.h"
#include "df/enabler.h"
#include "df/graphic.h"
#include "df/renderer.h"
#pragma once
namespace DFHack { namespace Renderer {
struct DFHACK_EXPORT renderer_wrap : public df::renderer {
void set_to_null();
void copy_from_parent();
void copy_to_parent();
renderer_wrap *parent;
renderer_wrap *child;
virtual void update_tile(int32_t x, int32_t y);
virtual void update_all();
virtual void render();
virtual void set_fullscreen();
virtual void zoom(df::zoom_commands z);
virtual void resize(int32_t w, int32_t h);
virtual void grid_resize(int32_t w, int32_t h);
virtual ~renderer_wrap() {
// All necessary cleanup should be handled by RemoveRenderer()
};
virtual bool get_mouse_coords(int32_t *x, int32_t *y);
virtual bool uses_opengl();
};
// Returns the renderer instance given on success, or deletes it and returns NULL on failure
// Usage: renderer_foo *r = AddRenderer(new renderer_foo)
DFHACK_EXPORT renderer_wrap *AddRenderer(renderer_wrap*, bool refresh_screen = false);
// Removes and deletes the given renderer instance
DFHACK_EXPORT void RemoveRenderer(renderer_wrap*);
DFHACK_EXPORT bool RendererExists(renderer_wrap*);
inline renderer_wrap *GetRenderer()
{
return (renderer_wrap*)(df::global::enabler ? df::global::enabler->renderer : NULL);
}
}}

@ -285,6 +285,27 @@ function dfhack.buildings.getSize(bld)
return bld.x2+1-x, bld.y2+1-y, bld.centerx-x, bld.centery-y
end
function dfhack.gui.getViewscreenByType(scr_type, n)
-- translated from modules/Gui.cpp
if n == nil then
n = 1
end
local limit = (n > 0)
local scr = dfhack.gui.getCurViewscreen()
while scr do
if limit then
n = n - 1
if n < 0 then
return nil
end
end
if scr_type:is_instance(scr) then
return scr
end
scr = scr.parent
end
end
-- Interactive
local print_banner = true

@ -0,0 +1,156 @@
#include "DataDefs.h"
#include "MiscUtils.h"
#include "modules/Renderer.h"
using namespace DFHack;
using df::global::enabler;
using df::global::gps;
using DFHack::Renderer::renderer_wrap;
static renderer_wrap *original_renderer = NULL;
bool init()
{
if (!original_renderer)
original_renderer = Renderer::GetRenderer();
return original_renderer;
}
renderer_wrap *Renderer::AddRenderer (renderer_wrap *r, bool refresh_screen)
{
if (!init())
{
delete r;
return NULL;
}
renderer_wrap *cur = GetRenderer();
r->parent = cur;
r->child = NULL;
r->copy_from_parent();
if (cur != original_renderer)
{
cur->child = original_renderer;
}
r->copy_from_parent();
enabler->renderer = r;
if (refresh_screen && gps)
gps->force_full_display_count++;
return r;
}
void Renderer::RemoveRenderer (renderer_wrap *r)
{
if (r)
{
if (original_renderer)
{
r->parent->child = r->child;
if (r->child)
{
r->child->parent = r->parent;
}
enabler->renderer = r->parent;
}
delete r;
}
}
bool Renderer::RendererExists (renderer_wrap *r)
{
renderer_wrap *cur = GetRenderer();
while (cur && cur != r && cur != original_renderer)
cur = cur->parent;
return cur == r;
}
void Renderer::renderer_wrap::set_to_null() {
screen = NULL;
screentexpos = NULL;
screentexpos_addcolor = NULL;
screentexpos_grayscale = NULL;
screentexpos_cf = NULL;
screentexpos_cbr = NULL;
screen_old = NULL;
screentexpos_old = NULL;
screentexpos_addcolor_old = NULL;
screentexpos_grayscale_old = NULL;
screentexpos_cf_old = NULL;
screentexpos_cbr_old = NULL;
}
void Renderer::renderer_wrap::copy_from_parent() {
screen = parent->screen;
screentexpos = parent->screentexpos;
screentexpos_addcolor = parent->screentexpos_addcolor;
screentexpos_grayscale = parent->screentexpos_grayscale;
screentexpos_cf = parent->screentexpos_cf;
screentexpos_cbr = parent->screentexpos_cbr;
screen_old = parent->screen_old;
screentexpos_old = parent->screentexpos_old;
screentexpos_addcolor_old = parent->screentexpos_addcolor_old;
screentexpos_grayscale_old = parent->screentexpos_grayscale_old;
screentexpos_cf_old = parent->screentexpos_cf_old;
screentexpos_cbr_old = parent->screentexpos_cbr_old;
}
void Renderer::renderer_wrap::copy_to_parent() {
parent->screen = screen;
parent->screentexpos = screentexpos;
parent->screentexpos_addcolor = screentexpos_addcolor;
parent->screentexpos_grayscale = screentexpos_grayscale;
parent->screentexpos_cf = screentexpos_cf;
parent->screentexpos_cbr = screentexpos_cbr;
parent->screen_old = screen_old;
parent->screentexpos_old = screentexpos_old;
parent->screentexpos_addcolor_old = screentexpos_addcolor_old;
parent->screentexpos_grayscale_old = screentexpos_grayscale_old;
parent->screentexpos_cf_old = screentexpos_cf_old;
parent->screentexpos_cbr_old = screentexpos_cbr_old;
}
void Renderer::renderer_wrap::update_tile(int32_t x, int32_t y) {
copy_to_parent();
parent->update_tile(x,y);
};
void Renderer::renderer_wrap::update_all() {
copy_to_parent();
parent->update_all();
};
void Renderer::renderer_wrap::render() {
copy_to_parent();
parent->render();
};
void Renderer::renderer_wrap::set_fullscreen() {
copy_to_parent();
parent->set_fullscreen();
copy_from_parent();
};
void Renderer::renderer_wrap::zoom(df::zoom_commands z) {
copy_to_parent();
parent->zoom(z);
copy_from_parent();
};
void Renderer::renderer_wrap::resize(int32_t w, int32_t h) {
copy_to_parent();
parent->resize(w,h);
copy_from_parent();
};
void Renderer::renderer_wrap::grid_resize(int32_t w, int32_t h) {
copy_to_parent();
parent->grid_resize(w,h);
copy_from_parent();
};
bool Renderer::renderer_wrap::get_mouse_coords(int32_t* x, int32_t* y) {
return parent->get_mouse_coords(x,y);
};
bool Renderer::renderer_wrap::uses_opengl() {
return parent->uses_opengl();
};

@ -79,6 +79,9 @@ add_custom_target(generate_proto DEPENDS ${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_H
SET_SOURCE_FILES_PROPERTIES( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE )
ADD_LIBRARY(buildingplan-lib STATIC buildingplan-lib.cpp)
TARGET_LINK_LIBRARIES(buildingplan-lib dfhack)
# Plugins
OPTION(BUILD_SUPPORTED "Build the supported plugins (reveal, probe, etc.)." ON)
if (BUILD_SUPPORTED)
@ -95,7 +98,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(blueprint blueprint.cpp)
DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(building-hacks building-hacks.cpp LINK_LIBRARIES lua)
DFHACK_PLUGIN(buildingplan buildingplan-lib.cpp buildingplan.cpp)
DFHACK_PLUGIN(buildingplan buildingplan.cpp LINK_LIBRARIES buildingplan-lib)
DFHACK_PLUGIN(catsplosion catsplosion.cpp)
DFHACK_PLUGIN(changeitem changeitem.cpp)
DFHACK_PLUGIN(changelayer changelayer.cpp)
@ -126,7 +129,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(flows flows.cpp)
DFHACK_PLUGIN(follow follow.cpp)
DFHACK_PLUGIN(forceequip forceequip.cpp)
DFHACK_PLUGIN(fortplan buildingplan-lib.cpp fortplan.cpp)
DFHACK_PLUGIN(fortplan fortplan.cpp LINK_LIBRARIES buildingplan-lib)
DFHACK_PLUGIN(getplants getplants.cpp)
DFHACK_PLUGIN(hotkeys hotkeys.cpp)
DFHACK_PLUGIN(infiniteSky infiniteSky.cpp)

@ -14,6 +14,7 @@ DFHACK_PLUGIN(memview memview.cpp)
DFHACK_PLUGIN(nestboxes nestboxes.cpp)
DFHACK_PLUGIN(notes notes.cpp)
DFHACK_PLUGIN(onceExample onceExample.cpp)
DFHACK_PLUGIN(renderer-msg renderer-msg.cpp)
DFHACK_PLUGIN(rprobe rprobe.cpp)
DFHACK_PLUGIN(stepBetween stepBetween.cpp)
DFHACK_PLUGIN(stockcheck stockcheck.cpp)

@ -0,0 +1,58 @@
#include "Console.h"
#include "Core.h"
#include "DataDefs.h"
#include "Export.h"
#include "PluginManager.h"
#include "modules/Screen.h"
#include "modules/Renderer.h"
#include "df/enabler.h"
#include "df/renderer.h"
//#include "df/world.h"
using namespace DFHack;
DFHACK_PLUGIN("renderer-msg");
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
REQUIRE_GLOBAL(enabler);
REQUIRE_GLOBAL(gps);
struct scdata {
uint8_t ch, fg, bg, bold;
};
struct renderer_msg : public Renderer::renderer_wrap {
virtual void update_tile (int32_t x, int32_t y) {
static std::string str = std::string("DFHack: ") + plugin_name + " active";
Screen::paintString(Screen::Pen(' ', 9, 0), 0, gps->dimy - 1, str);
for (size_t i = 0; i < gps->dimx; ++i)
((scdata*)screen)[i * gps->dimy + gps->dimy - 1].bg = 2;
renderer_wrap::update_tile(x, y);
};
};
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
{
return CR_OK;
}
static Renderer::renderer_wrap *w = NULL;
DFhackCExport command_result plugin_enable (color_ostream &out, bool enable)
{
if (is_enabled == enable)
return CR_OK;
CoreSuspender s;
is_enabled = enable;
if (enable)
w = Renderer::AddRenderer(new renderer_msg, true);
else
Renderer::RemoveRenderer(w);
return CR_OK;
}
DFhackCExport command_result plugin_shutdown (color_ostream &out)
{
plugin_enable(out, false);
return plugin_enable(out, false);
}

@ -1977,6 +1977,8 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector <Plugin
));
dm_lua::state = Lua::Open(out);
if (dm_lua::state == NULL)
return CR_FAILURE;
return CR_OK;
}

@ -33,6 +33,8 @@ struct title_version_hook : df::viewscreen_titlest {
INTERPOSE_NEXT(render)();
int x = 0, y = 0;
OutputString(COLOR_WHITE, x, y, string("DFHack ") + DFHACK_VERSION);
if (!DFHACK_IS_RELEASE)
OutputString(COLOR_WHITE, x, y, " (dev)");
}
};

@ -195,8 +195,14 @@ end
function GmEditorUi:getSelectedEnumType()
local trg=self:currentTarget()
local trg_key=trg.keys[self.subviews.list_main:getSelected()]
if trg.target._field==nil then return nil end
if trg.target:_field(trg_key)==nil then return nil end
local ok,ret=pcall(function () --super safe way to check if the field has enum
return trg.target._field==nil or trg.target:_field(trg_key)==nil
end)
if not ok or ret==true then
return nil
end
local enum=trg.target:_field(trg_key)._type
if enum._kind=="enum-type" then
return enum

@ -0,0 +1,2 @@
-- Force a migrant wave (only works after hardcoded waves)
--@alias = 'devel/migrants-now'

@ -337,6 +337,7 @@ validArgs = --[[validArgs or]]utils.invert({
'flagSet',
'flagClear',
'name',
'nick',
'location',
'age'
})
@ -376,6 +377,8 @@ arguments:
set the unit's name to be a random name appropriate for the given entity
examples:
MOUNTAIN
-nick nickname
set the unit's nickname directly
-location [ x y z ]
create the unit at the specified coordinates
-age howOld
@ -512,6 +515,10 @@ else
end--]]
end
if args.nick and type(args.nick) == 'string' then
dfhack.units.setNickname(df.unit.find(unitId), args.nick)
end
if civ_id then
local u = df.unit.find(unitId)
u.civ_id = civ_id

@ -0,0 +1,57 @@
-- create unit at pointer or given location
-- wraps modtools/create-unit.lua
usage = [[Usage:
- spawnunit RACE CASTE
- spawnunit RACE CASTE NAME
- spawnunit RACE CASTE NAME x y z
- spawnunit RACE CASTE [NAME] [additional arguments]
Create a unit
- spawnunit -help
Display this help message
- spawnunit -command ARGUMENTS
Display the command used to invoke modtools/create-unit
additional arguments are passed to modtools/create-unit -
see "modtools/create-unit -help" for more information.
]]
function extend(tbl, tbl2)
for _, v in pairs(tbl2) do
table.insert(tbl, v)
end
end
local show_command = false
local args = {...}
local first_arg = (args[1] or ''):gsub('^-*', '')
if first_arg == 'help' or #args < 2 then
print(usage)
return
elseif first_arg == 'command' then
show_command = true
table.remove(args, 1)
end
local new_args = {'-race', args[1], '-caste', args[2]}
if #args == 3 then
extend(new_args, {'-nick', args[3]})
elseif #args == 6 then
if tonumber(args[4]) and tonumber(args[5]) and tonumber(args[6]) then
extend(new_args, {'-nick', args[3], '-location', '[', args[4], args[5], args[6], ']'})
end
else
local start = 3
if #args >= 3 and args[3]:sub(1, 1) ~= '-' then
extend(new_args, {'-nick', args[3]})
start = 4
end
for i = start, #args do
table.insert(new_args, args[i])
end
end
if show_command then
print('modtools/create-unit ' .. table.concat(new_args, ' '))
return
end
dfhack.run_script('modtools/create-unit', table.unpack(new_args))