| 
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -12,12 +12,14 @@
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#include "modules/Gui.h"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#include "modules/Job.h"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#include "LuaTools.h"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#include "uicommon.h"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#include "buildingplan-planner.h"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#include "buildingplan-lib.h"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static const std::string planned_building_persistence_key_v1 = "buildingplan/constraints";
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static const std::string planned_building_persistence_key_v2 = "buildingplan/constraints2";
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				/*
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * ItemFilter
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -37,24 +39,24 @@ void ItemFilter::clear()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    materials.clear();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				bool ItemFilter::deserialize(PersistentDataItem &config)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				bool ItemFilter::deserialize(std::string ser)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    clear();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    std::vector<std::string> tokens;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    split_string(&tokens, config.val(), "/");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (tokens.size() != 2)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    split_string(&tokens, ser, "/");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (tokens.size() != 5)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("invalid ItemFilter serialization: '%s'", config.val().c_str());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("invalid ItemFilter serialization: '%s'", ser.c_str());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!deserializeMaterialMask(tokens[0]) || !deserializeMaterials(tokens[1]))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    setMinQuality(config.ival(2) - 1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    setMaxQuality(config.ival(4) - 1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    decorated_only = config.ival(3) - 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    setMinQuality(atoi(tokens[2].c_str()));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    setMaxQuality(atoi(tokens[3].c_str()));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    decorated_only = static_cast<bool>(atoi(tokens[4].c_str()));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -91,7 +93,8 @@ bool ItemFilter::deserializeMaterials(std::string ser)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				void ItemFilter::serialize(PersistentDataItem &config) const
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// format: mat,mask,elements/materials,list/minq/maxq/decorated
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				std::string ItemFilter::serialize() const
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    std::ostringstream ser;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    ser << bitfield_to_string(mat_mask, ",") << "/";
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -101,10 +104,10 @@ void ItemFilter::serialize(PersistentDataItem &config) const
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        for (size_t i = 1; i < materials.size(); ++i)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            ser << "," << materials[i].getToken();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    config.val() = ser.str();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    config.ival(2) = min_quality + 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    config.ival(4) = max_quality + 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    config.ival(3) = static_cast<int>(decorated_only) + 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    ser << "/" << static_cast<int>(min_quality);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    ser << "/" << static_cast<int>(max_quality);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    ser << "/" << static_cast<int>(decorated_only);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return ser.str();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				void ItemFilter::clearMaterialMask()
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -230,20 +233,59 @@ bool ItemFilter::matches(df::item *item) const
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * PlannedBuilding
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static std::vector<ItemFilter> deserializeFilters(PersistentDataItem &config)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// format: itemfilterser|itemfilterser|...
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static std::string serializeFilters(const std::vector<ItemFilter> &filters)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // simplified implementation while we can assume there is only one filter
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    std::ostringstream ser;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!filters.empty())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ser << filters[0].serialize();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        for (size_t i = 1; i < filters.size(); ++i)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            ser << "|" << filters[i].serialize();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return ser.str();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static std::vector<ItemFilter> deserializeFilters(std::string ser)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    std::vector<std::string> isers;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    split_string(&isers, ser, "|");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    std::vector<ItemFilter> ret;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    ItemFilter itemFilter;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    itemFilter.deserialize(config);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    ret.push_back(itemFilter);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (auto & iser : isers)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ItemFilter filter;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (filter.deserialize(iser))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            ret.push_back(filter);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return ret;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static size_t getNumFilters(BuildingTypeKey key)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // TODO: get num filters in Lua when we handle all building types
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    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", "get_num_filters"))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("failed to push the lua method on the stack");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Lua::Push(L, std::get<0>(key));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Lua::Push(L, std::get<1>(key));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Lua::Push(L, std::get<2>(key));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!Lua::SafeCall(out, L, 3, 1))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("lua call failed");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    int num_filters = lua_tonumber(L, -1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    lua_pop(L, 1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return num_filters;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				PlannedBuilding::PlannedBuilding(df::building *building, const std::vector<ItemFilter> &filters)
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -251,92 +293,27 @@ PlannedBuilding::PlannedBuilding(df::building *building, const std::vector<ItemF
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      building_id(building->id),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      filters(filters)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    config = DFHack::World::AddPersistentData(planned_building_persistence_key_v1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    config.ival(1) = building_id;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // assume all filter vectors are length 1 for now
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    filters[0].serialize(config);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    config = DFHack::World::AddPersistentData(planned_building_persistence_key_v2);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    config.ival(0) = building_id;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    config.val() = serializeFilters(filters);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				PlannedBuilding::PlannedBuilding(PersistentDataItem &config)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    : config(config),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      building(df::building::find(config.ival(1))),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      building_id(config.ival(1)),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      filters(deserializeFilters(config))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{ }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				bool PlannedBuilding::assignClosestItem(std::vector<df::item *> *items_vector)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    decltype(items_vector->begin()) closest_item;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    int32_t closest_distance = -1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (auto item_iter = items_vector->begin(); item_iter != items_vector->end(); item_iter++)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto item = *item_iter;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (!filters[0].matches(item))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto pos = item->pos;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto distance = abs(pos.x - building->centerx) +
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            abs(pos.y - building->centery) +
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            abs(pos.z - building->z) * 50;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (closest_distance > -1 && distance >= closest_distance)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        closest_distance = distance;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        closest_item = item_iter;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (closest_distance > -1 && assignItem(*closest_item))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("Item assigned");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        items_vector->erase(closest_item);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        remove();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				void delete_item_fn(df::job_item *x) { delete x; }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				bool PlannedBuilding::assignItem(df::item *item)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      building(df::building::find(config.ival(0))),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      building_id(config.ival(0)),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      filters(deserializeFilters(config.val()))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    auto ref = df::allocate<df::general_ref_building_holderst>();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!ref)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        Core::printerr("Could not allocate general_ref_building_holderst\n");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    ref->building_id = building->id;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (building->jobs.size() != 1)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    auto job = building->jobs[0];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for_each_(job->job_items, delete_item_fn);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    job->job_items.clear();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    job->flags.bits.suspend = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    bool rough = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Job::attachJobItem(job, item, df::job_item_ref::Hauled);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (item->getType() == item_type::BOULDER)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        rough = true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    building->mat_type = item->getMaterial();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    building->mat_index = item->getMaterialIndex();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    job->mat_type = building->mat_type;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    job->mat_index = building->mat_index;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (building->needsDesign())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (building)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto act = (df::building_actual *) building;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        act->design = new df::building_design();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        act->design->flags.bits.rough = rough;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (filters.size() !=
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            getNumFilters(toBuildingTypeKey(building)))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            debug("invalid ItemFilter vector serialization: '%s'",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                config.val().c_str());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            building = NULL;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// Ensure the building still exists and is in a valid state. It can disappear
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -361,6 +338,8 @@ df::building * PlannedBuilding::getBuilding()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				const std::vector<ItemFilter> & PlannedBuilding::getFilters() const
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // if we want to be able to dynamically change the filters, we'll need to
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // re-bucket the tasks in Planner.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return filters;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -412,45 +391,112 @@ std::size_t BuildingTypeKeyHash::operator() (const BuildingTypeKey & key) const
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * Planner
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				void Planner::initialize()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#define add_building_type(btype, itype) \
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    item_for_building_type[df::building_type::btype] = df::item_type::itype; \
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    available_item_vectors[df::item_type::itype] = std::vector<df::item *>(); \
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    is_relevant_item_type[df::item_type::itype] = true; \
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    FOR_ENUM_ITEMS(item_type, it)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        is_relevant_item_type[it] = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Armorstand, ARMORSTAND);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Bed, BED);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Chair, CHAIR);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Coffin, COFFIN);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Door, DOOR);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Floodgate, FLOODGATE);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Hatch, HATCH_COVER);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(GrateWall, GRATE);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(GrateFloor, GRATE);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(BarsVertical, BAR);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(BarsFloor, BAR);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Cabinet, CABINET);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Box, BOX);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // skip kennels, farm plot
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Weaponrack, WEAPONRACK);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Statue, STATUE);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Slab, SLAB);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Table, TABLE);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // skip roads ... furnaces
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(WindowGlass, WINDOW);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // skip gem window ... support
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(AnimalTrap, ANIMALTRAP);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Chain, CHAIN);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(Cage, CAGE);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // skip archery target
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    add_building_type(TractionBench, TRACTION_BENCH);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // skip nest box, hive (tools)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#undef add_building_type
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// convert v1 persistent data into v2 format
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// we can remove this conversion code once v2 has been live for a while
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				void migrateV1ToV2()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    std::vector<PersistentDataItem> configs;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    DFHack::World::GetPersistentData(&configs, planned_building_persistence_key_v1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (configs.empty())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    debug("migrating %zu persisted configs to new format", configs.size());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (auto config : configs)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        df::building *bld = df::building::find(config.ival(1));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (!bld)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            debug("buliding no longer exists; removing config");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            DFHack::World::DeletePersistentData(config);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (bld->getBuildStage() != 0 || bld->jobs.size() != 1
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            || bld->jobs[0]->job_items.size() != 1)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            debug("building in invalid state; removing config");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            DFHack::World::DeletePersistentData(config);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // fix up the building so we can set the material properties later
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        bld->mat_type = -1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        bld->mat_index = -1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // the v1 filters are not initialized correctly and will match any item.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // we need to fix them up a bit.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto filter = bld->jobs[0]->job_items[0];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        df::item_type type;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        switch (bld->getType())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Armorstand: type = df::item_type::ARMORSTAND; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Bed: type = df::item_type::BED; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Chair: type = df::item_type::CHAIR; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Coffin: type = df::item_type::COFFIN; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Door: type = df::item_type::DOOR; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Floodgate: type = df::item_type::FLOODGATE; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Hatch: type = df::item_type::HATCH_COVER; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::GrateWall: type = df::item_type::GRATE; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::GrateFloor: type = df::item_type::GRATE; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::BarsVertical: type = df::item_type::BAR; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::BarsFloor: type = df::item_type::BAR; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Cabinet: type = df::item_type::CABINET; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Box: type = df::item_type::BOX; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Weaponrack: type = df::item_type::WEAPONRACK; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Statue: type = df::item_type::STATUE; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Slab: type = df::item_type::SLAB; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Table: type = df::item_type::TABLE; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::WindowGlass: type = df::item_type::WINDOW; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::AnimalTrap: type = df::item_type::ANIMALTRAP; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Chain: type = df::item_type::CHAIN; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::Cage: type = df::item_type::CAGE; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::building_type::TractionBench: type = df::item_type::TRACTION_BENCH; break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        default:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            debug("building has unhandled type; removing config");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            DFHack::World::DeletePersistentData(config);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->item_type = type;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->item_subtype = -1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->mat_type = -1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->mat_index = -1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->flags1.whole = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->flags2.whole = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->flags2.bits.allow_artifact = true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->flags3.whole = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->flags4 = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->flags5 = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->metal_ore = -1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->min_dimension = -1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->has_tool_use = df::tool_uses::NONE;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        filter->quantity = 1;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        std::vector<std::string> tokens;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        split_string(&tokens, config.val(), "/");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (tokens.size() != 2)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            debug("invalid v1 format; removing config");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            DFHack::World::DeletePersistentData(config);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ItemFilter item_filter;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        item_filter.deserializeMaterialMask(tokens[0]);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        item_filter.deserializeMaterials(tokens[1]);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        item_filter.setMinQuality(config.ival(2) - 1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        item_filter.setMaxQuality(config.ival(4) - 1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (config.ival(3) - 1)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            item_filter.toggleDecoratedOnly();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // create the v2 record
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        std::vector<ItemFilter> item_filters;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        item_filters.push_back(item_filter);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        PlannedBuilding pb(bld, item_filters);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // remove the v1 record
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        DFHack::World::DeletePersistentData(config);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("v1 record successfully migrated");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				void Planner::reset()
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -458,9 +504,12 @@ void Planner::reset()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    debug("resetting Planner state");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    default_item_filters.clear();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    planned_buildings.clear();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    tasks.clear();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    migrateV1ToV2();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    std::vector<PersistentDataItem> items;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    DFHack::World::GetPersistentData(&items, planned_building_persistence_key_v1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    DFHack::World::GetPersistentData(&items, planned_building_persistence_key_v2);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    debug("found data for %zu planned buildings", items.size());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (auto i = items.begin(); i != items.end(); i++)
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -472,7 +521,8 @@ void Planner::reset()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        planned_buildings.push_back(pb);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (registerTasks(pb))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            planned_buildings.insert(std::make_pair(pb.getBuilding()->id, pb));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -487,19 +537,19 @@ void Planner::addPlannedBuilding(df::building *bld)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // protect against multiple registrations
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (getPlannedBuilding(bld))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (planned_buildings.count(bld->id) != 0)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("building already registered");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("failed to add building: already registered");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    PlannedBuilding pb(bld, item_filters);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (pb.isValid())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (pb.isValid() && registerTasks(pb))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        for (auto job : bld->jobs)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            job->flags.bits.suspend = true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        planned_buildings.push_back(pb);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        planned_buildings.insert(std::make_pair(bld->id, pb));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    else
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -507,19 +557,107 @@ void Planner::addPlannedBuilding(df::building *bld)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				PlannedBuilding * Planner::getPlannedBuilding(df::building *bld)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static std::string getBucket(const df::job_item & ji,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                             const std::vector<ItemFilter> & item_filters)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    std::ostringstream ser;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // pull out and serialize only known relevant fields. if we miss a few, then
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // the filter bucket will be slighly less specific than it could be, but
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // that's probably ok. we'll just end up bucketing slightly different items
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // together. this is only a problem if the different filter at the front of
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // the queue doesn't match any available items and blocks filters behind it
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // that could be matched.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    ser << ji.item_type << ':' << ji.item_subtype << ':' << ji.mat_type << ':'
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        << ji.mat_index << ':' << ji.flags1.whole << ':' << ji.flags2.whole
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        << ':' << ji.flags3.whole << ':' << ji.flags4 << ':' << ji.flags5 << ':'
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        << ji.metal_ore << ':' << ji.has_tool_use;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (auto & item_filter : item_filters)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        ser << ':' << item_filter.serialize();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return ser.str();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				bool Planner::registerTasks(PlannedBuilding & pb)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (auto & pb : planned_buildings)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    df::building * bld = pb.getBuilding();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (bld->jobs.size() != 1)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("unexpected number of jobs: want 1, got %zu", bld->jobs.size());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    auto job_items = bld->jobs[0]->job_items;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    int num_job_items = job_items.size();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (num_job_items < 1)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (pb.getBuilding() == bld)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            return &pb;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("unexpected number of job items: want >0, got %d", num_job_items);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return NULL;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (int job_item_idx = 0; job_item_idx < num_job_items; ++job_item_idx)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto vector_id = df::job_item_vector_id::IN_PLAY;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto job_item = job_items[job_item_idx];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (job_item->vector_id)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          vector_id = job_item->vector_id;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto bucket = getBucket(*job_item, pb.getFilters());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        for (int item_num = 0; item_num < job_item->quantity; ++item_num)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            int32_t id = bld->id;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            tasks[vector_id][bucket].push(std::make_pair(id, job_item_idx));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            debug("added task: %s/%s/%d,%d; "
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  "%zu vectors, %zu buckets, %zu tasks in bucket",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  ENUM_KEY_STR(job_item_vector_id, vector_id).c_str(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  bucket.c_str(), id, job_item_idx, tasks.size(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  tasks[vector_id].size(), tasks[vector_id][bucket].size());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				PlannedBuilding * Planner::getPlannedBuilding(df::building *bld)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!bld || planned_buildings.count(bld->id) == 0)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return NULL;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return &planned_buildings.at(bld->id);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				bool Planner::isPlannableBuilding(BuildingTypeKey key)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return item_for_building_type.count(std::get<0>(key)) > 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (getNumFilters(key) == 0)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // restrict supported types to be the same as the previous implementation
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    switch(std::get<0>(key))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Armorstand:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Bed:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Chair:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Coffin:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Door:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Floodgate:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Hatch:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::GrateWall:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::GrateFloor:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::BarsVertical:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::BarsFloor:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Cabinet:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Box:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Weaponrack:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Statue:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Slab:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Table:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::WindowGlass:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::AnimalTrap:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Chain:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::Cage:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case df::enums::building_type::TractionBench:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            return true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        default:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				Planner::ItemFiltersWrapper Planner::getItemFilters(BuildingTypeKey key)
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -535,84 +673,257 @@ Planner::ItemFiltersWrapper Planner::getItemFilters(BuildingTypeKey key)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return ItemFiltersWrapper(default_item_filters[key]);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				void Planner::doCycle()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// precompute a bitmask with bad item flags
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				struct BadFlags
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    debug("Running Cycle");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (planned_buildings.size() == 0)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    debug("Planned count: %zu", planned_buildings.size());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    uint32_t whole;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    gather_available_items();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (auto building_iter = planned_buildings.begin(); building_iter != planned_buildings.end();)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    BadFlags()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (building_iter->isValid())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            auto type = building_iter->getBuilding()->getType();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            debug("Trying to allocate %s", enum_item_key_str(type));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        df::item_flags flags;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        #define F(x) flags.bits.x = true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        F(dump); F(forbid); F(garbage_collect);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        F(hostile); F(on_fire); F(rotten); F(trader);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        F(in_building); F(construction); F(in_job);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        F(owned); F(in_chest); F(removed); F(encased);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        #undef F
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        whole = flags.whole;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				};
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            auto required_item_type = item_for_building_type[type];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            auto items_vector = &available_item_vectors[required_item_type];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            if (items_vector->size() == 0 || !building_iter->assignClosestItem(items_vector))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                debug("Unable to allocate an item");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                ++building_iter;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("Removing building plan");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        building_iter->remove();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        building_iter = planned_buildings.erase(building_iter);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static bool itemPassesScreen(df::item * item)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    static BadFlags bad_flags;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return !(item->flags.whole & bad_flags.whole)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        && !item->isAssignedToStockpile()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // TODO: make this configurable
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        && !(item->getType() == df::item_type::BOX && item->isBag());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static bool matchesFilters(df::item * item,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                           df::job_item * job_item,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                           const ItemFilter & item_filter)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // check the properties that are not checked by Job::isSuitableItem()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (job_item->item_type > -1 && job_item->item_type != item->getType())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (job_item->item_subtype > -1 &&
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        job_item->item_subtype != item->getSubtype())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (job_item->flags2.bits.building_material && !item->isBuildMat())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (job_item->metal_ore > -1 && !item->isMetalOre(job_item->metal_ore))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (job_item->has_tool_use > df::tool_uses::NONE
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        && !item->hasToolUse(job_item->has_tool_use))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return DFHack::Job::isSuitableItem(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            job_item, item->getType(), item->getSubtype())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        && DFHack::Job::isSuitableMaterial(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            job_item, item->getMaterial(), item->getMaterialIndex())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        && item_filter.matches(item);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// note that this just removes the PlannedBuilding. the tasks will get dropped
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// as we discover them in the tasks queues and they fail their isValid() check.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// this "lazy" task cleaning algorithm works because there is no way to
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// re-register a building once it has been removed -- if it fails isValid()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// then it has either been built or desroyed. therefore there is no chance of
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// duplicate tasks getting added to the tasks queues.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				void Planner::unregisterBuilding(int32_t id)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (planned_buildings.count(id) > 0)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        planned_buildings.at(id).remove();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        planned_buildings.erase(id);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				void Planner::gather_available_items()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static bool isJobReady(df::job * job)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    debug("Gather available items");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (auto iter = available_item_vectors.begin(); iter != available_item_vectors.end(); iter++)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    int needed_items = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (auto job_item : job->job_items) { needed_items += job_item->quantity; }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (needed_items)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        iter->second.clear();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("building needs %d more item(s)", needed_items);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Precompute a bitmask with the bad flags
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    df::item_flags bad_flags;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    bad_flags.whole = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static bool job_item_idx_lt(df::job_item_ref *a, df::job_item_ref *b)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // we want the items in the opposite order of the filters
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return a->job_item_idx > b->job_item_idx;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    #define F(x) bad_flags.bits.x = true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    F(dump); F(forbid); F(garbage_collect);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    F(hostile); F(on_fire); F(rotten); F(trader);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    F(in_building); F(construction); F(artifact);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    #undef F
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// this function does not remove the job_items since their quantity fields are
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// now all at 0, so there is no risk of having extra items attached. we don't
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// remove them to keep the "finalize with buildingplan active" path as similar
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// as possible to the "finalize with buildingplan disabled" path.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				static void finalizeBuilding(df::building * bld)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    debug("finalizing building %d", bld->id);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    auto job = bld->jobs[0];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    std::vector<df::item*> &items = df::global::world->items.other[df::items_other_id::IN_PLAY];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // sort the items so they get added to the structure in the correct order
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    std::sort(job->items.begin(), job->items.end(), job_item_idx_lt);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (size_t i = 0; i < items.size(); i++)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // derive the material properties of the building and job from the first
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // applicable item, though if any boulders are involved, it makes the whole
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // structure "rough".
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    bool rough = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (auto attached_item : job->items)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        df::item *item = items[i];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (item->flags.whole & bad_flags.whole)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        df::item_type itype = item->getType();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (!is_relevant_item_type[itype])
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        df::item *item = attached_item->item;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        rough = rough || item->getType() == item_type::BOULDER;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (bld->mat_type == -1)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            bld->mat_type = item->getMaterial();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            job->mat_type = bld->mat_type;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (bld->mat_index == -1)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            bld->mat_index = item->getMaterialIndex();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            job->mat_index = bld->mat_index;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (itype == df::item_type::BOX && item->isBag())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            continue; //Skip bags
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (bld->needsDesign())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto act = (df::building_actual *)bld;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (!act->design)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            act->design = new df::building_design();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        act->design->flags.bits.rough = rough;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (item->flags.bits.artifact)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // we're good to go!
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    job->flags.bits.suspend = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Job::checkBuildingsNow();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (item->flags.bits.in_job ||
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            item->isAssignedToStockpile() ||
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            item->flags.bits.owned ||
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            item->flags.bits.in_chest)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				void Planner::popInvalidTasks(std::queue<std::pair<int32_t, int>> & task_queue)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    while (!task_queue.empty())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto & task = task_queue.front();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto id = task.first;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (planned_buildings.count(id) > 0)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            PlannedBuilding & pb = planned_buildings.at(id);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            if (pb.isValid() &&
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                pb.getBuilding()->jobs[0]->job_items[task.second]->quantity)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("discarding invalid task: bld=%d, job_item_idx=%d",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              id, task.second);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        task_queue.pop();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        unregisterBuilding(id);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        available_item_vectors[itype].push_back(item);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				void Planner::doCycle()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				{
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    debug("running cycle for %zu registered buildings",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          planned_buildings.size());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    for (auto it = tasks.begin(); it != tasks.end();)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto & buckets = it->second;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto other_id = ENUM_ATTR(job_item_vector_id, other, it->first);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        auto item_vector = df::global::world->items.other[other_id];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debug("matching %zu items in vector %s against %zu buckets",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              item_vector.size(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              ENUM_KEY_STR(job_item_vector_id, it->first).c_str(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				              buckets.size());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        for (auto item_it = item_vector.rbegin();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				             item_it != item_vector.rend();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				             ++item_it)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            auto item = *item_it;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            if (!itemPassesScreen(item))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            for (auto bucket_it = buckets.begin(); bucket_it != buckets.end();)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                auto & task_queue = bucket_it->second;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                popInvalidTasks(task_queue);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                if (task_queue.empty())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    debug("removing empty bucket: %s/%s; %zu buckets left",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                          ENUM_KEY_STR(job_item_vector_id, it->first).c_str(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                          bucket_it->first.c_str(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                          buckets.size() - 1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    bucket_it = buckets.erase(bucket_it);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    continue;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                auto & task = task_queue.front();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                auto id = task.first;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                auto & pb = planned_buildings.at(id);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                auto building = pb.getBuilding();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                auto job = building->jobs[0];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                auto filter_idx = task.second;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                if (matchesFilters(item, job->job_items[filter_idx],
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                        pb.getFilters()[filter_idx])
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                   && DFHack::Job::attachJobItem(job, item,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                            df::job_item_ref::Hauled, filter_idx))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    MaterialInfo material;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    material.decode(item);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    ItemTypeInfo item_type;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    item_type.decode(item);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    debug("attached %s %s to filter %d for %s(%d): %s/%s",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                          material.toString().c_str(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                          item_type.toString().c_str(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                          filter_idx,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                          ENUM_KEY_STR(building_type, building->getType()).c_str(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                          id,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                          ENUM_KEY_STR(job_item_vector_id, it->first).c_str(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                          bucket_it->first.c_str());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    // keep quantity aligned with the actual number of remaining
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    // items so if buildingplan is turned off, the building will
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    // be completed with the correct number of items.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    --job->job_items[filter_idx]->quantity;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    task_queue.pop();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    if (isJobReady(job))
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                        finalizeBuilding(building);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                        unregisterBuilding(id);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    if (task_queue.empty())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                        debug(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                            "removing empty item bucket: %s/%s; %zu remaining",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                            ENUM_KEY_STR(job_item_vector_id, it->first).c_str(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                            bucket_it->first.c_str(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                            buckets.size() - 1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                        buckets.erase(bucket_it);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    // we found a home for this item; no need to look further
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                ++bucket_it;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            if (buckets.empty())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (buckets.empty())
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            debug("removing empty vector: %s; %zu vectors left",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  ENUM_KEY_STR(job_item_vector_id, it->first).c_str(),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                  tasks.size() - 1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            it = tasks.erase(it);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        else
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            ++it;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    debug("cycle done; %zu registered buildings left",
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          planned_buildings.size());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				Planner planner;
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
					 | 
				
			
			 | 
			 | 
			
				
 
 |