Merge pull request #352 from eswald/trackstop

New trackstop plugin
develop
expwnent 2014-11-02 03:34:22 -05:00
commit bfac2b97cc
6 changed files with 334 additions and 1 deletions

@ -5,6 +5,9 @@ DFHack future
Fixes: Fixes:
- autotrade: "Mark all" no longer double-marks bin contents - autotrade: "Mark all" no longer double-marks bin contents
New plugins:
- trackstop: Shows track stop friction and dump direction in its 'q' menu
DFHack 0.40.13-r1 DFHack 0.40.13-r1
Internals: Internals:
- unified spatter structs - unified spatter structs

@ -529,6 +529,7 @@ access DF memory and allow for easier development of new tools.</p>
<li><a class="reference internal" href="#search" id="id158">Search</a></li> <li><a class="reference internal" href="#search" id="id158">Search</a></li>
<li><a class="reference internal" href="#automaterial" id="id159">AutoMaterial</a></li> <li><a class="reference internal" href="#automaterial" id="id159">AutoMaterial</a></li>
<li><a class="reference internal" href="#stockpile-automation" id="idautomelt">Stockpile Automation</a></li> <li><a class="reference internal" href="#stockpile-automation" id="idautomelt">Stockpile Automation</a></li>
<li><a class="reference internal" href="#track-stop-menu" id="idtrackstop">Track Stop Menu</a></li>
<li><a class="reference internal" href="#gui-advfort" id="id160">gui/advfort</a></li> <li><a class="reference internal" href="#gui-advfort" id="id160">gui/advfort</a></li>
<li><a class="reference internal" href="#gui-assign-rack" id="id161">gui/assign-rack</a></li> <li><a class="reference internal" href="#gui-assign-rack" id="id161">gui/assign-rack</a></li>
<li><a class="reference internal" href="#gui-choose-weapons" id="id162">gui/choose-weapons</a></li> <li><a class="reference internal" href="#gui-choose-weapons" id="id162">gui/choose-weapons</a></li>
@ -3275,6 +3276,20 @@ enable automelt
<p>When querying a stockpile an option will appear to toggle automelt for this stockpile. <p>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.</p> Any items placed in this stockpile will be designated to be melted.</p>
</div> </div>
<div class="section" id="track-stop-menu">
<h2><a class="toc-backref" href="#idtrackstop">Track Stop Menu</a></h2>
<p>The <cite>q</cite> menu of track stops is completely blank by default. To enable one:</p>
<pre class="literal-block">
enable trackstop
</pre>
<p>This allows you to view and/or change the track stop's friction and dump direction settings.
It re-uses the keybindings from the track stop building interface:</p>
<ul class="simple">
<li>BUILDING_TRACK_STOP_FRICTION_UP</li>
<li>BUILDING_TRACK_STOP_FRICTION_DOWN</li>
<li>BUILDING_TRACK_STOP_DUMP</li>
</ul>
</div>
<div class="section" id="gui-advfort"> <div class="section" id="gui-advfort">
<h2><a class="toc-backref" href="#id160">gui/advfort</a></h2> <h2><a class="toc-backref" href="#id160">gui/advfort</a></h2>
<p>This script allows to perform jobs in adventure mode. For more complete help <p>This script allows to perform jobs in adventure mode. For more complete help

@ -2655,6 +2655,19 @@ Enable the automelt plugin in your dfhack.init with::
When querying a stockpile an option will appear to toggle automelt for this stockpile. 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. Any items placed in this stockpile will be designated to be melted.
Track Stop Menu
===============
The `q` menu of track stops is completely blank by default. To enable one::
enable trackstop
This allows you to view and/or change the track stop's friction and dump direction settings.
It re-uses the keybindings from the track stop building interface:
* BUILDING_TRACK_STOP_FRICTION_UP
* BUILDING_TRACK_STOP_FRICTION_DOWN
* BUILDING_TRACK_STOP_DUMP
gui/advfort gui/advfort
=========== ===========

@ -183,7 +183,7 @@ enable search
enable automaterial enable automaterial
# Other interface improvement tools # Other interface improvement tools
enable dwarfmonitor mousequery automelt autotrade buildingplan resume zone enable dwarfmonitor mousequery automelt autotrade buildingplan resume trackstop zone
# allow the fortress bookkeeper to queue jobs through the manager # allow the fortress bookkeeper to queue jobs through the manager
stockflow enable stockflow enable

