-- Common startup file for all dfhack plugins with lua support -- The global dfhack table is already created by C++ init code. -- Console color constants COLOR_RESET = -1 COLOR_BLACK = 0 COLOR_BLUE = 1 COLOR_GREEN = 2 COLOR_CYAN = 3 COLOR_RED = 4 COLOR_MAGENTA = 5 COLOR_BROWN = 6 COLOR_GREY = 7 COLOR_DARKGREY = 8 COLOR_LIGHTBLUE = 9 COLOR_LIGHTGREEN = 10 COLOR_LIGHTCYAN = 11 COLOR_LIGHTRED = 12 COLOR_LIGHTMAGENTA = 13 COLOR_YELLOW = 14 COLOR_WHITE = 15 -- Error handling safecall = dfhack.safecall function dfhack.pcall(f, ...) return xpcall(f, dfhack.onerror, ...) end function dfhack.with_finalize(...) return dfhack.call_with_finalizer(0,true,...) end function dfhack.with_onerror(...) return dfhack.call_with_finalizer(0,false,...) end local function call_delete(obj) if obj then obj:delete() end end function dfhack.with_temp_object(obj,fn,...) return dfhack.call_with_finalizer(1,true,call_delete,obj,fn,obj,...) end -- Module loading function mkmodule(module,env) local pkg = package.loaded[module] if pkg == nil then pkg = {} else if type(pkg) ~= 'table' then error("Not a table in package.loaded["..module.."]") end end local plugname = string.match(module,'^plugins%.(%w+)$') if plugname then dfhack.open_plugin(pkg,plugname) end setmetatable(pkg, { __index = (env or _G) }) return pkg end function reload(module) if type(package.loaded[module]) ~= 'table' then error("Module not loaded: "..module) end local path,err = package.searchpath(module,package.path) if not path then error(err) end dofile(path) end -- Misc functions function printall(table) if type(table) == 'table' or df.isvalid(table) == 'ref' then for k,v in pairs(table) do print(string.format("%-23s\t = %s",tostring(k),tostring(v))) end end end function copyall(table) local rv = {} for k,v in pairs(table) do rv[k] = v end return rv end function pos2xyz(pos) local x = pos.x if x and x ~= -30000 then return x, pos.y, pos.z end end function xyz2pos(x,y,z) if x then return {x=x,y=y,z=z} else return {x=-30000,y=-30000,z=-30000} end end function dfhack.event:__tostring() return "" end function dfhack.persistent:__tostring() return "" end function dfhack.matinfo:__tostring() return "" end function dfhack.maps.getSize() local map = df.global.world.map return map.x_count_block, map.y_count_block, map.z_count_block end function dfhack.maps.getTileSize() local map = df.global.world.map return map.x_count, map.y_count, map.z_count end -- Interactive local print_banner = true function dfhack.interpreter(prompt,hfile,env) if not dfhack.is_interactive() then return nil, 'not interactive' end print("Type quit to exit interactive lua interpreter.") if print_banner then print("Shortcuts:\n".. " '= foo' => '_1,_2,... = foo'\n".. " '! foo' => 'print(foo)'\n".. "Both save the first result as '_'.") print_banner = false end local prompt_str = "["..(prompt or 'lua').."]# " local prompt_env = {} local t_prompt local vcnt = 1 setmetatable(prompt_env, { __index = env or _G }) local cmdlinelist={} while true do local cmdline = dfhack.lineedit(t_prompt or prompt_str, hfile) if cmdline == nil or cmdline == 'quit' then break elseif cmdline ~= '' then local pfix = string.sub(cmdline,1,1) if pfix == '!' or pfix == '=' then cmdline = 'return '..string.sub(cmdline,2) end table.insert(cmdlinelist,cmdline) local code,err = load(table.concat(cmdlinelist,'\n'), '=(interactive)', 't', prompt_env) if code == nil then if err:sub(-5)=="" then t_prompt="[cont]" else dfhack.printerr(err) cmdlinelist={} t_prompt=nil end else cmdlinelist={} t_prompt=nil local data = table.pack(safecall(code)) if data[1] and data.n > 1 then prompt_env._ = data[2] if pfix == '!' then safecall(print, table.unpack(data,2,data.n)) else for i=2,data.n do local varname = '_'..vcnt prompt_env[varname] = data[i] dfhack.print(varname..' = ') safecall(print, data[i]) vcnt = vcnt + 1 end end end end end end return true end -- Feed the table back to the require() mechanism. return dfhack