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

develop
Japa 2016-05-28 21:24:02 +05:30
commit 3dcd3e14f0
34 changed files with 508 additions and 424 deletions

@ -105,9 +105,9 @@ if (NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl OR NOT EXISTS ${dfhac
endif()
# set up versioning.
set(DF_VERSION "0.42.06")
SET(DFHACK_RELEASE "r1")
SET(DFHACK_PRERELEASE FALSE)
set(DF_VERSION "0.43.03")
SET(DFHACK_RELEASE "alpha0")
SET(DFHACK_PRERELEASE TRUE)
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")

@ -33,12 +33,33 @@ Changelog
DFHack future
=============
DFHack 0.42.06-r1
=================
Lua
---
- Label widgets can now easily register handlers for mouse clicks
New Features
------------
- `gui/gm-editor` it's now possible to insert default types to containers. For primitive types leave the type entry empty, for references use ``*``.
Fixes
-----
- `createitem`: Now moves multiple created items to cursor correctly
- `exportlegends`: Improved handling of unknown enum items (fixes many errors)
- `gui/create-item`: Fixed quality when creating multiple items
- `gui/mod-manager`: Fixed error when mods folder doesn't exist
- `modtools/item-trigger`: Fixed handling of items with subtypes
Misc Improvements
-----------------
- `fix/diplomats`: replaces ``fixdiplomats``
- `fix/merchants`: replaces ``fixmerchants``
Removed
-------
- `tweak` manager-quantity: no longer needed
DFHack 0.42.06-r1
=================
Internals
---------

@ -128,10 +128,6 @@ keybinding add Alt-P@dwarfmode/Hauling/DefineStop/Cond/Guide gui/guide-path
# workshop job details
keybinding add Alt-A@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workshop-job
# workflow front-end
keybinding add Alt-W@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workflow
keybinding add Alt-W@overallstatus "gui/workflow status"
# autobutcher front-end
keybinding add Shift-B@pet/List/Unit "gui/autobutcher"
@ -220,7 +216,6 @@ enable \
zone \
stocks \
autochop \
stockflow \
stockpiles
#end a line with a backslash to make it continue to the next line. The \ is deleted for the final command.
# Multiline commands are ONLY supported for scripts like dfhack.init. You cannot do multiline command manually on the DFHack console.

@ -16,6 +16,7 @@ AndreasPK AndreasPK
Angus Mezick amezick
Antalia tamarakorr
Anuradha Dissanayake falconne
AtomicChicken AtomicChicken
belal jimhester
Ben Lubar BenLubar
Caldfir caldfir
@ -32,6 +33,7 @@ Erik Youngren Artanis
Espen Wiborg
expwnent expwnent
Feng
gchristopher gchristopher
Harlan Playford playfordh
IndigoFenix
James Logsdon jlogsdon
@ -83,6 +85,7 @@ root
Roses Pheosics
Ross M RossM
rout
rubybrowncoat rubybrowncoat
Rumrusher rumrusher
RusAnon RusAnon
sami

@ -3051,10 +3051,13 @@ It has the following attributes:
:text_pen: Specifies the pen for active text.
:text_dpen: Specifies the pen for disabled text.
:text_hpen: Specifies the pen for text hovered over by the mouse, if a click handler is registered.
:disabled: Boolean or a callback; if true, the label is disabled.
:enabled: Boolean or a callback; if false, the label is disabled.
:auto_height: Sets self.frame.h from the text height.
:auto_width: Sets self.frame.w from the text width.
:on_click: A callback called when the label is clicked (optional)
:on_rclick: A callback called when the label is right-clicked (optional)
The text itself is represented as a complex structure, and passed
to the object via the ``text`` argument of the constructor, or via

@ -180,18 +180,6 @@ Bugfixes
.. contents::
:local:
fixdiplomats
============
Adds a Diplomat position to all Elven civilizations, allowing them to negotiate
tree cutting quotas - and you to violate them and start wars.
This was vanilla behaviour until ``0.31.12``, in which the "bug" was "fixed".
fixmerchants
============
Adds the Guild Representative position to all Human civilizations,
allowing them to make trade agreements. This was the default behaviour in
``0.28.181.40d`` and earlier.
.. _fix-unit-occupancy:
fix-unit-occupancy
@ -302,7 +290,6 @@ Subcommands that persist until disabled or DF quits:
:kitchen-keys: Fixes DF kitchen meal keybindings (:bug:`614`)
:kitchen-prefs-color: Changes color of enabled items to green in kitchen preferences
:kitchen-prefs-empty: Fixes a layout issue with empty kitchen tabs (:bug:`9000`)
:manager-quantity: Removes the limit of 30 jobs per manager order
:max-wheelbarrow: Allows assigning more than 3 wheelbarrows to a stockpile
:military-color-assigned:
Color squad candidates already assigned to other squads in yellow/green

@ -24,6 +24,7 @@ distribution.
#include "Internal.h"
#include <iostream>
#include <string>
#include <vector>
#include <map>
@ -414,7 +415,11 @@ bool VMethodInterposeLinkBase::apply(bool enable)
if (is_applied())
return true;
if (!host->vtable_ptr)
{
std::cerr << "VMethodInterposeLinkBase::apply(" << enable << "): " << name()
<< ": no vtable pointer: " << host->getName() << endl;
return false;
}
// Retrieve the current vtable entry
VMethodInterposeLinkBase *old_link = host->interpose_list[vmethod_idx];
@ -440,6 +445,7 @@ bool VMethodInterposeLinkBase::apply(bool enable)
}
else if (!host->set_vmethod_ptr(patcher, vmethod_idx, interpose_method))
{
std::cerr << "VMethodInterposeLinkBase::apply(" << enable << "): " << name() << ": set_vmethod_ptr failed" << endl;
set_chain(NULL);
return false;
}

@ -0,0 +1,13 @@
df::enums::dfhack_knowledge_scholar_flag::dfhack_knowledge_scholar_flag value()
{
int32_t value = category * 32;
for (int32_t i = 0; i < 32; i++)
{
if (flags & (1 << i))
{
value += i;
break;
}
}
return df::enums::dfhack_knowledge_scholar_flag::dfhack_knowledge_scholar_flag(value);
}

@ -330,15 +330,21 @@ Label = defclass(Label, Widget)
Label.ATTRS{
text_pen = COLOR_WHITE,
text_dpen = COLOR_DARKGREY,
text_dpen = COLOR_DARKGREY, -- disabled
text_hpen = DEFAULT_NIL, -- highlight - default is text_pen with reversed brightness
disabled = DEFAULT_NIL,
enabled = DEFAULT_NIL,
auto_height = true,
auto_width = false,
on_click = DEFAULT_NIL,
on_rclick = DEFAULT_NIL,
}
function Label:init(args)
self:setText(args.text)
if not self.text_hpen then
self.text_hpen = ((tonumber(self.text_pen) or tonumber(self.text_pen.fg) or 0) + 8) % 16
end
end
function Label:setText(text)
@ -374,11 +380,21 @@ function Label:getTextWidth()
end
function Label:onRenderBody(dc)
render_text(self,dc,0,0,self.text_pen,self.text_dpen,is_disabled(self))
local text_pen = self.text_pen
if self:getMousePos() and (self.on_click or self.on_rclick) then
text_pen = self.text_hpen
end
render_text(self,dc,0,0,text_pen,self.text_dpen,is_disabled(self))
end
function Label:onInput(keys)
if not is_disabled(self) then
if keys._MOUSE_L_DOWN and self:getMousePos() and self.on_click then
self:on_click()
end
if keys._MOUSE_R_DOWN and self:getMousePos() and self.on_rclick then
self:on_rclick()
end
return check_text_keys(self, keys)
end
end

@ -51,12 +51,12 @@ using namespace DFHack;
#include "df/viewscreen_dwarfmodest.h"
#include "df/viewscreen_dungeonmodest.h"
#include "df/viewscreen_dungeon_monsterstatusst.h"
#include "df/viewscreen_jobst.h"
#include "df/viewscreen_joblistst.h"
#include "df/viewscreen_unitlistst.h"
#include "df/viewscreen_buildinglistst.h"
#include "df/viewscreen_itemst.h"
#include "df/viewscreen_layer.h"
#include "df/viewscreen_layer_workshop_profilest.h"
#include "df/viewscreen_layer_noblelistst.h"
#include "df/viewscreen_layer_overall_healthst.h"
#include "df/viewscreen_layer_assigntradest.h"
@ -66,6 +66,7 @@ using namespace DFHack;
#include "df/viewscreen_petst.h"
#include "df/viewscreen_tradegoodsst.h"
#include "df/viewscreen_storesst.h"
#include "df/viewscreen_workshop_profilest.h"
#include "df/ui_unit_view_mode.h"
#include "df/ui_sidebar_menus.h"
#include "df/ui_look_list.h"
@ -429,15 +430,21 @@ DEFINE_GET_FOCUS_STRING_HANDLER(layer_military)
}
}
DEFINE_GET_FOCUS_STRING_HANDLER(layer_workshop_profile)
DEFINE_GET_FOCUS_STRING_HANDLER(workshop_profile)
{
auto list1 = getLayerList(screen, 0);
if (!list1) return;
if (vector_get(screen->workers, list1->cursor))
typedef df::viewscreen_workshop_profilest::T_tab T_tab;
switch(screen->tab)
{
case T_tab::Workers:
focus += "/Unit";
else
focus += "/None";
break;
case T_tab::Orders:
focus += "/Orders";
break;
case T_tab::Restrictions:
focus += "/Restrictions";
break;
}
}
DEFINE_GET_FOCUS_STRING_HANDLER(layer_noblelist)
@ -755,6 +762,10 @@ df::job *Gui::getSelectedJob(color_ostream &out, bool quiet)
{
df::viewscreen *top = Core::getTopViewscreen();
if (VIRTUAL_CAST_VAR(screen, df::viewscreen_jobst, top))
{
return screen->job;
}
if (VIRTUAL_CAST_VAR(joblist, df::viewscreen_joblistst, top))
{
df::job *job = vector_get(joblist->jobs, joblist->cursor_pos);
@ -810,10 +821,10 @@ df::unit *Gui::getAnyUnit(df::viewscreen *top)
return ref ? ref->getUnit() : NULL;
}
if (VIRTUAL_CAST_VAR(screen, df::viewscreen_layer_workshop_profilest, top))
if (VIRTUAL_CAST_VAR(screen, df::viewscreen_workshop_profilest, top))
{
if (auto list1 = getLayerList(screen, 0))
return vector_get(screen->workers, list1->cursor);
if (screen->tab == df::viewscreen_workshop_profilest::Workers)
return vector_get(screen->workers, screen->worker_idx);
return NULL;
}
@ -1051,9 +1062,12 @@ df::building *Gui::getAnyBuilding(df::viewscreen *top)
using df::global::world;
using df::global::ui_sidebar_menus;
if (auto screen = strict_virtual_cast<df::viewscreen_buildinglistst>(top))
if (VIRTUAL_CAST_VAR(screen, df::viewscreen_buildinglistst, top))
return vector_get(screen->buildings, screen->cursor);
if (VIRTUAL_CAST_VAR(screen, df::viewscreen_workshop_profilest, top))
return df::building::find(screen->building_id);
if (auto dfscreen = dfhack_viewscreen::try_cast(top))
return dfscreen->getSelectedBuilding();

@ -1 +1 @@
Subproject commit d3182b7f9a241d75e556937cad1f2c8824845b4f
Subproject commit d48de9e904150c4cd0e8502b6edf246737849607

@ -14,4 +14,5 @@ fi
old_tty_settings=$(stty -g)
DYLD_INSERT_LIBRARIES=./hack/libdfhack.dylib ./dwarfort.exe "$@"
stty "$old_tty_settings"
tput sgr0
echo ""

@ -95,6 +95,7 @@ esac
# Restore previous terminal settings
stty "$old_tty_settings"
tput sgr0
echo
if [ -n "$DF_POST_CMD" ]; then

@ -3,6 +3,7 @@
#include <map>
#include <algorithm>
#include <vector>
#include <math.h>
#include "Core.h"
#include "Console.h"

@ -99,7 +99,6 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(fastdwarf fastdwarf.cpp)
DFHACK_PLUGIN(filltraffic filltraffic.cpp)
DFHACK_PLUGIN(fix-armory fix-armory.cpp)
DFHACK_PLUGIN(fixpositions fixpositions.cpp)
DFHACK_PLUGIN(fix-unit-occupancy fix-unit-occupancy.cpp)
DFHACK_PLUGIN(fixveins fixveins.cpp)
DFHACK_PLUGIN(flows flows.cpp)

@ -134,7 +134,7 @@ void create_jobs() {
for (auto w = workshops->begin(); w != workshops->end(); ++w) {
auto workshop = virtual_cast<df::building_workshopst>(*w);
auto links = workshop->links.take_from_pile;
auto links = workshop->profile.links.take_from_pile;
if (workshop->construction_stage < 3) {
// Construction in progress.

@ -128,7 +128,7 @@ bool makeItem (df::reaction_product_itemst *prod, df::unit *unit, bool second_it
}
}
if ((is_gloves || is_shoes) && !second_item)
return makeItem(prod, unit, true);
return makeItem(prod, unit, true, move_to_cursor);
return true;
}

@ -1,244 +0,0 @@
// Fix Entity Positions - make sure Elves have diplomats and Humans have guild representatives
#include "Core.h"
#include <Console.h>
#include <Export.h>
#include <PluginManager.h>
#include <DataDefs.h>
#include "df/world.h"
#include "df/historical_entity.h"
#include "df/entity_raw.h"
#include "df/entity_position.h"
#include "df/entity_position_responsibility.h"
#include "df/entity_position_assignment.h"
using std::string;
using std::vector;
using namespace DFHack;
using namespace df::enums;
DFHACK_PLUGIN("fixpositions");
REQUIRE_GLOBAL(world);
command_result df_fixdiplomats (color_ostream &out, vector<string> &parameters)
{
if (!parameters.empty())
return CR_WRONG_USAGE;
CoreSuspender suspend;
int checked = 0, fixed = 0;
for (int i = 0; i < world->entities.all.size(); i++)
{
df::historical_entity *ent = world->entities.all[i];
// only work with civilizations - ignore groups and religions
if (ent->type != 0)
continue;
// only add diplomats for tree cap diplomacy
if (!ent->entity_raw->flags.is_set(entity_raw_flags::TREE_CAP_DIPLOMACY))
continue;
checked++;
bool update = true;
df::entity_position *pos = NULL;
// see if we need to add a new position or modify an existing one
for (int j = 0; j < ent->positions.own.size(); j++)
{
pos = ent->positions.own[j];
if (pos->responsibilities[entity_position_responsibility::MAKE_INTRODUCTIONS] &&
pos->responsibilities[entity_position_responsibility::MAKE_PEACE_AGREEMENTS] &&
pos->responsibilities[entity_position_responsibility::MAKE_TOPIC_AGREEMENTS])
{
// a diplomat position exists with the proper responsibilities - skip to the end
update = false;
break;
}
// Diplomat position already exists, but has the wrong options - modify it instead of creating a new one
if (pos->code == "DIPLOMAT")
break;
pos = NULL;
}
if (update)
{
// either there's no diplomat, or there is one and it's got the wrong responsibilities
if (!pos)
{
// there was no diplomat - create it
pos = new df::entity_position;
ent->positions.own.push_back(pos);
pos->code = "DIPLOMAT";
pos->id = ent->positions.next_position_id++;
pos->flags.set(entity_position_flags::DO_NOT_CULL);
pos->flags.set(entity_position_flags::MENIAL_WORK_EXEMPTION);
pos->flags.set(entity_position_flags::SLEEP_PRETENSION);
pos->flags.set(entity_position_flags::PUNISHMENT_EXEMPTION);
pos->flags.set(entity_position_flags::ACCOUNT_EXEMPT);
pos->flags.set(entity_position_flags::DUTY_BOUND);
pos->flags.set(entity_position_flags::COLOR);
pos->flags.set(entity_position_flags::HAS_RESPONSIBILITIES);
pos->flags.set(entity_position_flags::IS_DIPLOMAT);
pos->flags.set(entity_position_flags::IS_LEADER);
// not sure what these flags do, but the game sets them for a valid diplomat
pos->flags.set(entity_position_flags::unk_12);
pos->flags.set(entity_position_flags::unk_1a);
pos->flags.set(entity_position_flags::unk_1b);
pos->name[0] = "Diplomat";
pos->name[1] = "Diplomats";
pos->precedence = 70;
pos->color[0] = 7;
pos->color[1] = 0;
pos->color[2] = 1;
}
// assign responsibilities
pos->responsibilities[entity_position_responsibility::MAKE_INTRODUCTIONS] = true;
pos->responsibilities[entity_position_responsibility::MAKE_PEACE_AGREEMENTS] = true;
pos->responsibilities[entity_position_responsibility::MAKE_TOPIC_AGREEMENTS] = true;
}
// make sure the diplomat position, whether we created it or not, is set up for proper assignment
bool assign = true;
for (int j = 0; j < ent->positions.assignments.size(); j++)
{
if (ent->positions.assignments[j]->position_id == pos->id)
{
// it is - nothing more to do here
assign = false;
break;
}
}
if (assign)
{
// it isn't - set it up
df::entity_position_assignment *asn = new df::entity_position_assignment;
ent->positions.assignments.push_back(asn);
asn->id = ent->positions.next_assignment_id++;
asn->position_id = pos->id;
asn->flags.extend(0x1F); // make room for 32 flags
asn->flags.set(0); // and set the first one
}
if (update || assign)
fixed++;
}
out.print("Fixed %d of %d civilizations to enable tree cap diplomacy.\n", fixed, checked);
return CR_OK;
}
command_result df_fixmerchants (color_ostream &out, vector<string> &parameters)
{
if (!parameters.empty())
return CR_WRONG_USAGE;
CoreSuspender suspend;
int checked = 0, fixed = 0;
for (int i = 0; i < world->entities.all.size(); i++)
{
df::historical_entity *ent = world->entities.all[i];
// only work with civilizations - ignore groups and religions
if (ent->type != 0)
continue;
// only add guild reps for merchant nobility
if (!ent->entity_raw->flags.is_set(entity_raw_flags::MERCHANT_NOBILITY))
continue;
checked++;
bool update = true;
df::entity_position *pos = NULL;
// see if we need to add a new position or modify an existing one
for (int j = 0; j < ent->positions.own.size(); j++)
{
pos = ent->positions.own[j];
if (pos->responsibilities[entity_position_responsibility::TRADE])
{
// a guild rep exists with the proper responsibilities - skip to the end
update = false;
break;
}
// Guild Representative position already exists, but has the wrong options - modify it instead of creating a new one
if (pos->code == "GUILD_REPRESENTATIVE")
break;
pos = NULL;
}
if (update)
{
// either there's no guild rep, or there is one and it's got the wrong responsibilities
if (!pos)
{
// there was no guild rep - create it
pos = new df::entity_position;
ent->positions.own.push_back(pos);
pos->code = "GUILD_REPRESENTATIVE";
pos->id = ent->positions.next_position_id++;
pos->flags.set(entity_position_flags::DO_NOT_CULL);
pos->flags.set(entity_position_flags::MENIAL_WORK_EXEMPTION);
pos->flags.set(entity_position_flags::SLEEP_PRETENSION);
pos->flags.set(entity_position_flags::PUNISHMENT_EXEMPTION);
pos->flags.set(entity_position_flags::ACCOUNT_EXEMPT);
pos->flags.set(entity_position_flags::DUTY_BOUND);
pos->flags.set(entity_position_flags::COLOR);
pos->flags.set(entity_position_flags::HAS_RESPONSIBILITIES);
pos->flags.set(entity_position_flags::IS_DIPLOMAT);
pos->flags.set(entity_position_flags::IS_LEADER);
// not sure what these flags do, but the game sets them for a valid guild rep
pos->flags.set(entity_position_flags::unk_12);
pos->flags.set(entity_position_flags::unk_1a);
pos->flags.set(entity_position_flags::unk_1b);
pos->name[0] = "Guild Representative";
pos->name[1] = "Guild Representatives";
pos->precedence = 40;
pos->color[0] = 7;
pos->color[1] = 0;
pos->color[2] = 1;
}
// assign responsibilities
pos->responsibilities[entity_position_responsibility::TRADE] = true;
}
// make sure the guild rep position, whether we created it or not, is set up for proper assignment
bool assign = true;
for (int j = 0; j < ent->positions.assignments.size(); j++)
{
if (ent->positions.assignments[j]->position_id == pos->id)
{
// it is - nothing more to do here
assign = false;
break;
}
}
if (assign)
{
// it isn't - set it up
df::entity_position_assignment *asn = new df::entity_position_assignment;
ent->positions.assignments.push_back(asn);
asn->id = ent->positions.next_assignment_id++;
asn->position_id = pos->id;
asn->flags.extend(0x1F); // make room for 32 flags
asn->flags.set(0); // and set the first one
}
if (update || assign)
fixed++;
}
out.print("Fixed %d of %d civilizations to enable merchant nobility.\n", fixed, checked);
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
"fixdiplomats", "Add Diplomat position to Elven civilizations for tree cap diplomacy.",
df_fixdiplomats, false));
commands.push_back(PluginCommand(
"fixmerchants", "Add Guild Representative position to Human civilizations for merchant nobility.",
df_fixmerchants, false));
return CR_OK;
}
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}

