Digging Invaders: reorganized the files to make it easier to work with.

develop
expwnent 2013-01-03 18:14:16 -05:00
parent 84b8fae326
commit 1d6dec54c7
6 changed files with 456 additions and 522 deletions

@ -2,10 +2,13 @@ PROJECT (diggingInvaders)
# A list of source files # A list of source files
SET(PROJECT_SRCS SET(PROJECT_SRCS
diggingInvaders.cpp diggingInvaders.cpp
edgeCost.cpp
assignJob.cpp
) )
# A list of headers # A list of headers
SET(PROJECT_HDRS SET(PROJECT_HDRS
edgeCost.h
assignJob.h
) )
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
@ -29,5 +32,4 @@ ELSE(UNIX)
ENDIF(UNIX) ENDIF(UNIX)
# this makes sure all the stuff is put in proper places and linked to dfhack # 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 ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS})
DFHACK_PLUGIN(diggingInvaders diggingInvaders.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<df::coord,df::coord,PointHash> parentMap, unordered_map<df::coord,int64_t,PointHash>& costMap, vector<df::unit*>& invaders, unordered_set<df::coord,PointHash>& requiresZNeg, unordered_set<df::coord,PointHash>& 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;
}

@ -0,0 +1,12 @@
#pragma once
#include "edgeCost.h"
#include "modules/MapCache.h"
#include <unordered_map>
#include <unordered_set>
using namespace std;
int32_t assignJob(color_ostream& out, Edge firstImportantEdge, unordered_map<df::coord,df::coord,PointHash> parentMap, unordered_map<df::coord,int64_t,PointHash>& costMap, vector<df::unit*>& invaders, unordered_set<df::coord,PointHash>& requiresZNeg, unordered_set<df::coord,PointHash>& requiresZPos, MapExtras::MapCache& cache);

@ -1,3 +1,6 @@
#include "assignJob.h"
#include "edgeCost.h"
#include "Core.h" #include "Core.h"
#include "Console.h" #include "Console.h"
#include "DataDefs.h" #include "DataDefs.h"
@ -44,6 +47,7 @@
#include "df/unit_inventory_item.h" #include "df/unit_inventory_item.h"
#include "df/world.h" #include "df/world.h"
#include <algorithm> #include <algorithm>
#include <ctime> #include <ctime>
#include <cstdlib> #include <cstdlib>
@ -81,72 +85,8 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
return CR_OK; 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<df::coord, int32_t> 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<Edge>* 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<df::coord, df::coord>& rootMap); df::coord getRoot(df::coord point, unordered_map<df::coord, df::coord>& rootMap);
struct PointHash {
size_t operator()(const df::coord c) const {
return c.x * 65537 + c.y * 17 + c.z;
}
};
class PointComp { class PointComp {
public: public:
unordered_map<df::coord, int64_t, PointHash> *pointCost; unordered_map<df::coord, int64_t, PointHash> *pointCost;
@ -347,461 +287,7 @@ int32_t doDiggingInvaders(color_ostream& out) {
requiresZPos.erase(toDelete.begin(), toDelete.end()); requiresZPos.erase(toDelete.begin(), toDelete.end());
toDelete.clear(); toDelete.clear();
//do whatever you need to do at the first important edge return assignJob(out, firstImportantEdge, parentMap, costMap, invaders, requiresZNeg, requiresZPos, cache);
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<Edge>* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax) {
vector<Edge>* result = new vector<Edge>;
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;
} }
df::coord getRoot(df::coord point, map<df::coord, df::coord>& rootMap) { df::coord getRoot(df::coord point, map<df::coord, df::coord>& rootMap) {

@ -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<Edge>* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax) {
vector<Edge>* result = new vector<Edge>;
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;
}

@ -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 <vector>
//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<df::coord, int32_t> 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<Edge>* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax);