From fa4fb4b407b701d707ac3e3a4a0e6804cca2c526 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 28 Jan 2012 16:03:56 +0400 Subject: [PATCH] Modify a number of commands to use CR_WRONG_USAGE for displaying help. --- library/Core.cpp | 1 + plugins/autodump.cpp | 29 +++++++------- plugins/cleaners.cpp | 50 ++++++++++++------------ plugins/cleanowned.cpp | 44 +++++++++------------ plugins/colonies.cpp | 27 +++++-------- plugins/deramp.cpp | 23 +++++------ plugins/filltraffic.cpp | 73 ++++++++++++++++++----------------- plugins/getplants.cpp | 25 ++++++------ plugins/mode.cpp | 19 ++++----- plugins/prospector.cpp | 25 ++++++------ plugins/skeleton/skeleton.cpp | 70 +++++++++++++++++++++++---------- plugins/weather.cpp | 25 ++++++------ plugins/workflow.cpp | 5 +++ 13 files changed, 212 insertions(+), 204 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index 0d02861a0..81dafdd76 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -204,6 +204,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue "by clicking on the program icon in the top bar of the window.\n\n" "Basic commands:\n" " help|? - This text.\n" + " help COMMAND - Usage help for the given command.\n" " ls|dir [PLUGIN] - List available commands. Optionally for single plugin.\n" " cls - Clear the console.\n" " fpause - Force DF to pause.\n" diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index f726502ce..7b9c9d2c7 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -44,9 +44,17 @@ DFhackCExport const char * plugin_name ( void ) DFhackCExport command_result plugin_init ( Core * c, std::vector &commands) { commands.clear(); - commands.push_back(PluginCommand("autodump", - "Teleport items marked for dumping to the cursor.", - df_autodump)); + commands.push_back(PluginCommand( + "autodump", "Teleport items marked for dumping to the cursor.", + df_autodump, false, + " This utility lets you quickly move all items designated to be dumped.\n" + " Items are instantly moved to the cursor position, the dump flag is unset,\n" + " and the forbid flag is set, as if it had been dumped normally.\n" + " Be aware that any active dump item tasks still point at the item.\n" + "Options:\n" + " destroy - instead of dumping, destroy the items instantly.\n" + " destroy-here - only affect the tile under cursor.\n" + )); commands.push_back(PluginCommand( "autodump-destroy-here", "Destroy items marked for dumping under cursor.", df_autodump_destroy_here, cursor_hotkey, @@ -81,19 +89,8 @@ static command_result autodump_main(Core * c, vector & parameters) destroy = true; else if (p == "destroy-here") destroy = here = true; - else if(p == "?" || p == "help") - { - c->con.print( - "This utility lets you quickly move all items designated to be dumped.\n" - "Items are instantly moved to the cursor position, the dump flag is unset,\n" - "and the forbid flag is set, as if it had been dumped normally.\n" - "Be aware that any active dump item tasks still point at the item.\n\n" - "Options:\n" - "destroy - instead of dumping, destroy the items instantly.\n" - "destroy-here - only affect the tile under cursor.\n" - ); - return CR_OK; - } + else + return CR_WRONG_USAGE; } DFHack::VersionInfo *mem = c->vinfo; diff --git a/plugins/cleaners.cpp b/plugins/cleaners.cpp index 2ba87717f..8fb5af93c 100644 --- a/plugins/cleaners.cpp +++ b/plugins/cleaners.cpp @@ -148,7 +148,6 @@ DFhackCExport command_result spotclean (Core * c, vector & parameters) DFhackCExport command_result clean (Core * c, vector & parameters) { - bool help = false; bool map = false; bool snow = false; bool mud = false; @@ -168,33 +167,16 @@ DFhackCExport command_result clean (Core * c, vector & parameters) items = true; units = true; } - if(parameters[i] == "snow") + else if(parameters[i] == "snow") snow = true; else if(parameters[i] == "mud") mud = true; - else if(parameters[i] == "help" ||parameters[i] == "?") - { - help = true; - } + else + return CR_WRONG_USAGE; } if(!map && !units && !items) - help = true; - if(help) - { - c->con.print("Removes contaminants from map tiles, items and creatures.\n" - "Options:\n" - "map - clean the map tiles\n" - "items - clean all items\n" - "units - clean all creatures\n" - "all - clean everything.\n" - "More options for 'map':\n" - "snow - also remove snow\n" - "mud - also remove mud\n" - "Example: clean all mud snow\n" - "This removes all spatter, including mud and snow from map tiles.\n" - ); - return CR_OK; - } + return CR_WRONG_USAGE; + CoreSuspender suspend(c); if(map) cleanmap(c,snow,mud); @@ -213,8 +195,26 @@ DFhackCExport const char * plugin_name ( void ) DFhackCExport command_result plugin_init ( Core * c, std::vector &commands) { commands.clear(); - commands.push_back(PluginCommand("clean","Removes contaminants from map tiles, items and creatures.",clean)); - commands.push_back(PluginCommand("spotclean","Cleans map tile under cursor.",spotclean,cursor_hotkey)); + commands.push_back(PluginCommand( + "clean","Removes contaminants from map tiles, items and creatures.", + clean, false, + " Removes contaminants from map tiles, items and creatures.\n" + "Options:\n" + " map - clean the map tiles\n" + " items - clean all items\n" + " units - clean all creatures\n" + " all - clean everything.\n" + "More options for 'map':\n" + " snow - also remove snow\n" + " mud - also remove mud\n" + "Example:\n" + " clean all mud snow\n" + " Removes all spatter, including mud and snow from map tiles.\n" + )); + commands.push_back(PluginCommand( + "spotclean","Cleans map tile under cursor.", + spotclean,cursor_hotkey + )); return CR_OK; } diff --git a/plugins/cleanowned.cpp b/plugins/cleanowned.cpp index dec591639..a2ec4b763 100644 --- a/plugins/cleanowned.cpp +++ b/plugins/cleanowned.cpp @@ -36,9 +36,23 @@ DFhackCExport const char * plugin_name ( void ) DFhackCExport command_result plugin_init ( Core * c, std::vector &commands) { commands.clear(); - commands.push_back(PluginCommand("cleanowned", - "Confiscates and dumps garbage owned by dwarfs.", - df_cleanowned)); + commands.push_back(PluginCommand( + "cleanowned", "Confiscates and dumps garbage owned by dwarfs.", + df_cleanowned, false, + " This tool lets you confiscate and dump all the garbage\n" + " dwarves ultimately accumulate.\n" + " By default, only rotten and dropped food is confiscated.\n" + "Options:\n" + " dryrun - don't actually do anything, just print what would be done.\n" + " scattered - confiscate owned items on the ground\n" + " all - confiscate everything\n" + " x - confiscate & dump 'x' and worse damaged items\n" + " X - confiscate & dump 'X' and worse damaged items\n" + "Example:\n" + " confiscate scattered X\n" + " This will confiscate rotten and dropped food, garbage on the floors\n" + " and any worn items wit 'X' damage and above.\n" + )); return CR_OK; } @@ -67,30 +81,8 @@ DFhackCExport command_result df_cleanowned (Core * c, vector & paramete wear_dump_level = 1; else if(param == "X") wear_dump_level = 2; - else if(param == "?" || param == "help") - { - c->con.print("This tool lets you confiscate and dump all the garbage\n" - "dwarves ultimately accumulate.\n" - "By default, only rotten and dropped food is confiscated.\n" - "Options:\n" - " dryrun - don't actually do anything, just print what would be done.\n" - " scattered - confiscate owned items on the ground\n" - " all - confiscate everything\n" - " x - confiscate & dump 'x' and worse damaged items\n" - " X - confiscate & dump 'X' and worse damaged items\n" - " ? - this help\n" - "Example:\n" - " confiscate scattered X\n" - " This will confiscate rotten and dropped food, garbage on the floors\n" - " and any worn items wit 'X' damage and above.\n" - ); - return CR_OK; - } else - { - c->con.printerr("Parameter '%s' is not valid. See 'cleanowned help'.\n",param.c_str()); - return CR_FAILURE; - } + return CR_WRONG_USAGE; } CoreSuspender suspend(c); diff --git a/plugins/colonies.cpp b/plugins/colonies.cpp index 9ebbc8a92..7b9028997 100644 --- a/plugins/colonies.cpp +++ b/plugins/colonies.cpp @@ -22,9 +22,14 @@ DFhackCExport const char * plugin_name ( void ) DFhackCExport command_result plugin_init ( Core * c, std::vector &commands) { commands.clear(); - commands.push_back(PluginCommand("colonies", - "List or change wild colonies (ants hills and such)", - colonies)); + commands.push_back(PluginCommand( + "colonies", "List or change wild colonies (ants hills and such)", + colonies, false, + " Without any options, this command lists all the vermin colonies present.\n" + "Options:\n" + " kill - destroy colonies\n" + " bees - turn colonies into honey bees\n" + )); return CR_OK; } @@ -41,7 +46,6 @@ DFhackCExport command_result colonies (Core * c, vector & parameters) { bool destroy = false; bool convert = false; - bool help = false; for(int i = 0; i < parameters.size();i++) { @@ -49,19 +53,8 @@ DFhackCExport command_result colonies (Core * c, vector & parameters) destroy = true; else if(parameters[i] == "bees") convert = true; - else if(parameters[i] == "help" || parameters[i] == "?") - { - help = true; - } - } - if(help) - { - c->con.print("Without any options, this command lists all the vermin colonies present.\n" - "Options:\n" - "kill - destroy colonies\n" - "bees - turn colonies into honey bees\n" - ); - return CR_OK; + else + return CR_WRONG_USAGE; } if (destroy && convert) { diff --git a/plugins/deramp.cpp b/plugins/deramp.cpp index bc3e4b7ab..bb59238ad 100644 --- a/plugins/deramp.cpp +++ b/plugins/deramp.cpp @@ -19,17 +19,8 @@ using df::global::world; DFhackCExport command_result df_deramp (Core * c, vector & parameters) { - for(int i = 0; i < parameters.size();i++) - { - if(parameters[i] == "help" || parameters[i] == "?") - { - c->con.print("This command does two things:\n" - "If there are any ramps designated for removal, they will be instantly removed.\n" - "Any ramps that don't have their counterpart will be removed (fixes bugs with caveins)\n" - ); - return CR_OK; - } - } + if (!parameters.empty()) + return CR_WRONG_USAGE; CoreSuspender suspend(c); @@ -97,9 +88,13 @@ DFhackCExport const char * plugin_name ( void ) DFhackCExport command_result plugin_init ( Core * c, std::vector &commands) { commands.clear(); - commands.push_back(PluginCommand("deramp", - "De-ramp. All ramps marked for removal are replaced with floors.", - df_deramp)); + commands.push_back(PluginCommand( + "deramp", "De-ramp. All ramps marked for removal are replaced with floors.", + df_deramp, false, + " If there are any ramps designated for removal, they will be instantly\n" + " removed. Any ramps that don't have their counterpart will also be removed\n" + " (fixes bugs with caveins)\n" + )); return CR_OK; } diff --git a/plugins/filltraffic.cpp b/plugins/filltraffic.cpp index ef5a9b617..042ca0d1d 100644 --- a/plugins/filltraffic.cpp +++ b/plugins/filltraffic.cpp @@ -42,9 +42,34 @@ DFhackCExport const char * plugin_name ( void ) DFhackCExport command_result plugin_init ( Core * c, std::vector &commands) { commands.clear(); - commands.push_back(PluginCommand("filltraffic","Flood-fill with selected traffic designation from cursor",filltraffic)); - commands.push_back(PluginCommand("alltraffic","Set traffic for the entire map",alltraffic)); - + commands.push_back(PluginCommand( + "filltraffic","Flood-fill with selected traffic designation from cursor", + filltraffic, cursor_hotkey, + " Flood-fill selected traffic type from the cursor.\n" + "Traffic Type Codes:\n" + " H: High Traffic\n" + " N: Normal Traffic\n" + " L: Low Traffic\n" + " R: Restricted Traffic\n" + "Other Options:\n" + " X: Fill across z-levels.\n" + " B: Include buildings and stockpiles.\n" + " P: Include empty space.\n" + "Example:\n" + " filltraffic H\n" + " When used in a room with doors,\n" + " it will set traffic to HIGH in just that room.\n" + )); + commands.push_back(PluginCommand( + "alltraffic","Set traffic for the entire map", + alltraffic, false, + " Set traffic types for all tiles on the map.\n" + "Traffic Type Codes:\n" + " H: High Traffic\n" + " N: Normal Traffic\n" + " L: Low Traffic\n" + " R: Restricted Traffic\n" + )); return CR_OK; } @@ -55,6 +80,8 @@ DFhackCExport command_result plugin_shutdown ( Core * c ) DFhackCExport command_result filltraffic(DFHack::Core * c, std::vector & params) { + // HOTKEY COMMAND; CORE ALREADY SUSPENDED + //Maximum map size. uint32_t x_max,y_max,z_max; //Source and target traffic types. @@ -68,24 +95,8 @@ DFhackCExport command_result filltraffic(DFHack::Core * c, std::vectorcon.print("Flood-fill selected traffic type from the cursor.\n" - "Traffic Type Codes:\n" - "\tH: High Traffic\n" - "\tN: Normal Traffic\n" - "\tL: Low Traffic\n" - "\tR: Restricted Traffic\n" - "Other Options:\n" - "\tX: Fill across z-levels.\n" - "\tB: Include buildings and stockpiles.\n" - "\tP: Include empty space.\n" - "Example:\n" - "'filltraffic H' - When used in a room with doors,\n" - " it will set traffic to HIGH in just that room." - ); - return CR_OK; - } + if (params[i] == "help" || params[i] == "?" || params[i].size() != 1) + return CR_WRONG_USAGE; switch (toupper(params[i][0])) { @@ -103,12 +114,11 @@ DFhackCExport command_result filltraffic(DFHack::Core * c, std::vectorgetGui(); if (!Maps::IsValid()) { @@ -238,17 +248,8 @@ DFhackCExport command_result alltraffic(DFHack::Core * c, std::vectorcon.print("Set traffic types for all tiles on the map.\n" - "Traffic Type Codes:\n" - " H: High Traffic\n" - " N: Normal Traffic\n" - " L: Low Traffic\n" - " R: Restricted Traffic\n" - ); - return CR_OK; - } + if (params[i] == "help" || params[i] == "?" || params[i].size() != 1) + return CR_WRONG_USAGE; //Pick traffic type. Possibly set bounding rectangle later. switch (toupper(params[i][0])) @@ -261,6 +262,8 @@ DFhackCExport command_result alltraffic(DFHack::Core * c, std::vector & parameter for (size_t i = 0; i < parameters.size(); i++) { if(parameters[i] == "help" || parameters[i] == "?") - { - c->con.print("Specify the types of trees to cut down and/or shrubs to gather by their plant IDs, separated by spaces.\n" - "Options:\n" - "\t-t - Select trees only (exclude shrubs)\n" - "\t-s - Select shrubs only (exclude trees)\n" - "\t-c - Clear designations instead of setting them\n" - "\t-x - Apply selected action to all plants except those specified\n" - "Specifying both -t and -s will have no effect.\n" - "If no plant IDs are specified, all valid plant IDs will be listed.\n" - ); return CR_WRONG_USAGE; - } else if(parameters[i] == "-t") treesonly = true; else if(parameters[i] == "-s") @@ -152,7 +141,19 @@ DFhackCExport const char * plugin_name ( void ) DFhackCExport command_result plugin_init ( Core * c, vector &commands) { commands.clear(); - commands.push_back(PluginCommand("getplants", "Cut down all of the specified trees or gather all of the specified shrubs", df_getplants)); + commands.push_back(PluginCommand( + "getplants", "Cut down all of the specified trees or gather specified shrubs", + df_getplants, false, + " Specify the types of trees to cut down and/or shrubs to gather by their\n" + " plant IDs, separated by spaces.\n" + "Options:\n" + " -t - Select trees only (exclude shrubs)\n" + " -s - Select shrubs only (exclude trees)\n" + " -c - Clear designations instead of setting them\n" + " -x - Apply selected action to all plants except those specified\n" + "Specifying both -t and -s will have no effect.\n" + "If no plant IDs are specified, all valid plant IDs will be listed.\n" + )); return CR_OK; } diff --git a/plugins/mode.cpp b/plugins/mode.cpp index 89728a341..e8ef7fa4a 100644 --- a/plugins/mode.cpp +++ b/plugins/mode.cpp @@ -22,7 +22,13 @@ DFhackCExport const char * plugin_name ( void ) DFhackCExport command_result plugin_init ( Core * c, std::vector &commands) { commands.clear(); - commands.push_back(PluginCommand("mode","View, change and track game mode.", mode, true)); + commands.push_back(PluginCommand( + "mode","View, change and track game mode.", + mode, true, + " Without any parameters, this command prints the current game mode\n" + " You can interactively set the game mode with 'mode set'.\n" + "!!Setting the game modes can be dangerous and break your game!!\n" + )); return CR_OK; } @@ -96,17 +102,8 @@ DFhackCExport command_result mode (Core * c, vector & parameters) { set = true; } - else if(parameters[0] == "?" || parameters[0] == "help") - { - c->con.print("Without any parameters, this command prints the current game mode\n" - "You can interactively set the game mode with 'mode set'.\n"); - c->con.printerr("!!Setting the game modes can be dangerous and break your game!!\n"); - return CR_OK; - } else - { - c->con.printerr("Unrecognized parameter: %s\n",parameters[0].c_str()); - } + return CR_WRONG_USAGE; } c->Suspend(); World *world = c->getWorld(); diff --git a/plugins/prospector.cpp b/plugins/prospector.cpp index 861b09d33..4727460f9 100644 --- a/plugins/prospector.cpp +++ b/plugins/prospector.cpp @@ -178,7 +178,16 @@ DFhackCExport const char * plugin_name ( void ) DFhackCExport command_result plugin_init ( Core * c, std::vector &commands) { commands.clear(); - commands.push_back(PluginCommand("prospect","Show stats of available raw resources. Use option 'all' to show hidden resources.",prospector)); + commands.push_back(PluginCommand( + "prospect", "Show stats of available raw resources.", + prospector, false, + " Prints a big list of all the present minerals.\n" + " By default, only the visible part of the map is scanned.\n" + "Options:\n" + " all - Scan the whole map, as if it was revealed.\n" + " value - Show material value in the output. Most useful for gems.\n" + " hell - Show the Z range of HFS tubes. Implies 'all'.\n" + )); return CR_OK; } @@ -210,18 +219,8 @@ DFhackCExport command_result prospector (DFHack::Core * c, vector & par { showHidden = showTube = true; } - else if(parameters[i] == "help" || parameters[i] == "?") - { - c->con.print("Prints a big list of all the present minerals.\n" - "By default, only the visible part of the map is scanned.\n" - "\n" - "Options:\n" - "all - Scan the whole map, as if it was revealed.\n" - "value - Show material value in the output.\n" - "hell - Show the Z range of HFS tubes.\n" - ); - return CR_OK; - } + else + return CR_WRONG_USAGE; } uint32_t x_max = 0, y_max = 0, z_max = 0; CoreSuspender suspend(c); diff --git a/plugins/skeleton/skeleton.cpp b/plugins/skeleton/skeleton.cpp index 1b3ad398d..a5a66f35e 100644 --- a/plugins/skeleton/skeleton.cpp +++ b/plugins/skeleton/skeleton.cpp @@ -5,7 +5,13 @@ #include #include #include + +// DF data structure definition headers +#include "DataDefs.h" +//#include "df/world.h" + using namespace DFHack; +using namespace df::enums; // our own, empty header. #include "skeleton.h" @@ -13,7 +19,7 @@ using namespace DFHack; // Here go all the command declarations... // mostly to allow having the mandatory stuff on top of the file and commands on the bottom -DFhackCExport command_result skeleton (Core * c, std::vector & parameters); +command_result skeleton (Core * c, std::vector & parameters); // A plugins must be able to return its name. This must correspond to the filename - skeleton.plug.so or skeleton.plug.dll DFhackCExport const char * plugin_name ( void ) @@ -26,10 +32,15 @@ DFhackCExport command_result plugin_init ( Core * c, std::vector { // Fill the command list with your commands. commands.clear(); - commands.push_back(PluginCommand("skeleton", - "Do nothing, look pretty.", - skeleton /*, - true or false - true means that the command can't be used from non-interactive user interface'*/)); + commands.push_back(PluginCommand( + "skeleton", "Do nothing, look pretty.", + skeleton, false, /* true means that the command can't be used from non-interactive user interface */ + // Extended help string. Used by CR_WRONG_USAGE and the help command: + " This command does nothing at all.\n" + "Example:\n" + " skeleton\n" + " Does nothing.\n" + )); return CR_OK; } @@ -42,6 +53,26 @@ DFhackCExport command_result plugin_shutdown ( Core * c ) return CR_OK; } +// Called to notify the plugin about important state changes. +// Invoked with DF suspended, and always before the matching plugin_onupdate. +// More event codes may be added in the future. +/* +DFhackCExport command_result plugin_onstatechange(Core* c, state_change_event event) +{ + switch (event) { + case SC_GAME_LOADED: + // initialize from the world just loaded + break; + case SC_GAME_UNLOADED: + // cleanup + break; + default: + break; + } + return CR_OK; +} +*/ + // Whatever you put here will be done in each game step. Don't abuse it. // It's optional, so you can just comment it out like this if you don't need it. /* @@ -53,26 +84,23 @@ DFhackCExport command_result plugin_onupdate ( Core * c ) */ // A command! It sits around and looks pretty. And it's nice and friendly. -DFhackCExport command_result skeleton (Core * c, std::vector & parameters) +command_result skeleton (Core * c, std::vector & parameters) { - // It's nice to provide a 'help' option for your command. - // It's also nice to print the same help if you get invalid options from the user instead of just acting strange - for(int i = 0; i < parameters.size();i++) - { - if(parameters[i] == "help" || parameters[i] == "?") - { - // Core has a handle to the console. The console is thread-safe. - // Only one thing can read from it at a time though... - c->con.print("This command does nothing!\n"); - return CR_OK; - } - } + // It's nice to print a help message you get invalid options + // from the user instead of just acting strange. + // This can be achieved by adding the extended help string to the + // PluginCommand registration as show above, and then returning + // CR_WRONG_USAGE from the function. The same string will also + // be used by 'help your-command'. + if (!parameters.empty()) + return CR_WRONG_USAGE; // Commands are called from threads other than the DF one. - // Suspend this thread until DF has time for us. - c->Suspend(); + // Suspend this thread until DF has time for us. If you + // use CoreSuspender, it'll automatically resume DF when + // execution leaves the current scope. + CoreSuspender suspend(c); // Actually do something here. Yay. c->con.print("Hello! I do nothing, remember?\n"); // Give control back to DF. - c->Resume(); return CR_OK; } diff --git a/plugins/weather.cpp b/plugins/weather.cpp index 73eda774c..9c161c8e5 100644 --- a/plugins/weather.cpp +++ b/plugins/weather.cpp @@ -23,7 +23,15 @@ DFhackCExport const char * plugin_name ( void ) DFhackCExport command_result plugin_init ( Core * c, std::vector &commands) { commands.clear(); - commands.push_back(PluginCommand("weather", "Print the weather map or change weather.",weather)); + commands.push_back(PluginCommand( + "weather", "Print the weather map or change weather.", + weather, false, + " Prints the current weather map by default.\n" + "Options:\n" + " snow - make it snow everywhere.\n" + " rain - make it rain.\n" + " clear - clear the sky.\n" + )); return CR_OK; } @@ -40,7 +48,6 @@ DFhackCExport command_result weather (Core * c, vector & parameters) bool snow = false; bool rain = false; bool clear = false; - bool help = false; for(int i = 0; i < parameters.size();i++) { if(parameters[i] == "rain") @@ -53,18 +60,8 @@ DFhackCExport command_result weather (Core * c, vector & parameters) lock = true; else if(parameters[i] == "unlock") unlock = true; - else if(parameters[i] == "help" || parameters[i] == "?") - help = true; - } - if(help) - { - c->con.print("Prints the current weather map by default.\n" - "Options:\n" - "snow - make it snow everywhere.\n" - "rain - make it rain.\n" - "clear - clear the sky.\n" - ); - return CR_OK; + else + return CR_WRONG_USAGE; } if(lock && unlock) { diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index dffce5a4c..0213e460a 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -1402,6 +1402,11 @@ static command_result workflow_cmd(Core *c, vector & parameters) { CoreSuspender suspend(c); + if (!c->isWorldLoaded()) { + c->con.printerr("World is not loaded: please load a game first.\n"); + return CR_FAILURE; + } + if (enabled) { check_lost_jobs(c, 0); recover_jobs(c);