@ -24,9 +24,9 @@ entry_ints = {
trigger_number = 3,
}
PageSize = 16
FirstRow = 4
FirstRow = 3
CenterCol = 38
ExtraLines = 9
-- Populate the reaction and stockpile order lists.
-- To be called whenever a world is loaded.
@ -797,10 +797,30 @@ screen = gui.FramedScreen {
function screen:onRenderBody(dc)
-- Emulates the built-in manager screen.
if not (self.page_size == self.frame_rect.height - ExtraLines) then
-- The screen size has changed.
self:refilter()
end
-- Top instruction line.
dc:seek(1, 1):string("Type in parts of the name to narrow your search. ", COLOR_WHITE)
dc:string(gui.getKeyDisplay("LEAVESCREEN"), COLOR_LIGHTGREEN)
dc:string(" to abort.", COLOR_WHITE)
dc:seek(1, PageSize + 5):string(self.search_string, COLOR_LIGHTCYAN)
dc:key("LEAVESCREEN"):string(" to abort.", COLOR_WHITE)
-- Search term, if any.
dc:seek(1, FirstRow + self.page_size + 1):string(self.search_string, COLOR_LIGHTCYAN)
-- Bottom instruction line.
dc:seek(1, FirstRow + self.page_size + 2)
dc:key("STANDARDSCROLL_UP"):key("STANDARDSCROLL_DOWN")
dc:key("STANDARDSCROLL_PAGEUP"):key("STANDARDSCROLL_PAGEDOWN")
dc:key("STANDARDSCROLL_LEFT"):key("STANDARDSCROLL_RIGHT")
dc:string(": Select", COLOR_WHITE)
dc:seek(CenterCol, FirstRow + self.page_size + 2)
dc:key("SETUPGAME_SAVE_PROFILE_ABORT"):string(": No order", COLOR_WHITE)
-- Reaction lines.
for _, item in ipairs(self.displayed) do
dc:seek(item.x, item.y):string(item.name, item.color)
end
@ -815,28 +835,69 @@ function screen:onInput(keys)
if selected then
store_order(self.stockpile, selected.index)
end
elseif keys.SETUPGAME_SAVE_PROFILE_ABORT then
self:dismiss()
clear_order(self.stockpile)
elseif keys.STANDARDSCROLL_UP then
self.position = self.position - 1
elseif keys.STANDARDSCROLL_DOWN then
self.position = self.position + 1
elseif keys.STANDARDSCROLL_LEFT then
self.position = self.position - PageSize
if self.position == 1 then
-- Move from the very first item to the very last item.
self.position = #self.reactions
elseif self.position < self.page_size then
-- On the first column, move to the very first item.
self.position = 1
else
-- Move to the same position on the previous column.
self.position = self.position - self.page_size
end
elseif keys.STANDARDSCROLL_RIGHT then
self.position = self.position + PageSize
if self.position == #self.reactions then
-- Move from the very last item to the very first item.
self.position = 1
else
-- Move to the same position on the next column.
self.position = self.position + self.page_size
if self.position > #self.reactions then
-- If that's past the end, move to the very last item.
self.position = #self.reactions
end
end
elseif keys.STANDARDSCROLL_PAGEUP then
-- Moves to the first item displayed on the new page, for some reason.
self.position = self.position - PageSize*2 - ((self.position-1) % (PageSize*2))
if self.position == 1 then
-- Move from the very first item to the very last item.
self.position = #self.reactions
elseif self.position < self.page_size*2 then
-- On the first page, move to the very first item.
self.position = 1
else
-- Move to the same position on the previous page.
self.position = self.position - self.page_size*2
end
elseif keys.STANDARDSCROLL_PAGEDOWN then
-- Moves to the first item displayed on the new page, for some reason.
self.position = self.position + PageSize*2 - ((self.position-1) % (PageSize*2))
if self.position == #self.reactions then
-- Move from the very last item to the very first item.
self.position = 1
else
-- Move to the same position on the next page.
self.position = self.position + self.page_size*2
if self.position > #self.reactions then
-- If that's past the end, move to the very last item.
self.position = #self.reactions
end
end
elseif keys.STRING_A000 then
-- This seems like an odd way to check for Backspace.
self.search_string = string.sub(self.search_string, 1, -2)
self.position = 1
elseif keys._STRING and keys._STRING >= 32 then
-- This interface only accepts letters and spaces.
local char = string.char(keys._STRING)
if char == " " or string.find(char, "^%a") then
self.search_string = self.search_string .. string.upper(char)
self.position = 1
end
end
@ -886,6 +947,13 @@ function splitstring(full, pattern)
end
function screen:refilter()
-- Determine which rows to show, and in which colors.
-- Todo: The official one now has three categories of search results:
-- * Cyan: Contains at least one exact word from the search terms
-- * Yellow: At least one word starts with at least one search term
-- * Grey: Each search term is found in the middle of a word
self.page_size = self.frame_rect.height - ExtraLines
local filtered = {}
local needles = splitstring(self.search_string, " ")
for key, value in ipairs(reaction_list) do
@ -904,12 +972,12 @@ function screen:refilter()
end
local start = 1
while self.position >= start + PageSize*2 do
start = start + PageSize*2
while self.position >= start + self.page_size*2 do
start = start + self.page_size*2
end
local displayed = {}
for n = 0, PageSize*2 - 1 do
for n = 0, self.page_size*2 - 1 do
local item = filtered[start + n]
if not item then
break
@ -918,9 +986,9 @@ function screen:refilter()
local x = 1
local y = FirstRow + n
if n >= PageSize then
if n >= self.page_size then
x = CenterCol
y = y - PageSize
y = y - self.page_size
name = " "..name
end
@ -941,6 +1009,14 @@ function screen:refilter()
self.displayed = displayed
end
function clear_order(stockpile)
local saved = saved_orders[stockpile.id]
if saved then
saved.entry:delete()
saved_orders[stockpile.id] = nil
end
end
function store_order(stockpile, order_number)
local name = reaction_list[order_number].name
-- print("Setting stockpile #"..stockpile.stockpile_number.." to "..name.." (#"..order_number..")")
@ -1011,12 +1087,6 @@ function order_quantity(order, quantity)
end
end
if amount > 30 then
-- Respect the quantity limit.
-- With this many in the queue, we can wait for the next cycle.
return 30
end
return amount
end
@ -1026,7 +1096,10 @@ function create_orders(order, amount)
new_order.amount_left = amount
new_order.amount_total = amount
-- Todo: Create in a validated state if the fortress is small enough?
new_order.is_validated = 0
new_order.status.validated = false
new_order.status.active = false
new_order.id = df.global.world.manager_order_next_id
df.global.world.manager_order_next_id = df.global.world.manager_order_next_id + 1
df.global.world.manager_orders:insert('#', new_order)
end

@ -15,7 +15,7 @@
#include "df/viewscreen_layer_stockpilest.h"
#include "df/viewscreen_layer_militaryst.h"
#include "df/viewscreen_layer_noblelistst.h"
#include "df/viewscreen_layer_workshop_profilest.h"
#include "df/viewscreen_workshop_profilest.h"
#include "df/viewscreen_topicmeeting_fill_land_holder_positionsst.h"
#include "df/viewscreen_tradegoodsst.h"
#include "df/viewscreen_unitlistst.h"
@ -1160,6 +1160,9 @@ private:
if (!in_entry_mode())
{
// Changing screens, reset search
int32_t *cursor_pos = get_viewscreen_cursor();
if (cursor_pos && *cursor_pos < 0)
*cursor_pos = 0;
clear_search();
reset_all();
}
@ -1654,11 +1657,16 @@ IMPLEMENT_HOOKS(df::viewscreen_layer_noblelistst, nobles_search);
//
// START: Workshop profiles search list
//
typedef layered_search<df::viewscreen_layer_workshop_profilest, df::unit*, 0> profiles_search_base;
typedef search_generic<df::viewscreen_workshop_profilest, df::unit*> profiles_search_base;
class profiles_search : public profiles_search_base
{
public:
bool can_init (df::viewscreen_workshop_profilest *screen)
{
return screen->tab == df::viewscreen_workshop_profilest::T_tab::Workers;
}
string get_element_description(df::unit *element) const
{
return get_unit_description(element);
@ -1666,16 +1674,21 @@ public:
void render() const
{
print_search_option(2, 23);
print_search_option(2, gps->dimy - 5);
}
vector<df::unit *> *get_primary_list()
{
return &viewscreen->workers;
}
int32_t *get_viewscreen_cursor()
{
return &viewscreen->worker_idx;
}
};
IMPLEMENT_HOOKS(df::viewscreen_layer_workshop_profilest, profiles_search);
IMPLEMENT_HOOKS(df::viewscreen_workshop_profilest, profiles_search);
//
// END: Workshop profiles search list

@ -16,7 +16,6 @@
#include "df/viewscreen_joblistst.h"
#include "df/viewscreen_unitlistst.h"
#include "df/viewscreen_layer_militaryst.h"
#include "df/viewscreen_layer_workshop_profilest.h"
#include "df/viewscreen_layer_noblelistst.h"
#include "df/viewscreen_layer_overall_healthst.h"
#include "df/viewscreen_layer_assigntradest.h"
@ -24,6 +23,7 @@
#include "df/viewscreen_dwarfmodest.h"
#include "df/viewscreen_petst.h"
#include "df/viewscreen_storesst.h"
#include "df/viewscreen_workshop_profilest.h"
#include "df/layer_object_listst.h"
#include "df/assign_trade_status.h"
@ -310,23 +310,6 @@ DEFINE_SORT_HANDLER(unit_sorters, layer_military, "/Positions/Candidates", milit
}
/*
* Sort units in the workshop 'q'uery 'P'rofile modification screen.
*/
DEFINE_SORT_HANDLER(unit_sorters, layer_workshop_profile, "/Unit", profile)
{
auto list1 = getLayerList(profile, 0);
PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, profile->workers))
{
reorder_cursor(&list1->cursor, order);
reorder_vector(&profile->workers, order);
}
}
DEFINE_SORT_HANDLER(unit_sorters, layer_noblelist, "/Appoint", nobles)
{
auto list2 = getLayerList(nobles, 1);
@ -443,6 +426,21 @@ DEFINE_SORT_HANDLER(unit_sorters, dwarfmode, "/QueryBuilding/Some/Assign", scree
}
}
/*
* Sort units in the workshop 'q'uery 'P'rofile modification screen.
*/
DEFINE_SORT_HANDLER(unit_sorters, workshop_profile, "/Unit", profile)
{
PARSE_SPEC("units", parameters);
if (compute_order(*pout, L, top, &order, profile->workers))
{
reorder_cursor(&profile->worker_idx, order);
reorder_vector(&profile->workers, order);
}
}
/*
* Sort pen assignment candidate units in 'z'->'N'.
*/