@ -156,6 +156,7 @@ if (BUILD_SUPPORTED)
DFHACK_PLUGIN(stocks stocks.cpp) DFHACK_PLUGIN(stocks stocks.cpp)
DFHACK_PLUGIN(strangemood strangemood.cpp) DFHACK_PLUGIN(strangemood strangemood.cpp)
DFHACK_PLUGIN(tiletypes tiletypes.cpp Brushes.h) DFHACK_PLUGIN(tiletypes tiletypes.cpp Brushes.h)
DFHACK_PLUGIN(trackstop trackstop.cpp)
# DFHACK_PLUGIN(treefarm treefarm.cpp) # DFHACK_PLUGIN(treefarm treefarm.cpp)
DFHACK_PLUGIN(tubefill tubefill.cpp) DFHACK_PLUGIN(tubefill tubefill.cpp)
DFHACK_PLUGIN(tweak tweak.cpp) DFHACK_PLUGIN(tweak tweak.cpp)

@ -0,0 +1,301 @@
/*
* Trackstop plugin.
* Shows track stop friction and direction in its 'q' menu.
*/
#include "uicommon.h"
#include "LuaTools.h"
#include "df/building_rollersst.h"
#include "df/building_trapst.h"
#include "df/viewscreen_dwarfmodest.h"
#include "modules/Gui.h"
using namespace DFHack;
using namespace std;
using df::global::world;
using df::global::ui;
using df::building_rollersst;
using df::building_trapst;
using df::enums::trap_type::trap_type;
using df::enums::screw_pump_direction::screw_pump_direction;
DFHACK_PLUGIN("trackstop");
#define AUTOENABLE false
DFHACK_PLUGIN_IS_ENABLED(enabled);
/*
* Interface hooks
*/
struct trackstop_hook : public df::viewscreen_dwarfmodest {
typedef df::viewscreen_dwarfmodest interpose_base;
enum Friction {
Lowest = 10,
Low = 50,
Medium = 500,
High = 10000,
Highest = 50000
};
building_trapst *get_selected_trackstop() {
if (!Gui::dwarfmode_hotkey(Core::getTopViewscreen()) || ui->main.mode != ui_sidebar_mode::QueryBuilding) {
return nullptr;
}
building_trapst *ts = virtual_cast<building_trapst>(world->selected_building);
if (ts && ts->trap_type == trap_type::TrackStop && ts->construction_stage) {
return ts;
}
return nullptr;
}
bool handleInput(set<df::interface_key> *input) {
building_trapst *ts = get_selected_trackstop();
if (!ts) {
return false;
}
if (input->count(interface_key::BUILDING_TRACK_STOP_DUMP)) {
// Change track stop dump direction.
// There might be a more elegant way to do this.
if (!ts->use_dump) {
// No -> North
ts->use_dump = 1;
ts->dump_x_shift = 0;
ts->dump_y_shift = -1;
} else if (ts->dump_x_shift == 0 && ts->dump_y_shift == -1) {
// North -> South
ts->dump_x_shift = 0;
ts->dump_y_shift = 1;
} else if (ts->dump_x_shift == 0 && ts->dump_y_shift == 1) {
// South -> East
ts->dump_x_shift = 1;
ts->dump_y_shift = 0;
} else if (ts->dump_x_shift == 1 && ts->dump_y_shift == 0) {
// East -> West
ts->dump_x_shift = -1;
ts->dump_y_shift = 0;
} else {
// West (or Elsewhere) -> No
ts->use_dump = 0;
ts->dump_x_shift = 0;
ts->dump_y_shift = 0;
}
return true;
} else if (input->count(interface_key::BUILDING_TRACK_STOP_FRICTION_UP)) {
ts->friction = (
(ts->friction < Friction::Lowest)? Friction::Lowest:
(ts->friction < Friction::Low)? Friction::Low:
(ts->friction < Friction::Medium)? Friction::Medium:
(ts->friction < Friction::High)? Friction::High:
(ts->friction < Friction::Highest)? Friction::Highest:
ts->friction
);
return true;
} else if (input->count(interface_key::BUILDING_TRACK_STOP_FRICTION_DOWN)) {
ts->friction = (
(ts->friction > Friction::Highest)? Friction::Highest:
(ts->friction > Friction::High)? Friction::High:
(ts->friction > Friction::Medium)? Friction::Medium:
(ts->friction > Friction::Low)? Friction::Low:
(ts->friction > Friction::Lowest)? Friction::Lowest:
ts->friction
);
return true;
}
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_trapst *ts = get_selected_trackstop();
if (ts) {
auto dims = Gui::getDwarfmodeViewDims();
int left_margin = dims.menu_x1 + 1;
int x = left_margin;
int y = dims.y1 + 1;
OutputString(COLOR_WHITE, x, y, "Track Stop", true, left_margin);
y += 3;
OutputString(COLOR_WHITE, x, y, "Friction: ", false);
OutputString(COLOR_WHITE, x, y, (
(ts->friction <= Friction::Lowest)? "Lowest":
(ts->friction <= Friction::Low)? "Low":
(ts->friction <= Friction::Medium)? "Medium":
(ts->friction <= Friction::High)? "High":
"Highest"
), true, left_margin);
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_TRACK_STOP_FRICTION_DOWN));
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_TRACK_STOP_FRICTION_UP));
OutputString(COLOR_WHITE, x, y, ": Change Friction", true, left_margin);
y += 1;
OutputString(COLOR_WHITE, x, y, "Dump on arrival: ", false);
OutputString(COLOR_WHITE, x, y, (
(!ts->use_dump)? "No":
(ts->dump_x_shift == 0 && ts->dump_y_shift == -1)? "North":
(ts->dump_x_shift == 0 && ts->dump_y_shift == 1)? "South":
(ts->dump_x_shift == 1 && ts->dump_y_shift == 0)? "East":
(ts->dump_x_shift == -1 && ts->dump_y_shift == 0)? "West":
"Elsewhere"
), true, left_margin);
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_TRACK_STOP_DUMP));
OutputString(COLOR_WHITE, x, y, ": Activate/change direction", true, left_margin);
}
}
};
struct roller_hook : public df::viewscreen_dwarfmodest {
typedef df::viewscreen_dwarfmodest interpose_base;
enum Speed {
Lowest = 10000,
Low = 20000,
Medium = 30000,
High = 40000,
Highest = 50000
};
building_rollersst *get_selected_roller() {
if (!Gui::dwarfmode_hotkey(Core::getTopViewscreen()) || ui->main.mode != ui_sidebar_mode::QueryBuilding) {
return nullptr;
}
building_rollersst *roller = virtual_cast<building_rollersst>(world->selected_building);
if (roller && roller->construction_stage) {
return roller;
}
return nullptr;
}
bool handleInput(set<df::interface_key> *input) {
building_rollersst *roller = get_selected_roller();
if (!roller) {
return false;
}
if (input->count(interface_key::BUILDING_ORIENT_NONE)) {
// Flip roller orientation.
// Long rollers can only be oriented along their length.
// Todo: Only add 1 to 1x1 rollers: x ^= ((x&1)<<1)|1
// Todo: This could have been elegant without all the casting,
// but as an enum it might be better off listing each case.
roller->direction = (df::enums::screw_pump_direction::screw_pump_direction)(((int8_t)roller->direction) ^ 2);
return true;
} else if (input->count(interface_key::BUILDING_ROLLERS_SPEED_UP)) {
if (roller->speed < Speed::Highest) {
roller->speed += Speed::Lowest;
}
return true;
} else if (input->count(interface_key::BUILDING_ROLLERS_SPEED_DOWN)) {
if (roller->speed > Speed::Lowest) {
roller->speed -= Speed::Lowest;
}
return true;
}
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_rollersst *roller = get_selected_roller();
if (roller) {
auto dims = Gui::getDwarfmodeViewDims();
int left_margin = dims.menu_x1 + 1;
int x = left_margin;
int y = dims.y1 + 6;
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_ORIENT_NONE));
OutputString(COLOR_WHITE, x, y, ": Rolls ", false);
OutputString(COLOR_WHITE, x, y, (
(roller->direction == screw_pump_direction::FromNorth)? "Southward":
(roller->direction == screw_pump_direction::FromEast)? "Westward":
(roller->direction == screw_pump_direction::FromSouth)? "Northward":
(roller->direction == screw_pump_direction::FromWest)? "Eastward":
""
), true, left_margin);
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_ROLLERS_SPEED_DOWN));
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(interface_key::BUILDING_ROLLERS_SPEED_UP));
OutputString(COLOR_WHITE, x, y, ": ");
OutputString(COLOR_WHITE, x, y, (
(roller->speed <= Speed::Lowest)? "Lowest":
(roller->speed <= Speed::Low)? "Low":
(roller->speed <= Speed::Medium)? "Medium":
(roller->speed <= Speed::High)? "High":
"Highest"
));
OutputString(COLOR_WHITE, x, y, " Speed", true, left_margin);
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(trackstop_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(trackstop_hook, render);
IMPLEMENT_VMETHOD_INTERPOSE(roller_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(roller_hook, render);
DFhackCExport command_result plugin_enable(color_ostream& out, bool enable) {
// Accept the "enable trackstop" / "disable trackstop" commands.
if (enable != enabled) {
// Check for global variables that, if missing, result in total failure.
// Missing enabler and ui_menu_width also produce visible effects, but not nearly as severe.
// This could be moved to the plugin_init step, but that's louder for no real benefit.
if (!(gps && ui && world)) {
out.printerr("trackstop: Missing required global variables.\n");
return CR_FAILURE;
}
if (!INTERPOSE_HOOK(trackstop_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(trackstop_hook, render).apply(enable) ||
!INTERPOSE_HOOK(roller_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(roller_hook, render).apply(enable)) {
out.printerr("Could not %s trackstop hooks!\n", enable? "insert": "remove");
return CR_FAILURE;
}
enabled = enable;
}
return CR_OK;
}
DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &commands) {
return plugin_enable(out, AUTOENABLE);
}
DFhackCExport command_result plugin_shutdown(color_ostream &out) {
return plugin_enable(out, false);
}