Merge pull request #359 from lethosor/embark-tools-oo-pr

embark-tools cleanup
develop
expwnent 2014-11-02 03:30:38 -05:00
commit 6c356b5ae9
1 changed files with 366 additions and 290 deletions

@ -11,98 +11,13 @@
#include <VTableInterpose.h> #include <VTableInterpose.h>
#include "ColorText.h" #include "ColorText.h"
#include "uicommon.h"
#include "df/viewscreen_choose_start_sitest.h" #include "df/viewscreen_choose_start_sitest.h"
#include "df/interface_key.h" #include "df/interface_key.h"
using namespace DFHack; using namespace DFHack;
struct EmbarkTool #define FOR_ITER_TOOLS(iter) for(auto iter = tools.begin(); iter != tools.end(); iter++)
{
std::string id;
std::string name;
std::string desc;
bool enabled;
df::interface_key toggle_key;
};
EmbarkTool embark_tools[] = {
{"anywhere", "Embark anywhere", "Allows embarking anywhere on the world map",
false, df::interface_key::CUSTOM_A},
{"nano", "Nano embark", "Allows the embark size to be decreased below 2x2",
false, df::interface_key::CUSTOM_N},
{"sand", "Sand indicator", "Displays an indicator when sand is present on the given embark site",
false, df::interface_key::CUSTOM_S},
{"sticky", "Stable position", "Maintains the selected local area while navigating the world map",
false, df::interface_key::CUSTOM_P},
};
#define NUM_TOOLS int(sizeof(embark_tools) / sizeof(EmbarkTool))
command_result embark_tools_cmd (color_ostream &out, std::vector <std::string> & parameters);
void OutputString (int8_t color, int &x, int y, const std::string &text);
bool tool_exists (std::string tool_name);
bool tool_enabled (std::string tool_name);
bool tool_enable (std::string tool_name, bool enable_state);
void tool_update (std::string tool_name);
class embark_tools_settings : public dfhack_viewscreen
{
public:
embark_tools_settings () { };
~embark_tools_settings () { };
void help () { };
std::string getFocusString () { return "embark-tools/options"; };
void render ()
{
parent->render();
int x;
auto dim = Screen::getWindowSize();
int width = 50,
height = 4 + 1 + NUM_TOOLS, // Padding + lower row
min_x = (dim.x - width) / 2,
max_x = (dim.x + width) / 2,
min_y = (dim.y - height) / 2,
max_y = min_y + height;
Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_DARKGREY), min_x, min_y, max_x, max_y);
Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), min_x + 1, min_y + 1, max_x - 1, max_y - 1);
x = min_x + 2;
OutputString(COLOR_LIGHTRED, x, max_y - 2, Screen::getKeyDisplay(df::interface_key::SELECT));
OutputString(COLOR_WHITE, x, max_y - 2, "/");
OutputString(COLOR_LIGHTRED, x, max_y - 2, Screen::getKeyDisplay(df::interface_key::LEAVESCREEN));
OutputString(COLOR_WHITE, x, max_y - 2, ": Done");
for (int i = 0, y = min_y + 2; i < NUM_TOOLS; i++, y++)
{
EmbarkTool t = embark_tools[i];
x = min_x + 2;
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(t.toggle_key));
OutputString(COLOR_WHITE, x, y, ": " + t.name + (t.enabled ? ": Enabled" : ": Disabled"));
}
};
void feed (std::set<df::interface_key> * input)
{
if (input->count(df::interface_key::SELECT) || input->count(df::interface_key::LEAVESCREEN))
{
Screen::dismiss(this);
return;
}
for (auto iter = input->begin(); iter != input->end(); iter++)
{
df::interface_key key = *iter;
for (int i = 0; i < NUM_TOOLS; i++)
{
if (embark_tools[i].toggle_key == key)
{
embark_tools[i].enabled = !embark_tools[i].enabled;
}
}
}
};
};
/*
* Logic
*/
void update_embark_sidebar (df::viewscreen_choose_start_sitest * screen) void update_embark_sidebar (df::viewscreen_choose_start_sitest * screen)
{ {
@ -159,88 +74,81 @@ void resize_embark (df::viewscreen_choose_start_sitest * screen, int dx, int dy)
update_embark_sidebar(screen); update_embark_sidebar(screen);
} }
std::string sand_indicator = ""; typedef df::viewscreen_choose_start_sitest start_sitest;
bool sand_dirty = true; // Flag set when update is needed typedef std::set<df::interface_key> ikey_set;
void sand_update (df::viewscreen_choose_start_sitest * screen)
{
CoreSuspendClaimer suspend;
buffered_color_ostream out;
Core::getInstance().runCommand(out, "prospect");
auto fragments = out.fragments();
sand_indicator = "";
for (auto iter = fragments.begin(); iter != fragments.end(); iter++)
{
std::string fragment = iter->second;
if (fragment.find("SAND_") != std::string::npos)
{
sand_indicator = "Sand";
break;
}
}
sand_dirty = false;
}
int sticky_pos[] = {0, 0, 3, 3}; class EmbarkTool
bool sticky_moved = false;
void sticky_save (df::viewscreen_choose_start_sitest * screen)
{ {
sticky_pos[0] = screen->location.embark_pos_min.x; protected:
sticky_pos[1] = screen->location.embark_pos_max.x; bool enabled;
sticky_pos[2] = screen->location.embark_pos_min.y; public:
sticky_pos[3] = screen->location.embark_pos_max.y; EmbarkTool()
} :enabled(false)
{ }
void sticky_apply (df::viewscreen_choose_start_sitest * screen) virtual bool getEnabled() { return enabled; }
{ virtual void setEnabled(bool state) { enabled = state; }
if (screen->finder.finder_state != -1) virtual void toggleEnabled() { setEnabled(!enabled); }
{ virtual std::string getId() = 0;
// Site finder is active - don't override default local position virtual std::string getName() = 0;
return; virtual std::string getDesc() = 0;
} virtual df::interface_key getToggleKey() = 0;
screen->location.embark_pos_min.x = sticky_pos[0]; virtual void before_render(start_sitest* screen) { };
screen->location.embark_pos_max.x = sticky_pos[1]; virtual void after_render(start_sitest* screen) { };
screen->location.embark_pos_min.y = sticky_pos[2]; virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel) { };
screen->location.embark_pos_max.y = sticky_pos[3]; virtual void after_feed(start_sitest* screen, ikey_set* input) { };
update_embark_sidebar(screen); };
} std::vector<EmbarkTool*> tools;
/* /*
* Viewscreen hooks
*/
void OutputString (int8_t color, int &x, int y, const std::string &text) class SampleTool : public EmbarkTool
{ {
Screen::paintString(Screen::Pen(' ', color, 0), x, y, text); virtual std::string getId() { return "id"; }
x += text.length(); virtual std::string getName() { return "Name"; }
} virtual std::string getDesc() { return "Description"; }
virtual df::interface_key getToggleKey() { return df::interface_key::KEY; }
virtual void before_render(start_sitest* screen) { }
virtual void after_render(start_sitest* screen) { }
virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel) { };
virtual void after_feed(start_sitest* screen, ikey_set* input) { };
};
struct choose_start_site_hook : df::viewscreen_choose_start_sitest */
{
typedef df::viewscreen_choose_start_sitest interpose_base;
DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input)) class EmbarkAnywhere : public EmbarkTool
{
bool prevent_default = false;
if (tool_enabled("anywhere"))
{ {
for (auto iter = input->begin(); iter != input->end(); iter++) public:
virtual std::string getId() { return "anywhere"; }
virtual std::string getName() { return "Embark anywhere"; }
virtual std::string getDesc() { return "Allows embarking anywhere on the world map"; }
virtual df::interface_key getToggleKey() { return df::interface_key::CUSTOM_A; }
virtual void after_render(start_sitest* screen)
{ {
df::interface_key key = *iter; auto dim = Screen::getWindowSize();
if (key == df::interface_key::SETUP_EMBARK) int x = 20, y = dim.y - 2;
if (screen->page >= 0 && screen->page <= 4)
{ {
prevent_default = true; OutputString(COLOR_WHITE, x, y, ": Embark!");
this->in_embark_normal = 1;
}
} }
} }
virtual void before_feed(start_sitest* screen, ikey_set *input, bool &cancel)
if (input->count(df::interface_key::CUSTOM_S))
{ {
Screen::show(new embark_tools_settings); if (input->count(df::interface_key::SETUP_EMBARK))
return; {
cancel = true;
screen->in_embark_normal = 1;
} }
};
};
if (tool_enabled("nano")) class NanoEmbark : public EmbarkTool
{
public:
virtual std::string getId() { return "nano"; }
virtual std::string getName() { return "Nano embark"; }
virtual std::string getDesc() { return "Allows the embark size to be decreased below 2x2"; }
virtual df::interface_key getToggleKey() { return df::interface_key::CUSTOM_N; }
virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel)
{ {
for (auto iter = input->begin(); iter != input->end(); iter++) for (auto iter = input->begin(); iter != input->end(); iter++)
{ {
@ -266,14 +174,117 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest
} }
if (is_resize) if (is_resize)
{ {
prevent_default = true; cancel = true;
resize_embark(this, dx, dy); resize_embark(screen, dx, dy);
return;
}
}
};
};
class SandIndicator : public EmbarkTool
{
protected:
bool dirty;
std::string indicator;
void update_indicator()
{
CoreSuspendClaimer suspend;
buffered_color_ostream out;
Core::getInstance().runCommand(out, "prospect");
auto fragments = out.fragments();
indicator = "";
for (auto iter = fragments.begin(); iter != fragments.end(); iter++)
{
std::string fragment = iter->second;
if (fragment.find("SAND_") != std::string::npos)
{
indicator = "Sand";
break;
}
}
dirty = false;
} }
public:
SandIndicator()
:EmbarkTool(),
dirty(true),
indicator("")
{ }
virtual void setEnabled(bool state)
{
EmbarkTool::setEnabled(state);
dirty = true;
}
virtual std::string getId() { return "sand"; }
virtual std::string getName() { return "Sand indicator"; }
virtual std::string getDesc() { return "Displays an indicator when sand is present on the given embark site"; }
virtual df::interface_key getToggleKey() { return df::interface_key::CUSTOM_S; }
virtual void after_render(start_sitest* screen)
{
if (dirty)
update_indicator();
auto dim = Screen::getWindowSize();
int x = dim.x - 28,
y = 13;
if (screen->page == 0)
{
OutputString(COLOR_YELLOW, x, y, indicator);
} }
} }
virtual void after_feed(start_sitest* screen, ikey_set* input)
{
dirty = true;
};
};
if (tool_enabled("sticky")) class StablePosition : public EmbarkTool
{ {
protected:
int prev_position[4];
bool moved_position;
void save_position(start_sitest* screen)
{
prev_position[0] = screen->location.embark_pos_min.x;
prev_position[1] = screen->location.embark_pos_max.x;
prev_position[2] = screen->location.embark_pos_min.y;
prev_position[3] = screen->location.embark_pos_max.y;
}
void restore_position(start_sitest* screen)
{
if (screen->finder.finder_state != -1)
{
// Site finder is active - don't override default local position
return;
}
screen->location.embark_pos_min.x = prev_position[0];
screen->location.embark_pos_max.x = prev_position[1];
screen->location.embark_pos_min.y = prev_position[2];
screen->location.embark_pos_max.y = prev_position[3];
update_embark_sidebar(screen);
}
public:
StablePosition()
:EmbarkTool(),
moved_position(false)
{
prev_position[0] = 0;
prev_position[1] = 0;
prev_position[2] = 3;
prev_position[3] = 3;
}
virtual std::string getId() { return "sticky"; }
virtual std::string getName() { return "Stable position"; }
virtual std::string getDesc() { return "Maintains the selected local area while navigating the world map"; }
virtual df::interface_key getToggleKey() { return df::interface_key::CUSTOM_P; }
virtual void before_render(start_sitest* screen) {
if (moved_position)
{
restore_position(screen);
moved_position = false;
}
}
virtual void before_feed(start_sitest* screen, ikey_set* input, bool &cancel) {
for (auto iter = input->begin(); iter != input->end(); iter++) for (auto iter = input->begin(); iter != input->end(); iter++)
{ {
df::interface_key key = *iter; df::interface_key key = *iter;
@ -299,46 +310,130 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest
case df::interface_key::CURSOR_DOWNRIGHT_FAST: case df::interface_key::CURSOR_DOWNRIGHT_FAST:
is_motion = true; is_motion = true;
break; break;
default:
is_motion = false;
} }
if (is_motion && !sticky_moved) if (is_motion && !moved_position)
{ {
sticky_save(this); save_position(screen);
sticky_moved = true; moved_position = true;
} }
} }
};
};
class embark_tools_settings : public dfhack_viewscreen
{
public:
embark_tools_settings () { };
~embark_tools_settings () { };
void help () { };
std::string getFocusString () { return "embark-tools/options"; };
void render ()
{
parent->render();
int x, y;
auto dim = Screen::getWindowSize();
int width = 50,
height = 4 + 1 + tools.size(), // Padding + lower row
min_x = (dim.x - width) / 2,
max_x = (dim.x + width) / 2,
min_y = (dim.y - height) / 2,
max_y = min_y + height;
Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_DARKGREY), min_x, min_y, max_x, max_y);
Screen::fillRect(Screen::Pen(' ', COLOR_BLACK, COLOR_BLACK), min_x + 1, min_y + 1, max_x - 1, max_y - 1);
x = min_x + 2;
y = max_y - 2;
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::SELECT));
OutputString(COLOR_WHITE, x, y, "/");
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::LEAVESCREEN));
OutputString(COLOR_WHITE, x, y, ": Done");
y = min_y + 2;
FOR_ITER_TOOLS(iter)
{
EmbarkTool* t = *iter;
x = min_x + 2;
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(t->getToggleKey()));
OutputString(COLOR_WHITE, x, y, ": " + t->getName() +
(t->getEnabled() ? ": Enabled" : ": Disabled"));
y++;
}
};
void feed (std::set<df::interface_key> * input)
{
if (input->count(df::interface_key::SELECT) || input->count(df::interface_key::LEAVESCREEN))
{
Screen::dismiss(this);
return;
}
for (auto iter = input->begin(); iter != input->end(); iter++)
{
df::interface_key key = *iter;
FOR_ITER_TOOLS(iter)
{
EmbarkTool* t = *iter;
if (t->getToggleKey() == key)
{
t->toggleEnabled();
}
} }
}
};
};
if (tool_enabled("sand")) bool tool_exists (std::string tool_name)
{ {
sand_dirty = true; FOR_ITER_TOOLS(iter)
{
EmbarkTool* tool = *iter;
if (tool->getId() == tool_name)
return true;
} }
if (!prevent_default) return false;
INTERPOSE_NEXT(feed)(input);
} }
DEFINE_VMETHOD_INTERPOSE(void, render, ()) bool tool_enabled (std::string tool_name)
{
FOR_ITER_TOOLS(iter)
{
EmbarkTool* tool = *iter;
if (tool->getId() == tool_name)
return tool->getEnabled();
}
return false;
}
bool tool_enable (std::string tool_name, bool enable_state)
{ {
if (tool_enabled("sticky") && sticky_moved) int n = 0;
FOR_ITER_TOOLS(iter)
{
EmbarkTool* tool = *iter;
if (tool->getId() == tool_name || tool_name == "all")
{ {
sticky_apply(this); tool->setEnabled(enable_state);
sticky_moved = false; n++;
}
}
return (bool)n;
} }
INTERPOSE_NEXT(render)(); struct choose_start_site_hook : df::viewscreen_choose_start_sitest
{
typedef df::viewscreen_choose_start_sitest interpose_base;
void display_tool_status()
{
auto dim = Screen::getWindowSize(); auto dim = Screen::getWindowSize();
int x = 1, int x = 1,
y = dim.y - 5; y = dim.y - 5;
OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::CUSTOM_S)); OutputString(COLOR_LIGHTRED, x, y, Screen::getKeyDisplay(df::interface_key::CUSTOM_S));
OutputString(COLOR_WHITE, x, y, ": Enabled: "); OutputString(COLOR_WHITE, x, y, ": Enabled: ");
std::list<std::string> parts; std::list<std::string> parts;
for (int i = 0; i < NUM_TOOLS; i++) FOR_ITER_TOOLS(iter)
{ {
if (embark_tools[i].enabled) EmbarkTool* tool = *iter;
if (tool->getEnabled())
{ {
parts.push_back(embark_tools[i].name); parts.push_back(tool->getName());
parts.push_back(", "); parts.push_back(", ");
} }
} }
@ -354,97 +449,77 @@ struct choose_start_site_hook : df::viewscreen_choose_start_sitest
{ {
OutputString(COLOR_LIGHTMAGENTA, x, y, "(none)"); OutputString(COLOR_LIGHTMAGENTA, x, y, "(none)");
} }
if (tool_enabled("anywhere"))
{
x = 20; y = dim.y - 2;
if (this->page >= 0 && this->page <= 4)
{
// Only display on five map pages, not on site finder or notes
OutputString(COLOR_WHITE, x, y, ": Embark!");
}
} }
if (tool_enabled("sand"))
{ void display_settings()
if (sand_dirty)
{ {
sand_update(this); Screen::show(new embark_tools_settings);
} }
x = dim.x - 28; y = 13;
if (this->page == 0) inline bool is_valid_page()
{ {
OutputString(COLOR_YELLOW, x, y, sand_indicator); return (page >= 0 && page <= 4);
} }
}
}
};
IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, render);
/* DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key> *input))
* Tool management
*/
bool tool_exists (std::string tool_name)
{ {
for (int i = 0; i < NUM_TOOLS; i++) bool cancel = false;
FOR_ITER_TOOLS(iter)
{ {
if (embark_tools[i].id == tool_name) EmbarkTool* tool = *iter;
return true; if (tool->getEnabled())
tool->before_feed(this, input, cancel);
} }
return false; if (cancel)
} return;
INTERPOSE_NEXT(feed)(input);
bool tool_enabled (std::string tool_name) if (input->count(df::interface_key::CUSTOM_S) && is_valid_page())
{ display_settings();
for (int i = 0; i < NUM_TOOLS; i++) FOR_ITER_TOOLS(iter)
{ {
if (embark_tools[i].id == tool_name) EmbarkTool* tool = *iter;
return embark_tools[i].enabled; if (tool->getEnabled())
tool->after_feed(this, input);
} }
return false;
} }
DEFINE_VMETHOD_INTERPOSE(void, render, ())
bool tool_enable (std::string tool_name, bool enable_state)
{
int n = 0;
for (int i = 0; i < NUM_TOOLS; i++)
{ {
if (embark_tools[i].id == tool_name || tool_name == "all") FOR_ITER_TOOLS(iter)
{ {
embark_tools[i].enabled = enable_state; EmbarkTool* tool = *iter;
tool_update(tool_name); if (tool->getEnabled())
n++; tool->before_render(this);
}
}
return (bool)n;
} }
INTERPOSE_NEXT(render)();
void tool_update (std::string tool_name) display_tool_status();
{ FOR_ITER_TOOLS(iter)
// Called whenever a tool is enabled/disabled
if (tool_name == "sand")
{ {
sand_dirty = true; EmbarkTool* tool = *iter;
if (tool->getEnabled())
tool->after_render(this);
} }
} }
};
/* IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, feed);
* Plugin management IMPLEMENT_VMETHOD_INTERPOSE(choose_start_site_hook, render);
*/
DFHACK_PLUGIN("embark-tools"); DFHACK_PLUGIN("embark-tools");
DFHACK_PLUGIN_IS_ENABLED(is_enabled); DFHACK_PLUGIN_IS_ENABLED(is_enabled);
command_result embark_tools_cmd (color_ostream &out, std::vector <std::string> & parameters);
DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands) DFhackCExport command_result plugin_init (color_ostream &out, std::vector <PluginCommand> &commands)
{ {
tools.push_back(new EmbarkAnywhere);
tools.push_back(new NanoEmbark);
tools.push_back(new SandIndicator);
tools.push_back(new StablePosition);
std::string help = ""; std::string help = "";
help += "embark-tools (enable/disable) tool [tool...]\n" help += "embark-tools (enable/disable) tool [tool...]\n"
"Tools:\n"; "Tools:\n";
for (int i = 0; i < NUM_TOOLS; i++) FOR_ITER_TOOLS(iter)
{ {
help += (" " + embark_tools[i].id + ": " + embark_tools[i].desc + "\n"); help += (" " + (*iter)->getId() + ": " + (*iter)->getDesc() + "\n");
} }
commands.push_back(PluginCommand( commands.push_back(PluginCommand(
"embark-tools", "embark-tools",
@ -504,10 +579,11 @@ command_result embark_tools_cmd (color_ostream &out, std::vector <std::string> &
if (is_enabled) if (is_enabled)
{ {
out << "Tool status:" << std::endl; out << "Tool status:" << std::endl;
for (int i = 0; i < NUM_TOOLS; i++) FOR_ITER_TOOLS(iter)
{ {
EmbarkTool t = embark_tools[i]; EmbarkTool* t = *iter;
out << t.name << " (" << t.id << "): " << (t.enabled ? "Enabled" : "Disabled") << std::endl; out << t->getName() << " (" << t->getId() << "): "
<< (t->getEnabled() ? "Enabled" : "Disabled") << std::endl;
} }
} }
else else