@ -885,10 +885,10 @@ command_result df_strangemood (color_ostream &out, vector <string> & parameters)
case job_skill::WEAVING:
case job_skill::CLOTHESMAKING:
filter = NULL;
// TODO: do proper search through world->items.other[items_other_id::ANY_GENERIC35] for item_type CLOTH, mat_type 0, flags2.deep_material, and min_dimension 10000
for (size_t i = 0; i < world->items.other[items_other_id::ANY_GENERIC35].size(); i++)
// TODO: do proper search through world->items.other[items_other_id::ANY_GENERIC36] for item_type CLOTH, mat_type 0, flags2.deep_material, and min_dimension 10000
for (size_t i = 0; i < world->items.other[items_other_id::ANY_GENERIC36].size(); i++)
{
filter = world->items.other[items_other_id::ANY_GENERIC35][i];
filter = world->items.other[items_other_id::ANY_GENERIC36][i];
if (filter->getType() != item_type::CLOTH)
{
filter = NULL;
@ -973,10 +973,10 @@ command_result df_strangemood (color_ostream &out, vector <string> & parameters)
case job_skill::FORGE_FURNITURE:
case job_skill::METALCRAFT:
filter = NULL;
// TODO: do proper search through world->items.other[items_other_id::ANY_GENERIC35] for item_type BAR, mat_type 0, and flags2.deep_material
for (size_t i = 0; i < world->items.other[items_other_id::ANY_GENERIC35].size(); i++)
// TODO: do proper search through world->items.other[items_other_id::ANY_GENERIC36] for item_type BAR, mat_type 0, and flags2.deep_material
for (size_t i = 0; i < world->items.other[items_other_id::ANY_GENERIC36].size(); i++)
{
filter = world->items.other[items_other_id::ANY_GENERIC35][i];
filter = world->items.other[items_other_id::ANY_GENERIC36][i];
if (filter->getType() != item_type::BAR)
{
filter = NULL;

@ -93,7 +93,6 @@
#include "tweaks/kitchen-keys.h"
#include "tweaks/kitchen-prefs-color.h"
#include "tweaks/kitchen-prefs-empty.h"
#include "tweaks/manager-quantity.h"
#include "tweaks/max-wheelbarrow.h"
#include "tweaks/military-assign.h"
#include "tweaks/nestbox-color.h"
@ -212,8 +211,6 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector <Plugi
" Changes color of enabled items to green in kitchen preferences\n"
" tweak kitchen-prefs-empty [disable]\n"
" Fixes a layout issue with empty kitchen tabs (bug 9000)\n"
" tweak manager-quantity [disable]\n"
" Removes the limit of 30 jobs per manager order\n"
" tweak max-wheelbarrow [disable]\n"
" Allows assigning more than 3 wheelbarrows to a stockpile\n"
" tweak nestbox-color [disable]\n"
@ -280,8 +277,6 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector <Plugi
TWEAK_HOOK("kitchen-prefs-empty", kitchen_prefs_empty_hook, render);
TWEAK_HOOK("manager-quantity", manager_quantity_hook, feed);
TWEAK_HOOK("max-wheelbarrow", max_wheelbarrow_hook, render);
TWEAK_HOOK("max-wheelbarrow", max_wheelbarrow_hook, feed);

@ -1,53 +0,0 @@
#include <stdexcept>
#include "df/viewscreen_createquotast.h"
#include "df/manager_order.h"
using df::global::world;
struct manager_quantity_hook : df::viewscreen_createquotast {
typedef df::viewscreen_createquotast interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key>* input))
{
bool cancel = false;
bool wanted_quantity = want_quantity;
if (want_quantity)
{
for (auto it = input->begin(); it != input->end(); ++it)
{
char c = DFHack::Screen::keyToChar(*it);
if (c >= '0' && c <= '9')
{
cancel = true;
size_t len = strlen(str_filter);
if (len < 5)
{
str_filter[len] = c;
str_filter[len + 1] = '\0';
}
}
}
}
if (cancel)
return;
// Native feed() adds manager order, updates want_quantity, and removes SELECT from input
int select = input->count(df::interface_key::SELECT);
INTERPOSE_NEXT(feed)(input);
if (wanted_quantity && select && strlen(str_filter) > 0)
{
df::manager_order* order = world->manager_orders[world->manager_orders.size() - 1];
int16_t count = 1;
try
{
count = std::stoi(str_filter);
}
catch (...) { }
if (count < 1)
count = 1;
order->amount_total = count;
order->amount_left = count;
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(manager_quantity_hook, feed);

@ -1 +1 @@
Subproject commit 26c60013223e02b5558e31bed8e0625495430995
Subproject commit 704aed4447f27ae802dae6994479ebc9c46568cc

@ -1604,7 +1604,7 @@ local function find_process_jobs()
if dwarfmode_to_top() and dfhack.internal.getAddress('cursor') then
local cursor = df.global.T_cursor:new()
addr = zone:find_interactive([[
Searching for process_jobs. Please position the cursor to the right
Searching for process_jobs. Please position the cursor to the left
of at least 10 vacant natural floor tiles.]],
'int8_t',
function(idx)
@ -1660,7 +1660,7 @@ local function find_process_dig()
if dwarfmode_to_top() and dfhack.internal.getAddress('cursor') then
local cursor = df.global.T_cursor:new()
addr = zone:find_interactive([[
Searching for process_dig. Please position the cursor to the right
Searching for process_dig. Please position the cursor to the left
of at least 10 unmined, unrevealed tiles.]],
'int8_t',
function(idx)

@ -47,6 +47,7 @@ versions = {
[1360] = "0.31.23",
[1361] = "0.31.24",
[1362] = "0.31.25",
[1372] = "0.34.01",
[1374] = "0.34.02",
[1376] = "0.34.03",
@ -58,6 +59,7 @@ versions = {
[1402] = "0.34.09",
[1403] = "0.34.10",
[1404] = "0.34.11",
[1441] = "0.40.01",
[1442] = "0.40.02",
[1443] = "0.40.03",
@ -82,12 +84,16 @@ versions = {
[1479] = "0.40.22",
[1480] = "0.40.23",
[1481] = "0.40.24",
[1531] = "0.42.01",
[1532] = "0.42.02",
[1533] = "0.42.03",
[1534] = "0.42.04",
[1537] = "0.42.05",
[1542] = "0.42.06",
[1551] = "0.43.01",
[1552] = "0.43.02",
}
min_version = math.huge

@ -0,0 +1,106 @@
-- Add Elven diplomats to negotiate tree caps
--[[=begin
fix/diplomats
=============
Adds a Diplomat position to all Elven civilizations, allowing them to negotiate
tree cutting quotas - and you to violate them and start wars.
This was vanilla behaviour until ``0.31.12``, in which the "bug" was "fixed".
=end]]
function update_pos(ent)
local pos = df.entity_position:new()
ent.positions.own:insert('#', pos)
pos.code = "DIPLOMAT"
pos.id = ent.positions.next_position_id + 1
ent.positions.next_position_id = ent.positions.next_position_id + 1
pos.flags.DO_NOT_CULL = true
pos.flags.MENIAL_WORK_EXEMPTION = true
pos.flags.SLEEP_PRETENSION = true
pos.flags.PUNISHMENT_EXEMPTION = true
pos.flags.ACCOUNT_EXEMPT = true
pos.flags.DUTY_BOUND = true
pos.flags.COLOR = true
pos.flags.HAS_RESPONSIBILITIES = true
pos.flags.IS_DIPLOMAT = true
pos.flags.IS_LEADER = true
-- not sure what these flags do, but the game sets them for a valid diplomat
pos.flags.unk_12 = true
pos.flags.unk_1a = true
pos.flags.unk_1b = true
pos.name[0] = "Diplomat"
pos.name[1] = "Diplomats"
pos.precedence = 70
pos.color[0] = 7
pos.color[1] = 0
pos.color[2] = 1
return pos
end
local checked = 0
local fixed = 0
for _,ent in pairs(df.global.world.entities.all) do
if ent.type == df.historical_entity_type.Civilization and ent.entity_raw.flags.TREE_CAP_DIPLOMACY then
checked = checked + 1
update = true
local found_position
-- see if we need to add a new position or modify an existing one
for _,pos in pairs(ent.positions.own) do
if pos.responsibilities.MAKE_INTRODUCTIONS and
pos.responsibilities.MAKE_PEACE_AGREEMENTS and
pos.responsibilities.MAKE_TOPIC_AGREEMENTS then
-- a diplomat position exists with the proper responsibilities - skip to the end
update = false
found_position=pos
break
end
-- Diplomat position already exists, but has the wrong options - modify it instead of creating a new one
if pos.code == "DIPLOMAT" then
found_position=pos
break
end
end
if update then
-- either there's no diplomat, or there is one and it's got the wrong responsibilities
if not found_position then
found_position = add_guild_rep( ent )
end
-- assign responsibilities
found_position.responsibilities.MAKE_INTRODUCTIONS = true
found_position.responsibilities.MAKE_PEACE_AGREEMENTS = true
found_position.responsibilities.MAKE_TOPIC_AGREEMENTS = true
end
-- make sure the diplomat position, whether we created it or not, is set up for proper assignment
local assign = true
for _,p in pairs(ent.positions.assignments) do
if p.position_id == found_position.id then -- it is - nothing more to do here
assign = false
break
end
end
if assign then -- it isn't - set it up
local asn = df.entity_position_assignment:new()
ent.positions.assignments:insert('#', asn);
asn.id = ent.positions.next_assignment_id
ent.positions.next_assignment_id = asn.id + 1
asn.position_id = found_position.id
asn.flags:resize(math.max(32, #asn.flags)) -- make room for 32 flags
asn.flags[0] = true -- and set the first one
end
if update or assign then
fixed = fixed + 1
end
end
end
print("Enabled tree cap diplomacy for "..fixed.." of "..checked.." civilizations.")

@ -0,0 +1,104 @@
-- Allow humans to make trade agreements
--[[=begin
fix/merchants
=============
Adds the Guild Representative position to all Human civilizations,
allowing them to make trade agreements. This was the default behaviour in
``0.28.181.40d`` and earlier.
=end]]
function add_guild_rep(ent)
-- there was no guild rep - create it
local pos = df.entity_position:new()
ent.positions.own:insert('#', pos)
pos.code = "GUILD_REPRESENTATIVE"
pos.id = ent.positions.next_position_id + 1
ent.positions.next_position_id = ent.positions.next_position_id + 1
pos.flags.DO_NOT_CULL = true
pos.flags.MENIAL_WORK_EXEMPTION = true
pos.flags.SLEEP_PRETENSION = true
pos.flags.PUNISHMENT_EXEMPTION = true
pos.flags.ACCOUNT_EXEMPT = true
pos.flags.DUTY_BOUND = true
pos.flags.COLOR = true
pos.flags.HAS_RESPONSIBILITIES = true
pos.flags.IS_DIPLOMAT = true
pos.flags.IS_LEADER = true
-- not sure what these flags do, but the game sets them for a valid guild rep
pos.flags.unk_12 = true
pos.flags.unk_1a = true
pos.flags.unk_1b = true
pos.name[0] = "Guild Representative"
pos.name[1] = "Guild Representatives"
pos.precedence = 40
pos.color[0] = 7
pos.color[1] = 0
pos.color[2] = 1
return pos
end
local checked = 0
local fixed = 0
for _,ent in pairs(df.global.world.entities.all) do
if ent.type == df.historical_entity_type.Civilization and ent.entity_raw.flags.MERCHANT_NOBILITY then
checked = checked + 1
update = true
-- see if we need to add a new position or modify an existing one
local found_position
for _,pos in pairs(ent.positions.own) do
if pos.responsibilities.TRADE and pos.responsibilities.ESTABLISH_COLONY_TRADE_AGREEMENTS then
-- a guild rep exists with the proper responsibilities - skip to the end
update = false
found_position=pos
break
end
-- Guild Representative position already exists, but has the wrong options - modify it instead of creating a new one
if pos.code == "GUILD_REPRESENTATIVE" then
found_position=pos
break
end
end
if update then
-- either there's no guild rep, or there is one and it's got the wrong responsibilities
if not found_position then
found_position = add_guild_rep(ent)
end
-- assign responsibilities
found_position.responsibilities.ESTABLISH_COLONY_TRADE_AGREEMENTS = true
found_position.responsibilities.TRADE=true
end
-- make sure the guild rep position, whether we created it or not, is set up for proper assignment
local assign = true
for _,p in pairs(ent.positions.assignments) do
if p.position_id == found_position.id then -- it is - nothing more to do here
assign = false
break
end
end
if assign then
-- it isn't - set it up
local asn = df.entity_position_assignment:new()
ent.positions.assignments:insert('#', asn)
asn.id = ent.positions.next_assignment_id
ent.positions.next_assignment_id = asn.id + 1
asn.position_id = found_position.id
asn.flags:resize(math.max(32, #asn.flags)) -- make room for 32 flags
asn.flags[0] = true -- and set the first one
end
if update or assign then
fixed = fixed + 1
end
end
end
print("Added merchant nobility for "..fixed.." of "..checked.." civilizations.")

@ -117,7 +117,7 @@ local function getMatFilter(itemtype)
return itemTypes[df.item_type[itemtype]] or getRestrictiveMatFilter(itemtype)
end
local function createItem(mat,itemType,quality,creator,description)
local function createItem(mat,itemType,quality,creator,description,amount)
local item=df.item.find(dfhack.items.createItem(itemType[1], itemType[2], mat[1], mat[2], creator))
assert(item, 'failed to create item')
quality = math.max(0, math.min(5, quality - 1))
@ -125,6 +125,9 @@ local function createItem(mat,itemType,quality,creator,description)
if df.item_type[itemType[1]]=='SLAB' then
item.description=description
end
if tonumber(amount) > 1 then
item:setStackSize(amount)
end
end
local function qualityTable()
@ -207,11 +210,10 @@ function hackWish(unit)
if not amountok then return end
if mattype and itemtype then
if df.item_type.attrs[itemtype].is_stackable then
local proper_item=df.item.find(dfhack.items.createItem(itemtype, itemsubtype, mattype, matindex, unit))
proper_item:setStackSize(amount)
createItem({mattype,matindex},{itemtype,itemsubtype},quality,unit,description,amount)
else
for i=1,amount do
dfhack.items.createItem(itemtype, itemsubtype, mattype, matindex, unit)
createItem({mattype,matindex},{itemtype,itemsubtype},quality,unit,description,1)
end
end
return true
@ -219,7 +221,7 @@ function hackWish(unit)
return false
else
if mattype and itemtype then
createItem({mattype,matindex},{itemtype,itemsubtype},quality,unit,description)
createItem({mattype,matindex},{itemtype,itemsubtype},quality,unit,description,1)
return true
end
return false

@ -136,7 +136,8 @@ function GmEditorUi:init(args)
subviews={
mainList,
widgets.Label{text={{text="<no item>",id="name"},{gap=1,text="Help",key=keybindings.help.key,key_sep = '()'}}, view_id = 'lbl_current_item',frame = {l=1,t=1,yalign=0}},
widgets.Label{text={{text="Search",key=keybindings.start_filter.key,key_sep = '()'},{text=": "}},frame={l=1,t=2}},
widgets.Label{text={{text="Search",key=keybindings.start_filter.key,key_sep = '()'},{text=": "}},frame={l=1,t=2},
on_click=function() self:enable_input(true) end},
widgets.EditField{frame={l=12,t=2},active=false,on_change=self:callback('text_input'),on_submit=self:callback("enable_input",false),view_id="filter_input"},
--widgets.Label{text="BLAH2"}
}
@ -187,7 +188,7 @@ function GmEditorUi:find(test)
end
end
function GmEditorUi:find_id()
local key = self:getSelectedKey()
local key = tostring(self:getSelectedKey())
local id = tonumber(self:getSelectedValue())
if not id then return end
local opts = {}
@ -212,21 +213,25 @@ end
function GmEditorUi:insertNew(typename)
local tp=typename
if typename == nil then
dialog.showInputPrompt("Class type","Input class type:",COLOR_WHITE,"",self:callback("insertNew"))
dialog.showInputPrompt("Class type","You can:\n * Enter type name (without 'df.')\n * Leave empty for default type and 'nil' value\n * Enter '*' for default type and 'new' constructed pointer value",COLOR_WHITE,"",self:callback("insertNew"))
return
end
local trg=self:currentTarget()
if trg.target and trg.target._kind and trg.target._kind=="container" then
if tp == "" then
trg.target:resize(#trg.target+1)
elseif tp== "*" then
trg.target:insert("#",{new=true})
else
local ntype=df[tp]
if ntype== nil then
dialog.showMessage("Error!","Type '"..tp.." not found",COLOR_LIGHTRED)
dialog.showMessage("Error!","Type '"..tp.." not found",COLOR_RED)
return
end
local trg=self:currentTarget()
if trg.target and trg.target._kind and trg.target._kind=="container" then
local thing=ntype:new()
dfhack.call_with_finalizer(1,false,df.delete,thing,function (tscreen,target,to_insert)
target:insert("#",to_insert); tscreen:updateTarget(true,true);end,self,trg.target,thing)
trg.target:insert("#",{new=ntype})
end
self:updateTarget(true,true)
end
end
function GmEditorUi:deleteSelected(key)

@ -159,7 +159,7 @@ function manager:init(args)
local mods=self.mods
local mlist=dfhack.internal.getDir(mod_dir)
if #mlist==0 then
if mlist==nil or #mlist==0 then
qerror("Mod directory not found! Are you sure it is in:"..mod_dir)
end
for k,v in ipairs(mlist) do

@ -360,6 +360,10 @@ descriptions = {
ITEM_TOOL_FORK_CARVING = {
"A carving fork typically has only two prongs and is exceptionally long.",
"It is used to hold down a piece of cooked meat while using a knife."},
ITEM_TOOL_HELVE = {
"A helve is the handle of a tool such as an axe.",
"It is not useful in this state - but adding a rock makes a stone axe,",
"which can be used for woodcutting in Adventure mode."},
ITEM_TOOL_HIVE = {
"Hives are structures that house colonies of honey bees. To be",
"productive, they need to be constructed on an above-ground tile with",
@ -417,6 +421,9 @@ descriptions = {
ITEM_TOOL_STEPLADDER = {
"A small stepladder. If you have one of these, you can use zones to",
"assign your dwarves to pick fruit from certain trees."},
ITEM_TOOL_STONE_AXE = {
"This tool can be made in Adventure mode, and is used to cut",
"trees for building or carpentry."},
ITEM_TOOL_WHEELBARROW = {
"A small hand-cart with long handles and a single wheel, this",
"wheelbarrow makes heavy hauling jobs much more manageable."},

@ -64,10 +64,19 @@ function processTrigger(command)
dfhack.run_command(table.unpack(command2))
end
function getitemType(item)
if item:getSubtype() ~= -1 then
itemType = dfhack.items.getSubtypeDef(item:getType(),item:getSubtype()).id
else
itemType = df.item_type[item:getType()]
end
return itemType
end
function handler(table)
local itemMat = dfhack.matinfo.decode(table.item)
local itemMatStr = itemMat:getToken()
local itemType = dfhack.items.getSubtypeDef(table.item:getType(),table.item:getSubtype()).id
local itemType = getitemType(table.item)
table.itemMat = itemMat
table.itemType = itemType
@ -177,6 +186,7 @@ arguments:
trigger the command for items of this type
examples:
ITEM_WEAPON_PICK
RING
-onStrike
trigger the command when someone strikes someone with an appropriate weapon
-onEquip
@ -243,6 +253,7 @@ if not args.command then
end
if args.itemType then
if dfhack.items.findType(args.itemType) == -1 then
local temp
for _,itemdef in ipairs(df.global.world.raws.itemdefs.all) do
if itemdef.id == args.itemType then
@ -255,6 +266,7 @@ if args.itemType then
end
args.itemType = temp
end
end
local numConditions = (args.material and 1 or 0) + (args.itemType and 1 or 0) + (args.contaminant and 1 or 0)
if numConditions > 1 then