Merge remote-tracking branch 'lethosor/update-falconne-plugins' into develop

develop
expwnent 2014-11-15 16:50:09 -05:00
commit 1aeb4e5769
9 changed files with 630 additions and 33 deletions

12
NEWS

@ -4,6 +4,8 @@ DFHack Future
EventManager should no longer trigger REPORT events for old reports after loading a save.
lua/persist-table.lua: a convenient way of using persistent tables of arbitrary structure and dimension in Lua
Fixes:
mousequery: Disabled when linking levers
stocks: Melting should work now
scripts/full-heal: Updated with proper argument handling.
scripts/modtools/reaction-trigger-transition.lua
should produce the correct syntax now
@ -23,6 +25,14 @@ DFHack Future
devel/
all-bob.lua: renames everyone Bob to help test interaction-trigger
Misc improvements:
autodump:
Can now mark a stockpile for auto-dumping (similar to
automelt and autotrade)
buildingplan:
Can now auto-allocate rooms to dwarves with specific positions
(e.g. expedition leader, mayor)
dwarfmonitor
Now displays a weather indicator and date
lua/syndrome-util.lua
now you can remove syndromes by SYN_CLASS
scripts/modtools/add-syndrome.lua
@ -246,7 +256,7 @@ DFHack v0.34.11-r5
New plugins:
- rendermax: replace the renderer with something else. Most interesting is "rendermax light"- a lighting engine for df.
- stockflow (by eswald): queues manager jobs of the configured type based on the state of a stockpile.
- automelt: allows marking stockpiles for automelt (i.e. any items placed in stocpile will be designated for melting)
- embark-tools: implementations of Embark Anywhere, Nano Embark, and a few other embark-related utilities
- building-hacks: Allows to add custom functionality and/or animations to buildings.
- petcapRemover: triggers pregnancies in creatures so that you can effectively raise the default pet population cap from the default 50

@ -2859,6 +2859,36 @@ globally active hotkey in dfhack.init, e.g.::
keybinding add Ctrl-F1 hotkeys
Hotkeys
=======
Opens an in-game screen showing DFHack keybindings that are valid in the current mode.
.. image:: images/hotkeys.png
Type ``hotkeys`` into the DFHack console to open the screen, or bind the command to a
globally active hotkey in dfhack.init, e.g.:
``keybinding add Ctrl-F1 hotkeys``
Stockpile Automation
====================
Enable the autodump plugin in your dfhack.init with
``enable autodump``
When querying a stockpile an option will appear to toggle autodump for this stockpile.
Any items placed in this stockpile will be designated to be dumped.
Stockpile Automation
====================
Enable the automelt plugin in your dfhack.init with
``enable automelt``
When querying a stockpile an option will appear to toggle automelt for this stockpile.
Any items placed in this stockpile will be designated to be melted.
gui/liquids
===========

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

