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 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).
- `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/companion-order`:
- fixed error when resetting group leaders

@ -1,5 +1,9 @@
// (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 "Core.h"
@ -11,12 +15,14 @@
#include "df/map_block.h"
#include "df/plant.h"
#include "df/plant_growth.h"
#include "df/plant_raw.h"
#include "df/tile_dig_designation.h"
#include "df/world.h"
#include "modules/Designations.h"
#include "modules/Maps.h"
#include "modules/Materials.h"
using std::string;
using std::vector;
@ -27,15 +33,136 @@ using namespace df::enums;
DFHACK_PLUGIN("getplants");
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)
{
string plantMatStr = "";
set<int> plantIDs;
std::vector<selectability> plantSelections;
std::vector<size_t> collectionCount;
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;
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++)
{
if(parameters[i] == "help" || parameters[i] == "?")
@ -50,6 +177,8 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
exclude = true;
else if(parameters[i] == "-a")
all = true;
else if(parameters[i] == "-v")
verbose = true;
else
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];
if (all)
plantIDs.insert(i);
{
// plantSelections[i] = selectablePlant(out, plant);
plantSelections[i] = selectablePlant(plant);
}
else if (plantNames.find(plant->id) != plantNames.end())
{
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)
@ -91,15 +248,44 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
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");
for (size_t i = 0; i < world->raws.plants.all.size(); 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;
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;
}
@ -113,9 +299,11 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
int x = plant->pos.x % 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;
}
else
@ -134,15 +322,30 @@ command_result df_getplants (color_ostream &out, vector <string> & parameters)
continue;
if (deselect && Designations::unmarkPlant(plant))
{
collectionCount[plant->material]++;
++count;
}
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;
}
}
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;
}
@ -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"
" 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"
" -a - Select every type of plant (obeys -t/-s)\n"
" -t - Tree: Select trees only (exclude shrubs)\n"
" -s - Shrub: Select shrubs only (exclude trees)\n"
" -c - Clear: Clear designations instead of setting them\n"
" -x - eXept: Apply selected action to all plants except those specified\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"
"If no plant IDs are specified, all valid plant IDs will be listed.\n"
));