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

Conflicts:
	plugins/remotefortressreader.cpp
develop
Japa 2015-09-05 15:00:03 +05:30
commit 07c601be99
7 changed files with 277 additions and 128 deletions

@ -9,6 +9,7 @@ DFHack Future
Stopped DF window from receiving input when unfocused on OS X
Fixed issues with keybindings involving Ctrl-A and Ctrl-Z, as well as Alt-E/U/N on OS X
Multiple contexts can now be specified when adding keybindings
Keybindings can now use F10-F12 and 0-9
Plugin system is no longer restricted to plugins that exist on startup
Lua
Scripts can be enabled with the built-in enable/disable commands
@ -66,6 +67,7 @@ DFHack Future
- widgets' positions, formats, etc. are now customizable (see Readme)
- weather display now separated from the date display
- New mouse cursor widget
dfstatus: Can enable/disable individual categories and customize metal bar list
full-heal: "-r" option removes corpses
gui/gm-editor
- Pointers can now be displaced

@ -197,7 +197,7 @@ To set keybindings, use the built-in ``keybinding`` command. Like any other
command it can be used at any time from the console, but it is also meaningful
in the DFHack init file.
Currently it supports any combination of Ctrl/Alt/Shift with F1-F9, or A-Z.
Currently, any combinations of Ctrl/Alt/Shift with A-Z, 0-9, or F1-F12 are supported.
Possible ways to call the command:
@ -214,7 +214,7 @@ The *<key>* parameter above has the following *case-sensitive* syntax::
[Ctrl-][Alt-][Shift-]KEY[@context[|context...]]
where the *KEY* part can be F1-F9 or A-Z, and [] denote optional parts.
where the *KEY* part can be any recognized key and [] denote optional parts.
When multiple commands are bound to the same key combination, DFHack selects
the first applicable one. Later 'add' commands, and earlier entries within one
@ -2365,6 +2365,7 @@ directory.
* gui/dfstatus
Show a quick overview of critical stock quantities, including food, drinks, wood, and various bars.
Sections can be enabled/disabled/configured by editing ``dfhack-config/dfstatus.lua``.
* gui/stockpiles

@ -0,0 +1,28 @@
-- dfstatus config
-- the dfstatus script can be found in hack/scripts/gui/
--[[
The following variables can be set to true/false to enable/disable categories (all true by default)
* drink
* wood
* fuel
* prepared_meals
* tanned_hides
* cloth
* metals
Example:
drink = false
fuel = true
To add metals:
* metal 'IRON'
* metals "GOLD" 'SILVER'
* metal('COPPER')
* metals("BRONZE", 'HORN_SILVER')
Use '-' for a blank line:
* metal '-'
]]
metals 'IRON' 'PIG_IRON' 'STEEL'
metals '-'
metals 'GOLD' 'SILVER' 'COPPER'

