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

@ -44,7 +44,7 @@ also broadly maps to the ``df`` namespace in the headers generated for C++.
The wrapper provides almost raw access to the memory
of the game, so mistakes in manipulating objects are as likely to
crash the game as equivalent plain C++ code would be.
eg. NULL pointer access is safely detected, but dangling pointers aren't.
Objects managed by the wrapper can be broadly classified into the following groups:
@ -180,7 +180,7 @@ They implement the following features:
In case of inheritance, *superclass* fields have precedence
over the subclass, but fields shadowed in this way can still
be accessed as ``ref['subclasstype.field']``.
This shadowing order is necessary because vtable-based classes
are automatically exposed in their exact type, and the reverse
rule would make access to superclass fields unreliable.
@ -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
@ -3499,7 +3502,7 @@ Functions
:name:
custom workshop id e.g. ``SOAPMAKER``
.. note:: this is the only mandatory field.
:fix_impassible:
@ -3678,15 +3681,15 @@ Note that this function lets errors propagate to the caller.
Run an Lua script and return its environment.
This command allows you to use scripts like modules for increased portability.
It is highly recommended that if you are a modder you put your custom modules in ``raw/scripts`` and use ``script_environment`` instead of ``require`` so that saves with your mod installed will be self-contained and can be transferred to people who do have DFHack but do not have your mod installed.
You can say ``dfhack.script_environment('add-thought').addEmotionToUnit([arguments go here])`` and it will have the desired effect.
It will call the script in question with the global ``moduleMode`` set to ``true`` so that the script can return early.
This is useful because if the script is called from the console it should deal with its console arguments and if it is called by ``script_environment`` it should only create its global functions and return.
You can also access global variables with, for example ``print(dfhack.script_environment('add-thought').validArgs)``
The function ``script_environment`` is fast enough that it is recommended that you not store its result in a nonlocal variable, because your script might need to load a different version of that script if the save is unloaded and a save with a different mod that overrides the same script with a slightly different functionality is loaded.
This will not be an issue in most cases.
This function also permits circular dependencies of scripts.
* ``dfhack.reqscript(name)`` or ``reqscript(name)``

@ -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 = {}
@ -211,22 +212,26 @@ function GmEditorUi:find_id()
end
function GmEditorUi:insertNew(typename)
local tp=typename
if typename== nil then
dialog.showInputPrompt("Class type","Input class type:",COLOR_WHITE,"",self:callback("insertNew"))
return
end
local ntype=df[tp]
if ntype== nil then
dialog.showMessage("Error!","Type '"..tp.." not found",COLOR_LIGHTRED)
if typename == nil then
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
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)
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_RED)
return
end
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,8 +360,12 @@ 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",
"Hives are structures that house colonies of honey bees. To be",
"productive, they need to be constructed on an above-ground tile with",
"an accessible honey bee colony somewhere on the map. Some time after",
"bees are 'installed' by a beekeeper, the hive will be ready to harvest",
@ -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
@ -254,6 +265,7 @@ if args.itemType then
error 'Could not find item type.'
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)