#include "edgeCost.h" #include "modules/Buildings.h" #include "modules/Maps.h" #include "modules/MapCache.h" #include "df/building.h" #include "df/building_bridgest.h" #include "df/building_hatchst.h" #include "df/building_type.h" #include "df/construction.h" #include "df/coord.h" #include "df/item_type.h" #include "df/map_block.h" #include "df/tile_building_occ.h" #include "df/tiletype.h" #include "df/tiletype_material.h" #include "df/tiletype_shape.h" #include using namespace DFHack; /* cost_t costWeight[] = { //Distance 1, //Destroy Building 2, //Dig 10000, //DestroyConstruction 100, }; int32_t jobDelay[] = { //Distance -1, //Destroy Building 1000, //Dig 1000, //DestroyConstruction 1000 }; */ using namespace std; /* limitations ramps cave-ins */ cost_t getEdgeCost(color_ostream& out, df::coord pt1, df::coord pt2, DigAbilities& abilities) { int32_t dx = pt2.x - pt1.x; int32_t dy = pt2.y - pt1.y; int32_t dz = pt2.z - pt1.z; cost_t cost = abilities.costWeight[CostDimension::Walk]; if ( cost < 0 ) return -1; if ( Maps::getTileBlock(pt1) == NULL || Maps::getTileBlock(pt2) == NULL ) return -1; df::tiletype* type2 = Maps::getTileType(pt2); df::tiletype_shape shape2 = ENUM_ATTR(tiletype, shape, *type2); if ( Maps::getTileBlock(pt1)->designation[pt1.x&0xF][pt1.y&0xF].bits.flow_size >= 4 ) return -1; if ( Maps::getTileBlock(pt2)->designation[pt2.x&0xF][pt2.y&0xF].bits.flow_size >= 4 ) return -1; if ( shape2 == df::enums::tiletype_shape::EMPTY ) { return -1; } if ( shape2 == df::enums::tiletype_shape::BRANCH || shape2 == df::enums::tiletype_shape::TRUNK_BRANCH || shape2 == df::enums::tiletype_shape::TWIG ) return -1; /* if () { df::map_block* temp = Maps::getTileBlock(df::coord(pt1.x,pt1.y,pt1.z-1)); if ( temp && temp->designation[pt1.x&0xF][pt1.y&0xF] } */ if ( Maps::canStepBetween(pt1, pt2) ) { return cost; } df::building* building2 = Buildings::findAtTile(pt2); if ( building2 ) { if ( abilities.costWeight[CostDimension::DestroyBuilding] < 0 ) return -1; cost += abilities.costWeight[CostDimension::DestroyBuilding]; if ( dx*dx + dy*dy > 1 ) return -1; } bool construction2 = ENUM_ATTR(tiletype, material, *type2) == df::enums::tiletype_material::CONSTRUCTION; if ( construction2 ) { //smooth or not? df::construction* constr = df::construction::find(pt2); bool smooth = constr != NULL && constr->item_type != df::enums::item_type::BOULDER; if ( smooth ) { if ( abilities.costWeight[CostDimension::DestroySmoothConstruction] < 0 ) return -1; cost += abilities.costWeight[CostDimension::DestroySmoothConstruction]; } else { if ( abilities.costWeight[CostDimension::DestroyRoughConstruction] < 0 ) return -1; cost += abilities.costWeight[CostDimension::DestroyRoughConstruction]; } } if ( dz == 0 ) { if ( !building2 && !construction2 ) { //it has to be a wall if ( shape2 == df::enums::tiletype_shape::RAMP_TOP ) { return -1; } else if ( shape2 != df::enums::tiletype_shape::WALL ) { //out << "shape = " << (int32_t)shape2 << endl; //out << __FILE__ << ", line " << __LINE__ << ": WTF?" << endl; return cost; } if ( abilities.costWeight[CostDimension::Dig] < 0 ) { return -1; } cost += abilities.costWeight[CostDimension::Dig]; } } else { if ( dx == 0 && dy == 0 ) { df::tiletype* type1 = Maps::getTileType(pt1); df::tiletype_shape shape1 = ENUM_ATTR(tiletype, shape, *type1); if ( dz > 0 ) { bool walkable_low2 = shape2 == df::tiletype_shape::STAIR_DOWN || shape2 == df::tiletype_shape::STAIR_UPDOWN; if ( !walkable_low2 ) { if ( building2 || construction2 ) return -1; if ( abilities.costWeight[CostDimension::Dig] < 0 ) { return -1; } cost += abilities.costWeight[CostDimension::Dig]; } bool walkable_high1 = shape1 == df::tiletype_shape::STAIR_UP || shape1 == df::tiletype_shape::STAIR_UPDOWN; if ( !walkable_high1 ) { if ( shape1 != df::enums::tiletype_shape::WALL ) { return -1; } if ( abilities.costWeight[CostDimension::Dig] < 0 ) { return -1; } cost += abilities.costWeight[CostDimension::Dig]; } if ( building2 ) { //moving up through an open bridge or a usable hatch is fine. other buildings are not bool unforbiddenHatch = false; if ( building2->getType() == df::building_type::Hatch ) { df::building_hatchst* hatch = (df::building_hatchst*)building2; if ( !hatch->door_flags.bits.forbidden && !(hatch->door_flags.bits.operated_by_mechanisms&&hatch->door_flags.bits.closed) ) unforbiddenHatch = true; } bool inactiveBridge = false; if ( building2->getType() == df::building_type::Bridge ) { df::building_bridgest* bridge = (df::building_bridgest*)building2; bool xMin = pt2.x == bridge->x1; bool xMax = pt2.x == bridge->x2; bool yMin = pt2.y == bridge->y1; bool yMax = pt2.y == bridge->y2; if ( !bridge->gate_flags.bits.closed ) { //if it's open, we could still be in the busy part of it if ( bridge->direction == df::building_bridgest::T_direction::Left && !xMin ) { inactiveBridge = true; } else if ( bridge->direction == df::building_bridgest::T_direction::Right && !xMax ) { inactiveBridge = true; } else if ( bridge->direction == df::building_bridgest::T_direction::Up && !yMax ) { inactiveBridge = true; } else if ( bridge->direction == df::building_bridgest::T_direction::Down && !yMin ) { inactiveBridge = true; } else if ( bridge->direction == df::building_bridgest::T_direction::Retracting ) { inactiveBridge = true; } } } if ( !unforbiddenHatch && !inactiveBridge ) return -1; } /*bool forbidden = false; if ( building2 && building2->getType() == df::building_type::Hatch ) { df::building_hatchst* hatch = (df::building_hatchst*)building2; if ( hatch->door_flags.bits.forbidden ) forbidden = true; } if ( forbidden ) return -1;*/ } else { bool walkable_high2 = shape2 == df::tiletype_shape::STAIR_UP || shape2 == df::tiletype_shape::STAIR_UPDOWN; if ( !walkable_high2 ) { if ( building2 || construction2 ) return -1; if ( shape2 != df::enums::tiletype_shape::WALL ) return -1; if ( abilities.costWeight[CostDimension::Dig] < 0 ) { return -1; } cost += abilities.costWeight[CostDimension::Dig]; } bool walkable_low1 = shape1 == df::tiletype_shape::STAIR_DOWN || shape1 == df::tiletype_shape::STAIR_UPDOWN; if ( !walkable_low1 ) { //if ( building1 || construction1 ) //return -1; //TODO: consider ramps if ( shape1 == df::tiletype_shape::RAMP ) return -1; if ( abilities.costWeight[CostDimension::Dig] < 0 ) { return -1; } cost += abilities.costWeight[CostDimension::Dig]; } df::building* building1 = Buildings::findAtTile(pt1); //if you're moving down, and you're on a bridge, and that bridge is lowered, then you can't do it if ( building1 && building1->getType() == df::building_type::Bridge ) { df::building_bridgest* bridge = (df::building_bridgest*)building2; if ( bridge->gate_flags.bits.closed ) { return -1; } //open bridges moving down, standing on bad spot if ( bridge->direction == df::building_bridgest::T_direction::Left && pt1.x == bridge->x1 ) return -1; if ( bridge->direction == df::building_bridgest::T_direction::Right && pt1.x == bridge->x2 ) return -1; if ( bridge->direction == df::building_bridgest::T_direction::Up && pt1.y == bridge->y1 ) return -1; if ( bridge->direction == df::building_bridgest::T_direction::Down && pt1.y == bridge->y2 ) return -1; } bool forbidden = false; if ( building1 && building1->getType() == df::building_type::Hatch ) { df::building_hatchst* hatch = (df::building_hatchst*)building1; if ( hatch->door_flags.bits.forbidden || ( hatch->door_flags.bits.closed && hatch->door_flags.bits.operated_by_mechanisms ) ) forbidden = true; } //you can deconstruct a hatch from the side if ( building1 && forbidden /*&& building1->getType() == df::building_type::Hatch*/ ) { /* df::coord support[] = {df::coord(pt1.x-1, pt1.y, pt1.z), df::coord(pt1.x+1,pt1.y,pt1.z), df::coord(pt1.x,pt1.y-1,pt1.z), df::coord(pt1.x,pt1.y+1,pt1.z)}; if ( abilities.costWeight[CostDimension::DestroyBuilding] < 0 ) { return -1; } cost_t minCost = -1; for ( size_t a = 0; a < 4; a++ ) { df::tiletype* supportType = Maps::getTileType(support[a]); df::tiletype_shape shape = ENUM_ATTR(tiletype, shape, *supportType); df::tiletype_shape_basic basic = ENUM_ATTR(tiletype_shape, basic_shape, shape); cost_t cost2 = 2*abilities.costWeight[CostDimension::Walk] + abilities.costWeight[CostDimension::DestroyBuilding]; if ( !Maps::canStepBetween(pt1, support[a]) ) { switch(basic) { case tiletype_shape_basic::Open: //TODO: check for a hatch or a bridge: that makes it ok continue; case tiletype_shape_basic::Wall: if ( ENUM_ATTR(tiletype, material, *supportType) == df::enums::tiletype_material::CONSTRUCTION ) { if ( abilities.costWeight[CostDimension::DestroyConstruction] < 0 ) { continue; } cost2 += abilities.costWeight[CostDimension::DestroyConstruction]; } else { if ( abilities.costWeight[CostDimension::Dig] < 0 ) { continue; } cost2 += abilities.costWeight[CostDimension::Dig]; } case tiletype_shape_basic::Ramp: //TODO: check for a hatch or a bridge: that makes it ok if ( shape == df::enums::tiletype_shape::RAMP_TOP ) { continue; } case tiletype_shape_basic::Stair: case tiletype_shape_basic::Floor: break; } if ( Buildings::findAtTile(support[a]) ) { if ( abilities.costWeight[CostDimension::DestroyBuilding] < 0 ) { continue; } cost2 += abilities.costWeight[CostDimension::DestroyBuilding]; } } if ( minCost == -1 || cost2 < minCost ) minCost = cost2; } if ( minCost == -1 ) return -1; cost += minCost; */ //note: assignJob is not ready for this level of sophistication, so don't allow it return -1; } } } else { //nonvertical //out.print("%s, line %d: (%d,%d,%d)->(%d,%d,%d)\n", __FILE__, __LINE__, pt1.x,pt1.y,pt1.z, pt2.x,pt2.y,pt2.z); return -1; } } return cost; } /* cost_t getEdgeCostOld(color_ostream& out, df::coord pt1, df::coord pt2) { //first, list all the facts int32_t dx = pt2.x - pt1.x; int32_t dy = pt2.y - pt1.y; int32_t dz = pt2.z - pt1.z; cost_t cost = costWeight[CostDimension::Walk]; if ( false ) { if ( Maps::canStepBetween(pt1,pt2) ) return cost; return 100 + cost; } Maps::ensureTileBlock(pt1); Maps::ensureTileBlock(pt2); df::tiletype* type1 = Maps::getTileType(pt1); df::tiletype* type2 = Maps::getTileType(pt2); df::map_block* block1 = Maps::getTileBlock(pt1); df::map_block* block2 = Maps::getTileBlock(pt2); df::tiletype_shape shape1 = ENUM_ATTR(tiletype, shape, *type1); df::tiletype_shape shape2 = ENUM_ATTR(tiletype, shape, *type2); bool construction1 = ENUM_ATTR(tiletype, material, *type1) == df::enums::tiletype_material::CONSTRUCTION; bool construction2 = ENUM_ATTR(tiletype, material, *type2) == df::enums::tiletype_material::CONSTRUCTION; bool passable1 = block1->walkable[pt1.x&0xF][pt1.y&0xF] != 0; bool passable2 = block2->walkable[pt2.x&0xF][pt2.y&0xF] != 0; bool passable_high1 = ENUM_ATTR(tiletype_shape, passable_high, shape1); bool passable_high2 = ENUM_ATTR(tiletype_shape, passable_high, shape2); bool passable_low1 = ENUM_ATTR(tiletype_shape, passable_low, shape1); bool passable_low2 = ENUM_ATTR(tiletype_shape, passable_low, shape2); bool building1, building2; bool sameBuilding = false; { df::enums::tile_building_occ::tile_building_occ awk = block1->occupancy[pt1.x&0x0F][pt1.y&0x0F].bits.building; building1 = awk == df::enums::tile_building_occ::Obstacle || awk == df::enums::tile_building_occ::Impassable; awk = block2->occupancy[pt2.x&0x0F][pt2.y&0x0F].bits.building; building2 = awk == df::enums::tile_building_occ::Obstacle || awk == df::enums::tile_building_occ::Impassable; if ( building1 && building2 ) { df::building* b1 = Buildings::findAtTile(pt1); df::building* b2 = Buildings::findAtTile(pt2); sameBuilding = b1 == b2; } } if ( Maps::canStepBetween(pt1, pt2) ) { if ( building2 && !sameBuilding ) { cost += costWeight[CostDimension::DestroyBuilding]; } return cost; } if ( shape2 == df::enums::tiletype_shape::EMPTY ) { return -1; } //cannot step between: find out why if ( dz == 0 ) { if ( passable2 && !passable1 ) { return cost; } if ( passable1 && passable2 ) { out << __FILE__ << ", line " << __LINE__ << ": WTF?" << endl; return cost; } //pt2 is not passable. it must be a construction, a building, or a wall. if ( building2 ) { if ( sameBuilding ) { //don't charge twice for destroying the same building return cost; } cost += costWeight[CostDimension::DestroyBuilding]; return cost; } if ( construction2 ) { //impassible constructions must be deconstructed cost += costWeight[CostDimension::DestroyConstruction]; return cost; } if ( shape2 == df::enums::tiletype_shape::TREE ) { return -1; } if ( shape2 == df::enums::tiletype_shape::RAMP_TOP ) { return -1; } //it has to be a wall if ( shape2 != df::enums::tiletype_shape::WALL ) { out << "shape = " << (int32_t)shape2 << endl; out << __FILE__ << ", line " << __LINE__ << ": WTF?" << endl; return cost; } cost += costWeight[CostDimension::Dig]; return cost; } //dz != 0 if ( dx == 0 && dy == 0 ) { if ( dz > 0 ) { if ( passable_low2 ) return cost; if ( building2 || construction2 ) { return -1; } cost += costWeight[CostDimension::Dig]; return cost; } //descending if ( passable_high2 ) return cost; if ( building2 || construction2 ) { return -1; } //must be a wall? if ( shape2 != df::enums::tiletype_shape::WALL ) { out.print("%s, line %n: WTF?\n", __FILE__, __LINE__); return cost; } cost += costWeight[CostDimension::Dig]; return cost; } //moving diagonally return -1; } */ vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax, DigAbilities& abilities) { //vector* result = new vector(26); vector* result = new vector(); result->reserve(26); //size_t count = 0; for ( int32_t dx = -1; dx <= 1; dx++ ) { for ( int32_t dy = -1; dy <= 1; dy++ ) { for ( int32_t dz = -1; dz <= 1; dz++ ) { df::coord neighbor(point.x+dx, point.y+dy, point.z+dz); if ( !Maps::isValidTilePos(neighbor) ) continue; if ( dz != 0 && /*(point.x == 0 || point.y == 0 || point.z == 0 || point.x == xMax-1 || point.y == yMax-1 || point.z == zMax-1) ||*/ (neighbor.x == 0 || neighbor.y == 0 || neighbor.z == 0 || neighbor.x == xMax-1 || neighbor.y == yMax-1 || neighbor.z == zMax-1) ) continue; if ( dx == 0 && dy == 0 && dz == 0 ) continue; cost_t cost = getEdgeCost(out, point, neighbor, abilities); if ( cost == -1 ) continue; Edge edge(point, neighbor, cost); //(*result)[count] = edge; result->push_back(edge); //count++; } } } return result; }