Petr Mrázek 2011-12-31 01:40:08 +01:00
commit 1284b30f79
7 changed files with 509 additions and 104 deletions

@ -51,13 +51,20 @@ using namespace DFHack;
#include "dfhack/SDL_fakes/events.h"
#include "dfhack/df/ui.h"
#include "dfhack/df/world.h"
#include "dfhack/df/world_data.h"
#include "dfhack/df/interface.h"
#include "dfhack/df/viewscreen_dwarfmodest.h"
#include <stdio.h>
#include <iomanip>
#include <stdlib.h>
#include <fstream>
#include "tinythread.h"
using namespace tthread;
using namespace tthread;
using namespace df::enums;
struct Core::Cond
{
@ -145,8 +152,17 @@ void fHKthread(void * iodata)
std::string stuff = core->getHotkeyCmd(); // waits on mutex!
if(!stuff.empty())
{
vector <string> crap;
command_result cr = plug_mgr->InvokeCommand(stuff, crap, false);
vector <string> args;
cheap_tokenise(stuff, args);
if (args.empty()) {
core->con.printerr("Empty hotkey command.\n");
continue;
}
string first = args[0];
args.erase(args.begin());
command_result cr = plug_mgr->InvokeCommand(first, args, false);
if(cr == CR_WOULD_BREAK)
{
core->con.printerr("It isn't possible to run an interactive command outside the console.\n");
@ -155,53 +171,25 @@ void fHKthread(void * iodata)
}
}
// A thread function... for the interactive console.
void fIOthread(void * iodata)
static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clueless_counter, const string &command)
{
IODATA * iod = ((IODATA*) iodata);
Core * core = iod->core;
PluginManager * plug_mgr = ((IODATA*) iodata)->plug_mgr;
CommandHistory main_history;
main_history.load("dfhack.history");
Console & con = core->con;
if(plug_mgr == 0 || core == 0)
{
con.printerr("Something horrible happened in Core's constructor...\n");
return;
}
con.print("DFHack is ready. Have a nice day!\n"
"Type in '?' or 'help' for general help, 'ls' to see all commands.\n");
int clueless_counter = 0;
while (true)
{
string command = "";
int ret = con.lineedit("[DFHack]# ",command, main_history);
if(ret == -2)
{
cerr << "Console is shutting down properly." << endl;
return;
}
else if(ret == -1)
{
cerr << "Console caught an unspecified error." << endl;
continue;
}
else if(ret)
if (!command.empty())
{
// a proper, non-empty command was entered
main_history.add(command);
main_history.save("dfhack.history");
}
// cut the input into parts
vector <string> parts;
cheap_tokenise(command,parts);
if(parts.size() == 0)
{
clueless_counter ++;
continue;
return;
}
string first = parts[0];
parts.erase(parts.begin());
if (first[0] == '#') return;
cerr << "Invoking: " << command << endl;
// let's see what we actually got
@ -220,6 +208,7 @@ void fIOthread(void * iodata)
" cls - Clear the console.\n"
" fpause - Force DF to pause.\n"
" die - Force DF to close immediately\n"
" keybinding - Modify bindings of commands to keys\n"
"Plugin management (useful for developers):\n"
//" belongs COMMAND - Tell which plugin a command belongs to.\n"
" plug [PLUGIN|v] - List plugin state and description.\n"
@ -370,6 +359,40 @@ void fIOthread(void * iodata)
con.print("%s\n", plug->getName().c_str());
}
}
else if(first == "keybinding")
{
if (parts.size() >= 3 && (parts[0] == "set" || parts[0] == "add"))
{
std::string keystr = parts[1];
if (parts[0] == "set")
core->ClearKeyBindings(keystr);
for (int i = parts.size()-1; i >= 2; i--)
{
if (!core->AddKeyBinding(keystr, parts[i])) {
con.printerr("Invalid key spec: %s\n", keystr.c_str());
break;
}
}
}
else if (parts.size() >= 2 && parts[0] == "clear")
{
for (unsigned i = 1; i < parts.size(); i++)
{
if (!core->ClearKeyBindings(parts[i])) {
con.printerr("Invalid key spec: %s\n", parts[i].c_str());
break;
}
}
}
else
{
con << "Usage:" << endl
<< " keybinding clear <key> <key>..." << endl
<< " keybinding set <key> \"cmdline\" \"cmdline\"..." << endl
<< " keybinding add <key> \"cmdline\" \"cmdline\"..." << endl
<< "Later adds, and earlier items within one command have priority." << endl;
}
}
else if(first == "fpause")
{
World * w = core->getWorld();
@ -410,6 +433,70 @@ void fIOthread(void * iodata)
*/
}
}
}
}
static void loadInitFile(Core *core, PluginManager *plug_mgr, string fname)
{
ifstream init(fname);
if (init.bad())
return;
int tmp = 0;
string command;
while (getline(init, command))
{
if (!command.empty())
runInteractiveCommand(core, plug_mgr, tmp, command);
}
}
// A thread function... for the interactive console.
void fIOthread(void * iodata)
{
IODATA * iod = ((IODATA*) iodata);
Core * core = iod->core;
PluginManager * plug_mgr = ((IODATA*) iodata)->plug_mgr;
CommandHistory main_history;
main_history.load("dfhack.history");
Console & con = core->con;
if(plug_mgr == 0 || core == 0)
{
con.printerr("Something horrible happened in Core's constructor...\n");
return;
}
loadInitFile(core, plug_mgr, "dfhack.init");
con.print("DFHack is ready. Have a nice day!\n"
"Type in '?' or 'help' for general help, 'ls' to see all commands.\n");
int clueless_counter = 0;
while (true)
{
string command = "";
int ret = con.lineedit("[DFHack]# ",command, main_history);
if(ret == -2)
{
cerr << "Console is shutting down properly." << endl;
return;
}
else if(ret == -1)
{
cerr << "Console caught an unspecified error." << endl;
continue;
}
else if(ret)
{
// a proper, non-empty command was entered
main_history.add(command);
main_history.save("dfhack.history");
}
runInteractiveCommand(core, plug_mgr, clueless_counter, command);
if(clueless_counter == 3)
{
con.print("Do 'help' or '?' for the list of available commands.\n");
@ -434,11 +521,12 @@ Core::Core()
StackMutex = 0;
core_cond = 0;
// set up hotkey capture
memset(hotkey_states,0,sizeof(hotkey_states));
hotkey_set = false;
HotkeyMutex = 0;
HotkeyCond = 0;
misc_data_mutex=0;
last_world_data_ptr = NULL;
top_viewscreen = NULL;
};
void Core::fatal (std::string output, bool deactivate)
@ -631,8 +719,36 @@ int Core::Update()
if(errorstate)
return -1;
// detect if the game was loaded or unloaded in the meantime
void *new_wdata = NULL;
if (df::global::world) {
df::world_data *wdata = df::global::world->world_data;
// when the game is unloaded, world_data isn't deleted, but its contents are
if (wdata && !wdata->sites.empty())
new_wdata = wdata;
}
if (new_wdata != last_world_data_ptr) {
last_world_data_ptr = new_wdata;
plug_mgr->OnStateChange(new_wdata ? SC_GAME_LOADED : SC_GAME_UNLOADED);
}
// detect if the viewscreen changed
if (df::global::gview)
{
df::viewscreen *screen = &df::global::gview->view;
while (screen->child)
screen = screen->child;
if (screen != top_viewscreen)
{
top_viewscreen = screen;
plug_mgr->OnStateChange(SC_VIEWSCREEN_CHANGED);
}
}
// notify all the plugins that a game tick is finished
plug_mgr->OnUpdate();
// wake waiting tools
// do not allow more tools to join in while we process stuff here
StackMutex->lock();
@ -721,37 +837,149 @@ int Core::SDL_Event(SDL::Event* ev, int orig_return)
if(ev && ev->type == SDL::ET_KEYDOWN || ev->type == SDL::ET_KEYUP)
{
SDL::KeyboardEvent * ke = (SDL::KeyboardEvent *)ev;
bool shift = ke->ksym.mod & SDL::KMOD_SHIFT;
// consuming F1 .. F8
int idx = ke->ksym.sym - SDL::K_F1;
if(idx < 0 || idx > 7)
return orig_return;
idx += 8*shift;
// now we have the real index...
if(ke->state == SDL::BTN_PRESSED && !hotkey_states[idx])
if(ke->state == SDL::BTN_PRESSED && !hotkey_states[ke->ksym.sym])
{
hotkey_states[idx] = 1;
Gui * g = getGui();
if(g->hotkeys && g->df_interface && g->df_menu_state)
hotkey_states[ke->ksym.sym] = true;
int mod = 0;
if (ke->ksym.mod & SDL::KMOD_SHIFT) mod |= 1;
if (ke->ksym.mod & SDL::KMOD_CTRL) mod |= 2;
if (ke->ksym.mod & SDL::KMOD_ALT) mod |= 4;
SelectHotkey(ke->ksym.sym, mod);
}
else if(ke->state == SDL::BTN_RELEASED)
{
t_viewscreen * ws = g->GetCurrentScreen();
// FIXME: put hardcoded values into memory.xml
if(ws->getClassName() == "viewscreen_dwarfmodest" && *g->df_menu_state == 0x23)
hotkey_states[ke->ksym.sym] = false;
}
}
return orig_return;
else
// do stuff with the events...
}
bool Core::SelectHotkey(int sym, int modifiers)
{
t_hotkey & hotkey = (*g->hotkeys)[idx];
setHotkeyCmd(hotkey.name);
// Find the topmost viewscreen
if (!df::global::gview || !df::global::ui)
return false;
df::viewscreen *screen = &df::global::gview->view;
while (screen->child)
screen = screen->child;
std::string cmd;
{
tthread::lock_guard<tthread::mutex> lock(*HotkeyMutex);
// Check the internal keybindings
std::vector<KeyBinding> &bindings = key_bindings[sym];
for (int i = bindings.size()-1; i >= 0; --i) {
if (bindings[i].modifiers != modifiers)
continue;
if (!plug_mgr->CanInvokeHotkey(bindings[i].command[0], screen))
continue;
cmd = bindings[i].cmdline;
break;
}
if (cmd.empty()) {
// Check the hotkey keybindings
int idx = sym - SDL::K_F1;
if(idx >= 0 && idx < 8)
{
if (modifiers & 1)
idx += 8;
if (!strict_virtual_cast<df::viewscreen_dwarfmodest>(screen) ||
df::global::ui->main.mode != ui_sidebar_mode::Hotkeys)
{
cmd = df::global::ui->main.hotkeys[idx].name;
}
}
else if(ke->state == SDL::BTN_RELEASED)
}
}
if (!cmd.empty()) {
setHotkeyCmd(cmd);
return true;
}
else
return false;
}
static bool parseKeySpec(std::string keyspec, int *psym, int *pmod)
{
hotkey_states[idx] = 0;
*pmod = 0;
// ugh, ugly
for (;;) {
if (keyspec.size() > 6 && keyspec.substr(0, 6) == "Shift-") {
*pmod |= 1;
keyspec = keyspec.substr(6);
} else if (keyspec.size() > 5 && keyspec.substr(0, 5) == "Ctrl-") {
*pmod |= 2;
keyspec = keyspec.substr(5);
} else if (keyspec.size() > 4 && keyspec.substr(0, 4) == "Alt-") {
*pmod |= 4;
keyspec = keyspec.substr(4);
} else
break;
}
if (keyspec.size() == 1 && keyspec[0] >= 'A' && keyspec[0] <= 'Z') {
*psym = SDL::K_a + (keyspec[0]-'A');
return true;
} else if (keyspec.size() == 2 && keyspec[0] == 'F' && keyspec[1] >= '1' && keyspec[1] <= '9') {
*psym = SDL::K_F1 + (keyspec[1]-'1');
return true;
} else
return false;
}
return orig_return;
// do stuff with the events...
bool Core::ClearKeyBindings(std::string keyspec)
{
int sym, mod;
if (!parseKeySpec(keyspec, &sym, &mod))
return false;
tthread::lock_guard<tthread::mutex> lock(*HotkeyMutex);
std::vector<KeyBinding> &bindings = key_bindings[sym];
for (int i = bindings.size()-1; i >= 0; --i) {
if (bindings[i].modifiers == mod)
bindings.erase(bindings.begin()+i);
}
return true;
}
bool Core::AddKeyBinding(std::string keyspec, std::string cmdline)
{
int sym;
KeyBinding binding;
if (!parseKeySpec(keyspec, &sym, &binding.modifiers))
return false;
cheap_tokenise(cmdline, binding.command);
if (binding.command.empty())
return false;
tthread::lock_guard<tthread::mutex> lock(*HotkeyMutex);
binding.cmdline = cmdline;
key_bindings[sym].push_back(binding);
return true;
}
bool DFHack::default_hotkey(Core *, df::viewscreen *top)
{
// Default hotkey guard function
for (;top ;top = top->parent)
if (strict_virtual_cast<df::viewscreen_dwarfmodest>(top))
return true;
return false;
}
////////////////

@ -27,6 +27,10 @@ distribution.
#include "dfhack/Process.h"
#include "dfhack/PluginManager.h"
#include "dfhack/Console.h"
#include "dfhack/DataDefs.h"
#include "dfhack/df/viewscreen.h"
using namespace DFHack;
#include <string>
@ -135,6 +139,7 @@ Plugin::Plugin(Core * core, const std::string & filepath, const std::string & _f
plugin_shutdown = 0;
plugin_status = 0;
plugin_onupdate = 0;
plugin_onstatechange = 0;
state = PS_UNLOADED;
access = new RefLock();
}
@ -192,6 +197,7 @@ bool Plugin::load()
plugin_status = (command_result (*)(Core *, std::string &)) LookupPlugin(plug, "plugin_status");
plugin_onupdate = (command_result (*)(Core *)) LookupPlugin(plug, "plugin_onupdate");
plugin_shutdown = (command_result (*)(Core *)) LookupPlugin(plug, "plugin_shutdown");
plugin_onstatechange = (command_result (*)(Core *, state_change_event)) LookupPlugin(plug, "plugin_onstatechange");
//name = _PlugName();
plugin_lib = plug;
if(plugin_init(&c,commands) == CR_OK)
@ -271,13 +277,69 @@ command_result Plugin::invoke( std::string & command, std::vector <std::string>
{
for (int i = 0; i < commands.size();i++)
{
if(commands[i].name == command)
PluginCommand &cmd = commands[i];
if(cmd.name == command)
{
// running interactive things from some other source than the console would break it
if(!interactive_ && commands[i].interactive)
if(!interactive_ && cmd.interactive)
cr = CR_WOULD_BREAK;
else if (cmd.guard)
{
// Execute hotkey commands in a way where they can
// expect their guard conditions to be matched,
// so as to avoid duplicating checks.
// This means suspending the core beforehand.
CoreSuspender suspend(&c);
df::viewscreen *top = c.getTopViewscreen();
if ((cmd.viewscreen_type && !cmd.viewscreen_type->is_instance(top))
|| !cmd.guard(&c, top))
{
c.con.printerr("Could not invoke %s: unsuitable UI state.\n", command.c_str());
cr = CR_FAILURE;
}
else
{
cr = cmd.function(&c, parameters);
}
}
else
{
cr = cmd.function(&c, parameters);
}
break;
}
}
}
access->lock_sub();
return cr;
}
bool Plugin::can_invoke_hotkey( std::string & command, df::viewscreen *top )
{
Core & c = Core::getInstance();
bool cr = false;
access->lock_add();
if(state == PS_LOADED)
{
for (int i = 0; i < commands.size();i++)
{
PluginCommand &cmd = commands[i];
if(cmd.name == command)
{
if (cmd.interactive)
cr = false;
else if (cmd.guard)
{
cr = (!cmd.viewscreen_type || cmd.viewscreen_type->is_instance(top))
&& cmd.guard(&c, top);
}
else
cr = commands[i].function(&c, parameters);
{
cr = default_hotkey(&c, top);
}
break;
}
}
@ -299,6 +361,19 @@ command_result Plugin::on_update()
return cr;
}
command_result Plugin::on_state_change(state_change_event event)
{
Core & c = Core::getInstance();
command_result cr = CR_NOT_IMPLEMENTED;
access->lock_add();
if(state == PS_LOADED && plugin_onstatechange)
{
cr = plugin_onstatechange(&c, event);
}
access->lock_sub();
return cr;
}
Plugin::plugin_state Plugin::getState() const
{
return state;
@ -348,19 +423,27 @@ Plugin *PluginManager::getPluginByName (const std::string & name)
return 0;
}
// FIXME: handle name collisions...
command_result PluginManager::InvokeCommand( std::string & command, std::vector <std::string> & parameters, bool interactive)
Plugin *PluginManager::getPluginByCommand(const std::string &command)
{
command_result cr = CR_NOT_IMPLEMENTED;
Core * c = &Core::getInstance();
cmdlist_mutex->lock();
tthread::lock_guard<tthread::mutex> lock(*cmdlist_mutex);
map <string, Plugin *>::iterator iter = belongs.find(command);
if (iter != belongs.end())
return iter->second;
else
return NULL;
}
// FIXME: handle name collisions...
command_result PluginManager::InvokeCommand( std::string & command, std::vector <std::string> & parameters, bool interactive)
{
cr = iter->second->invoke(command, parameters, interactive);
Plugin *plugin = getPluginByCommand(command);
return plugin ? plugin->invoke(command, parameters, interactive) : CR_NOT_IMPLEMENTED;
}
cmdlist_mutex->unlock();
return cr;
bool PluginManager::CanInvokeHotkey(std::string &command, df::viewscreen *top)
{
Plugin *plugin = getPluginByCommand(command);
return plugin ? plugin->can_invoke_hotkey(command, top) : false;
}
void PluginManager::OnUpdate( void )
@ -370,6 +453,15 @@ void PluginManager::OnUpdate( void )
all_plugins[i]->on_update();
}
}
void PluginManager::OnStateChange( state_change_event event )
{
for(int i = 0; i < all_plugins.size(); i++)
{
all_plugins[i]->on_state_change(event);
}
}
// FIXME: doesn't check name collisions!
void PluginManager::registerCommands( Plugin * p )
{

@ -43,6 +43,11 @@ namespace tthread
class thread;
}
namespace df
{
struct viewscreen;
}
namespace DFHack
{
class Process;
@ -134,6 +139,12 @@ namespace DFHack
/// returns a named pointer.
void *GetData(std::string key);
bool ClearKeyBindings(std::string keyspec);
bool AddKeyBinding(std::string keyspec, std::string cmdline);
bool isWorldLoaded() { return (last_world_data_ptr != NULL); }
df::viewscreen *getTopViewscreen() { return top_viewscreen; }
DFHack::Process * p;
DFHack::VersionInfo * vinfo;
DFHack::Console con;
@ -178,12 +189,25 @@ namespace DFHack
} s_mods;
std::vector <Module *> allModules;
DFHack::PluginManager * plug_mgr;
// hotkey-related stuff
int hotkey_states[16];
struct KeyBinding {
int modifiers;
std::vector<std::string> command;
std::string cmdline;
};
std::map<int, std::vector<KeyBinding> > key_bindings;
std::map<int, bool> hotkey_states;
std::string hotkey_cmd;
bool hotkey_set;
tthread::mutex * HotkeyMutex;
tthread::condition_variable * HotkeyCond;
bool SelectHotkey(int key, int modifiers);
void *last_world_data_ptr; // for state change tracking
df::viewscreen *top_viewscreen;
// Very important!
bool started;