@ -24,16 +24,253 @@ using namespace std;
#include "df/item.h"
#include "df/world.h"
#include "df/general_ref.h"
#include "df/viewscreen_dwarfmodest.h"
#include "df/building_stockpilest.h"
#include "uicommon.h"
using namespace DFHack;
using namespace df::enums;
using MapExtras::Block;
using MapExtras::MapCache;
using df::global::world;
using df::building_stockpilest;
DFHACK_PLUGIN("autodump");
// Stockpile interface START
static const string PERSISTENCE_KEY = "autodump/stockpiles";
static void mark_all_in_stockpiles(vector<PersistentStockpileInfo> &stockpiles)
{
std::vector<df::item*> &items = world->items.other[items_other_id::IN_PLAY];
// Precompute a bitmask with the bad flags
df::item_flags bad_flags;
bad_flags.whole = 0;
#define F(x) bad_flags.bits.x = true;
F(dump); F(forbid); F(garbage_collect);
F(hostile); F(on_fire); F(rotten); F(trader);
F(in_building); F(construction); F(artifact);
F(spider_web); F(owned); F(in_job);
#undef F
size_t marked_count = 0;
for (size_t i = 0; i < items.size(); i++)
{
df::item *item = items[i];
if (item->flags.whole & bad_flags.whole)
continue;
for (auto it = stockpiles.begin(); it != stockpiles.end(); it++)
{
if (!it->inStockpile(item))
continue;
++marked_count;
item->flags.bits.dump = true;
}
}
if (marked_count)
Gui::showAnnouncement("Marked " + int_to_string(marked_count) + " items to dump", COLOR_GREEN, false);
}
class StockpileMonitor
{
public:
bool isMonitored(df::building_stockpilest *sp)
{
for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end(); it++)
{
if (it->matches(sp))
return true;
}
return false;
}
void add(df::building_stockpilest *sp)
{
auto pile = PersistentStockpileInfo(sp, PERSISTENCE_KEY);
if (pile.isValid())
{
monitored_stockpiles.push_back(PersistentStockpileInfo(pile));
monitored_stockpiles.back().save();
}
}
void remove(df::building_stockpilest *sp)
{
for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end(); it++)
{
if (it->matches(sp))
{
it->remove();
monitored_stockpiles.erase(it);
break;
}
}
}
void doCycle()
{
for (auto it = monitored_stockpiles.begin(); it != monitored_stockpiles.end();)
{
if (!it->isValid())
it = monitored_stockpiles.erase(it);
else
++it;
}
mark_all_in_stockpiles(monitored_stockpiles);
}
void reset()
{
monitored_stockpiles.clear();
std::vector<PersistentDataItem> items;
DFHack::World::GetPersistentData(&items, PERSISTENCE_KEY);
for (auto i = items.begin(); i != items.end(); i++)
{
auto pile = PersistentStockpileInfo(*i, PERSISTENCE_KEY);
if (pile.load())
monitored_stockpiles.push_back(PersistentStockpileInfo(pile));
else
pile.remove();
}
}
private:
vector<PersistentStockpileInfo> monitored_stockpiles;
};
static StockpileMonitor monitor;
#define DELTA_TICKS 620
DFhackCExport command_result plugin_onupdate ( color_ostream &out )
{
if(!Maps::IsValid())
return CR_OK;
static decltype(world->frame_counter) last_frame_count = 0;
if (DFHack::World::ReadPauseState())
return CR_OK;
if (world->frame_counter - last_frame_count < DELTA_TICKS)
return CR_OK;
last_frame_count = world->frame_counter;
monitor.doCycle();
return CR_OK;
}
struct dump_hook : public df::viewscreen_dwarfmodest
{
typedef df::viewscreen_dwarfmodest interpose_base;
bool handleInput(set<df::interface_key> *input)
{
building_stockpilest *sp = get_selected_stockpile();
if (!sp)
return false;
if (input->count(interface_key::CUSTOM_SHIFT_D))
{
if (monitor.isMonitored(sp))
monitor.remove(sp);
else
monitor.add(sp);
}
return false;
}
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
if (!handleInput(input))
INTERPOSE_NEXT(feed)(input);
}
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
INTERPOSE_NEXT(render)();
building_stockpilest *sp = get_selected_stockpile();
if (!sp)
return;
auto dims = Gui::getDwarfmodeViewDims();
int left_margin = dims.menu_x1 + 1;
int x = left_margin;
int y = dims.y2 - 7;
int links = 0;
links += sp->links.give_to_pile.size();
links += sp->links.take_from_pile.size();
links += sp->links.give_to_workshop.size();
links += sp->links.take_from_workshop.size();
bool state = monitor.isMonitored(sp);
if (links + 12 >= y) {
y = dims.y2;
OutputString(COLOR_WHITE, x, y, "Auto: ");
OutputString(COLOR_LIGHTRED, x, y, "D");
OutputString(state? COLOR_LIGHTGREEN: COLOR_GREY, x, y, "ump ");
} else {
OutputToggleString(x, y, "Auto dump", "D", state, true, left_margin, COLOR_WHITE, COLOR_LIGHTRED);
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(dump_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(dump_hook, render);
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
{
switch (event)
{
case DFHack::SC_MAP_LOADED:
monitor.reset();
break;
case DFHack::SC_MAP_UNLOADED:
break;
default:
break;
}
return CR_OK;
}
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_enable(color_ostream &out, bool enable)
{
if (!gps)
return CR_FAILURE;
if (enable != is_enabled)
{
if (!INTERPOSE_HOOK(dump_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(dump_hook, render).apply(enable))
return CR_FAILURE;
is_enabled = enable;
}
return CR_OK;
}
// Stockpile interface END
command_result df_autodump(color_ostream &out, vector <string> & parameters);
command_result df_autodump_destroy_here(color_ostream &out, vector <string> & parameters);
command_result df_autodump_destroy_item(color_ostream &out, vector <string> & parameters);

@ -233,10 +233,11 @@ struct melt_hook : public df::viewscreen_dwarfmodest
links += sp->links.give_to_workshop.size();
links += sp->links.take_from_workshop.size();
bool state = monitor.isMonitored(sp);
if (links + 12 >= y) {
y = dims.y2;
OutputString(COLOR_WHITE, x, y, "Auto: ");
x += 5;
OutputString(COLOR_LIGHTRED, x, y, "M");
OutputString(state? COLOR_LIGHTGREEN: COLOR_GREY, x, y, "elt ");
} else {

@ -432,7 +432,7 @@ struct trade_hook : public df::viewscreen_dwarfmodest
if (links + 12 >= y) {
y = dims.y2;
OutputString(COLOR_WHITE, x, y, "Auto: ");
x += 5;
x += 11;
OutputString(COLOR_LIGHTRED, x, y, "T");
OutputString(state? COLOR_LIGHTGREEN: COLOR_GREY, x, y, "rade ");
} else {

@ -16,11 +16,13 @@
#include "df/world.h"
#include "df/building_constructionst.h"
#include "df/building_design.h"
#include "df/entity_position.h"
#include "modules/Gui.h"
#include "modules/Buildings.h"
#include "modules/Maps.h"
#include "modules/Items.h"
#include "modules/Units.h"
#include "TileTypes.h"
#include "df/job_item.h"
@ -38,7 +40,7 @@ using df::global::ui_build_selector;
using df::global::world;
DFHACK_PLUGIN("buildingplan");
#define PLUGIN_VERSION 0.12
#define PLUGIN_VERSION 0.14
struct MaterialDescriptor
{
@ -461,6 +463,245 @@ private:
static void delete_item_fn(df::job_item *x) { delete x; }
// START Room Reservation
static bool canReserveRoom(df::building *building)
{
if (!building)
return false;
if (building->jobs.size() > 0 && building->jobs[0]->job_type == job_type::DestroyBuilding)
return false;
return building->is_room;
}
static std::vector<Units::NoblePosition> getUniqueNoblePositions(df::unit *unit)
{
std::vector<Units::NoblePosition> np;
Units::getNoblePositions(&np, unit);
for (auto iter = np.begin(); iter != np.end(); iter++)
{
if (iter->position->code == "MILITIA_CAPTAIN")
{
np.erase(iter);
break;
}
}
return np;
}
class ReservedRoom
{
public:
ReservedRoom(df::building *building, string noble_code)
{
this->building = building;
config = DFHack::World::AddPersistentData("buildingplan/reservedroom");
config.val() = noble_code;
config.ival(1) = building->id;
pos = df::coord(building->centerx, building->centery, building->z);
}
ReservedRoom(PersistentDataItem &config, color_ostream &out)
{
this->config = config;
building = df::building::find(config.ival(1));
if (!building)
return;
pos = df::coord(building->centerx, building->centery, building->z);
}
void remove()
{
DFHack::World::DeletePersistentData(config);
}
bool isValid()
{
if (!building)
return false;
if (Buildings::findAtTile(pos) != building)
return false;
return canReserveRoom(building);
}
int32_t getId()
{
if (!isValid())
return 0;
return building->id;
}
string getCode()
{
return config.val();
}
void setCode(const string &noble_code)
{
config.val() = noble_code;
}
bool checkRoomAssignment()
{
if (!isValid())
return false;
auto np = getOwnersNobleCode();
bool correctOwner = false;
for (auto iter = np.begin(); iter != np.end(); iter++)
{
if (iter->position->code == getCode())
{
correctOwner = true;
break;
}
}
if (correctOwner)
return true;
for (auto iter = world->units.active.begin(); iter != world->units.active.end(); iter++)
{
df::unit* unit = *iter;
if (!Units::isCitizen(unit))
continue;
if (DFHack::Units::isDead(unit))
continue;
np = getUniqueNoblePositions(unit);
for (auto iter = np.begin(); iter != np.end(); iter++)
{
if (iter->position->code == getCode())
{
Buildings::setOwner(building, unit);
break;
}
}
}
return true;
}
private:
df::building *building;
PersistentDataItem config;
df::coord pos;
std::vector<Units::NoblePosition> getOwnersNobleCode()
{
if (!building->owner)
return std::vector<Units::NoblePosition> ();
return getUniqueNoblePositions(building->owner);
}
};
class RoomMonitor
{
public:
RoomMonitor()
{
}
string getReservedNobleCode(int32_t buildingId)
{
for (auto iter = reservedRooms.begin(); iter != reservedRooms.end(); iter++)
{
if (buildingId == iter->getId())
return iter->getCode();
}
return "";
}
void toggleRoomForPosition(int32_t buildingId, string noble_code)
{
bool found = false;
for (auto iter = reservedRooms.begin(); iter != reservedRooms.end(); iter++)
{
if (buildingId != iter->getId())
{
continue;
}
else
{
if (noble_code == iter->getCode())
{
iter->remove();
reservedRooms.erase(iter);
}
else
{
iter->setCode(noble_code);
}
found = true;
break;
}
}
if (!found)
{
ReservedRoom room(df::building::find(buildingId), noble_code);
reservedRooms.push_back(room);
}
}
void doCycle()
{
for (auto iter = reservedRooms.begin(); iter != reservedRooms.end();)
{
if (iter->checkRoomAssignment())
{
++iter;
}
else
{
iter->remove();
iter = reservedRooms.erase(iter);
}
}
}
void reset(color_ostream &out)
{
reservedRooms.clear();
std::vector<PersistentDataItem> items;
DFHack::World::GetPersistentData(&items, "buildingplan/reservedroom");
for (auto i = items.begin(); i != items.end(); i++)
{
ReservedRoom rr(*i, out);
if (rr.isValid())
addRoom(rr);
}
}
private:
vector<ReservedRoom> reservedRooms;
void addRoom(ReservedRoom &rr)
{
for (auto iter = reservedRooms.begin(); iter != reservedRooms.end(); iter++)
{
if (iter->getId() == rr.getId())
return;
}
reservedRooms.push_back(rr);
}
};
static RoomMonitor roomMonitor;
// START Planning
class PlannedBuilding
{
@ -896,6 +1137,7 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out)
{
last_frame_count = world->frame_counter;
planner.doCycle();
roomMonitor.doCycle();
}
return CR_OK;
@ -929,6 +1171,37 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest
planner.isPlanableBuilding(ui_build_selector->building_type);
}
std::vector<Units::NoblePosition> getNoblePositionOfSelectedBuildingOwner()
{
std::vector<Units::NoblePosition> np;
if (ui->main.mode != df::ui_sidebar_mode::QueryBuilding ||
!world->selected_building ||
!world->selected_building->owner)
{
return np;
}
switch (world->selected_building->getType())
{
case building_type::Bed:
case building_type::Chair:
case building_type::Table:
break;
default:
return np;
}
return getUniqueNoblePositions(world->selected_building->owner);
}
bool isInNobleRoomQueryMode()
{
if (getNoblePositionOfSelectedBuildingOwner().size() > 0)
return canReserveRoom(world->selected_building);
else
return false;
}
bool handleInput(set<df::interface_key> *input)
{
if (isInPlannedBuildingPlacementMode())
@ -1020,6 +1293,19 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest
}
}
else if (isInNobleRoomQueryMode())
{
auto np = getNoblePositionOfSelectedBuildingOwner();
df::interface_key last_token = *input->rbegin();
if (last_token >= interface_key::STRING_A048 && last_token <= interface_key::STRING_A058)
{
int selection = last_token - interface_key::STRING_A048;
if (np.size() < selection)
return false;
roomMonitor.toggleRoomForPosition(world->selected_building->id, np.at(selection-1).position->code);
return true;
}
}
return false;
}
@ -1130,6 +1416,20 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest
for (auto it = filters.begin(); it != filters.end(); ++it)
OutputString(COLOR_BLUE, x, y, "*" + *it, true, left_margin);
}
else if (isInNobleRoomQueryMode())
{
auto np = getNoblePositionOfSelectedBuildingOwner();
int y = 24;
OutputString(COLOR_BROWN, x, y, "DFHack", true, left_margin);
OutputString(COLOR_WHITE, x, y, "Auto-allocate to:", true, left_margin);
for (int i = 0; i < np.size() && i < 9; i++)
{
bool enabled = (roomMonitor.getReservedNobleCode(world->selected_building->id)
== np[i].position->code);
OutputToggleString(x, y, np[i].position->name[0].c_str(),
int_to_string(i+1).c_str(), enabled, true, left_margin);
}
}
else
{
planner.in_dummmy_screen = false;
@ -1198,6 +1498,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan
switch (event) {
case SC_MAP_LOADED:
planner.reset(out);
roomMonitor.reset(out);
break;
default:
break;

@ -48,7 +48,7 @@ using df::global::ui;
typedef int16_t activity_type;
#define PLUGIN_VERSION 0.8
#define PLUGIN_VERSION 0.9
#define DAY_TICKS 1200
#define DELTA_TICKS 100
@ -66,6 +66,7 @@ struct less_second {
static bool monitor_jobs = false;
static bool monitor_misery = true;
static bool monitor_date = true;
static map<df::unit *, deque<activity_type>> work_history;
static int misery[] = { 0, 0, 0, 0, 0, 0, 0 };
@ -1689,24 +1690,49 @@ struct dwarf_monitor_hook : public df::viewscreen_dwarfmodest
{
INTERPOSE_NEXT(render)();
if (monitor_misery && Maps::IsValid())
if (Maps::IsValid())
{
string entries[7];
size_t length = 9;
for (int i = 0; i < 7; i++)
if (monitor_misery)
{
entries[i] = int_to_string(misery[i]);
length += entries[i].length();
string entries[7];
size_t length = 9;
for (int i = 0; i < 7; i++)
{
entries[i] = int_to_string(misery[i]);
length += entries[i].length();
}
int x = gps->dimx - length;
int y = gps->dimy - 1;
OutputString(COLOR_WHITE, x, y, "H:");
for (int i = 0; i < 7; i++)
{
OutputString(monitor_colors[i], x, y, entries[i]);
if (i < 6)
OutputString(COLOR_WHITE, x, y, "/");
}
}
int x = gps->dimx - length;
int y = gps->dimy - 1;
OutputString(COLOR_WHITE, x, y, "H:");
for (int i = 0; i < 7; i++)
if (monitor_date)
{
OutputString(monitor_colors[i], x, y, entries[i]);
if (i < 6)
OutputString(COLOR_WHITE, x, y, "/");
int x = gps->dimx - 30;
int y = 0;
ostringstream date_str;
auto month = World::ReadCurrentMonth();
auto day = World::ReadCurrentDay();
date_str << "Date:" << World::ReadCurrentYear() << "/" <<
((month < 10) ? "0" : "") << month << "/" <<
((day < 10) ? "0" : "") << day;
OutputString(COLOR_GREY, x, y, date_str.str());
x = 1;
y = gps->dimy - 1;
if (World::ReadCurrentWeather() == weather_type::Rain)
OutputString(COLOR_LIGHTBLUE, x, y, "Rain");
else if (World::ReadCurrentWeather() == weather_type::Snow)
OutputString(COLOR_WHITE, x, y, "Snow");
}
}
}
@ -1732,12 +1758,16 @@ static bool set_monitoring_mode(const string &mode, const bool &state)
if (!monitor_jobs)
reset();
}
if (mode == "misery" || mode == "all")
else if (mode == "misery" || mode == "all")
{
mode_recognized = true;
monitor_misery = state;
}
else if (mode == "date" || mode == "all")
{
mode_recognized = true;
monitor_date = state;
}
return mode_recognized;
}

@ -410,9 +410,6 @@ struct item_grouped_entry
bool canMelt() const
{
// TODO: Fix melting
return false;
df::item *item = getFirstItem();
if (!item)
return false;
@ -829,9 +826,6 @@ public:
}
else if (input->count(interface_key::CUSTOM_SHIFT_M))
{
//TODO: Fix melting
return;
toggleMelt();
populateItems();
}
@ -939,10 +933,7 @@ public:
OutputString(COLOR_BROWN, x, y, (apply_to_all) ? "Listed" : "Selected", true, left_margin);
OutputHotkeyString(x, y, "Dump", "Shift-D", true, left_margin);
OutputHotkeyString(x, y, "Forbid", "Shift-F", true, left_margin);
//TODO: Fix melting
//OutputHotkeyString(x, y, "Melt", "Shift-M", true, left_margin);
OutputHotkeyString(x, y, "Melt", "Shift-M", true, left_margin);
if (depot_info.canTrade())
OutputHotkeyString(x, y, "Mark for Trade", "Shift-T", true, left_margin);
@ -1031,9 +1022,6 @@ private:
void toggleMelt()
{
//TODO: Fix melting
return;
int set_to_melt = -1;
auto selected = getSelectedItems();
vector<df::item *> items;