Merge pull request #2975 from myk002/myk_stockpiles

[stockpiles] get savestock and loadstock minimally functional
develop
Myk 2023-03-06 00:24:30 -08:00 committed by GitHub
commit e4596e6d2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 25 additions and 415 deletions

@ -5,32 +5,20 @@ stockpiles
.. dfhack-tool:: .. dfhack-tool::
:summary: Import and export stockpile settings. :summary: Import and export stockpile settings.
:tags: untested fort design productivity stockpiles :tags: fort design productivity stockpiles
:no-command: :no-command:
.. dfhack-command:: copystock
:summary: Copies the configuration of the selected stockpile.
.. dfhack-command:: savestock .. dfhack-command:: savestock
:summary: Exports the configuration of the selected stockpile. :summary: Exports the configuration of the selected stockpile.
.. dfhack-command:: loadstock .. dfhack-command:: loadstock
:summary: Imports the configuration of the selected stockpile. :summary: Imports configuration for the selected stockpile.
When the plugin is enabled, the :kbd:`q` menu of each stockpile will have an Select a stockpile in the UI first to use these commands.
option for saving or loading the stockpile settings. See `gui/stockpiles` for
an in-game interface.
Usage Usage
----- -----
``enable stockpiles``
Add a hotkey that you can hit to easily save and load settings from
stockpiles selected in :kbd:`q` mode.
``copystock``
Copies the parameters of the currently highlighted stockpile to the custom
stockpile settings and switches to custom stockpile placement mode,
effectively allowing you to copy/paste stockpiles easily.
``savestock <filename>`` ``savestock <filename>``
Saves the currently highlighted stockpile's settings to a file in your Saves the currently highlighted stockpile's settings to a file in your
Dwarf Fortress folder. This file can be used to copy settings between game Dwarf Fortress folder. This file can be used to copy settings between game
@ -45,9 +33,9 @@ etc. are not saved as they are different in every world.
Examples Examples
-------- --------
``savestock food_settings.dfstock`` ``savestock food``
Export the stockpile settings for the stockpile currently selected in Export the stockpile settings for the currently selected stockpile to a
:kbd:`q` mode to a file named ``food_settings.dfstock``. file named ``food.dfstock``.
``loadstock food_settings.dfstock`` ``loadstock food``
Set the selected stockpile settings to those saved in the Set the selected stockpile settings to those saved in the ``food.dfstock``
``food_settings.dfstock`` file. file.

@ -156,7 +156,7 @@ dfhack_plugin(showmood showmood.cpp)
#dfhack_plugin(steam-engine steam-engine.cpp) #dfhack_plugin(steam-engine steam-engine.cpp)
#add_subdirectory(spectate) #add_subdirectory(spectate)
#dfhack_plugin(stockflow stockflow.cpp LINK_LIBRARIES lua) #dfhack_plugin(stockflow stockflow.cpp LINK_LIBRARIES lua)
#add_subdirectory(stockpiles) add_subdirectory(stockpiles)
#dfhack_plugin(stocks stocks.cpp) #dfhack_plugin(stocks stocks.cpp)
dfhack_plugin(strangemood strangemood.cpp) dfhack_plugin(strangemood strangemood.cpp)
dfhack_plugin(tailor tailor.cpp LINK_LIBRARIES lua) dfhack_plugin(tailor tailor.cpp LINK_LIBRARIES lua)

@ -474,7 +474,6 @@ void StockpileSerializer::write_general()
mBuffer.set_max_wheelbarrows ( mPile->max_wheelbarrows ); mBuffer.set_max_wheelbarrows ( mPile->max_wheelbarrows );
mBuffer.set_max_barrels ( mPile->max_barrels ); mBuffer.set_max_barrels ( mPile->max_barrels );
mBuffer.set_use_links_only ( mPile->use_links_only ); mBuffer.set_use_links_only ( mPile->use_links_only );
mBuffer.set_unknown1 ( mPile->settings.unk1 );
mBuffer.set_allow_inorganic ( mPile->settings.allow_inorganic ); mBuffer.set_allow_inorganic ( mPile->settings.allow_inorganic );
mBuffer.set_allow_organic ( mPile->settings.allow_organic ); mBuffer.set_allow_organic ( mPile->settings.allow_organic );
mBuffer.set_corpses ( mPile->settings.flags.bits.corpses ); mBuffer.set_corpses ( mPile->settings.flags.bits.corpses );
@ -490,8 +489,6 @@ void StockpileSerializer::read_general()
mPile->max_barrels = mBuffer.max_barrels(); mPile->max_barrels = mBuffer.max_barrels();
if ( mBuffer.has_use_links_only() ) if ( mBuffer.has_use_links_only() )
mPile->use_links_only = mBuffer.use_links_only(); mPile->use_links_only = mBuffer.use_links_only();
if ( mBuffer.has_unknown1() )
mPile->settings.unk1 = mBuffer.unknown1();
if ( mBuffer.has_allow_inorganic() ) if ( mBuffer.has_allow_inorganic() )
mPile->settings.allow_inorganic = mBuffer.allow_inorganic(); mPile->settings.allow_inorganic = mBuffer.allow_inorganic();
if ( mBuffer.has_allow_organic() ) if ( mBuffer.has_allow_organic() )

