MouseQuery Plugin
parent
d9cd427121
commit
e750cbe5e9
@ -0,0 +1,264 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "Core.h"
|
||||
#include <Console.h>
|
||||
#include <Export.h>
|
||||
#include <PluginManager.h>
|
||||
#include <VTableInterpose.h>
|
||||
|
||||
#include "DataDefs.h"
|
||||
|
||||
#include "df/building.h"
|
||||
#include "df/enabler.h"
|
||||
#include "df/item.h"
|
||||
#include "df/ui.h"
|
||||
#include "df/unit.h"
|
||||
#include "df/viewscreen_dwarfmodest.h"
|
||||
#include "df/world.h"
|
||||
|
||||
#include "modules/Gui.h"
|
||||
#include "modules/Screen.h"
|
||||
|
||||
|
||||
using std::set;
|
||||
using std::string;
|
||||
using std::ostringstream;
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace df::enums;
|
||||
|
||||
using df::global::enabler;
|
||||
using df::global::gps;
|
||||
using df::global::world;
|
||||
using df::global::ui;
|
||||
|
||||
|
||||
static int32_t last_x, last_y, last_z;
|
||||
static size_t max_list_size = 100000; // Avoid iterating over huge lists
|
||||
|
||||
struct mousequery_hook : public df::viewscreen_dwarfmodest
|
||||
{
|
||||
typedef df::viewscreen_dwarfmodest interpose_base;
|
||||
|
||||
void send_key(const df::interface_key &key)
|
||||
{
|
||||
set<df::interface_key> tmp;
|
||||
tmp.insert(key);
|
||||
//INTERPOSE_NEXT(feed)(&tmp);
|
||||
this->feed(&tmp);
|
||||
}
|
||||
|
||||
df::interface_key get_default_query_mode(const int32_t &x, const int32_t &y, const int32_t &z)
|
||||
{
|
||||
bool fallback_to_building_query = false;
|
||||
|
||||
// Check for unit under cursor
|
||||
size_t count = world->units.all.size();
|
||||
if (count <= max_list_size)
|
||||
{
|
||||
for(size_t i = 0; i < count; i++)
|
||||
{
|
||||
df::unit *unit = world->units.all[i];
|
||||
|
||||
if(unit->pos.x == x && unit->pos.y == y && unit->pos.z == z)
|
||||
return df::interface_key::D_VIEWUNIT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fallback_to_building_query = true;
|
||||
}
|
||||
|
||||
// Check for building under cursor
|
||||
count = world->buildings.all.size();
|
||||
if (count <= max_list_size)
|
||||
{
|
||||
for(size_t i = 0; i < count; i++)
|
||||
{
|
||||
df::building *bld = world->buildings.all[i];
|
||||
|
||||
if (z == bld->z &&
|
||||
x >= bld->x1 && x <= bld->x2 &&
|
||||
y >= bld->y1 && y <= bld->y2)
|
||||
{
|
||||
df::building_type type = bld->getType();
|
||||
|
||||
if (type == building_type::Stockpile)
|
||||
{
|
||||
fallback_to_building_query = true;
|
||||
break; // Check for items in stockpile first
|
||||
}
|
||||
|
||||
// For containers use item view, fir everything else, query view
|
||||
return (type == building_type::Box || type == building_type::Cabinet ||
|
||||
type == building_type::Weaponrack || type == building_type::Armorstand)
|
||||
? df::interface_key::D_BUILDITEM : df::interface_key::D_BUILDJOB;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fallback_to_building_query = true;
|
||||
}
|
||||
|
||||
|
||||
// Check for items under cursor
|
||||
count = world->items.all.size();
|
||||
if (count <= max_list_size)
|
||||
{
|
||||
for(size_t i = 0; i < count; i++)
|
||||
{
|
||||
df::item *item = world->items.all[i];
|
||||
if (z == item->pos.z && x == item->pos.x && y == item->pos.y &&
|
||||
!item->flags.bits.in_building && !item->flags.bits.hidden &&
|
||||
!item->flags.bits.in_job && !item->flags.bits.in_chest &&
|
||||
!item->flags.bits.in_inventory)
|
||||
{
|
||||
return df::interface_key::D_LOOK;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fallback_to_building_query = true;
|
||||
}
|
||||
|
||||
return (fallback_to_building_query) ? df::interface_key::D_BUILDJOB : df::interface_key::D_LOOK;
|
||||
}
|
||||
|
||||
bool handle_mouse(const set<df::interface_key> *input)
|
||||
{
|
||||
int32_t cx, cy, vz;
|
||||
if (enabler->tracking_on)
|
||||
{
|
||||
if (enabler->mouse_lbut)
|
||||
{
|
||||
int32_t mx, my;
|
||||
if (Gui::getMousePos(mx, my))
|
||||
{
|
||||
int32_t vx, vy;
|
||||
if (Gui::getViewCoords(vx, vy, vz))
|
||||
{
|
||||
cx = vx + mx - 1;
|
||||
cy = vy + my - 1;
|
||||
|
||||
using namespace df::enums::ui_sidebar_mode;
|
||||
df::interface_key key = interface_key::NONE;
|
||||
bool cursor_still_here = (last_x == cx && last_y == cy && last_z == vz);
|
||||
switch(ui->main.mode)
|
||||
{
|
||||
case QueryBuilding:
|
||||
if (cursor_still_here)
|
||||
key = df::interface_key::D_BUILDITEM;
|
||||
break;
|
||||
|
||||
case BuildingItems:
|
||||
if (cursor_still_here)
|
||||
key = df::interface_key::D_VIEWUNIT;
|
||||
break;
|
||||
|
||||
case ViewUnits:
|
||||
if (cursor_still_here)
|
||||
key = df::interface_key::D_LOOK;
|
||||
break;
|
||||
|
||||
case LookAround:
|
||||
if (cursor_still_here)
|
||||
key = df::interface_key::D_BUILDJOB;
|
||||
break;
|
||||
|
||||
case Default:
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
enabler->mouse_lbut = 0;
|
||||
|
||||
// Can't check limits earlier as we must be sure we are in query or default mode we can clear the button flag
|
||||
// Otherwise the feed gets stuck in a loop
|
||||
uint8_t menu_width, area_map_width;
|
||||
Gui::getMenuWidth(menu_width, area_map_width);
|
||||
int32_t w = gps->dimx;
|
||||
if (menu_width == 1) w -= 57; //Menu is open doubly wide
|
||||
else if (menu_width == 2 && area_map_width == 3) w -= 33; //Just the menu is open
|
||||
else if (menu_width == 2 && area_map_width == 2) w -= 26; //Just the area map is open
|
||||
|
||||
if (mx < 1 || mx > w || my < 1 || my > gps->dimy - 2)
|
||||
return false;
|
||||
|
||||
while (ui->main.mode != Default)
|
||||
{
|
||||
send_key(df::interface_key::LEAVESCREEN);
|
||||
}
|
||||
|
||||
if (key == interface_key::NONE)
|
||||
key = get_default_query_mode(cx, cy, vz);
|
||||
|
||||
send_key(key);
|
||||
|
||||
// Force UI refresh
|
||||
Gui::setCursorCoords(cx, cy, vz);
|
||||
send_key(interface_key::CURSOR_DOWN_Z);
|
||||
send_key(interface_key::CURSOR_UP_Z);
|
||||
last_x = cx;
|
||||
last_y = cy;
|
||||
last_z = vz;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (enabler->mouse_rbut)
|
||||
{
|
||||
// Escape out of query mode
|
||||
using namespace df::enums::ui_sidebar_mode;
|
||||
if (ui->main.mode == QueryBuilding || ui->main.mode == BuildingItems ||
|
||||
ui->main.mode == ViewUnits || ui->main.mode == LookAround)
|
||||
{
|
||||
while (ui->main.mode != Default)
|
||||
{
|
||||
send_key(df::interface_key::LEAVESCREEN);
|
||||
}
|
||||
|
||||
enabler->mouse_rbut = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
|
||||
{
|
||||
if (!handle_mouse(input))
|
||||
INTERPOSE_NEXT(feed)(input);
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(mousequery_hook, feed);
|
||||
|
||||
DFHACK_PLUGIN("mousequery");
|
||||
|
||||
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
|
||||
{
|
||||
if (!gps || !INTERPOSE_HOOK(mousequery_hook, feed).apply())
|
||||
out.printerr("Could not insert mousequery hooks!\n");
|
||||
|
||||
last_x = last_y = last_z = -1;
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
|
||||
{
|
||||
switch (event) {
|
||||
case SC_MAP_LOADED:
|
||||
last_x = last_y = last_z = -1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return CR_OK;
|
||||
}
|
Loading…
Reference in New Issue