@ -35,10 +35,16 @@ namespace tthread
class mutex;
class condition_variable;
}
namespace df
{
struct viewscreen;
}
namespace DFHack
{
class Core;
class PluginManager;
struct virtual_identity;
enum command_result
{
CR_WOULD_BREAK = -2,
@ -46,33 +52,48 @@ namespace DFHack
CR_FAILURE = 0,
CR_OK = 1
};
struct PluginCommand
enum state_change_event
{
SC_GAME_LOADED,
SC_GAME_UNLOADED,
SC_VIEWSCREEN_CHANGED
};
struct DFHACK_EXPORT PluginCommand
{
typedef command_result (*command_function)(Core *, std::vector <std::string> &);
typedef bool (*command_hotkey_guard)(Core *, df::viewscreen *);
/// create a command with a name, description, function pointer to its code
/// and saying if it needs an interactive terminal
/// Most commands shouldn't require an interactive terminal!
PluginCommand(const char * _name,
const char * _description,
command_result (*function_)(Core *, std::vector <std::string> &),
command_function function_,
bool interactive_ = false
)
: name(_name), description(_description),
function(function_), interactive(interactive_),
guard(NULL), viewscreen_type(NULL)
{
name = _name;
description = _description;
function = function_;
interactive = interactive_;
}
PluginCommand (const PluginCommand & rhs)
PluginCommand(const char * _name,
const char * _description,
command_function function_,
command_hotkey_guard guard_,
virtual_identity *viewscreen_type_ = NULL)
: name(_name), description(_description),
function(function_), interactive(false),
guard(guard_), viewscreen_type(viewscreen_type_)
{
name = rhs.name;
description = rhs.description;
function = rhs.function;
interactive = rhs.interactive;
}
std::string name;
std::string description;
command_result (*function)(Core *, std::vector <std::string> &);
command_function function;
bool interactive;
command_hotkey_guard guard;
virtual_identity *viewscreen_type;
};
class Plugin
{
@ -87,11 +108,13 @@ namespace DFHack
Plugin(DFHack::Core* core, const std::string& filepath, const std::string& filename, PluginManager * pm);
~Plugin();
command_result on_update();
command_result on_state_change(state_change_event event);
public:
bool load();
bool unload();
bool reload();
command_result invoke( std::string & command, std::vector <std::string> & parameters, bool interactive );
bool can_invoke_hotkey( std::string & command, df::viewscreen *top );
plugin_state getState () const;
const PluginCommand& operator[] (std::size_t index) const
{
@ -117,6 +140,7 @@ namespace DFHack
command_result (*plugin_status)(Core *, std::string &);
command_result (*plugin_shutdown)(Core *);
command_result (*plugin_onupdate)(Core *);
command_result (*plugin_onstatechange)(Core *, state_change_event);
};
class DFHACK_EXPORT PluginManager
{
@ -126,12 +150,15 @@ namespace DFHack
PluginManager(Core * core);
~PluginManager();
void OnUpdate( void );
void OnStateChange( state_change_event event );
void registerCommands( Plugin * p );
void unregisterCommands( Plugin * p );
// PUBLIC METHODS
public:
Plugin *getPluginByName (const std::string & name);
Plugin *getPluginByCommand (const std::string &command);
command_result InvokeCommand( std::string & command, std::vector <std::string> & parameters, bool interactive = true );
bool CanInvokeHotkey(std::string &command, df::viewscreen *top);
Plugin* operator[] (std::size_t index)
{
if(index >= all_plugins.size())
@ -149,5 +176,7 @@ namespace DFHack
std::vector <Plugin *> all_plugins;
std::string plugin_path;
};
DFHACK_EXPORT bool default_hotkey(Core *, df::viewscreen *);
}

@ -91,6 +91,11 @@
<stl-string name="str_visit"/>
<stl-string name="str_site"/>
</class-type>
<class-type type-name='viewscreen_dwarfmodest' inherits-from='viewscreen'>
todo
</class-type>
</data-definition>
<!--

@ -288,10 +288,32 @@ DFhackCExport DFHack::command_result plugin_init(DFHack::Core* pCore, std::vecto
return DFHack::CR_OK;
}
DFhackCExport DFHack::command_result plugin_onstatechange(DFHack::Core* pCore, DFHack::state_change_event event)
{
switch (event) {
case DFHack::SC_GAME_LOADED:
case DFHack::SC_GAME_UNLOADED:
if (running)
pCore->con.printerr("seedwatch deactivated due to game load/unload\n");
running = false;
break;
default:
break;
}
return DFHack::CR_OK;
}
DFhackCExport DFHack::command_result plugin_onupdate(DFHack::Core* pCore)
{
if (running)
{
// reduce processing rate
static int counter = 0;
if (++counter < 500)
return DFHack::CR_OK;
counter = 0;
DFHack::Core& core = *pCore;
DFHack::World *w = core.getWorld();
DFHack::t_gamemodes gm;

@ -8,6 +8,7 @@
#include <dfhack/df/ui.h>
#include <dfhack/df/building_stockpilest.h>
#include <dfhack/df/selection_rect.h>
#include <dfhack/df/viewscreen_dwarfmodest.h>
using std::vector;
using std::string;
@ -21,7 +22,8 @@ using df::global::selection_rect;
using df::building_stockpilest;
DFhackCExport command_result copystock(Core * c, vector <string> & parameters);
static command_result copystock(Core *c, vector <string> & parameters);
static bool copystock_guard(Core *c, df::viewscreen *top);
DFhackCExport const char * plugin_name ( void )
{
@ -32,7 +34,12 @@ DFhackCExport command_result plugin_init (Core *c, std::vector <PluginCommand> &
{
commands.clear();
if (world && ui) {
commands.push_back(PluginCommand("copystock", "Copy stockpile under cursor.", copystock));
commands.push_back(
PluginCommand(
"copystock", "Copy stockpile under cursor.", copystock,
copystock_guard, &df::viewscreen_dwarfmodest::_identity
)
);
}
std::cerr << "world: " << sizeof(df::world) << " ui: " << sizeof(df::ui)
<< " b_stock: " << sizeof(building_stockpilest) << endl;
@ -44,21 +51,24 @@ DFhackCExport command_result plugin_shutdown ( Core * c )
return CR_OK;
}
bool inSelectMode() {
static bool copystock_guard(Core *c, df::viewscreen *)
{
using namespace ui_sidebar_mode;
switch (ui->main.mode) {
case Stockpiles:
return true;
case BuildingItems:
case QueryBuilding:
return true;
return !!virtual_cast<building_stockpilest>(world->selected_building);
default:
return false;
}
}
DFhackCExport command_result copystock(Core * c, vector <string> & parameters)
static command_result copystock(Core * c, vector <string> & parameters)
{
CoreSuspender suspend(c);
/* HOTKEY COMMAND: CORE ALREADY SUSPENDED */
// For convenience: when used in the stockpiles mode, switch to 'q'
if (ui->main.mode == ui_sidebar_mode::Stockpiles) {
@ -70,15 +80,10 @@ DFhackCExport command_result copystock(Core * c, vector <string> & parameters)
return CR_OK;
}
if (!inSelectMode()) {
c->con << "Cannot copy stockpile in mode " << ENUM_KEY_STR(ui_sidebar_mode, ui->main.mode) << endl;
return CR_OK;
}
building_stockpilest *sp = virtual_cast<building_stockpilest>(world->selected_building);
if (!sp) {
c->con << "Selected building isn't a stockpile." << endl;
return CR_OK;
c->con.printerr("Selected building isn't a stockpile.\n");
return CR_FAILURE;
}
ui->stockpile.custom_settings = sp->settings;