@ -136,7 +136,7 @@ message StockpileSettings {
optional AnimalsSet animals = 1; optional AnimalsSet animals = 1;
optional FoodSet food = 2; optional FoodSet food = 2;
optional FurnitureSet furniture = 3; optional FurnitureSet furniture = 3;
optional int32 unknown1 = 4; optional int32 unknown1 = 4 [deprecated=true];
optional RefuseSet refuse = 5; optional RefuseSet refuse = 5;
optional StoneSet stone = 6; optional StoneSet stone = 6;
optional OreSet ore = 7; optional OreSet ore = 7;

@ -1,82 +1,32 @@
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h" #include "PluginManager.h"
#include "DataFuncs.h"
#include "LuaTools.h"
#include "modules/Filesystem.h"
#include "../uicommon.h"
#include "StockpileUtils.h" #include "StockpileUtils.h"
#include "StockpileSerializer.h" #include "StockpileSerializer.h"
#include "modules/Filesystem.h" #include "modules/Filesystem.h"
#include "modules/Gui.h" #include "modules/Gui.h"
#include "modules/Filesystem.h"
#include "df/world.h"
#include "df/world_data.h"
#include "DataDefs.h"
#include "df/plotinfost.h"
#include "df/building_stockpilest.h"
#include "df/stockpile_settings.h"
#include "df/global_objects.h"
#include "df/viewscreen_dwarfmodest.h"
// stl
#include <functional>
#include <vector>
using std::vector; using std::vector;
using std::string; using std::string;
using std::endl;
using namespace DFHack;
using namespace df::enums;
using namespace google::protobuf;
using namespace dfstockpiles;
DFHACK_PLUGIN ( "stockpiles" );
REQUIRE_GLOBAL(gps);
REQUIRE_GLOBAL(world);
REQUIRE_GLOBAL(plotinfo);
REQUIRE_GLOBAL(selection_rect);
using df::building_stockpilest; using namespace DFHack;
using std::placeholders::_1;
static command_result copystock ( color_ostream &out, vector <string> & parameters ); DFHACK_PLUGIN("stockpiles");
static bool copystock_guard ( df::viewscreen *top );
static command_result savestock ( color_ostream &out, vector <string> & parameters ); static command_result savestock ( color_ostream &out, vector <string> & parameters );
static bool savestock_guard ( df::viewscreen *top );
static command_result loadstock ( color_ostream &out, vector <string> & parameters ); static command_result loadstock ( color_ostream &out, vector <string> & parameters );
static bool loadstock_guard ( df::viewscreen *top );
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands ) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands )
{ {
if ( world && plotinfo ) commands.push_back(PluginCommand(
{ "savestock",
commands.push_back(PluginCommand( "Save the active stockpile's settings to a file.",
"copystock", savestock,
"Copy stockpile under cursor.", Gui::any_stockpile_hotkey));
copystock, commands.push_back(PluginCommand(
copystock_guard)); "loadstock",
commands.push_back(PluginCommand( "Load and apply stockpile settings from a file.",
"savestock", loadstock,
"Save the active stockpile's settings to a file.", Gui::any_stockpile_hotkey));
savestock,
savestock_guard));
commands.push_back(PluginCommand(
"loadstock",
"Load and apply stockpile settings from a file.",
loadstock,
loadstock_guard));
}
return CR_OK; return CR_OK;
} }
@ -86,111 +36,10 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
return CR_OK; return CR_OK;
} }
DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event event )
{
switch ( event )
{
case SC_MAP_LOADED:
break;
default:
break;
}
return CR_OK;
}
static bool copystock_guard ( df::viewscreen *top )
{
using namespace ui_sidebar_mode;
if ( !Gui::dwarfmode_hotkey ( top ) )
return false;
switch ( plotinfo->main.mode )
{
case Stockpiles:
return true;
case BuildingItems:
case QueryBuilding:
return !!virtual_cast<building_stockpilest> ( world->selected_building );
default:
return false;
}
}
static command_result copystock ( color_ostream &out, vector <string> & parameters )
{
// HOTKEY COMMAND: CORE ALREADY SUSPENDED
// For convenience: when used in the stockpiles mode, switch to 'q'
if ( plotinfo->main.mode == ui_sidebar_mode::Stockpiles )
{
world->selected_building = NULL; // just in case it contains some kind of garbage
plotinfo->main.mode = ui_sidebar_mode::QueryBuilding;
selection_rect->start_x = -30000;
out << "Switched back to query building." << endl;
return CR_OK;
}
building_stockpilest *sp = virtual_cast<building_stockpilest> ( world->selected_building );
if ( !sp )
{
out.printerr ( "Selected building isn't a stockpile.\n" );
return CR_WRONG_USAGE;
}
plotinfo->stockpile.custom_settings = sp->settings;
plotinfo->main.mode = ui_sidebar_mode::Stockpiles;
world->selected_stockpile_type = stockpile_category::Custom;
out << "Stockpile options copied." << endl;
return CR_OK;
}
static bool savestock_guard ( df::viewscreen *top )
{
using namespace ui_sidebar_mode;
if ( !Gui::dwarfmode_hotkey ( top ) )
return false;
switch ( plotinfo->main.mode )
{
case Stockpiles:
return true;
case BuildingItems:
case QueryBuilding:
return !!virtual_cast<building_stockpilest> ( world->selected_building );
default:
return false;
}
}
static bool loadstock_guard ( df::viewscreen *top )
{
using namespace ui_sidebar_mode;
if ( !Gui::dwarfmode_hotkey ( top ) )
return false;
switch ( plotinfo->main.mode )
{
case Stockpiles:
return true;
case BuildingItems:
case QueryBuilding:
return !!virtual_cast<building_stockpilest> ( world->selected_building );
default:
return false;
}
}
// exporting // exporting
static command_result savestock ( color_ostream &out, vector <string> & parameters ) static command_result savestock ( color_ostream &out, vector <string> & parameters )
{ {
building_stockpilest *sp = virtual_cast<building_stockpilest> ( world->selected_building ); df::building_stockpilest *sp = Gui::getSelectedStockpile(out, true);
if ( !sp ) if ( !sp )
{ {
out.printerr ( "Selected building isn't a stockpile.\n" ); out.printerr ( "Selected building isn't a stockpile.\n" );
@ -247,7 +96,7 @@ static command_result savestock ( color_ostream &out, vector <string> & paramete
// importing // importing
static command_result loadstock ( color_ostream &out, vector <string> & parameters ) static command_result loadstock ( color_ostream &out, vector <string> & parameters )
{ {
building_stockpilest *sp = virtual_cast<building_stockpilest> ( world->selected_building ); df::building_stockpilest *sp = Gui::getSelectedStockpile(out, true);
if ( !sp ) if ( !sp )
{ {
out.printerr ( "Selected building isn't a stockpile.\n" ); out.printerr ( "Selected building isn't a stockpile.\n" );
@ -302,227 +151,3 @@ static command_result loadstock ( color_ostream &out, vector <string> & paramete
} }
return CR_OK; return CR_OK;
} }
/**
* calls the lua function manage_settings() to kickoff the GUI
*/
bool manage_settings ( building_stockpilest *sp )
{
auto L = Lua::Core::State;
color_ostream_proxy out ( Core::getInstance().getConsole() );
CoreSuspendClaimer suspend;
Lua::StackUnwinder top ( L );
if ( !lua_checkstack ( L, 2 ) )
return false;
if ( !Lua::PushModulePublic ( out, L, "plugins.stockpiles", "manage_settings" ) )
return false;
Lua::Push ( L, sp );
if ( !Lua::SafeCall ( out, L, 1, 2 ) )
return false;
return true;
}
bool show_message_box ( const std::string & title, const std::string & msg, bool is_error = false )
{
auto L = Lua::Core::State;
color_ostream_proxy out ( Core::getInstance().getConsole() );
CoreSuspendClaimer suspend;
Lua::StackUnwinder top ( L );
if ( !lua_checkstack ( L, 4 ) )
return false;
if ( !Lua::PushModulePublic ( out, L, "plugins.stockpiles", "show_message_box" ) )
return false;
Lua::Push ( L, title );
Lua::Push ( L, msg );
Lua::Push ( L, is_error );
if ( !Lua::SafeCall ( out, L, 3, 0 ) )
return false;
return true;
}
struct stockpiles_import_hook : public df::viewscreen_dwarfmodest
{
typedef df::viewscreen_dwarfmodest interpose_base;
bool handleInput ( set<df::interface_key> *input )
{
if ( Gui::inRenameBuilding() )
return false;
df::building_stockpilest *sp = get_selected_stockpile();
if ( !sp )
return false;
if ( input->count ( interface_key::CUSTOM_L ) )
{
manage_settings ( sp );
return true;
}
return false;
}
DEFINE_VMETHOD_INTERPOSE ( void, feed, ( set<df::interface_key> *input ) )
{
if ( !handleInput ( input ) )
INTERPOSE_NEXT ( feed ) ( input );
}
DEFINE_VMETHOD_INTERPOSE ( void, render, () )
{
INTERPOSE_NEXT ( render ) ();
df::building_stockpilest *sp = get_selected_stockpile();
if ( !sp )
return;
auto dims = Gui::getDwarfmodeViewDims();
int left_margin = dims.menu_x1 + 1;
int x = left_margin;
int y = dims.y2 - 3; // below autodump, automelt, autotrade, stocks; above stockflow
OutputHotkeyString ( x, y, "Load/Save Settings", "l", true, left_margin, COLOR_WHITE, COLOR_LIGHTRED );
}
};
IMPLEMENT_VMETHOD_INTERPOSE ( stockpiles_import_hook, feed );
IMPLEMENT_VMETHOD_INTERPOSE ( stockpiles_import_hook, render );
DFHACK_PLUGIN_IS_ENABLED ( is_enabled );
DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable )
{
if ( !gps )
return CR_FAILURE;
if ( enable != is_enabled )
{
if (
!INTERPOSE_HOOK ( stockpiles_import_hook, feed ).apply ( enable ) ||
!INTERPOSE_HOOK ( stockpiles_import_hook, render ).apply ( enable )
)
return CR_FAILURE;
is_enabled = enable;
}
return CR_OK;
}
static std::vector<std::string> list_dir ( const std::string &path, bool recursive = false )
{
// color_ostream_proxy out ( Core::getInstance().getConsole() );
std::vector<std::string> files;
std::stack<std::string> dirs;
dirs.push(path);
// out << "list_dir start" << endl;
while (!dirs.empty() ) {
const std::string current = dirs.top();
// out << "\t walking " << current << endl;
dirs.pop();
std::vector<std::string> entries;
const int res = DFHack::getdir(current, entries);
if ( res != 0 )
continue;
for ( std::vector<std::string>::iterator it = entries.begin() ; it != entries.end(); ++it )
{
if ( (*it).empty() || (*it)[0] == '.' ) continue;
// shitty cross platform c++ we've got to construct the actual path manually
std::ostringstream child_path_s;
child_path_s << current << "/" << *it;
const std::string child = child_path_s.str();
if ( recursive && Filesystem::isdir ( child ) )
{
// out << "\t\tgot child dir: " << child << endl;
dirs.push ( child );
}
else if ( Filesystem::isfile ( child ) )
{
const std::string rel_path ( child.substr ( std::string ( "./"+path).length()-1 ) );
// out << "\t\t adding file: " << child << " as " << rel_path << endl;
files.push_back ( rel_path );
}
}
}
// out << "listdir_stop" << endl;
return files;
}
static std::vector<std::string> clean_dfstock_list ( const std::string &path )
{
if ( !Filesystem::exists ( path ) )
{
return std::vector<std::string>();
}
std::vector<std::string> files ( list_dir ( path, true) );
files.erase ( std::remove_if ( files.begin(), files.end(), [] ( const std::string &f )
{
return !is_dfstockfile ( f );
} ), files.end() );
std::transform ( files.begin(), files.end(), files.begin(), [] ( const std::string &f )
{
return f.substr ( 0, f.find_last_of ( "." ) );
} );
std::sort ( files.begin(),files.end(), CompareNoCase );
return files;
}
static int stockpiles_list_settings ( lua_State *L )
{
auto path = luaL_checkstring ( L, 1 );
if ( Filesystem::exists ( path ) && !Filesystem::isdir ( path ) )
{
lua_pushfstring ( L, "stocksettings path invalid: %s", path );
lua_error ( L );
return 0;
}
std::vector<std::string> files = clean_dfstock_list ( path );
Lua::PushVector ( L, files, true );
return 1;
}
const std::string err_title = "Stockpile Settings Error";
const std::string err_help = "Does the folder exist?\nCheck the console for more information.";
static void stockpiles_load ( color_ostream &out, std::string filename )
{
std::vector<std::string> params;
params.push_back ( filename );
command_result r = loadstock ( out, params );
if ( r != CR_OK )
show_message_box ( err_title, "Couldn't load. " + err_help, true );
}
static void stockpiles_save ( color_ostream &out, std::string filename )
{
std::vector<std::string> params;
params.push_back ( filename );
command_result r = savestock ( out, params );
if ( r != CR_OK )
show_message_box ( err_title, "Couldn't save. " + err_help, true );
}
DFHACK_PLUGIN_LUA_FUNCTIONS
{
DFHACK_LUA_FUNCTION ( stockpiles_load ),
DFHACK_LUA_FUNCTION ( stockpiles_save ),
DFHACK_LUA_END
};
DFHACK_PLUGIN_LUA_COMMANDS
{
DFHACK_LUA_COMMAND ( stockpiles_list_settings ),
DFHACK_LUA_END
};