@ -1397,6 +1397,8 @@ bool Core::Init()
if (std::find(config_files.begin(), config_files.end(), filename) == config_files.end())
{
std::string src_file = std::string("dfhack-config/default/") + filename;
if (!Filesystem::isfile(src_file))
continue;
std::string dest_file = std::string("dfhack-config/") + filename;
std::ifstream src(src_file, std::ios::binary);
std::ofstream dest(dest_file, std::ios::binary);
@ -2157,9 +2159,15 @@ static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string
if (keyspec.size() == 1 && keyspec[0] >= 'A' && keyspec[0] <= 'Z') {
*psym = SDL::K_a + (keyspec[0]-'A');
return true;
} else if (keyspec.size() == 1 && keyspec[0] >= '0' && keyspec[0] <= '9') {
*psym = SDL::K_0 + (keyspec[0]-'0');
return true;
} else if (keyspec.size() == 2 && keyspec[0] == 'F' && keyspec[1] >= '1' && keyspec[1] <= '9') {
*psym = SDL::K_F1 + (keyspec[1]-'1');
return true;
} else if (keyspec.size() == 3 && keyspec.substr(0, 2) == "F1" && keyspec[2] >= '0' && keyspec[2] <= '2') {
*psym = SDL::K_F10 + (keyspec[2]-'0');
return true;
} else if (keyspec == "Enter") {
*psym = SDL::K_RETURN;
return true;

@ -147,9 +147,9 @@ DFhackCExport RPCService *plugin_rpcconnect(color_ostream &)
svc->addFunction("GetItemList", GetItemList);
svc->addFunction("GetBuildingDefList", GetBuildingDefList);
svc->addFunction("GetWorldMap", GetWorldMap);
svc->addFunction("GetRegionMaps", GetRegionMaps);
svc->addFunction("GetCreatureRaws", GetCreatureRaws);
return svc;
svc->addFunction("GetRegionMaps", GetRegionMaps);
svc->addFunction("GetCreatureRaws", GetCreatureRaws);
return svc;
}
// This is called right before the plugin library is removed from memory.
@ -182,20 +182,20 @@ uint16_t fletcher16(uint8_t const *data, size_t bytes)
void ConvertDfColor(int16_t index, RemoteFortressReader::ColorDefinition * out)
{
if (!df::global::enabler)
return;
if (!df::global::enabler)
return;
auto enabler = df::global::enabler;
auto enabler = df::global::enabler;
out->set_red((int)(enabler->ccolor[index][0] * 255));
out->set_green((int)(enabler->ccolor[index][1] * 255));
out->set_blue((int)(enabler->ccolor[index][2] * 255));
out->set_red((int)(enabler->ccolor[index][0] * 255));
out->set_green((int)(enabler->ccolor[index][1] * 255));
out->set_blue((int)(enabler->ccolor[index][2] * 255));
}
void ConvertDfColor(int16_t in[3], RemoteFortressReader::ColorDefinition * out)
{
int index = in[0] + 8 * in[1];
ConvertDfColor(index, out);
int index = in[0] + 8 * in[1];
ConvertDfColor(index, out);
}
@ -788,7 +788,7 @@ static command_result GetBlockList(color_ostream &stream, const BlockRequest *in
int max_x = in->max_x();
int max_y = in->max_y();
//stream.print("Got request for blocks from (%d, %d, %d) to (%d, %d, %d).\n", in->min_x(), in->min_y(), in->min_z(), in->max_x(), in->max_y(), in->max_z());
for (int zz = in->max_z()-1; zz >= in->min_z(); zz--)
for (int zz = in->max_z() - 1; zz >= in->min_z(); zz--)
{
// (di, dj) is a vector - direction in which we move right now
int di = 1;
@ -959,13 +959,13 @@ static command_result GetUnitList(color_ostream &stream, const EmptyMessage *in,
send_unit->set_pos_x(unit->pos.x);
send_unit->set_pos_y(unit->pos.y);
send_unit->set_pos_z(unit->pos.z);
send_unit->mutable_race()->set_mat_type(unit->race);
send_unit->mutable_race()->set_mat_index(unit->caste);
ConvertDfColor(Units::getProfessionColor(unit), send_unit->mutable_profession_color());
send_unit->set_flags1(unit->flags1.whole);
send_unit->set_flags2(unit->flags2.whole);
send_unit->set_flags3(unit->flags3.whole);
}
send_unit->mutable_race()->set_mat_type(unit->race);
send_unit->mutable_race()->set_mat_index(unit->caste);
ConvertDfColor(Units::getProfessionColor(unit), send_unit->mutable_profession_color());
send_unit->set_flags1(unit->flags1.whole);
send_unit->set_flags2(unit->flags2.whole);
send_unit->set_flags3(unit->flags3.whole);
}
return CR_OK;
}
@ -1236,7 +1236,7 @@ static command_result GetWorldMap(color_ostream &stream, const EmptyMessage *in,
out->set_name(Translation::TranslateName(&(data->name), false));
out->set_name_english(Translation::TranslateName(&(data->name), true));
for (int yy = 0; yy < height; yy++)
for (int xx = 0; xx < width; xx ++)
for (int xx = 0; xx < width; xx++)
{
df::region_map_entry * map_entry = &data->region_map[xx][yy];
out->add_elevation(map_entry->elevation);
@ -1384,58 +1384,58 @@ static command_result GetRegionMaps(color_ostream &stream, const EmptyMessage *i
static command_result GetCreatureRaws(color_ostream &stream, const EmptyMessage *in, CreatureRawList *out)
{
if (!df::global::world)
return CR_FAILURE;
if (!df::global::world)
return CR_FAILURE;
df::world * world = df::global::world;
df::world * world = df::global::world;
for (int i = 0; i < world->raws.creatures.all.size(); i++)
{
df::creature_raw * orig_creature = world->raws.creatures.all[i];
for (int i = 0; i < world->raws.creatures.all.size(); i++)
{
df::creature_raw * orig_creature = world->raws.creatures.all[i];
auto send_creature = out->add_creature_raws();
auto send_creature = out->add_creature_raws();
send_creature->set_index(i);
send_creature->set_creature_id(orig_creature->creature_id);
send_creature->add_name(orig_creature->name[0]);
send_creature->add_name(orig_creature->name[1]);
send_creature->add_name(orig_creature->name[2]);
send_creature->set_index(i);
send_creature->set_creature_id(orig_creature->creature_id);
send_creature->add_name(orig_creature->name[0]);
send_creature->add_name(orig_creature->name[1]);
send_creature->add_name(orig_creature->name[2]);
send_creature->add_general_baby_name(orig_creature->general_baby_name[0]);
send_creature->add_general_baby_name(orig_creature->general_baby_name[1]);
send_creature->add_general_baby_name(orig_creature->general_baby_name[0]);
send_creature->add_general_baby_name(orig_creature->general_baby_name[1]);
send_creature->add_general_child_name(orig_creature->general_child_name[0]);
send_creature->add_general_child_name(orig_creature->general_child_name[1]);
send_creature->add_general_child_name(orig_creature->general_child_name[0]);
send_creature->add_general_child_name(orig_creature->general_child_name[1]);
send_creature->set_creature_tile(orig_creature->creature_tile);
send_creature->set_creature_soldier_tile(orig_creature->creature_soldier_tile);
send_creature->set_creature_tile(orig_creature->creature_tile);
send_creature->set_creature_soldier_tile(orig_creature->creature_soldier_tile);
ConvertDfColor(orig_creature->color, send_creature->mutable_color());
ConvertDfColor(orig_creature->color, send_creature->mutable_color());
send_creature->set_adultsize(orig_creature->adultsize);
send_creature->set_adultsize(orig_creature->adultsize);
for (int j = 0; j < orig_creature->caste.size(); j++)
{
auto orig_caste = orig_creature->caste[j];
if (!orig_caste)
continue;
auto send_caste = send_creature->add_caste();
for (int j = 0; j < orig_creature->caste.size(); j++)
{
auto orig_caste = orig_creature->caste[j];
if (!orig_caste)
continue;
auto send_caste = send_creature->add_caste();
send_caste->set_index(j);
send_caste->set_index(j);
send_caste->set_caste_id(orig_caste->caste_id);
send_caste->set_caste_id(orig_caste->caste_id);
send_caste->add_caste_name(orig_caste->caste_name[0]);
send_caste->add_caste_name(orig_caste->caste_name[1]);
send_caste->add_caste_name(orig_caste->caste_name[2]);
send_caste->add_caste_name(orig_caste->caste_name[0]);
send_caste->add_caste_name(orig_caste->caste_name[1]);
send_caste->add_caste_name(orig_caste->caste_name[2]);
send_caste->add_baby_name(orig_caste->baby_name[0]);
send_caste->add_baby_name(orig_caste->baby_name[1]);
send_caste->add_baby_name(orig_caste->baby_name[0]);
send_caste->add_baby_name(orig_caste->baby_name[1]);
send_caste->add_child_name(orig_caste->child_name[0]);
send_caste->add_child_name(orig_caste->child_name[1]);
}
}
send_caste->add_child_name(orig_caste->child_name[0]);
send_caste->add_child_name(orig_caste->child_name[1]);
}
}
return CR_OK;
return CR_OK;
}

@ -1,8 +1,87 @@
-- dfstatus 1.5 - a quick access status screen.
-- originally written by enjia2000@gmail.com
-- a quick access status screen
-- originally written by enjia2000@gmail.com (stolencatkarma)
local gui = require 'gui'
function warn(msg)
dfhack.color(COLOR_LIGHTRED)
print(msg)
dfhack.color(nil)
end
config = {
flags = {
drink = true,
wood = true,
fuel = true,
prepared_meals = true,
tanned_hides = true,
cloth = true,
metals = true,
},
metal_ids = {},
}
function parse_config()
local metal_map = {}
for id, raw in pairs(df.global.world.raws.inorganics) do
if raw.material.flags.IS_METAL then
metal_map[raw.id:upper()] = id
metal_map[id] = raw.id:upper()
end
end
local function add_metal(...)
for _, m in pairs({...}) do
id = metal_map[tostring(m):upper()]
if id ~= nil then
table.insert(config.metal_ids, id)
elseif m == '-' then
table.insert(config.metal_ids, '-')
else
warn('Invalid metal: ' .. tostring(m))
end
end
return add_metal
end
local env = {}
setmetatable(env, {
__index = function(_, k)
if k == 'metal' or k == 'metals' then
return add_metal
elseif k == 'flags' then
return config.flags
else
error('unknown name: ' .. k, 2)
end
end,
__newindex = function(_, k, v)
if config.flags[k] ~= nil then
if v ~= nil then
config.flags[k] = v
else
config.flags[k] = false
end
else
error('unknown flag: ' .. k, 2)
end
end,
})
local f, err = loadfile('dfhack-config/dfstatus.lua', 't', env)
if not f then
qerror('error loading config: ' .. err)
end
local ok, err = pcall(f)
if not ok then
qerror('error parsing config: ' .. err)
end
end
function getInorganicName(id)
return (df.inorganic_raw.find(id).material.state_name.Solid:gsub('^[a-z]', string.upper))
end
dfstatus = defclass(dfstatus, gui.FramedScreen)
dfstatus.ATTRS = {
frame_style = gui.GREY_LINE_FRAME,
@ -13,95 +92,126 @@ dfstatus.ATTRS = {
focus_path = 'dfstatus',
}
function dfstatus:onRenderBody(dc)
function dfstatus:init()
self.text = {}
self.start = 1
local function write(line)
table.insert(self.text, line)
-- ensure that the window is wide enough for this line plus a scroll arrow
if #line + 1 > self.frame_width then
self.frame_width = #line + 1
end
end
local function newline() write('') end
local f = config.flags
local drink = 0
local wood = 0
--local meat = 0
--local raw_fish = 0
--local plants = 0
local prepared_meals = 0
local fuel = 0
local pigiron = 0
local iron = 0
local steel = 0
local silver = 0
local copper = 0
local gold = 0
local tannedhides = 0
local prepared_meals = 0
local tanned_hides = 0
local cloth = 0
local metals = {}
for _, id in pairs(config.metal_ids) do
metals[id] = 0
end
for _, item in ipairs(df.global.world.items.all) do
if not item.flags.rotten and not item.flags.dump and not item.flags.forbid then
if (item:getType() == df.item_type.WOOD) then wood = wood + item:getStackSize()
elseif (item:getType() == df.item_type.DRINK) then drink = drink + item:getStackSize()
elseif (item:getType() == df.item_type.SKIN_TANNED) then tannedhides = tannedhides + item:getStackSize()
elseif (item:getType() == df.item_type.CLOTH) then cloth = cloth + item:getStackSize()
--elseif (item:getType() == df.item_type.MEAT) then meat = meat + item:getStackSize()
--elseif (item:getType() == df.item_type.FISH_RAW) then raw_fish = raw_fish + item:getStackSize()
--elseif (item:getType() == df.item_type.PLANT) then plants = plants + item:getStackSize()
elseif (item:getType() == df.item_type.FOOD) then prepared_meals = prepared_meals + item:getStackSize()
elseif (item:getType() == df.item_type.BAR) then
for token in string.gmatch(dfhack.items.getDescription(item,0),"[^%s]+") do
if (token == "silver") then silver = silver + item:getStackSize()
elseif (token == "charcoal" or token == "coke") then fuel = fuel + item:getStackSize()
elseif (token == "iron") then iron = iron + item:getStackSize()
elseif (token == "pig") then pigiron = pigiron + item:getStackSize()
elseif (token == "copper") then copper = copper + item:getStackSize()
elseif (token == "gold") then gold = gold + item:getStackSize()
elseif (token == "steel") then steel = steel + item:getStackSize()
if item:getType() == df.item_type.WOOD then
wood = wood + item:getStackSize()
elseif item:getType() == df.item_type.DRINK then
drink = drink + item:getStackSize()
elseif item:getType() == df.item_type.SKIN_TANNED then
tanned_hides = tanned_hides + item:getStackSize()
elseif item:getType() == df.item_type.CLOTH then
cloth = cloth + item:getStackSize()
elseif item:getType() == df.item_type.FOOD then
prepared_meals = prepared_meals + item:getStackSize()
elseif item:getType() == df.item_type.BAR then
if item:getMaterial() == df.builtin_mats.COAL then
fuel = fuel + item:getStackSize()
elseif item:getMaterial() == df.builtin_mats.INORGANIC then
local mat_idx = item:getMaterialIndex()
if metals[mat_idx] ~= nil then
metals[mat_idx] = metals[mat_idx] + item:getStackSize()
end
break -- only need to look at the 1st token of each item.
end
end
end
end
if f.drink then
write("Drinks: " .. drink)
end
if f.prepared_meals then
write("Meals: " .. prepared_meals)
end
if f.drink or f.prepared_meals then
newline()
end
if f.wood then
write("Wood: " .. wood)
end
if f.fuel then
write("Fuel: " .. fuel)
end
if f.wood or f.fuel then
newline()
end
if f.tanned_hides then
write("Hides: " .. tanned_hides)
end
if f.cloth then
write("Cloth: " .. cloth)
end
if f.tanned_hides or f.cloth then
newline()
end
if f.metals then
write("Metal bars:")
for _, id in pairs(config.metal_ids) do
if id == '-' then
newline()
else
write(' ' .. ('%-10s'):format(getInorganicName(id) .. ': ') .. metals[id])
end
end
end
self.start_min = 1
self.start_max = #self.text - self.frame_height + 1
end
function dfstatus:onRenderBody(dc)
dc:pen(COLOR_LIGHTGREEN)
dc:string("Drinks: " .. drink)
dc:newline(0)
dc:string("Meals: " .. prepared_meals)
dc:newline(0)
dc:newline(0)
dc:string("Wood: " .. wood)
dc:newline(0)
dc:newline(0)
dc:string("Hides: " .. tannedhides)
dc:newline(0)
dc:string("Cloth: " .. cloth)
dc:newline(0)
-- dc:string("Raw Fish: ".. raw_fish)
-- dc:newline(0)
-- dc:string("Plants: ".. plants)
-- dc:newline(0)
dc:newline(0)
dc:string("Bars:")
dc:newline(1)
dc:string("Fuel: " .. fuel)
dc:newline(1)
dc:string("Pig Iron: " .. pigiron)
dc:newline(1)
dc:string("Steel: " .. steel)
dc:newline(1)
dc:string("Iron: " .. iron)
dc:newline(1)
dc:newline(1)
dc:string("Copper: " .. copper)
dc:newline(1)
dc:string("Silver: " .. silver)
dc:newline(1)
dc:string("Gold: " .. gold)
for id, line in pairs(self.text) do
if id >= self.start then
dc:string(line):newline()
end
end
dc:pen(COLOR_LIGHTCYAN)
if self.start > self.start_min then
dc:seek(self.frame_width - 1, 0):char(24)
end
if self.start < self.start_max then
dc:seek(self.frame_width - 1, self.frame_height - 1):char(25)
end
end
function dfstatus:onInput(keys)
if keys.LEAVESCREEN or keys.SELECT then
self:dismiss()
scr = nil
elseif keys.STANDARDSCROLL_UP then
self.start = math.max(self.start - 1, self.start_min)
elseif keys.STANDARDSCROLL_DOWN then
self.start = math.min(self.start + 1, self.start_max)
end
end
if not scr then
parse_config()
scr = dfstatus()
scr:show()
else

@ -85,7 +85,7 @@ class TabLinter(Linter):
def fix_line(self, line):
return line.replace('\t', ' ')
linters = [NewlineLinter(), TrailingWhitespaceLinter(), TabLinter()]
linters = [cls() for cls in Linter.__subclasses__()]
def main():
root_path = os.path.abspath(sys.argv[1] if len(sys.argv) > 1 else '.')