dfhack/plugins/automaterial.cpp

1327 lines
42 KiB
C++

2012-11-20 03:12:11 -07:00
// Auto Material Select
#include <map>
#include <string>
#include <vector>
#include "Core.h"
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
#include "LuaTools.h"
2012-11-20 03:12:11 -07:00
#include <Console.h>
#include <Export.h>
#include <PluginManager.h>
#include <VTableInterpose.h>
// DF data structure definition headers
#include "DataDefs.h"
#include "MiscUtils.h"
#include "TileTypes.h"
2012-11-20 03:12:11 -07:00
#include "df/build_req_choice_genst.h"
#include "df/build_req_choice_specst.h"
2012-11-21 03:45:26 -07:00
#include "df/construction_type.h"
2012-11-20 03:12:11 -07:00
#include "df/item.h"
#include "df/ui.h"
2012-11-21 03:45:26 -07:00
#include "df/ui_build_selector.h"
#include "df/viewscreen_dwarfmodest.h"
#include "df/items_other_id.h"
#include "df/job.h"
#include "df/world.h"
#include "df/building_constructionst.h"
#include "df/job_item.h"
2012-11-21 03:45:26 -07:00
2012-11-20 03:12:11 -07:00
#include "modules/Gui.h"
#include "modules/Screen.h"
#include "modules/Items.h"
#include "modules/Constructions.h"
#include "modules/Buildings.h"
#include "modules/Maps.h"
#include "modules/MapCache.h"
#include "uicommon.h"
2012-11-20 03:12:11 -07:00
using namespace std;
2012-11-20 03:12:11 -07:00
using std::map;
using std::string;
using std::vector;
using namespace DFHack;
using namespace df::enums;
DFHACK_PLUGIN("automaterial");
REQUIRE_GLOBAL(gps);
REQUIRE_GLOBAL(ui);
REQUIRE_GLOBAL(ui_build_selector);
2012-11-20 03:12:11 -07:00
struct MaterialDescriptor
{
df::item_type item_type;
int16_t item_subtype;
int16_t type;
int32_t index;
bool valid;
bool matches(const MaterialDescriptor &a) const
{
2015-02-14 20:53:06 -07:00
return a.valid && valid &&
a.type == type &&
2012-11-20 03:12:11 -07:00
a.index == index &&
a.item_type == item_type &&
a.item_subtype == item_subtype;
}
};
DFhackCExport command_result plugin_shutdown ( color_ostream &out )
{
return CR_OK;
}
void AMOutputToggleString(int &x, int &y, const char *text, const char *hotkey, bool state, bool newline = true, int left_margin = 0, int8_t color = COLOR_WHITE)
2013-01-12 03:32:30 -07:00
{
OutputHotkeyString(x, y, text, hotkey);
OutputString(COLOR_WHITE, x, y, ": ");
if (state)
OutputString(COLOR_GREEN, x, y, "Enabled", newline, left_margin);
else
OutputString(COLOR_GREY, x, y, "Disabled", newline, left_margin);
}
//START UI Functions
static enum t_box_select_mode {SELECT_FIRST, SELECT_SECOND, SELECT_MATERIALS, AUTOSELECT_MATERIALS} box_select_mode = SELECT_FIRST;
static coord32_t box_first, box_second;
static bool box_select_enabled = false;
static bool show_box_selection = true;
static bool hollow_selection = false;
static deque<df::item*> box_select_materials;
#define SELECTION_IGNORE_TICKS 10
static int ignore_selection = SELECTION_IGNORE_TICKS;
static map<int16_t, MaterialDescriptor> last_used_material;
static map<int16_t, MaterialDescriptor> last_moved_material;
static map< int16_t, vector<MaterialDescriptor> > preferred_materials;
static map< int16_t, df::interface_key > hotkeys;
static bool last_used_moved = false;
static bool auto_choose_materials = true;
static bool revert_to_last_used_type = false;
static bool allow_future_placement = false;
2012-11-20 03:12:11 -07:00
static inline bool in_material_choice_stage()
{
2015-02-14 20:53:06 -07:00
return Gui::build_selector_hotkey(Core::getTopViewscreen()) &&
ui_build_selector->building_type == df::building_type::Construction &&
2015-02-14 20:53:06 -07:00
ui->main.mode == ui_sidebar_mode::Build &&
2012-11-20 03:12:11 -07:00
ui_build_selector->stage == 2;
}
static inline bool in_placement_stage()
2012-11-20 03:12:11 -07:00
{
2015-02-14 20:53:06 -07:00
return Gui::dwarfmode_hotkey(Core::getTopViewscreen()) &&
2012-11-20 03:12:11 -07:00
ui->main.mode == ui_sidebar_mode::Build &&
ui_build_selector &&
ui_build_selector->building_type == df::building_type::Construction &&
2012-11-20 03:12:11 -07:00
ui_build_selector->stage == 1;
}
static inline bool in_type_choice_stage()
{
2015-02-14 20:53:06 -07:00
return Gui::dwarfmode_hotkey(Core::getTopViewscreen()) &&
ui->main.mode == ui_sidebar_mode::Build &&
ui_build_selector &&
ui_build_selector->building_type < 0;
}
2012-11-20 03:12:11 -07:00
static inline vector<MaterialDescriptor> &get_curr_constr_prefs()
{
if (preferred_materials.find(ui_build_selector->building_subtype) == preferred_materials.end())
preferred_materials[ui_build_selector->building_subtype] = vector<MaterialDescriptor>();
return preferred_materials[ui_build_selector->building_subtype];
}
static inline MaterialDescriptor &get_last_used_material()
{
if (last_used_material.find(ui_build_selector->building_subtype) == last_used_material.end())
last_used_material[ui_build_selector->building_subtype] = MaterialDescriptor();
return last_used_material[ui_build_selector->building_subtype];
}
static void set_last_used_material(const MaterialDescriptor &matetial)
2012-11-20 03:12:11 -07:00
{
last_used_material[ui_build_selector->building_subtype] = matetial;
}
static MaterialDescriptor &get_last_moved_material()
{
if (last_moved_material.find(ui_build_selector->building_subtype) == last_moved_material.end())
last_moved_material[ui_build_selector->building_subtype] = MaterialDescriptor();
return last_moved_material[ui_build_selector->building_subtype];
}
static void set_last_moved_material(const MaterialDescriptor &matetial)
2012-11-20 03:12:11 -07:00
{
last_moved_material[ui_build_selector->building_subtype] = matetial;
}
static MaterialDescriptor get_material_in_list(size_t i)
{
MaterialDescriptor result;
result.valid = false;
if (VIRTUAL_CAST_VAR(gen, df::build_req_choice_genst, ui_build_selector->choices[i]))
{
result.item_type = gen->item_type;
result.item_subtype = gen->item_subtype;
result.type = gen->mat_type;
result.index = gen->mat_index;
result.valid = true;
}
else if (VIRTUAL_CAST_VAR(spec, df::build_req_choice_specst, ui_build_selector->choices[i]))
{
result.item_type = spec->candidate->getType();
result.item_subtype = spec->candidate->getSubtype();
2012-11-20 03:12:11 -07:00
result.type = spec->candidate->getActualMaterial();
result.index = spec->candidate->getActualMaterialIndex();
result.valid = true;
}
return result;
}
static bool is_material_in_autoselect(size_t &i, MaterialDescriptor &material)
{
for (i = 0; i < get_curr_constr_prefs().size(); i++)
{
if (get_curr_constr_prefs()[i].matches(material))
return true;
}
return false;
}
static bool is_material_in_list(size_t &i, MaterialDescriptor &material)
{
const size_t size = ui_build_selector->choices.size(); //Just because material list could be very big
for (i = 0; i < size; i++)
{
if (get_material_in_list(i).matches(material))
return true;
}
return false;
}
static bool move_material_to_top(MaterialDescriptor &material)
{
size_t i;
if (is_material_in_list(i, material))
{
auto sel_item = ui_build_selector->choices[i];
ui_build_selector->choices.erase(ui_build_selector->choices.begin() + i);
ui_build_selector->choices.insert(ui_build_selector->choices.begin(), sel_item);
ui_build_selector->sel_index = 0;
set_last_moved_material(material);
return true;
}
set_last_moved_material(MaterialDescriptor());
return false;
}
static bool check_autoselect(MaterialDescriptor &material, bool toggle)
{
size_t idx;
if (is_material_in_autoselect(idx, material))
{
2015-02-14 20:53:06 -07:00
if (toggle)
2012-11-20 03:12:11 -07:00
vector_erase_at(get_curr_constr_prefs(), idx);
return true;
}
else
{
if (toggle)
get_curr_constr_prefs().push_back(material);
return false;
}
}
static void cancel_box_selection()
{
if (box_select_mode == SELECT_FIRST)
return;
box_select_mode = SELECT_FIRST;
box_select_materials.clear();
if (!show_box_selection)
Gui::setDesignationCoords(-1, -1, -1);
}
//END UI Functions
//START Building and Verification
struct building_site
{
df::coord pos;
bool in_open_air;
building_site(df::coord pos, bool in_open_air)
{
this->pos = pos;
this->in_open_air = in_open_air;
}
building_site() {}
};
static deque<building_site> valid_building_sites;
static deque<building_site> open_air_sites;
static building_site anchor;
static bool is_orthogonal_to_pending_construction(building_site &site)
{
for (deque<building_site>::iterator it = valid_building_sites.begin(); it != valid_building_sites.end(); it++)
{
if ((it->pos.x == site.pos.x && abs(it->pos.y - site.pos.y) == 1) || (it->pos.y == site.pos.y && abs(it->pos.x - site.pos.x) == 1))
{
site.in_open_air = true;
return true;
}
}
return false;
}
static df::building_constructionst *get_construction_on_tile(const df::coord &pos)
{
auto current = Buildings::findAtTile(pos);
if (current)
return strict_virtual_cast<df::building_constructionst>(current);
return NULL;
}
static df::tiletype *read_tile_shapes(const df::coord &pos, df::tiletype_shape &shape, df::tiletype_shape_basic &shape_basic)
{
if (!Maps::isValidTilePos(pos))
return NULL;
auto ttype = Maps::getTileType(pos);
if (!ttype)
return NULL;
shape = tileShape(*ttype);
shape_basic = tileShapeBasic(shape);
return ttype;
}
static bool is_valid_building_site(building_site &site, bool orthogonal_check, bool check_placed_constructions, bool in_future_placement_mode)
{
df::tiletype_shape shape;
df::tiletype_shape_basic shape_basic;
auto ttype = read_tile_shapes(site.pos, shape, shape_basic);
if (!ttype)
return false;
if (shape_basic == tiletype_shape_basic::Open)
{
if (orthogonal_check)
{
// Check if this is a valid tile to have a construction placed orthogonally to it
if (!in_future_placement_mode)
return false;
df::building_constructionst *cons = get_construction_on_tile(site.pos);
2013-04-24 09:49:18 -06:00
if (cons && cons->type == construction_type::Floor)
{
site.in_open_air = true;
return true;
}
return false;
}
// Stairs can be placed in open space, if they can connect to other stairs
df::tiletype_shape shape_s;
df::tiletype_shape_basic shape_basic_s;
if (ui_build_selector->building_subtype == construction_type::DownStair ||
ui_build_selector->building_subtype == construction_type::UpDownStair)
{
df::coord below(site.pos.x, site.pos.y, site.pos.z - 1);
auto ttype_s = read_tile_shapes(below, shape_s, shape_basic_s);
if (ttype_s)
{
if (shape_s == tiletype_shape::STAIR_UP || shape_s == tiletype_shape::STAIR_UPDOWN)
return true;
}
}
if (ui_build_selector->building_subtype == construction_type::UpStair ||
ui_build_selector->building_subtype == construction_type::UpDownStair)
{
df::coord above(site.pos.x, site.pos.y, site.pos.z + 1);
auto ttype_s = read_tile_shapes(above, shape_s, shape_basic_s);
if (ttype_s)
{
if (shape_s == tiletype_shape::STAIR_DOWN || shape_s == tiletype_shape::STAIR_UPDOWN)
return true;
}
}
// Check if there is a valid tile orthogonally adjacent
bool valid_orthogonal_tile_found = false;
df::coord orthagonal_pos;
orthagonal_pos.z = site.pos.z;
for (orthagonal_pos.x = site.pos.x-1; orthagonal_pos.x <= site.pos.x+1 && !valid_orthogonal_tile_found; orthagonal_pos.x++)
{
for (orthagonal_pos.y = site.pos.y-1; orthagonal_pos.y <= site.pos.y+1; orthagonal_pos.y++)
{
if ((site.pos.x == orthagonal_pos.x) == (site.pos.y == orthagonal_pos.y))
continue;
building_site orthogonal_site(orthagonal_pos, false);
if (is_valid_building_site(orthogonal_site, true, check_placed_constructions, in_future_placement_mode))
{
valid_orthogonal_tile_found = true;
if (orthogonal_site.in_open_air)
site.in_open_air = true;
break;
}
}
}
if (!(valid_orthogonal_tile_found || (check_placed_constructions && is_orthogonal_to_pending_construction(site))))
{
site.in_open_air = true;
return false;
}
}
else if (orthogonal_check)
{
2015-02-14 20:53:06 -07:00
if (shape != tiletype_shape::RAMP &&
shape_basic != tiletype_shape_basic::Floor &&
shape_basic != tiletype_shape_basic::Stair)
return false;
}
else
{
auto material = tileMaterial(*ttype);
if (shape == tiletype_shape::RAMP)
{
if (material == tiletype_material::CONSTRUCTION)
return false;
}
else
{
if (shape != tiletype_shape::STAIR_DOWN && shape_basic != tiletype_shape_basic::Floor)
return false;
// Can build on top of a wall, but not on other construction
auto construction = Constructions::findAtTile(site.pos);
if (construction)
2015-11-01 21:05:17 -07:00
{
if (construction->flags.bits.top_of_wall==0)
return false;
}
2015-11-06 18:04:32 -07:00
if (material == tiletype_material::FIRE ||
material == tiletype_material::POOL ||
material == tiletype_material::BROOK ||
material == tiletype_material::RIVER ||
material == tiletype_material::MAGMA ||
material == tiletype_material::DRIFTWOOD ||
material == tiletype_material::CAMPFIRE
)
return false;
}
}
if (orthogonal_check)
return true;
auto designation = Maps::getTileDesignation(site.pos);
if (designation->bits.flow_size > 2)
return false;
auto current = Buildings::findAtTile(site.pos);
if (current)
return false;
df::coord2d size(1,1);
return Buildings::checkFreeTiles(site.pos, size, NULL, false, false);
}
static bool find_anchor_in_spiral(const df::coord &start)
{
bool found = false;
for (anchor.pos.z = start.z; anchor.pos.z > start.z - 4; anchor.pos.z--)
{
int x, y, dx, dy;
x = y = dx = 0;
dy = -1;
const int side = 11;
const int maxI = side*side;
for (int i = 0; i < maxI; i++)
{
if (-side/2 < x && x <= side/2 && -side/2 < y && y <= side/2)
{
anchor.pos.x = start.x + x;
anchor.pos.y = start.y + y;
if (is_valid_building_site(anchor, false, false, false))
{
found = true;
break;
}
}
if ((x == y) || ((x < 0) && (x == -y)) || ((x > 0) && (x == 1-y)))
{
int tmp = dx;
dx = -dy;
dy = tmp;
}
x += dx;
y += dy;
}
if (found)
break;
}
return found;
}
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
static bool find_valid_building_sites(bool in_future_placement_mode, bool use_buildingplan)
{
valid_building_sites.clear();
open_air_sites.clear();
int xD = (box_second.x > box_first.x) ? 1 : -1;
int yD = (box_second.y > box_first.y) ? 1 : -1;
for (int32_t xB = box_first.x; (xD > 0) ? (xB <= box_second.x) : (xB >= box_second.x); xB += xD)
{
for (int32_t yB = box_first.y; (yD > 0) ? (yB <= box_second.y) : (yB >= box_second.y); yB += yD)
{
if (hollow_selection && !(xB == box_first.x || xB == box_second.x || yB == box_first.y || yB == box_second.y))
continue;
building_site site(df::coord(xB, yB, box_second.z), false);
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
// if we're using buildingplan, it will take care of filtering out bad tiles
if (use_buildingplan || is_valid_building_site(site, false, true, in_future_placement_mode))
valid_building_sites.push_back(site);
else if (site.in_open_air)
{
if (in_future_placement_mode)
valid_building_sites.push_back(site);
else
open_air_sites.push_back(site);
}
}
}
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
if (!use_buildingplan)
{
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
size_t last_open_air_count = 0;
while (valid_building_sites.size() > 0 && open_air_sites.size() != last_open_air_count)
{
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
last_open_air_count = open_air_sites.size();
deque<building_site> current_open_air_list = open_air_sites;
open_air_sites.clear();
for (deque<building_site>::iterator it = current_open_air_list.begin(); it != current_open_air_list.end(); it++)
{
if (is_orthogonal_to_pending_construction(*it))
valid_building_sites.push_back(*it);
else
open_air_sites.push_back(*it);
}
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
}
}
return valid_building_sites.size() > 0;
}
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
static bool is_buildingplan_enabled()
{
auto L = Lua::Core::State;
color_ostream_proxy out(Core::getInstance().getConsole());
Lua::StackUnwinder top(L);
if (!(lua_checkstack(L, 1) &&
Lua::PushModulePublic(out, L, "plugins.buildingplan", "isEnabled") &&
Lua::SafeCall(out, L, 0, 1)))
{
return false;
}
return lua_toboolean(L, -1);
}
static bool is_buildingplan_planmode_enabled(
df::building_type type, int16_t subtype, int32_t custom)
{
auto L = Lua::Core::State;
color_ostream_proxy out(Core::getInstance().getConsole());
Lua::StackUnwinder top(L);
if (!lua_checkstack(L, 4) ||
!Lua::PushModulePublic(
out, L, "plugins.buildingplan", "isPlanModeEnabled"))
return false;
Lua::Push(L, type);
Lua::Push(L, subtype);
Lua::Push(L, custom);
if (!Lua::SafeCall(out, L, 3, 1))
return false;
return lua_toboolean(L, -1);
}
static bool is_buildingplan_managed()
{
return is_buildingplan_enabled() &&
is_buildingplan_planmode_enabled(ui_build_selector->building_type,
ui_build_selector->building_subtype,
ui_build_selector->custom_type);
}
static bool build_with_buildingplan_box_select(const df::coord &pos)
{
auto L = Lua::Core::State;
color_ostream_proxy out(Core::getInstance().getConsole());
CoreSuspendClaimer suspend;
Lua::StackUnwinder top(L);
if (!lua_checkstack(L, 5) ||
!Lua::PushModulePublic(
out, L, "plugins.automaterial",
"build_with_buildingplan_box_select"))
{
return false;
}
Lua::Push(L, ui_build_selector->building_subtype);
Lua::Push(L, pos.x);
Lua::Push(L, pos.y);
Lua::Push(L, pos.z);
if (!Lua::SafeCall(out, L, 4, 1))
return false;
return lua_toboolean(L, -1);
}
static bool build_with_buildingplan_ui()
{
auto L = Lua::Core::State;
color_ostream_proxy out(Core::getInstance().getConsole());
CoreSuspendClaimer suspend;
Lua::StackUnwinder top(L);
return lua_checkstack(L, 1) &&
Lua::PushModulePublic(out, L, "plugins.automaterial",
"build_with_buildingplan_ui") &&
Lua::SafeCall(out, L, 0, 1);
}
static bool designate_new_construction(df::coord &pos, df::construction_type &type, df::item *item)
{
auto newinst = Buildings::allocInstance(pos, building_type::Construction, type);
if (!newinst)
return false;
vector<df::item*> items;
items.push_back(item);
Maps::ensureTileBlock(pos);
if (!Buildings::constructWithItems(newinst, items))
{
delete newinst;
return false;
}
return true;
}
//END Building and Verification
//START Viewscreen Hook
2012-11-20 03:12:11 -07:00
struct jobutils_hook : public df::viewscreen_dwarfmodest
{
//START UI Methods
2012-11-20 03:12:11 -07:00
typedef df::viewscreen_dwarfmodest interpose_base;
void send_key(const df::interface_key &key)
{
set< df::interface_key > keys;
keys.insert(key);
this->feed(&keys);
}
bool select_material_at_index(size_t i)
{
ui_build_selector->sel_index = i;
std::set< df::interface_key > keys;
keys.insert(df::interface_key::SELECT_ALL);
this->feed(&keys);
return !in_material_choice_stage();
}
2012-11-21 03:45:26 -07:00
bool choose_materials()
{
if (!auto_choose_materials || get_curr_constr_prefs().size() == 0)
return false;
2012-11-21 03:45:26 -07:00
size_t size = ui_build_selector->choices.size();
for (size_t i = 0; i < size; i++)
{
MaterialDescriptor material = get_material_in_list(i);
size_t j;
if (is_material_in_autoselect(j, material))
{
return select_material_at_index(i);
2012-11-21 03:45:26 -07:00
}
}
return false;
}
void draw_box_selection()
{
if (!box_select_enabled)
return;
if (ui->main.mode != df::ui_sidebar_mode::Build ||
ui_build_selector->building_type != df::building_type::Construction)
return;
df::coord vport = Gui::getViewportPos();
//Even if selection drawing is disabled, paint a green cursor as we can place box selection anywhere
if (box_select_mode == SELECT_FIRST || (!show_box_selection && box_select_mode == SELECT_SECOND))
{
int32_t x, y, z;
if (!Gui::getCursorCoords(x, y, z))
return;
x = x - vport.x + 1;
y = y - vport.y + 1;
OutputString(COLOR_GREEN, x, y, "X", false, 0, 0, true /* map */);
}
else if (show_box_selection && box_select_mode == SELECT_SECOND)
{
if (!Gui::getCursorCoords(box_second.x, box_second.y, box_second.z))
return;
int32_t xD = (box_second.x > box_first.x) ? 1 : -1;
int32_t yD = (box_second.y > box_first.y) ? 1 : -1;
for (int32_t xB = box_first.x; (xD > 0) ? (xB <= box_second.x) : (xB >= box_second.x); xB += xD)
{
for (int32_t yB = box_first.y; (yD > 0) ? (yB <= box_second.y) : (yB >= box_second.y); yB += yD)
{
if (hollow_selection && !(xB == box_first.x || xB == box_second.x || yB == box_first.y || yB == box_second.y))
continue;
int8_t color = (xB == box_second.x && yB == box_second.y) ? COLOR_GREEN : COLOR_BROWN;
int32_t x = xB - vport.x + 1;
int32_t y = yB - vport.y + 1;
OutputString(color, x, y, "X", false, 0, 0, true /* map */);
}
}
}
else if (show_box_selection && box_select_mode == SELECT_MATERIALS)
{
for (deque<building_site>::iterator it = valid_building_sites.begin(); it != valid_building_sites.end(); it++)
{
int32_t x = it->pos.x - vport.x + 1;
int32_t y = it->pos.y - vport.y + 1;
OutputString(COLOR_GREEN, x, y, "X", false, 0, 0, true /* map */);
}
}
}
void reset_existing_selection()
{
for (int i = 0; i < 10; i++)
{
send_key(df::interface_key::BUILDING_DIM_Y_DOWN);
send_key(df::interface_key::BUILDING_DIM_X_DOWN);
}
}
void handle_input(set<df::interface_key> *input)
2012-11-20 03:12:11 -07:00
{
if (ui_build_selector->building_subtype >= 7)
return;
2012-11-20 03:12:11 -07:00
if (in_material_choice_stage())
{
if (input->count(interface_key::LEAVESCREEN))
{
box_select_mode = SELECT_FIRST;
}
2012-11-20 03:12:11 -07:00
MaterialDescriptor material = get_material_in_list(ui_build_selector->sel_index);
if (material.valid)
{
if (input->count(interface_key::SELECT) || input->count(interface_key::SELECT_ALL))
2012-11-20 03:12:11 -07:00
{
if (get_last_moved_material().matches(material))
last_used_moved = false; //Keep selected material on top
2012-11-20 03:12:11 -07:00
set_last_used_material(material);
if (box_select_enabled)
{
auto curr_index = ui_build_selector->sel_index;
vector<MaterialDescriptor> gen_material;
gen_material.push_back(get_material_in_list(curr_index));
box_select_materials.clear();
// Populate material list with selected material
populate_box_materials(gen_material, ((input->count(interface_key::SELECT_ALL) && ui_build_selector->is_grouped) ? -1 : 1));
input->clear(); // Let the apply_box_selection routine allocate the construction
input->insert(interface_key::LEAVESCREEN);
}
2012-11-20 03:12:11 -07:00
}
else if (input->count(interface_key::CUSTOM_A))
{
check_autoselect(material, true);
input->clear();
}
}
}
else if (in_placement_stage())
2012-11-20 03:12:11 -07:00
{
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
bool use_buildingplan = is_buildingplan_managed();
if (!use_buildingplan && input->count(interface_key::CUSTOM_A))
2012-11-20 03:12:11 -07:00
{
auto_choose_materials = !auto_choose_materials;
}
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
else if (!use_buildingplan && input->count(interface_key::CUSTOM_T))
2012-11-20 03:12:11 -07:00
{
revert_to_last_used_type = !revert_to_last_used_type;
}
else if (input->count(interface_key::CUSTOM_B))
{
reset_existing_selection();
box_select_enabled = !box_select_enabled;
if (!box_select_enabled)
cancel_box_selection();
return;
}
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
else if (!use_buildingplan && input->count(interface_key::CUSTOM_O))
{
allow_future_placement = !allow_future_placement;
}
else if (input->count(interface_key::LEAVESCREEN))
{
switch (box_select_mode)
{
case SELECT_FIRST:
case SELECT_SECOND:
cancel_box_selection();
default:
break;
}
}
else if (box_select_enabled)
{
if (input->count(interface_key::SELECT))
{
switch (box_select_mode)
{
case SELECT_FIRST:
if (!Gui::getCursorCoords(box_first.x, box_first.y, box_first.z))
{
cancel_box_selection();
return;
}
box_select_mode = SELECT_SECOND;
if (!show_box_selection)
Gui::setDesignationCoords(box_first.x, box_first.y, box_first.z);
input->clear();
return;
case SELECT_SECOND:
if (!Gui::getCursorCoords(box_second.x, box_second.y, box_second.z))
{
cancel_box_selection();
return;
}
cancel_box_selection();
input->clear();
apply_box_selection(true);
return;
default:
break;
}
}
else if (input->count(interface_key::CUSTOM_X))
{
show_box_selection = !show_box_selection;
if (box_select_mode == SELECT_SECOND)
{
if (show_box_selection)
{
Gui::setDesignationCoords(-1, -1, -1);
}
else
{
Gui::setDesignationCoords(box_first.x, box_first.y, box_first.z);
}
}
}
else if (input->count(interface_key::CUSTOM_H))
{
hollow_selection = !hollow_selection;
}
else if (input->count(interface_key::BUILDING_DIM_Y_UP) ||
input->count(interface_key::BUILDING_DIM_Y_DOWN) ||
input->count(interface_key::BUILDING_DIM_X_UP) ||
input->count(interface_key::BUILDING_DIM_X_DOWN))
{
input->clear();
return;
}
}
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
else if (use_buildingplan
&& ui_build_selector->errors.size() == 0
&& input->count(interface_key::SELECT))
{
build_with_buildingplan_ui();
Gui::refreshSidebar();
input->clear();
return;
}
}
}
//END UI Methods
//START Building Application
bool populate_box_materials(vector<MaterialDescriptor> &gen_materials, int32_t count = -1)
{
bool result = false;
if (gen_materials.size() == 0)
return result;
if (ui_build_selector->is_grouped)
send_key(interface_key::BUILDING_EXPAND_CONTRACT);
size_t size = ui_build_selector->choices.size();
vector<MaterialDescriptor>::iterator gen_material;
for (size_t i = 0; i < size; i++)
{
if (VIRTUAL_CAST_VAR(spec, df::build_req_choice_specst, ui_build_selector->choices[i]))
{
for (gen_material = gen_materials.begin(); gen_material != gen_materials.end(); gen_material++)
{
if (gen_material->item_type == spec->candidate->getType() &&
gen_material->item_subtype == spec->candidate->getSubtype() &&
gen_material->type == spec->candidate->getActualMaterial() &&
gen_material->index == spec->candidate->getActualMaterialIndex())
{
box_select_materials.push_back(spec->candidate);
if (count > -1)
return true; // Right now we only support 1 or all materials
result = true;
break;
}
}
}
}
send_key(interface_key::BUILDING_EXPAND_CONTRACT);
return result;
}
void move_cursor(df::coord &pos)
{
Gui::setCursorCoords(pos.x, pos.y, pos.z);
Gui::refreshSidebar();
}
void move_cursor(coord32_t &pos)
{
2012-12-27 00:15:32 -07:00
df::coord c((int16_t) pos.x, (int16_t) pos.y, (int16_t) pos.z);
move_cursor(c);
}
void apply_box_selection(bool new_start)
{
static bool saved_revert_setting = false;
static bool auto_select_applied = false;
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
bool use_buildingplan = is_buildingplan_managed();
box_select_mode = SELECT_MATERIALS;
if (new_start)
{
bool ok_to_continue = false;
bool in_future_placement_mode = false;
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
if (!find_valid_building_sites(false, use_buildingplan))
{
if (allow_future_placement)
{
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
in_future_placement_mode = find_valid_building_sites(true, use_buildingplan);
}
}
else
{
ok_to_continue = true;
}
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
// if using buildingplan, we don't need an anchor
if (!use_buildingplan)
{
if (in_future_placement_mode)
{
ok_to_continue =
find_anchor_in_spiral(valid_building_sites[0].pos);
}
else if (ok_to_continue)
{
// First valid site is guaranteed to be anchored, either on
// a tile or against a valid orthogonal tile
// Use it as an anchor point to generate materials list
anchor = valid_building_sites.front();
valid_building_sites.pop_front();
valid_building_sites.push_back(anchor);
}
}
if (!ok_to_continue)
{
cancel_box_selection();
hollow_selection = false;
return;
}
saved_revert_setting = revert_to_last_used_type;
revert_to_last_used_type = true;
auto_select_applied = false;
box_select_materials.clear();
}
while (valid_building_sites.size() > 0)
{
building_site site = valid_building_sites.front();
valid_building_sites.pop_front();
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
if (use_buildingplan)
{
// we don't actually care if this fails. buildingplan will return
// false when it filters out bad tiles, and that's ok.
build_with_buildingplan_box_select(site.pos);
continue;
}
if (box_select_materials.size() > 0)
{
df::construction_type type = (df::construction_type) ui_build_selector->building_subtype;
df::item *item = NULL;
while (box_select_materials.size() > 0)
{
item = box_select_materials.front();
if (!item->flags.bits.in_job)
break;
box_select_materials.pop_front();
item = NULL;
}
if (item != NULL)
{
if (designate_new_construction(site.pos, type, item))
{
box_select_materials.pop_front();
box_select_mode = AUTOSELECT_MATERIALS;
send_key(interface_key::LEAVESCREEN); //Must do this to register items in use
send_key(hotkeys[type]);
box_select_mode = SELECT_MATERIALS;
}
continue;
}
}
// Generate material list using regular construction placement routine
if (site.in_open_air)
{
// Cannot invoke material selection on an unconnected tile, use anchor instead
move_cursor(anchor.pos);
send_key(df::interface_key::SELECT);
}
move_cursor(site.pos);
if (!site.in_open_air)
send_key(df::interface_key::SELECT);
if (in_material_choice_stage())
{
valid_building_sites.push_front(site); //Redo current tile with whatever gets selected
if (!auto_select_applied)
{
// See if any auto select materials are available
auto_select_applied = true;
if (auto_choose_materials && populate_box_materials(preferred_materials[ui_build_selector->building_subtype]))
{
continue;
}
}
last_used_moved = false;
return; // No auto select materials left, ask user
}
}
// Allocation done, reset
move_cursor(box_second);
2020-10-29 12:53:31 -06:00
// if we're using buildingplan, we never actually leave the placement
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
// screen, so there's no need to re-enter the screen
revert_to_last_used_type = saved_revert_setting;
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
if (!use_buildingplan && !revert_to_last_used_type)
{
send_key(df::interface_key::LEAVESCREEN);
}
cancel_box_selection();
hollow_selection = false;
ignore_selection = 0;
}
//END Building Application
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input))
{
if (ignore_selection < SELECTION_IGNORE_TICKS)
{
//FIXME: Sometimes there's an extra ENTER key left over after box selection
ignore_selection = SELECTION_IGNORE_TICKS;
return;
2012-11-20 03:12:11 -07:00
}
if (box_select_mode != AUTOSELECT_MATERIALS)
handle_input(input);
2012-11-21 03:45:26 -07:00
int16_t last_used_constr_subtype = (in_material_choice_stage()) ? ui_build_selector->building_subtype : -1;
2012-11-20 03:12:11 -07:00
INTERPOSE_NEXT(feed)(input);
2015-02-14 20:53:06 -07:00
if (revert_to_last_used_type &&
last_used_constr_subtype >= 0 &&
in_type_choice_stage() &&
2012-11-21 03:45:26 -07:00
hotkeys.find(last_used_constr_subtype) != hotkeys.end())
2012-11-20 03:12:11 -07:00
{
input->clear();
2012-11-21 03:45:26 -07:00
input->insert(hotkeys[last_used_constr_subtype]);
INTERPOSE_NEXT(feed)(input);
if (box_select_mode == SELECT_MATERIALS)
{
apply_box_selection(false);
}
2012-11-20 03:12:11 -07:00
}
}
DEFINE_VMETHOD_INTERPOSE(void, render, ())
{
if (ignore_selection < SELECTION_IGNORE_TICKS)
{
++ignore_selection;
}
2012-11-20 03:12:11 -07:00
if (in_material_choice_stage())
{
if (!last_used_moved && ui_build_selector->is_grouped)
2012-11-20 03:12:11 -07:00
{
last_used_moved = true;
if (!box_select_enabled && choose_materials())
2012-11-20 03:12:11 -07:00
{
return;
2012-11-20 03:12:11 -07:00
}
else
2012-11-20 03:12:11 -07:00
{
move_material_to_top(get_last_used_material());
}
}
else if (!ui_build_selector->is_grouped)
{
last_used_moved = false;
}
}
else
{
last_used_moved = false;
}
INTERPOSE_NEXT(render)();
draw_box_selection();
if (in_type_choice_stage())
{
cancel_box_selection();
return;
}
auto dims = Gui::getDwarfmodeViewDims();
int left_margin = dims.menu_x1 + 1;
int x = left_margin;
int y = 25;
2012-11-20 03:12:11 -07:00
if (in_material_choice_stage())
{
MaterialDescriptor material = get_material_in_list(ui_build_selector->sel_index);
if (material.valid)
{
AMOutputToggleString(x, y, "Autoselect", "a", check_autoselect(material, false), true, left_margin);
if (box_select_mode == SELECT_MATERIALS)
{
++y;
OutputString(COLOR_BROWN, x, y, "Construction:", true, left_margin);
2015-11-06 18:04:32 -07:00
OutputString(COLOR_WHITE, x, y, int_to_string(valid_building_sites.size()) + " tiles to fill", true, left_margin);
}
2012-11-20 03:12:11 -07:00
}
}
else if (in_placement_stage() && ui_build_selector->building_subtype < 7)
2012-11-20 03:12:11 -07:00
{
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
bool use_buildingplan = is_buildingplan_managed();
OutputString(COLOR_BROWN, x, y, "DFHack Automaterial Options", true, left_margin);
if (use_buildingplan)
{
y += 2;
}
else
{
AMOutputToggleString(x, y, "Auto Mat-select", "a", auto_choose_materials, true, left_margin);
AMOutputToggleString(x, y, "Reselect Type", "t", revert_to_last_used_type, true, left_margin);
}
++y;
AMOutputToggleString(x, y, "Box Select", "b", box_select_enabled, true, left_margin);
if (box_select_enabled)
{
AMOutputToggleString(x, y, "Show Box Mask", "x", show_box_selection, true, left_margin);
OutputHotkeyString(x, y, (hollow_selection) ? "Make Solid" : "Make Hollow", "h", true, left_margin);
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
if (use_buildingplan)
++y;
else
AMOutputToggleString(x, y, "Open Placement", "o", allow_future_placement, true, left_margin);
}
improve UX between automaterial and buildingplan solves the confusing behavior when both automaterial and buildingplan are enabled for constructions. the two plugins now communicate with each other over the Lua layer to negotiate consistent behavior. if neither plugin is enabled, the standard DF UI acts as normal if automaterial is enabled but buildingplan is not, then automaterial behavior is unchanged. if buildingplan is enabled and automaterial is not then behavior is the same as other buildings with buildingplan (no material selection screen, screen stays on building placement screen after placement). this commit fixes a bug, though, where buildingplan would only lay down a single tile of contruction instead of a solid block when a block is requested. if both plugins are enabled but buildingplan is not enabled for the building type then automaterial is unchanged from previous behavior, execpt for an additional header showing the separation between automaterial hotkeys and buildingplan hotkeys. finally, if both plugins are enabled and buildingplan is enabled for the building type then buildingplan behavior prevails, but the box select and hollow designations features of automaterial are still usable and useful. the 'Auto Mat-select', 'Reselect Type', and "Open Placement" automaterial hotkeys are hidden in the UI and ignored in the feed. This is because buildingplan takes over material selection, so 'Auto Mat-select' doesn't make sense. Buildingplan also already stays on the placement screen after placement, so 'Reselect Type' is not necessary. And all buildingplan-placed buildings have relaxed placement restrictions (e.g. they can be built in mid-air) so 'Open Placement' is also not necessary. The missing options are replaced with blank lines so the vertical alignment of all other options stays constant. we also remove a few extra lua_pop() calls that are made superfluous by the StackUnwinder.
2020-10-29 12:00:49 -06:00
else
{
y += 3;
}
y += 2;
if (is_buildingplan_enabled())
OutputString(COLOR_BROWN, x, y, "DFHack Buildingplan Options", true, left_margin);
if (box_select_enabled)
{
Screen::Pen pen(' ',COLOR_BLACK);
y = dims.y1 + 2;
Screen::fillRect(pen, x, y, dims.menu_x2, y + 17);
y += 2;
switch (box_select_mode)
{
case SELECT_FIRST:
OutputString(COLOR_BROWN, x, y, "Choose first corner", true, left_margin);
break;
case SELECT_SECOND:
2018-04-06 00:18:15 -06:00
{
OutputString(COLOR_GREEN, x, y, "Choose second corner", true, left_margin);
int32_t curr_x, curr_y, curr_z;
Gui::getCursorCoords(curr_x, curr_y, curr_z);
int dX = abs(box_first.x - curr_x) + 1;
int dY = abs(box_first.y - curr_y) + 1;
stringstream label;
label << "Selection: " << dX << "x" << dY;
OutputString(COLOR_WHITE, x, ++y, label.str(), true, left_margin);
int cx = box_first.x;
int cy = box_first.y;
OutputString(COLOR_BROWN, cx, cy, "X", false, 0, 0, true /* map */);
2018-04-06 00:18:15 -06:00
break;
}
default:
break;
}
OutputString(COLOR_BROWN, x, ++y, "Ignore Building Restrictions", true, left_margin);
}
2012-11-20 03:12:11 -07:00
}
}
};
//END Viewscreen Hook
2012-11-20 03:12:11 -07:00
color_ostream_proxy console_out(Core::getInstance().getConsole());
IMPLEMENT_VMETHOD_INTERPOSE(jobutils_hook, feed);
IMPLEMENT_VMETHOD_INTERPOSE(jobutils_hook, render);
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable)
2012-11-20 03:12:11 -07:00
{
if (!gps)
return CR_FAILURE;
if (enable != is_enabled)
{
if (!INTERPOSE_HOOK(jobutils_hook, feed).apply(enable) ||
!INTERPOSE_HOOK(jobutils_hook, render).apply(enable))
return CR_FAILURE;
is_enabled = enable;
}
2012-11-20 03:12:11 -07:00
return CR_OK;
}
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
2012-11-21 03:45:26 -07:00
hotkeys[construction_type::Wall] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_WALL;
hotkeys[construction_type::Floor] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_FLOOR;
hotkeys[construction_type::Ramp] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_RAMP;
hotkeys[construction_type::UpStair] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_STAIR_UP;
hotkeys[construction_type::DownStair] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_STAIR_DOWN;
hotkeys[construction_type::UpDownStair] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_STAIR_UPDOWN;
hotkeys[construction_type::Fortification] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_FORTIFICATION;
//Ignore tracks, DF already returns to track menu
2012-11-20 03:12:11 -07:00
return CR_OK;
}