Merge remote-tracking branch 'PatrikLundell/getplants' into develop

develop
lethosor 2020-01-18 16:19:40 -05:00
commit af1668126d
2 changed files with 222 additions and 17 deletions

@ -54,6 +54,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- fixed bug causing mineral matching to fail to cut off at the magma sea, reporting presence of things that aren't (like DF does currently). - fixed bug causing mineral matching to fail to cut off at the magma sea, reporting presence of things that aren't (like DF does currently).
- fixed bug causing half of the river tiles not to be recognized. - fixed bug causing half of the river tiles not to be recognized.
- added logic to detect some river tiles DF doesn't generate data for (but are definitely present). - added logic to detect some river tiles DF doesn't generate data for (but are definitely present).
- `getplants`: fixed designation of plants out of season and added verbose flag, but failed to identify picked plants (which are still designated incorrectly)
- `gui/autogems`: fixed error when no world is loaded - `gui/autogems`: fixed error when no world is loaded
- `gui/companion-order`: - `gui/companion-order`:
- fixed error when resetting group leaders - fixed error when resetting group leaders

@ -1,5 +1,9 @@
// (un)designate matching plants for gathering/cutting // (un)designate matching plants for gathering/cutting
// Known issue:
// DF is capable of determining that a shrub has already been picked, leaving an unusable structure part
// behind. This code does not perform such a check (as the location of the required information is
// unknown to the writer of this comment). This leads to some shrubs being designated when they
// shouldn't be, causing a plant gatherer to walk there and do nothing (except clearing the designation).
#include <set> #include <set>
#include "Core.h" #include "Core.h"
@ -11,12 +15,14 @@
#include "df/map_block.h" #include "df/map_block.h"
#include "df/plant.h" #include "df/plant.h"
#include "df/plant_growth.h"
#include "df/plant_raw.h" #include "df/plant_raw.h"
#include "df/tile_dig_designation.h" #include "df/tile_dig_designation.h"
#include "df/world.h" #include "df/world.h"
#include "modules/Designations.h" #include "modules/Designations.h"
#include "modules/Maps.h" #include "modules/Maps.h"
#include "modules/Materials.h"
using std::string; using std::string;
using std::vector; using std::vector;
@ -27,15 +33,136 @@ using namespace df::enums;
DFHACK_PLUGIN("getplants"); DFHACK_PLUGIN("getplants");
REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(world);
REQUIRE_GLOBAL(cur_year_tick);
enum class selectability {
Selectable,
Grass,
Nonselectable,
OutOfSeason,
Unselected
};
//selectability selectablePlant(color_ostream &out, const df::plant_raw *plant)
selectability selectablePlant(const df::plant_raw *plant)
{
const DFHack::MaterialInfo basic_mat = DFHack::MaterialInfo(plant->material_defs.type_basic_mat, plant->material_defs.idx_basic_mat);
bool outOfSeason = false;
if (plant->flags.is_set(plant_raw_flags::TREE))
{
// out.print("%s is a selectable tree\n", plant->id.c_str());
return selectability::Selectable;
}
else if (plant->flags.is_set(plant_raw_flags::GRASS))
{
// out.print("%s is a non selectable Grass\n", plant->id.c_str());
return selectability::Grass;
}
if (basic_mat.material->flags.is_set(material_flags::EDIBLE_RAW) ||
basic_mat.material->flags.is_set(material_flags::EDIBLE_COOKED))
{
// out.print("%s is edible\n", plant->id.c_str());
return selectability::Selectable;
}
if (plant->flags.is_set(plant_raw_flags::THREAD) ||
plant->flags.is_set(plant_raw_flags::MILL) ||
plant->flags.is_set(plant_raw_flags::EXTRACT_VIAL) ||
plant->flags.is_set(plant_raw_flags::EXTRACT_BARREL) ||
plant->flags.is_set(plant_raw_flags::EXTRACT_STILL_VIAL))
{
// out.print("%s is thread/mill/extract\n", plant->id.c_str());
return selectability::Selectable;
}
if (basic_mat.material->reaction_product.id.size() > 0 ||
basic_mat.material->reaction_class.size() > 0)
{
// out.print("%s has a reaction\n", plant->id.c_str());
return selectability::Selectable;
}
for (size_t i = 0; i < plant->growths.size(); i++)
{
if (plant->growths[i]->item_type == df::item_type::SEEDS || // Only trees have seed growths in vanilla, but raws can be modded...
plant->growths[i]->item_type == df::item_type::PLANT_GROWTH)
{
const DFHack::MaterialInfo growth_mat = DFHack::MaterialInfo(plant->growths[i]->mat_type, plant->growths[i]->mat_index);
if ((plant->growths[i]->item_type == df::item_type::SEEDS &&
(growth_mat.material->flags.is_set(material_flags::EDIBLE_COOKED) ||
growth_mat.material->flags.is_set(material_flags::EDIBLE_RAW))) ||
(plant->growths[i]->item_type == df::item_type::PLANT_GROWTH &&
growth_mat.material->flags.is_set(material_flags::LEAF_MAT))) // Will change name to STOCKPILE_PLANT_GROWTH any day now...
{
if (*cur_year_tick >= plant->growths[i]->timing_1 &&
(plant->growths[i]->timing_2 == -1 ||
*cur_year_tick <= plant->growths[i]->timing_2))
{
// out.print("%s has an edible seed or a stockpile growth\n", plant->id.c_str());
return selectability::Selectable;
}
else
{
outOfSeason = true;
}
}
}
/* else if (plant->growths[i]->behavior.bits.has_seed) // This code designates beans, etc. when DF doesn't, but plant gatherers still fail to collect anything, so it's useless: bug #0006940.
{
const DFHack::MaterialInfo seed_mat = DFHack::MaterialInfo(plant->material_defs.type_seed, plant->material_defs.idx_seed);
if (seed_mat.material->flags.is_set(material_flags::EDIBLE_RAW) ||
seed_mat.material->flags.is_set(material_flags::EDIBLE_COOKED))
{
if (*cur_year_tick >= plant->growths[i]->timing_1 &&
(plant->growths[i]->timing_2 == -1 ||
*cur_year_tick <= plant->growths[i]->timing_2))
{
return selectability::Selectable;
}
else
{
outOfSeason = true;
}
}
} */
}
if (outOfSeason)
{
// out.print("%s has an out of season growth\n", plant->id.c_str());
return selectability::OutOfSeason;
}
else
{
// out.printerr("%s cannot be gathered\n", plant->id.c_str());
return selectability::Nonselectable;
}
}
command_result df_getplants (color_ostream &out, vector <string> & parameters) command_result df_getplants (color_ostream &out, vector <string> & parameters)
{ {
string plantMatStr = ""; string plantMatStr = "";
set<int> plantIDs; std::vector<selectability> plantSelections;
std::vector<size_t> collectionCount;
set<string> plantNames; set<string> plantNames;
bool deselect = false, exclude = false, treesonly = false, shrubsonly = false, all = false; bool deselect = false, exclude = false, treesonly = false, shrubsonly = false, all = false, verbose = false;
int count = 0; int count = 0;
plantSelections.resize(world->raws.plants.all.size());
collectionCount.resize(world->raws.plants.all.size());
for (size_t i = 0; i < plantSelections.size(); i++)
{
plantSelections[i] = selectability::Unselected;
collectionCount[i] = 0;
}
bool anyPlantsSelected = false;
for (size_t i = 0; i < parameters.size(); i++) for (size_t i = 0; i < parameters.size(); i++)
{ {
if(parameters[i] == "help" || parameters[i] == "?") if(parameters[i] == "help" || parameters[i] == "?")
@ -50,6 +177,8 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
exclude = true; exclude = true;
else if(parameters[i] == "-a") else if(parameters[i] == "-a")
all = true; all = true;
else if(parameters[i] == "-v")
verbose = true;
else else
plantNames.insert(parameters[i]); plantNames.insert(parameters[i]);
} }
@ -75,11 +204,39 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
{ {
df::plant_raw *plant = world->raws.plants.all[i]; df::plant_raw *plant = world->raws.plants.all[i];
if (all) if (all)
plantIDs.insert(i); {
else if (plantNames.find(plant->id) != plantNames.end()) // plantSelections[i] = selectablePlant(out, plant);
plantSelections[i] = selectablePlant(plant);
}
else if (plantNames.find(plant->id) != plantNames.end())
{ {
plantNames.erase(plant->id); plantNames.erase(plant->id);
plantIDs.insert(i); // plantSelections[i] = selectablePlant(out, plant);
plantSelections[i] = selectablePlant(plant);
switch (plantSelections[i])
{
case selectability::Grass:
{
out.printerr("%s is a Grass, and those can not be gathered\n", plant->id.c_str());
break;
}
case selectability::Nonselectable:
{
out.printerr("%s does not have any parts that can be gathered\n", plant->id.c_str());
break;
}
case selectability::OutOfSeason:
{
out.printerr("%s is out of season, with nothing that can be gathered now\n", plant->id.c_str());
break;
}
case selectability::Selectable:
break;
case selectability::Unselected:
break; // We won't get to this option
}
} }
} }
if (plantNames.size() > 0) if (plantNames.size() > 0)
@ -91,15 +248,44 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
return CR_FAILURE; return CR_FAILURE;
} }
if (plantIDs.size() == 0) for (size_t i = 0; i < plantSelections.size(); i++)
{
if (plantSelections[i] == selectability::OutOfSeason ||
plantSelections[i] == selectability::Selectable)
{
anyPlantsSelected = true;
break;
}
}
if (!anyPlantsSelected)
{ {
out.print("Valid plant IDs:\n"); out.print("Valid plant IDs:\n");
for (size_t i = 0; i < world->raws.plants.all.size(); i++) for (size_t i = 0; i < world->raws.plants.all.size(); i++)
{ {
df::plant_raw *plant = world->raws.plants.all[i]; df::plant_raw *plant = world->raws.plants.all[i];
if (plant->flags.is_set(plant_raw_flags::GRASS)) // switch (selectablePlant(out, plant))
switch (selectablePlant(plant))
{
case selectability::Grass:
case selectability::Nonselectable:
continue; continue;
out.print("* (%s) %s - %s\n", plant->flags.is_set(plant_raw_flags::TREE) ? "tree" : "shrub", plant->id.c_str(), plant->name.c_str());
case selectability::OutOfSeason:
{
out.print("* (shrub) %s - %s is out of season\n", plant->id.c_str(), plant->name.c_str());
break;
}
case selectability::Selectable:
{
out.print("* (%s) %s - %s\n", plant->flags.is_set(plant_raw_flags::TREE) ? "tree" : "shrub", plant->id.c_str(), plant->name.c_str());
break;
}
case selectability::Unselected: // Should never get this alternative
break;
}
} }
return CR_OK; return CR_OK;
} }
@ -113,9 +299,11 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
int x = plant->pos.x % 16; int x = plant->pos.x % 16;
int y = plant->pos.y % 16; int y = plant->pos.y % 16;
if (plantIDs.find(plant->material) != plantIDs.end()) if (plantSelections[plant->material] == selectability::OutOfSeason ||
plantSelections[plant->material] == selectability::Selectable)
{ {
if (exclude) if (exclude ||
plantSelections[plant->material] == selectability::OutOfSeason)
continue; continue;
} }
else else
@ -134,15 +322,30 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
continue; continue;
if (deselect && Designations::unmarkPlant(plant)) if (deselect && Designations::unmarkPlant(plant))
{ {
collectionCount[plant->material]++;
++count; ++count;
} }
if (!deselect && Designations::markPlant(plant)) if (!deselect && Designations::markPlant(plant))
{ {
// out.print("Designated %s at (%i, %i, %i), %d\n", world->raws.plants.all[plant->material]->id.c_str(), plant->pos.x, plant->pos.y, plant->pos.z, (int)i);
collectionCount[plant->material]++;
++count; ++count;
} }
} }
if (count) if (count)
out.print("Updated %d plant designations.\n", count); {
if (verbose)
{
for (size_t i = 0; i < plantSelections.size(); i++)
{
if (collectionCount[i] > 0)
out.print("Updated %d %s designations.\n", (int)collectionCount[i], world->raws.plants.all[i]->id.c_str());
}
out.print("\n");
}
}
out.print("Updated %d plant designations.\n", (int)count);
return CR_OK; return CR_OK;
} }
@ -154,11 +357,12 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector <PluginCom
" Specify the types of trees to cut down and/or shrubs to gather by their\n" " Specify the types of trees to cut down and/or shrubs to gather by their\n"
" plant IDs, separated by spaces.\n" " plant IDs, separated by spaces.\n"
"Options:\n" "Options:\n"
" -t - Select trees only (exclude shrubs)\n" " -t - Tree: Select trees only (exclude shrubs)\n"
" -s - Select shrubs only (exclude trees)\n" " -s - Shrub: Select shrubs only (exclude trees)\n"
" -c - Clear designations instead of setting them\n" " -c - Clear: Clear designations instead of setting them\n"
" -x - Apply selected action to all plants except those specified\n" " -x - eXept: Apply selected action to all plants except those specified\n"
" -a - Select every type of plant (obeys -t/-s)\n" " -a - All: Select every type of plant (obeys -t/-s)\n"
" -v - Verbose: lists the number of (un)designations per plant\n"
"Specifying both -t and -s will have no effect.\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" "If no plant IDs are specified, all valid plant IDs will be listed.\n"
)); ));