diff --git a/plugins/diggingInvaders/CMakeLists.txt b/plugins/diggingInvaders/CMakeLists.txt index d2324b8c9..a88c93ca1 100644 --- a/plugins/diggingInvaders/CMakeLists.txt +++ b/plugins/diggingInvaders/CMakeLists.txt @@ -2,10 +2,13 @@ PROJECT (diggingInvaders) # A list of source files SET(PROJECT_SRCS diggingInvaders.cpp + edgeCost.cpp + assignJob.cpp ) # A list of headers SET(PROJECT_HDRS - + edgeCost.h + assignJob.h ) SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) @@ -29,5 +32,4 @@ ELSE(UNIX) ENDIF(UNIX) # this makes sure all the stuff is put in proper places and linked to dfhack -#DFHACK_PLUGIN(diggingInvaders ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS}) -DFHACK_PLUGIN(diggingInvaders diggingInvaders.cpp) +DFHACK_PLUGIN(diggingInvaders ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS}) diff --git a/plugins/diggingInvaders/assignJob.cpp b/plugins/diggingInvaders/assignJob.cpp new file mode 100644 index 000000000..a11717f8a --- /dev/null +++ b/plugins/diggingInvaders/assignJob.cpp @@ -0,0 +1,188 @@ +#include "assignJob.h" + +#include "modules/Buildings.h" +#include "modules/Items.h" +#include "modules/Job.h" + +#include "df/building.h" +#include "df/coord.h" +#include "df/general_ref.h" +#include "df/general_ref_building_holderst.h" +#include "df/general_ref_unit.h" +//#include "df/general_ref_unit_holderst.h" +#include "df/general_ref_unit_workerst.h" +#include "df/item.h" +#include "df/itemdef_weaponst.h" +#include "df/item_quality.h" +#include "df/item_weaponst.h" +#include "df/job.h" +#include "df/job_type.h" +#include "df/unit.h" +#include "df/unit_inventory_item.h" + +int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_map parentMap, unordered_map& costMap, vector& invaders, unordered_set& requiresZNeg, unordered_set& requiresZPos, MapExtras::MapCache& cache) { + df::unit* firstInvader = invaders[0]; + + //do whatever you need to do at the first important edge + df::coord pt1 = firstImportantEdge.p1; + df::coord pt2 = firstImportantEdge.p2; + if ( costMap[pt1] > costMap[pt2] ) { + df::coord temp = pt1; + pt1 = pt2; + pt2 = temp; + } + out.print("first important edge: (%d,%d,%d) -> (%d,%d,%d)\n", pt1.x,pt1.y,pt1.z, pt2.x,pt2.y,pt2.z); + + int32_t jobId = -1; + + df::map_block* block1 = Maps::getTileBlock(pt1); + df::map_block* block2 = Maps::getTileBlock(pt2); + bool passable1 = block1->walkable[pt1.x&0x0F][pt1.y&0x0F]; + bool passable2 = block2->walkable[pt2.x&0x0F][pt2.y&0x0F]; + + df::building* building = Buildings::findAtTile(pt2); + df::coord buildingPos = pt2; + if ( pt1.z > pt2.z ) { + building = Buildings::findAtTile(df::coord(pt2.x,pt2.y,pt2.z+1)); + buildingPos = df::coord(pt2.x,pt2.y,pt2.z+1); + } + if ( building != NULL ) { + df::coord destroyFrom = parentMap[buildingPos]; + out.print("%s, line %d: Destroying building %d at (%d,%d,%d) from (%d,%d,%d).\n", __FILE__, __LINE__, building->id, buildingPos.x,buildingPos.y,buildingPos.z, destroyFrom.x,destroyFrom.y,destroyFrom.z); + + df::job* job = new df::job; + job->job_type = df::enums::job_type::DestroyBuilding; + job->flags.bits.special = 1; + df::general_ref_building_holderst* buildingRef = new df::general_ref_building_holderst; + buildingRef->building_id = building->id; + job->general_refs.push_back(buildingRef); + df::general_ref_unit_workerst* workerRef = new df::general_ref_unit_workerst; + workerRef->unit_id = firstInvader->id; + job->general_refs.push_back(workerRef); + firstInvader->job.current_job = job; + firstInvader->path.path.x.clear(); + firstInvader->path.path.y.clear(); + firstInvader->path.path.z.clear(); + firstInvader->path.dest = destroyFrom; + firstInvader->job.hunt_target = NULL; + firstInvader->job.destroy_target = NULL; + + building->jobs.clear(); + building->jobs.push_back(job); + Job::linkIntoWorld(job); + jobId = job->id; + } else { + df::tiletype* type1 = Maps::getTileType(pt1); + df::tiletype* type2 = Maps::getTileType(pt2); + df::tiletype_shape shape1 = ENUM_ATTR(tiletype, shape, *type1); + df::tiletype_shape shape2 = ENUM_ATTR(tiletype, shape, *type2); + bool construction2 = ENUM_ATTR(tiletype, material, *type2) == df::enums::tiletype_material::CONSTRUCTION; + if ( construction2 ) { + df::job* job = new df::job; + job->job_type = df::enums::job_type::RemoveConstruction; + df::general_ref_unit_workerst* workerRef = new df::general_ref_unit_workerst; + workerRef->unit_id = firstInvader->id; + job->general_refs.push_back(workerRef); + job->pos = pt2; + firstInvader->job.current_job = job; + firstInvader->path.path.x.clear(); + firstInvader->path.path.y.clear(); + firstInvader->path.path.z.clear(); + firstInvader->path.dest = pt1; + firstInvader->job.hunt_target = NULL; + firstInvader->job.destroy_target = NULL; + Job::linkIntoWorld(job); + jobId = job->id; + } else { + //must be a dig job + bool up = requiresZPos.find(pt2) != requiresZPos.end(); + bool down = requiresZNeg.find(pt2) != requiresZNeg.end(); + df::job* job = new df::job; + if ( up && down ) { + job->job_type = df::enums::job_type::CarveUpDownStaircase; + job->pos = pt2; + firstInvader->path.dest = pt2; + } else if ( up && !down ) { + job->job_type = df::enums::job_type::CarveUpwardStaircase; + job->pos = pt2; + firstInvader->path.dest = pt2; + } else if ( !up && down ) { + job->job_type = df::enums::job_type::CarveDownwardStaircase; + job->pos = pt2; + firstInvader->path.dest = pt2; + } else { + job->job_type = df::enums::job_type::Dig; + job->pos = pt2; + firstInvader->path.dest = pt1; + } + df::general_ref_unit_workerst* ref = new df::general_ref_unit_workerst; + ref->unit_id = firstInvader->id; + job->general_refs.push_back(ref); + firstInvader->job.hunt_target = NULL; + firstInvader->job.destroy_target = NULL; + firstInvader->job.current_job = job; + firstInvader->path.path.x.clear(); + firstInvader->path.path.y.clear(); + firstInvader->path.path.z.clear(); + Job::linkIntoWorld(job); + jobId = job->id; + + //TODO: test if he already has a pick + + //create and give a pick + df::item_weaponst* pick = new df::item_weaponst; + pick->pos = firstInvader->pos; + pick->flags.bits.forbid = 1; + pick->flags.bits.on_ground = 1; + pick->id = (*df::global::item_next_id)++; + pick->ignite_point = -1; + pick->heatdam_point = -1; + pick->colddam_point = -1; + pick->boiling_point = 11000; + pick->melting_point = 10500; + pick->fixed_temp = -1; + pick->weight = 0; + pick->weight_fraction = 0; + pick->stack_size = 1; + pick->temperature.whole = 10059; + pick->temperature.fraction = 0; + pick->mat_type = 0; + pick->mat_index = 5; + pick->maker_race = 0; //hehe + pick->quality = (df::enums::item_quality::item_quality)0; + pick->skill_used = (df::enums::job_skill::job_skill)0; + pick->maker = -1; + df::itemdef_weaponst* itemdef = NULL; + for ( size_t a = 0; a < df::global::world->raws.itemdefs.weapons.size(); a++ ) { + df::itemdef_weaponst* candidate = df::global::world->raws.itemdefs.weapons[a]; + if ( candidate->id == "ITEM_WEAPON_PICK" ) { + itemdef = candidate; + break; + } + } + if ( itemdef == NULL ) { + out.print("%s, %d: null itemdef.\n", __FILE__, __LINE__); + return -1; + } + pick->subtype = itemdef; + pick->sharpness = 5000; + + int32_t part = -1; + part = firstInvader->body.weapon_bp; //weapon_bp + if ( part == -1 ) { + out.print("%s, %d: no grasp part.\n", __FILE__, __LINE__); + return -1; + } + //check for existing item there + for ( size_t a = 0; a < firstInvader->inventory.size(); a++ ) { + df::unit_inventory_item* inv_item = firstInvader->inventory[a]; + if ( false || inv_item->body_part_id == part ) { + //throw it on the GROUND + Items::moveToGround(cache, inv_item->item, firstInvader->pos); + } + } + Items::moveToInventory(cache, pick, firstInvader, df::unit_inventory_item::T_mode::Weapon, part); + } + } + return jobId; +} diff --git a/plugins/diggingInvaders/assignJob.h b/plugins/diggingInvaders/assignJob.h new file mode 100644 index 000000000..735ba22ab --- /dev/null +++ b/plugins/diggingInvaders/assignJob.h @@ -0,0 +1,12 @@ +#pragma once + +#include "edgeCost.h" + +#include "modules/MapCache.h" + +#include +#include + +using namespace std; + +int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_map parentMap, unordered_map& costMap, vector& invaders, unordered_set& requiresZNeg, unordered_set& requiresZPos, MapExtras::MapCache& cache); diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index 6148a6bdd..57942fd00 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -1,3 +1,6 @@ +#include "assignJob.h" +#include "edgeCost.h" + #include "Core.h" #include "Console.h" #include "DataDefs.h" @@ -44,6 +47,7 @@ #include "df/unit_inventory_item.h" #include "df/world.h" + #include #include #include @@ -81,72 +85,8 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) return CR_OK; } -//cost is [path cost, building destruction cost, dig cost, construct cost]. Minimize constructions, then minimize dig cost, then minimize path cost. -enum CostDimension { - Distance, - DestroyBuilding, - Dig, - DestroyConstruction, - //Construct, - costDim -}; - -const int64_t costWeight[] = { -//Distance -1, -//Destroy Building -2, -//Dig -10000, -//DestroyConstruction -100, -}; - -class Edge { -public: - //static map pointCost; - df::coord p1; - df::coord p2; - int64_t cost; - Edge(df::coord p1In, df::coord p2In, int64_t costIn): cost(costIn) { - if ( p2In < p1In ) { - p1 = p2In; - p2 = p1In; - } else { - p1 = p1In; - p2 = p2In; - } - } - - bool operator==(const Edge& e) const { - return (cost == e.cost && p1 == e.p1 && p2 == e.p2); - } - - bool operator<(const Edge& e) const { - if ( cost != e.cost ) - return cost < e.cost; - if ( p1.z != e.p1.z ) - return p1.z < e.p1.z; - if ( p1 != e.p1 ) - return p1 < e.p1; - if ( p2.z != e.p2.z ) - return p2.z < e.p2.z; - if ( p2 != e.p2 ) - return p2 < e.p2; - return false; - } -}; - -vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax); - df::coord getRoot(df::coord point, unordered_map& rootMap); -struct PointHash { - size_t operator()(const df::coord c) const { - return c.x * 65537 + c.y * 17 + c.z; - } -}; - class PointComp { public: unordered_map *pointCost; @@ -346,462 +286,8 @@ int32_t doDiggingInvaders(color_ostream& out) { } requiresZPos.erase(toDelete.begin(), toDelete.end()); toDelete.clear(); - - //do whatever you need to do at the first important edge - df::coord pt1 = firstImportantEdge.p1; - df::coord pt2 = firstImportantEdge.p2; - if ( costMap[pt1] > costMap[pt2] ) { - df::coord temp = pt1; - pt1 = pt2; - pt2 = temp; - } - out.print("(%d,%d,%d) -> (%d,%d,%d)\n", pt1.x,pt1.y,pt1.z, pt2.x,pt2.y,pt2.z); - - int32_t jobId = -1; - - df::map_block* block1 = Maps::getTileBlock(pt1); - df::map_block* block2 = Maps::getTileBlock(pt2); - bool passable1 = block1->walkable[pt1.x&0x0F][pt1.y&0x0F]; - bool passable2 = block2->walkable[pt2.x&0x0F][pt2.y&0x0F]; - - df::building* building = Buildings::findAtTile(pt2); - df::coord buildingPos = pt2; - if ( pt1.z > pt2.z ) { - building = Buildings::findAtTile(df::coord(pt2.x,pt2.y,pt2.z+1)); - buildingPos = df::coord(pt2.x,pt2.y,pt2.z+1); - } - if ( building != NULL ) { - out.print("%s, line %d: Destroying building %d at (%d,%d,%d)\n", __FILE__, __LINE__, building->id, pt2.x,pt2.y,pt2.z); - - df::job* job = new df::job; - job->job_type = df::enums::job_type::DestroyBuilding; - job->flags.bits.special = 1; - df::general_ref_building_holderst* buildingRef = new df::general_ref_building_holderst; - buildingRef->building_id = building->id; - job->general_refs.push_back(buildingRef); - df::general_ref_unit_workerst* workerRef = new df::general_ref_unit_workerst; - workerRef->unit_id = firstInvader->id; - job->general_refs.push_back(workerRef); - firstInvader->job.current_job = job; - firstInvader->path.path.x.clear(); - firstInvader->path.path.y.clear(); - firstInvader->path.path.z.clear(); - firstInvader->path.dest = parentMap[buildingPos]; - firstInvader->job.hunt_target = NULL; - firstInvader->job.destroy_target = NULL; - - building->jobs.clear(); - building->jobs.push_back(job); - Job::linkIntoWorld(job); - jobId = job->id; - } else { - df::tiletype* type1 = Maps::getTileType(pt1); - df::tiletype* type2 = Maps::getTileType(pt2); - df::tiletype_shape shape1 = ENUM_ATTR(tiletype, shape, *type1); - df::tiletype_shape shape2 = ENUM_ATTR(tiletype, shape, *type2); - bool construction2 = ENUM_ATTR(tiletype, material, *type2) == df::enums::tiletype_material::CONSTRUCTION; - if ( construction2 ) { - df::job* job = new df::job; - job->job_type = df::enums::job_type::RemoveConstruction; - df::general_ref_unit_workerst* workerRef = new df::general_ref_unit_workerst; - workerRef->unit_id = firstInvader->id; - job->general_refs.push_back(workerRef); - job->pos = pt2; - firstInvader->job.current_job = job; - firstInvader->path.path.x.clear(); - firstInvader->path.path.y.clear(); - firstInvader->path.path.z.clear(); - firstInvader->path.dest = pt1; - firstInvader->job.hunt_target = NULL; - firstInvader->job.destroy_target = NULL; - Job::linkIntoWorld(job); - jobId = job->id; - } else { - //must be a dig job - bool up = requiresZPos.find(pt2) != requiresZPos.end(); - bool down = requiresZNeg.find(pt2) != requiresZNeg.end(); - df::job* job = new df::job; - if ( up && down ) { - job->job_type = df::enums::job_type::CarveUpDownStaircase; - job->pos = pt2; - firstInvader->path.dest = pt2; - } else if ( up && !down ) { - job->job_type = df::enums::job_type::CarveUpwardStaircase; - job->pos = pt2; - firstInvader->path.dest = pt2; - } else if ( !up && down ) { - job->job_type = df::enums::job_type::CarveDownwardStaircase; - job->pos = pt2; - firstInvader->path.dest = pt2; - } else { - job->job_type = df::enums::job_type::Dig; - job->pos = pt2; - firstInvader->path.dest = pt1; - } - df::general_ref_unit_workerst* ref = new df::general_ref_unit_workerst; - ref->unit_id = firstInvader->id; - job->general_refs.push_back(ref); - firstInvader->job.hunt_target = NULL; - firstInvader->job.destroy_target = NULL; - firstInvader->job.current_job = job; - firstInvader->path.path.x.clear(); - firstInvader->path.path.y.clear(); - firstInvader->path.path.z.clear(); - Job::linkIntoWorld(job); - jobId = job->id; - - //TODO: test if he already has a pick - - //create and give a pick - df::item_weaponst* pick = new df::item_weaponst; - pick->pos = firstInvader->pos; - pick->flags.bits.forbid = 1; - pick->flags.bits.on_ground = 1; - pick->id = (*df::global::item_next_id)++; - pick->ignite_point = -1; - pick->heatdam_point = -1; - pick->colddam_point = -1; - pick->boiling_point = 11000; - pick->melting_point = 10500; - pick->fixed_temp = -1; - pick->weight = 0; - pick->weight_fraction = 0; - pick->stack_size = 1; - pick->temperature.whole = 10059; - pick->temperature.fraction = 0; - pick->mat_type = 0; - pick->mat_index = 5; - pick->maker_race = 0; //hehe - pick->quality = (df::enums::item_quality::item_quality)0; - pick->skill_used = (df::enums::job_skill::job_skill)0; - pick->maker = -1; - df::itemdef_weaponst* itemdef = NULL; - for ( size_t a = 0; a < df::global::world->raws.itemdefs.weapons.size(); a++ ) { - df::itemdef_weaponst* candidate = df::global::world->raws.itemdefs.weapons[a]; - if ( candidate->id == "ITEM_WEAPON_PICK" ) { - itemdef = candidate; - break; - } - } - if ( itemdef == NULL ) { - out.print("%s, %d: null itemdef.\n", __FILE__, __LINE__); - return -1; - } - pick->subtype = itemdef; - pick->sharpness = 5000; - - int32_t part = -1; - part = firstInvader->body.weapon_bp; //weapon_bp - if ( part == -1 ) { - out.print("%s, %d: no grasp part.\n", __FILE__, __LINE__); - return -1; - } - //check for existing item there - for ( size_t a = 0; a < firstInvader->inventory.size(); a++ ) { - df::unit_inventory_item* inv_item = firstInvader->inventory[a]; - if ( false || inv_item->body_part_id == part ) { - //throw it on the GROUND - Items::moveToGround(cache, inv_item->item, firstInvader->pos); - } - } - Items::moveToInventory(cache, pick, firstInvader, df::unit_inventory_item::T_mode::Weapon, part); - } - } - return jobId; -} - -int64_t getEdgeCost(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; - int64_t cost = costWeight[CostDimension::Distance]; - - 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; -#if 0 - //if ( dz != 0 ) cost++; - if ( Maps::canStepBetween(point, neighbor) ) { - df::map_block* block2 = Maps::getTileBlock(neighbor); - df::enums::tile_building_occ::tile_building_occ occ = block2->occupancy[neighbor.x&0x0F][neighbor.y&0x0F].bits.building; - bool building2 = occ == df::enums::tile_building_occ::Obstacle || occ == df::enums::tile_building_occ::Impassable || occ == df::enums::tile_building_occ::Floored; - if ( building2 ) { - df::building* building = Buildings::findAtTile(neighbor); - if ( true || building->getType() == df::enums::building_type::Hatch ) { - if ( true || building->isForbidden() ) { - //TODO: worry about destroying hatches with nowhere to stand - cost += costWeight[CostDimension::DestroyBuilding]; - } - } - } - - Edge edge(point, neighbor, cost); - result->push_back(edge); - } else { - //find out WHY we can't walk there - //make it simple: don't deal with unallocated blocks - Maps::ensureTileBlock(point); - Maps::ensureTileBlock(neighbor); - df::tiletype* type1 = Maps::getTileType(point); - df::tiletype* type2 = Maps::getTileType(neighbor); - df::map_block* block1 = Maps::getTileBlock(point); - df::map_block* block2 = Maps::getTileBlock(neighbor); - - 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[point.x&0xF][point.y&0xF] != 0; - bool passable2 = block2->walkable[neighbor.x&0xF][neighbor.y&0xF] != 0; - - bool building1, building2; - bool sameBuilding = false; - { - df::enums::tile_building_occ::tile_building_occ awk = block1->occupancy[point.x&0x0F][point.y&0x0F].bits.building; - building1 = awk == df::enums::tile_building_occ::Obstacle || awk == df::enums::tile_building_occ::Impassable; - awk = block2->occupancy[neighbor.x&0x0F][neighbor.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(point); - df::building* b2 = Buildings::findAtTile(neighbor); - sameBuilding = b1 == b2; - } - } - - if ( !passable2 && building2 && !sameBuilding ) { - df::building* b2 = Buildings::findAtTile(neighbor); - cost += costWeight[CostDimension::DestroyBuilding]; - if ( dz != 0 ) - continue; - } else { - - if ( shape2 == df::enums::tiletype_shape::EMPTY ) { - //cost.cost[CostDimension::Construct] += getConstructCost(neighbor); - continue; - } else { - if ( dz == 0 ) { - if ( passable2 ) { - if ( passable1 ) { - out.print("%s, line %d: weirdness. (%d,%d,%d), (%d,%d,%d)\n", __FILE__, __LINE__, point.x,point.y,point.z, neighbor.x,neighbor.y,neighbor.z); - //exit(1); - //TODO: check - } else { - //this is fine: only charge once for digging through a wall, etc - } - } else { - //passable2 is false - if ( construction2 ) { - //must deconstruct orthogonally - if ( dx*dy != 0 ) - continue; - cost += costWeight[CostDimension::DestroyConstruction]; - } else { - cost += costWeight[CostDimension::Dig]; - } - } - } else { - //dz is nonzero - bool ascending = dz > 0; - if ( dx == 0 && dy == 0 ) { - if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Stair ) { - if ( (ascending && ENUM_ATTR(tiletype_shape, passable_low, shape2)) || (!ascending && ENUM_ATTR(tiletype_shape, walkable_up, shape2)) ) { - out.print("%s, line %d: weirdness. (%d,%d,%d), (%d,%d,%d)\n", __FILE__, __LINE__, point.x,point.y,point.z, neighbor.x,neighbor.y,neighbor.z); - //TODO: check for forbidden hatch - continue; - } else { - //figure out if we can dig something in there - df::enums::tiletype_shape_basic::tiletype_shape_basic basic = ENUM_ATTR(tiletype_shape, basic_shape, shape2); - if ( ascending ) { - if ( construction2 ) - continue; //technically possible: moving up, construction is up stair, hiding a floor with no down stair - if ( basic == df::enums::tiletype_shape_basic::Wall || basic == df::enums::tiletype_shape_basic::Stair || df::enums::tiletype_shape_basic::Floor ) { - cost += costWeight[CostDimension::Dig]; - } else { - continue; - } - } else { - //descending - if ( construction2 ) - continue; - if ( basic == df::enums::tiletype_shape_basic::Wall ) { - cost += costWeight[CostDimension::Dig]; - } else if ( basic == df::enums::tiletype_shape_basic::Ramp ) { - //do nothing - } else if ( basic == df::enums::tiletype_shape_basic::Floor ) { - //do nothing - } else { - continue; - } - } - } - } else { - //shape2 is not a stair - if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Wall ) { - if ( construction2 ) - continue; - cost += costWeight[CostDimension::Dig]; - } else if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Open ) { - continue; - } else { - if ( ascending && ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Floor && !construction2 ) { - cost += costWeight[CostDimension::Dig]; - } - continue; - } - } - } else { - //now we're talking about moving up and down nonvertically, ie with ramps - continue; - } - } - } - } - } - return cost; -#endif -} - -vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax) { - vector* result = new vector; - result->reserve(26); - 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 ( neighbor.x < 0 || neighbor.x >= xMax || neighbor.y < 0 || neighbor.y >= yMax || neighbor.z < 0 || neighbor.z >= zMax ) - 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; - int64_t cost = getEdgeCost(out, point, neighbor); - if ( cost == -1 ) - continue; - Edge edge(point, neighbor, cost); - result->push_back(edge); - } - } - } - - return result; + return assignJob(out, firstImportantEdge, parentMap, costMap, invaders, requiresZNeg, requiresZPos, cache); } df::coord getRoot(df::coord point, map& rootMap) { diff --git a/plugins/diggingInvaders/edgeCost.cpp b/plugins/diggingInvaders/edgeCost.cpp new file mode 100644 index 000000000..7e4fb071e --- /dev/null +++ b/plugins/diggingInvaders/edgeCost.cpp @@ -0,0 +1,169 @@ +#include "edgeCost.h" + +#include "modules/Buildings.h" +#include "modules/Maps.h" +#include "modules/MapCache.h" + +#include "df/building.h" +#include "df/coord.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" + +using namespace std; + +int64_t getEdgeCost(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; + int64_t cost = costWeight[CostDimension::Distance]; + + 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) { + vector* result = new vector; + result->reserve(26); + + 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 ( neighbor.x < 0 || neighbor.x >= xMax || neighbor.y < 0 || neighbor.y >= yMax || neighbor.z < 0 || neighbor.z >= zMax ) + 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; + int64_t cost = getEdgeCost(out, point, neighbor); + if ( cost == -1 ) + continue; + Edge edge(point, neighbor, cost); + result->push_back(edge); + } + } + } + + return result; +} + diff --git a/plugins/diggingInvaders/edgeCost.h b/plugins/diggingInvaders/edgeCost.h new file mode 100644 index 000000000..5b2d76b1b --- /dev/null +++ b/plugins/diggingInvaders/edgeCost.h @@ -0,0 +1,77 @@ +#pragma once + +#include "Core.h" +#include "Console.h" +#include "DataDefs.h" + +#include "modules/Maps.h" +#include "modules/MapCache.h" + +#include "df/coord.h" + +#include + +//cost is [path cost, building destruction cost, dig cost, construct cost]. Minimize constructions, then minimize dig cost, then minimize path cost. +enum CostDimension { + Distance, + DestroyBuilding, + Dig, + DestroyConstruction, + //Construct, + costDim +}; + +const int64_t costWeight[] = { +//Distance +1, +//Destroy Building +2, +//Dig +10000, +//DestroyConstruction +100, +}; + +class Edge { +public: + //static map pointCost; + df::coord p1; + df::coord p2; + int64_t cost; + Edge(df::coord p1In, df::coord p2In, int64_t costIn): cost(costIn) { + if ( p2In < p1In ) { + p1 = p2In; + p2 = p1In; + } else { + p1 = p1In; + p2 = p2In; + } + } + + bool operator==(const Edge& e) const { + return (cost == e.cost && p1 == e.p1 && p2 == e.p2); + } + + bool operator<(const Edge& e) const { + if ( cost != e.cost ) + return cost < e.cost; + if ( p1.z != e.p1.z ) + return p1.z < e.p1.z; + if ( p1 != e.p1 ) + return p1 < e.p1; + if ( p2.z != e.p2.z ) + return p2.z < e.p2.z; + if ( p2 != e.p2 ) + return p2 < e.p2; + return false; + } +}; + +struct PointHash { + size_t operator()(const df::coord c) const { + return c.x * 65537 + c.y * 17 + c.z; + } +}; + +std::vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax); +