From 6de9049dcc3f32166fe47b25865559d0be79697e Mon Sep 17 00:00:00 2001 From: expwnent Date: Thu, 23 Aug 2012 20:37:43 -0400 Subject: [PATCH 01/51] Very rough draft. Invaders tend to overdestroy buildings. They also overdig. Also they only do it on user request. Also it happens instantly. Also they can't dig through constructions. Also I have tabs in the indentation. --- plugins/CMakeLists.txt | 3 +- plugins/diggingInvaders/CMakeLists.txt | 33 ++ plugins/diggingInvaders/diggingInvaders.cpp | 492 ++++++++++++++++++++ 3 files changed, 527 insertions(+), 1 deletion(-) create mode 100644 plugins/diggingInvaders/CMakeLists.txt create mode 100644 plugins/diggingInvaders/diggingInvaders.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 2c54aebad..b6559f163 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -62,6 +62,8 @@ ADD_CUSTOM_COMMAND( SET_SOURCE_FILES_PROPERTIES( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE ) +add_subdirectory(diggingInvaders) + # Plugins OPTION(BUILD_SUPPORTED "Build the supported plugins (reveal, probe, etc.)." ON) if (BUILD_SUPPORTED) @@ -117,7 +119,6 @@ if (BUILD_SUPPORTED) #DFHACK_PLUGIN(versionosd versionosd.cpp) endif() - # this is the skeleton plugin. If you want to make your own, make a copy and then change it OPTION(BUILD_SKELETON "Build the skeleton plugin." OFF) if(BUILD_SKELETON) diff --git a/plugins/diggingInvaders/CMakeLists.txt b/plugins/diggingInvaders/CMakeLists.txt new file mode 100644 index 000000000..d2324b8c9 --- /dev/null +++ b/plugins/diggingInvaders/CMakeLists.txt @@ -0,0 +1,33 @@ +PROJECT (diggingInvaders) +# A list of source files +SET(PROJECT_SRCS + diggingInvaders.cpp +) +# A list of headers +SET(PROJECT_HDRS + +) +SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) + +# mash them together (headers are marked as headers and nothing will try to compile them) +LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS}) + +#linux +IF(UNIX) + add_definitions(-DLINUX_BUILD) + SET(PROJECT_LIBS + # add any extra linux libs here + ${PROJECT_LIBS} + ) +# windows +ELSE(UNIX) + SET(PROJECT_LIBS + # add any extra linux libs here + ${PROJECT_LIBS} + $(NOINHERIT) + ) +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) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp new file mode 100644 index 000000000..9899f0c96 --- /dev/null +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -0,0 +1,492 @@ +#include "Core.h" +#include +#include +#include + +#include "DataDefs.h" +#include "df/building.h" +#include "df/coord.h" +#include "df/map_block.h" +#include "df/ui.h" +#include "df/unit.h" +#include "df/world.h" +#include "modules/World.h" +#include "modules/MapCache.h" + +#include +#include +#include +#include +using namespace std; + +using namespace DFHack; +using namespace df::enums; + +command_result diggingInvadersFunc(color_ostream &out, std::vector & parameters); + +DFHACK_PLUGIN("diggingInvaders"); + +// Mandatory init function. If you have some global state, create it here. +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + //out.print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!\n\n\n\n\n"); + // Fill the command list with your commands. + commands.push_back(PluginCommand( + "diggingInvaders", "Makes invaders dig to your dwarves.", + diggingInvadersFunc, false, /* true means that the command can't be used from non-interactive user interface */ + // Extended help string. Used by CR_WRONG_USAGE and the help command: + "EXTRA HELP STRINGGNGNGNGNGNNGG.\n" + )); + return CR_OK; +} + +// This is called right before the plugin library is removed from memory. +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + // You *MUST* kill all threads you created before this returns. + // If everything fails, just return CR_FAILURE. Your plugin will be + // in a zombie state, but things won't crash. + return CR_OK; +} + +// Called to notify the plugin about important state changes. +// Invoked with DF suspended, and always before the matching plugin_onupdate. +// More event codes may be added in the future. +/* +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + switch (event) { + case SC_GAME_LOADED: + // initialize from the world just loaded + break; + case SC_GAME_UNLOADED: + // cleanup + break; + default: + break; + } + return CR_OK; +} +*/ + +// Whatever you put here will be done in each game step. Don't abuse it. +// It's optional, so you can just comment it out like this if you don't need it. +/* +DFhackCExport command_result plugin_onupdate ( color_ostream &out ) +{ + // whetever. You don't need to suspend DF execution here. + return CR_OK; +} +*/ + +/*class CompareEdge { + bool operator()(edge e1, edge e2) { + + } +};*/ + +class Edge { +public: + //static map pointCost; + df::coord p1; + df::coord p2; + int cost; + Edge(df::coord p1In, df::coord p2In, int costIn): p1(p1In), p2(p2In), cost(costIn) { + + } + + /*bool operator<(const Edge e) const { + int pCost = max(pointCost[p1], pointCost[p2]) + cost; + int e_pCost = max(pointCost[e.p1], pointCost[e.p2]) + e.cost; + if ( pCost != e_pCost ) + return pCost < e_pCost; + if ( p1 != e.p1 ) + return p1 < e.p1; + return p2 < e.p2; + }*/ +}; + +vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int xMax, int yMax, int zMax); +df::coord getRoot(df::coord point, map& rootMap); + +class PointComp { +public: + map *pointCost; + PointComp(map *p): pointCost(p) { + + } + + int operator()(df::coord p1, df::coord p2) { + map::iterator i1 = pointCost->find(p1); + map::iterator i2 = pointCost->find(p2); + if ( i1 == pointCost->end() && i2 == pointCost->end() ) + return p1 < p2; + if ( i1 == pointCost->end() ) + return 1; + if ( i2 == pointCost->end() ) + return -1; + int c1 = (*i1).second; + int c2 = (*i2).second; + if ( c1 != c2 ) + return c1 < c2; + return p1 < p2; + } +}; + +// A command! It sits around and looks pretty. And it's nice and friendly. +command_result diggingInvadersFunc(color_ostream &out, std::vector & parameters) +{ + if (!parameters.empty()) + return CR_WRONG_USAGE; + CoreSuspender suspend; + + //eventually we're going to want a path from each surviving invader to each dwarf, but for now, let's just do from each dwarf to each dwarf + int32_t race_id = df::global::ui->race_id; + int32_t civ_id = df::global::ui->civ_id; + + uint32_t xMax, yMax, zMax; + Maps::getSize(xMax,yMax,zMax); + xMax *= 16; + yMax *= 16; + MapExtras::MapCache cache; + + //TODO: consider whether to pursue hidden dwarf diplomats and merchants + vector locals; + vector invaders; + map dwarfCount; + //map*> edgeSet; + map rootMap; + map parentMap; + map pointCost; + PointComp comp(&pointCost); + set fringe(comp); + for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) { + df::unit* unit = df::global::world->units.all[a]; + bool isInvader = false; + if ( df::global::ui->invasions.next_id > 0 && unit->invasion_id+1 == df::global::ui->invasions.next_id ) { + invaders.push_back(unit); + //dwarfCount[unit->pos]++; + isInvader = true; + } + + if ( (!isInvader && (unit->race != race_id || unit->civ_id != civ_id)) || unit->flags1.bits.dead ) + continue; + + if ( !isInvader ) + locals.push_back(unit); + dwarfCount[unit->pos]++; + //edgeSet[unit->pos] = getEdgeSet(unit->pos, cache, xMax, yMax, zMax); + rootMap[unit->pos] = unit->pos; + parentMap[unit->pos] = unit->pos; + pointCost[unit->pos] = 0; + fringe.insert(unit->pos); + } + + //TODO: if only one connectivity group, return + if ( invaders.size() == 0 ) { + return CR_OK; //no invaders, no problem! + } + set importantPoints; + int a=0; + int dwarvesFound = 1; + while(dwarvesFound < invaders.size()+locals.size() && fringe.size() > 0) { + df::coord point = *fringe.begin(); + //out.print("%d: (%d,%d,%d); dwarvesFound = %d\n", a++, (int)point.x, (int)point.y, (int)point.z, dwarvesFound); + //if ( a > 10000 ) break; + fringe.erase(fringe.begin()); + //dwarfCount[getRoot(point, rootMap)] += dwarfCount[point]; + + if ( getRoot(point, rootMap) != point && dwarfCount[point] != 0 ) { + dwarfCount[getRoot(point, rootMap)] += dwarfCount[point]; + } + + int costSoFar = pointCost[point]; + vector* neighbors = getEdgeSet(out, point, cache, xMax, yMax, zMax); + for ( size_t a = 0; a < neighbors->size(); a++ ) { + df::coord neighbor = (*neighbors)[a].p2; + int neighborCost; + if ( pointCost.find(neighbor) == pointCost.end() ) + neighborCost = -1; + else + neighborCost = pointCost[neighbor]; + if ( neighborCost == -1 || neighborCost > costSoFar + (*neighbors)[a].cost ) { + fringe.erase(neighbor); + pointCost[neighbor] = costSoFar + (*neighbors)[a].cost; + parentMap[neighbor] = point; + //if ( getRoot(neighbor, rootMap) == neighbor ) + rootMap[neighbor] = rootMap[point]; + fringe.insert(neighbor); + } + df::coord pointRoot = getRoot(point, rootMap); + df::coord neighborRoot = getRoot(neighbor, rootMap); + //check for unified sections of the map + if ( neighborRoot != neighbor && neighborRoot != pointRoot ) { + //dwarvesFound++; + dwarfCount[pointRoot] += dwarfCount[neighborRoot]; + dwarfCount[neighborRoot] = 0; + dwarvesFound = max(dwarvesFound, dwarfCount[pointRoot]); + rootMap[neighborRoot] = rootMap[pointRoot]; + + df::coord temp = point; + while(true) { + importantPoints.insert(temp); + if ( parentMap[temp] != temp ) + temp = parentMap[temp]; + else break; + } + temp = neighbor; + while(true) { + importantPoints.insert(temp); + if ( parentMap[temp] != temp ) + temp = parentMap[temp]; + else break; + } + } + } + delete neighbors; + } + out.print("dwarves found: %d\n", dwarvesFound); + + out.print("Important points:\n"); + for ( set::iterator i = importantPoints.begin(); i != importantPoints.end(); i++ ) { + df::coord point = *i; + out.print(" (%d, %d, %d)\n", (int)point.x, (int)point.y, (int)point.z); + } + + //dig out all the important points + for ( set::iterator i = importantPoints.begin(); i != importantPoints.end(); i++ ) { + df::coord point = *i; + + //deal with buildings, hatches, and doors + { + df::map_block* block = cache.BlockAt(df::coord((point.x)/16, (point.y)/16, point.z))->getRaw(); + /*if ( block == NULL ) { + continue; + }*/ + df::tiletype type = cache.tiletypeAt(point); + df::tiletype_shape shape = tileShape(type); + df::tiletype_shape_basic basic = ENUM_ATTR(tiletype_shape, basic_shape, shape); + df::tile_building_occ building_occ = block->occupancy[point.x%16][point.y%16].bits.building; + int z = point.z; + if ( basic == df::tiletype_shape_basic::Ramp && building_occ == df::tile_building_occ::None ) { + df::map_block* block2 = cache.BlockAt(df::coord(point.x/16, point.y/16, point.z+1))->getRaw(); + if ( block2 != NULL ) { + building_occ = block2->occupancy[point.x%16][point.y%16].bits.building; + z = z+1; + if ( building_occ != df::tile_building_occ::None ) { + //if it doesn't block pathing, don't destroy it + + } + } + } + if ( building_occ != df::tile_building_occ::None ) { + //find the building there + bool foundIt = false; + for( size_t a = 0; a < df::global::world->buildings.all.size(); a++ ) { + df::building* building = df::global::world->buildings.all[a]; + if ( z != building->z ) + continue; + if ( building->x1 < point.x || building->x2 > point.x ) + continue; + if ( building->y1 < point.y || building->y2 > point.y ) + continue; + //found it! + foundIt = true; + //destroy it + building->deconstructItems(false, false); + //building->removeUses(false, false); + break; + } + if ( !foundIt ) { + out.print("Error: could not find building at (%d,%d,%d).\n", point.x, point.y, point.z); + } + } + } + + df::tiletype type = cache.tiletypeAt(point); + df::tiletype_shape shape = tileShape(type); + if ( shape == df::tiletype_shape::STAIR_UPDOWN ) + continue; + df::tiletype_shape_basic basicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + bool uppyDowny; + { + //only needs to change if we need uppy-downiness + df::coord up = df::coord(point.x, point.y, point.z+1); + df::coord down = df::coord(point.x, point.y, point.z-1); + uppyDowny = !(importantPoints.find(up) == importantPoints.end() && importantPoints.find(down) == importantPoints.end()); + } + if ( (basicShape == df::tiletype_shape_basic::Floor || basicShape == df::tiletype_shape_basic::Ramp) && !uppyDowny ) { + continue; + } + + if ( uppyDowny ) { + cache.setTiletypeAt(point, df::tiletype::StoneStairUD); + } else { + cache.setTiletypeAt(point, df::tiletype::StoneFloor1); + } + } + + cache.WriteAll(); + return CR_OK; +} + +vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int xMax, int yMax, int zMax) { + vector candidates; + for ( int dx = -1; dx <= 1; dx++ ) { + if ( point.x + dx < 0 ) continue; + for ( int dy = -1; dy <= 1; dy++ ) { + if ( point.y + dy < 0 ) continue; + if ( dx == 0 && dy == 0) + continue; + candidates.push_back(df::coord(point.x+dx,point.y+dy,point.z)); + } + } + for ( int dz = -1; dz <= 1; dz++ ) { + if ( point.z + dz < 0 ) continue; + if ( dz == 0 ) continue; + candidates.push_back(df::coord(point.x, point.y, point.z+dz)); + } + int connectivityType; + { + df::map_block* block = cache.BlockAt(df::coord(point.x/16, point.y/16, point.z))->getRaw(); + if ( block == NULL ) { + return new vector; + } + connectivityType = block->walkable[point.x%16][point.y%16]; + } + if ( connectivityType == 0 ) + return new vector; + for ( int dx = -1; dx <= 1; dx++ ) { + if ( point.x + dx < 0 ) + continue; + for ( int dy = -1; dy <= 1; dy++ ) { + if ( point.y + dy < 0 ) + continue; + for ( int dz = -1; dz <= 1; dz++ ) { + if ( dz == 0 ) continue; + if ( point.z + dz < 0 ) + continue; + if ( dx == 0 && dy == 0 ) + continue; + df::map_block* block = cache.BlockAt(df::coord((point.x+dx)/16, (point.y+dy)/16, point.z+dz))->getRaw(); + if ( block == NULL ) { + continue; + } + if ( block->walkable[(point.x+dx)%16][(point.y+dy)%16] != connectivityType ) { + continue; + } + candidates.push_back(df::coord(point.x+dx, point.y+dy, point.z+dz)); + } + } + } + + //TODO: ramps, buildings + + vector* result = new vector; + df::tiletype_shape_basic basePointBasicShape; + bool basePointIsWall; + { + df::tiletype type = cache.tiletypeAt(point); + df::tiletype_shape shape = tileShape(type); + if ( shape == df::tiletype_shape::EMPTY ) + return result; + basePointBasicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + //TODO: worry about up stairs vs down stairs vs updown stairs + } + + if ( basePointBasicShape == df::tiletype_shape_basic::Wall && cache.hasConstructionAt(point) ) + return result; + + /*if ( point.z < zMax-1 ) { + //ramps part 1: going up + //if I'm a ramp, and there's a wall in some direction, and there's nothing above me, and that tile is open, I can go there. + df::tiletype_shape_basic upBasicShape; + { + df::tiletype type = cache.tiletypeAt(df::coord(point.x, point.y, point.z+1)); + df::tiletype_shape shape = tileShape(type); + upBasicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + } + if ( upBasicShape == df::tiletype_shape_basic::Ramp ) { + for ( int dx = -1; dx <= 1; dx++ ) { + for ( int dy = -1; dy <= 1; dy++ ) { + if ( dx == 0 && dy == 0 ) + continue; + df::tiletype type = cache.tiletypeAt(df::coord(point.x+dx, point.y+dy, point.z+1)); + df::tiletype_shape shape = tileShape(type); + df::tiletype_shape_basic basicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + if ( basicShape == df::tiletype_shape_basic::Floor || + basicShape == df::tiletype_shape_basic::Stair || + basicShape == df::tiletype_shape_basic::Ramp ) { + candidates.push_back(df::coord(point.x+dx, point.y+dy, point.z+1)); + } + } + } + } + } + + if ( point.z >= 1 ) { + //ramps part 2: going down + + }*/ + + for ( size_t a = 0; a < candidates.size(); a++ ) { + if ( candidates[a].x <= 1 || candidates[a].x >= xMax-1 + || candidates[a].y <= 1 || candidates[a].y >= yMax-1 + || candidates[a].z <= 1 || candidates[a].z >= zMax-1 + ) { + continue; + } + df::tiletype type = cache.tiletypeAt(candidates[a]); + df::tiletype_shape shape = tileShape(type); //what namespace? + if ( shape == df::tiletype_shape::EMPTY ) + continue; + df::tiletype_shape_basic basicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + if ( basicShape == df::tiletype_shape_basic::Wall && cache.hasConstructionAt(candidates[a]) ) { + continue; + } + + //if it's a forbidden door, continue + df::map_block* block = cache.BlockAt(df::coord(candidates[a].x/16, candidates[a].y/16, candidates[a].z))->getRaw(); + if ( block == NULL ) { + continue; + } else { + df::tile_building_occ building_occ = block->occupancy[candidates[a].x%16][candidates[a].y%16].bits.building; + if ( building_occ == df::tile_building_occ::Obstacle ) + continue; + if ( building_occ == df::tile_building_occ::Impassable ) + continue; + if ( building_occ == df::tile_building_occ::Well ) + continue; + if ( building_occ == df::tile_building_occ::Dynamic ) { + //continue; //TODO: check df.map.xml.walkable + } + } + + int cost = 1; + if ( basePointIsWall || basicShape == df::tiletype_shape_basic::Wall ) { + cost += 1000000; //TODO: fancy cost + } + //if ( candidates[a] < point ) { + // result->push_back(Edge(candidates[a], point, cost)); + //} else { + result->push_back(Edge(point, candidates[a], cost)); + //} + } + return result; +} + +df::coord getRoot(df::coord point, map& rootMap) { + map::iterator i = rootMap.find(point); + if ( i == rootMap.end() ) { + rootMap[point] = point; + return point; + } + df::coord parent = (*i).second; + if ( parent == point ) + return parent; + df::coord root = getRoot(parent, rootMap); + rootMap[point] = root; + return root; +} + + From cb24f1e53a00e9b3d17c554c806cd87c6d5aca1b Mon Sep 17 00:00:00 2001 From: expwnent Date: Thu, 23 Aug 2012 22:25:25 -0400 Subject: [PATCH 02/51] Converted tabs to spaces. --- plugins/diggingInvaders/diggingInvaders.cpp | 762 ++++++++++---------- 1 file changed, 381 insertions(+), 381 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index 9899f0c96..de358399d 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -29,7 +29,7 @@ DFHACK_PLUGIN("diggingInvaders"); // Mandatory init function. If you have some global state, create it here. DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - //out.print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!\n\n\n\n\n"); + //out.print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!\n\n\n\n\n"); // Fill the command list with your commands. commands.push_back(PluginCommand( "diggingInvaders", "Makes invaders dig to your dwarves.", @@ -80,30 +80,30 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) */ /*class CompareEdge { - bool operator()(edge e1, edge e2) { - - } + bool operator()(edge e1, edge e2) { + + } };*/ class Edge { public: - //static map pointCost; - df::coord p1; - df::coord p2; - int cost; - Edge(df::coord p1In, df::coord p2In, int costIn): p1(p1In), p2(p2In), cost(costIn) { - - } - - /*bool operator<(const Edge e) const { - int pCost = max(pointCost[p1], pointCost[p2]) + cost; - int e_pCost = max(pointCost[e.p1], pointCost[e.p2]) + e.cost; - if ( pCost != e_pCost ) - return pCost < e_pCost; - if ( p1 != e.p1 ) - return p1 < e.p1; - return p2 < e.p2; - }*/ + //static map pointCost; + df::coord p1; + df::coord p2; + int cost; + Edge(df::coord p1In, df::coord p2In, int costIn): p1(p1In), p2(p2In), cost(costIn) { + + } + + /*bool operator<(const Edge e) const { + int pCost = max(pointCost[p1], pointCost[p2]) + cost; + int e_pCost = max(pointCost[e.p1], pointCost[e.p2]) + e.cost; + if ( pCost != e_pCost ) + return pCost < e_pCost; + if ( p1 != e.p1 ) + return p1 < e.p1; + return p2 < e.p2; + }*/ }; vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int xMax, int yMax, int zMax); @@ -111,26 +111,26 @@ df::coord getRoot(df::coord point, map& rootMap); class PointComp { public: - map *pointCost; - PointComp(map *p): pointCost(p) { - - } - - int operator()(df::coord p1, df::coord p2) { - map::iterator i1 = pointCost->find(p1); - map::iterator i2 = pointCost->find(p2); - if ( i1 == pointCost->end() && i2 == pointCost->end() ) - return p1 < p2; - if ( i1 == pointCost->end() ) - return 1; - if ( i2 == pointCost->end() ) - return -1; - int c1 = (*i1).second; - int c2 = (*i2).second; - if ( c1 != c2 ) - return c1 < c2; - return p1 < p2; - } + map *pointCost; + PointComp(map *p): pointCost(p) { + + } + + int operator()(df::coord p1, df::coord p2) { + map::iterator i1 = pointCost->find(p1); + map::iterator i2 = pointCost->find(p2); + if ( i1 == pointCost->end() && i2 == pointCost->end() ) + return p1 < p2; + if ( i1 == pointCost->end() ) + return 1; + if ( i2 == pointCost->end() ) + return -1; + int c1 = (*i1).second; + int c2 = (*i2).second; + if ( c1 != c2 ) + return c1 < c2; + return p1 < p2; + } }; // A command! It sits around and looks pretty. And it's nice and friendly. @@ -140,353 +140,353 @@ command_result diggingInvadersFunc(color_ostream &out, std::vector return CR_WRONG_USAGE; CoreSuspender suspend; - //eventually we're going to want a path from each surviving invader to each dwarf, but for now, let's just do from each dwarf to each dwarf - int32_t race_id = df::global::ui->race_id; - int32_t civ_id = df::global::ui->civ_id; - - uint32_t xMax, yMax, zMax; - Maps::getSize(xMax,yMax,zMax); - xMax *= 16; - yMax *= 16; - MapExtras::MapCache cache; - - //TODO: consider whether to pursue hidden dwarf diplomats and merchants - vector locals; - vector invaders; - map dwarfCount; - //map*> edgeSet; - map rootMap; - map parentMap; - map pointCost; - PointComp comp(&pointCost); - set fringe(comp); - for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) { - df::unit* unit = df::global::world->units.all[a]; - bool isInvader = false; - if ( df::global::ui->invasions.next_id > 0 && unit->invasion_id+1 == df::global::ui->invasions.next_id ) { - invaders.push_back(unit); - //dwarfCount[unit->pos]++; - isInvader = true; - } - - if ( (!isInvader && (unit->race != race_id || unit->civ_id != civ_id)) || unit->flags1.bits.dead ) - continue; - - if ( !isInvader ) - locals.push_back(unit); - dwarfCount[unit->pos]++; - //edgeSet[unit->pos] = getEdgeSet(unit->pos, cache, xMax, yMax, zMax); - rootMap[unit->pos] = unit->pos; - parentMap[unit->pos] = unit->pos; - pointCost[unit->pos] = 0; - fringe.insert(unit->pos); - } - - //TODO: if only one connectivity group, return - if ( invaders.size() == 0 ) { - return CR_OK; //no invaders, no problem! - } - set importantPoints; - int a=0; - int dwarvesFound = 1; - while(dwarvesFound < invaders.size()+locals.size() && fringe.size() > 0) { - df::coord point = *fringe.begin(); - //out.print("%d: (%d,%d,%d); dwarvesFound = %d\n", a++, (int)point.x, (int)point.y, (int)point.z, dwarvesFound); - //if ( a > 10000 ) break; - fringe.erase(fringe.begin()); - //dwarfCount[getRoot(point, rootMap)] += dwarfCount[point]; - - if ( getRoot(point, rootMap) != point && dwarfCount[point] != 0 ) { - dwarfCount[getRoot(point, rootMap)] += dwarfCount[point]; - } - - int costSoFar = pointCost[point]; - vector* neighbors = getEdgeSet(out, point, cache, xMax, yMax, zMax); - for ( size_t a = 0; a < neighbors->size(); a++ ) { - df::coord neighbor = (*neighbors)[a].p2; - int neighborCost; - if ( pointCost.find(neighbor) == pointCost.end() ) - neighborCost = -1; - else - neighborCost = pointCost[neighbor]; - if ( neighborCost == -1 || neighborCost > costSoFar + (*neighbors)[a].cost ) { - fringe.erase(neighbor); - pointCost[neighbor] = costSoFar + (*neighbors)[a].cost; - parentMap[neighbor] = point; - //if ( getRoot(neighbor, rootMap) == neighbor ) - rootMap[neighbor] = rootMap[point]; - fringe.insert(neighbor); - } - df::coord pointRoot = getRoot(point, rootMap); - df::coord neighborRoot = getRoot(neighbor, rootMap); - //check for unified sections of the map - if ( neighborRoot != neighbor && neighborRoot != pointRoot ) { - //dwarvesFound++; - dwarfCount[pointRoot] += dwarfCount[neighborRoot]; - dwarfCount[neighborRoot] = 0; - dwarvesFound = max(dwarvesFound, dwarfCount[pointRoot]); - rootMap[neighborRoot] = rootMap[pointRoot]; - - df::coord temp = point; - while(true) { - importantPoints.insert(temp); - if ( parentMap[temp] != temp ) - temp = parentMap[temp]; - else break; - } - temp = neighbor; - while(true) { - importantPoints.insert(temp); - if ( parentMap[temp] != temp ) - temp = parentMap[temp]; - else break; - } - } - } - delete neighbors; - } - out.print("dwarves found: %d\n", dwarvesFound); - - out.print("Important points:\n"); - for ( set::iterator i = importantPoints.begin(); i != importantPoints.end(); i++ ) { - df::coord point = *i; - out.print(" (%d, %d, %d)\n", (int)point.x, (int)point.y, (int)point.z); - } - - //dig out all the important points - for ( set::iterator i = importantPoints.begin(); i != importantPoints.end(); i++ ) { - df::coord point = *i; - - //deal with buildings, hatches, and doors - { - df::map_block* block = cache.BlockAt(df::coord((point.x)/16, (point.y)/16, point.z))->getRaw(); - /*if ( block == NULL ) { - continue; - }*/ - df::tiletype type = cache.tiletypeAt(point); - df::tiletype_shape shape = tileShape(type); - df::tiletype_shape_basic basic = ENUM_ATTR(tiletype_shape, basic_shape, shape); - df::tile_building_occ building_occ = block->occupancy[point.x%16][point.y%16].bits.building; - int z = point.z; - if ( basic == df::tiletype_shape_basic::Ramp && building_occ == df::tile_building_occ::None ) { - df::map_block* block2 = cache.BlockAt(df::coord(point.x/16, point.y/16, point.z+1))->getRaw(); - if ( block2 != NULL ) { - building_occ = block2->occupancy[point.x%16][point.y%16].bits.building; - z = z+1; - if ( building_occ != df::tile_building_occ::None ) { - //if it doesn't block pathing, don't destroy it - - } - } - } - if ( building_occ != df::tile_building_occ::None ) { - //find the building there - bool foundIt = false; - for( size_t a = 0; a < df::global::world->buildings.all.size(); a++ ) { - df::building* building = df::global::world->buildings.all[a]; - if ( z != building->z ) - continue; - if ( building->x1 < point.x || building->x2 > point.x ) - continue; - if ( building->y1 < point.y || building->y2 > point.y ) - continue; - //found it! - foundIt = true; - //destroy it - building->deconstructItems(false, false); - //building->removeUses(false, false); - break; - } - if ( !foundIt ) { - out.print("Error: could not find building at (%d,%d,%d).\n", point.x, point.y, point.z); - } - } - } - - df::tiletype type = cache.tiletypeAt(point); - df::tiletype_shape shape = tileShape(type); - if ( shape == df::tiletype_shape::STAIR_UPDOWN ) - continue; - df::tiletype_shape_basic basicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); - bool uppyDowny; - { - //only needs to change if we need uppy-downiness - df::coord up = df::coord(point.x, point.y, point.z+1); - df::coord down = df::coord(point.x, point.y, point.z-1); - uppyDowny = !(importantPoints.find(up) == importantPoints.end() && importantPoints.find(down) == importantPoints.end()); - } - if ( (basicShape == df::tiletype_shape_basic::Floor || basicShape == df::tiletype_shape_basic::Ramp) && !uppyDowny ) { - continue; - } - - if ( uppyDowny ) { - cache.setTiletypeAt(point, df::tiletype::StoneStairUD); - } else { - cache.setTiletypeAt(point, df::tiletype::StoneFloor1); - } - } - - cache.WriteAll(); + //eventually we're going to want a path from each surviving invader to each dwarf, but for now, let's just do from each dwarf to each dwarf + int32_t race_id = df::global::ui->race_id; + int32_t civ_id = df::global::ui->civ_id; + + uint32_t xMax, yMax, zMax; + Maps::getSize(xMax,yMax,zMax); + xMax *= 16; + yMax *= 16; + MapExtras::MapCache cache; + + //TODO: consider whether to pursue hidden dwarf diplomats and merchants + vector locals; + vector invaders; + map dwarfCount; + //map*> edgeSet; + map rootMap; + map parentMap; + map pointCost; + PointComp comp(&pointCost); + set fringe(comp); + for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) { + df::unit* unit = df::global::world->units.all[a]; + bool isInvader = false; + if ( df::global::ui->invasions.next_id > 0 && unit->invasion_id+1 == df::global::ui->invasions.next_id ) { + invaders.push_back(unit); + //dwarfCount[unit->pos]++; + isInvader = true; + } + + if ( (!isInvader && (unit->race != race_id || unit->civ_id != civ_id)) || unit->flags1.bits.dead ) + continue; + + if ( !isInvader ) + locals.push_back(unit); + dwarfCount[unit->pos]++; + //edgeSet[unit->pos] = getEdgeSet(unit->pos, cache, xMax, yMax, zMax); + rootMap[unit->pos] = unit->pos; + parentMap[unit->pos] = unit->pos; + pointCost[unit->pos] = 0; + fringe.insert(unit->pos); + } + + //TODO: if only one connectivity group, return + if ( invaders.size() == 0 ) { + return CR_OK; //no invaders, no problem! + } + set importantPoints; + int a=0; + int dwarvesFound = 1; + while(dwarvesFound < invaders.size()+locals.size() && fringe.size() > 0) { + df::coord point = *fringe.begin(); + //out.print("%d: (%d,%d,%d); dwarvesFound = %d\n", a++, (int)point.x, (int)point.y, (int)point.z, dwarvesFound); + //if ( a > 10000 ) break; + fringe.erase(fringe.begin()); + //dwarfCount[getRoot(point, rootMap)] += dwarfCount[point]; + + if ( getRoot(point, rootMap) != point && dwarfCount[point] != 0 ) { + dwarfCount[getRoot(point, rootMap)] += dwarfCount[point]; + } + + int costSoFar = pointCost[point]; + vector* neighbors = getEdgeSet(out, point, cache, xMax, yMax, zMax); + for ( size_t a = 0; a < neighbors->size(); a++ ) { + df::coord neighbor = (*neighbors)[a].p2; + int neighborCost; + if ( pointCost.find(neighbor) == pointCost.end() ) + neighborCost = -1; + else + neighborCost = pointCost[neighbor]; + if ( neighborCost == -1 || neighborCost > costSoFar + (*neighbors)[a].cost ) { + fringe.erase(neighbor); + pointCost[neighbor] = costSoFar + (*neighbors)[a].cost; + parentMap[neighbor] = point; + //if ( getRoot(neighbor, rootMap) == neighbor ) + rootMap[neighbor] = rootMap[point]; + fringe.insert(neighbor); + } + df::coord pointRoot = getRoot(point, rootMap); + df::coord neighborRoot = getRoot(neighbor, rootMap); + //check for unified sections of the map + if ( neighborRoot != neighbor && neighborRoot != pointRoot ) { + //dwarvesFound++; + dwarfCount[pointRoot] += dwarfCount[neighborRoot]; + dwarfCount[neighborRoot] = 0; + dwarvesFound = max(dwarvesFound, dwarfCount[pointRoot]); + rootMap[neighborRoot] = rootMap[pointRoot]; + + df::coord temp = point; + while(true) { + importantPoints.insert(temp); + if ( parentMap[temp] != temp ) + temp = parentMap[temp]; + else break; + } + temp = neighbor; + while(true) { + importantPoints.insert(temp); + if ( parentMap[temp] != temp ) + temp = parentMap[temp]; + else break; + } + } + } + delete neighbors; + } + out.print("dwarves found: %d\n", dwarvesFound); + + out.print("Important points:\n"); + for ( set::iterator i = importantPoints.begin(); i != importantPoints.end(); i++ ) { + df::coord point = *i; + out.print(" (%d, %d, %d)\n", (int)point.x, (int)point.y, (int)point.z); + } + + //dig out all the important points + for ( set::iterator i = importantPoints.begin(); i != importantPoints.end(); i++ ) { + df::coord point = *i; + + //deal with buildings, hatches, and doors + { + df::map_block* block = cache.BlockAt(df::coord((point.x)/16, (point.y)/16, point.z))->getRaw(); + /*if ( block == NULL ) { + continue; + }*/ + df::tiletype type = cache.tiletypeAt(point); + df::tiletype_shape shape = tileShape(type); + df::tiletype_shape_basic basic = ENUM_ATTR(tiletype_shape, basic_shape, shape); + df::tile_building_occ building_occ = block->occupancy[point.x%16][point.y%16].bits.building; + int z = point.z; + if ( basic == df::tiletype_shape_basic::Ramp && building_occ == df::tile_building_occ::None ) { + df::map_block* block2 = cache.BlockAt(df::coord(point.x/16, point.y/16, point.z+1))->getRaw(); + if ( block2 != NULL ) { + building_occ = block2->occupancy[point.x%16][point.y%16].bits.building; + z = z+1; + if ( building_occ != df::tile_building_occ::None ) { + //if it doesn't block pathing, don't destroy it + + } + } + } + if ( building_occ != df::tile_building_occ::None ) { + //find the building there + bool foundIt = false; + for( size_t a = 0; a < df::global::world->buildings.all.size(); a++ ) { + df::building* building = df::global::world->buildings.all[a]; + if ( z != building->z ) + continue; + if ( building->x1 < point.x || building->x2 > point.x ) + continue; + if ( building->y1 < point.y || building->y2 > point.y ) + continue; + //found it! + foundIt = true; + //destroy it + building->deconstructItems(false, false); + //building->removeUses(false, false); + break; + } + if ( !foundIt ) { + out.print("Error: could not find building at (%d,%d,%d).\n", point.x, point.y, point.z); + } + } + } + + df::tiletype type = cache.tiletypeAt(point); + df::tiletype_shape shape = tileShape(type); + if ( shape == df::tiletype_shape::STAIR_UPDOWN ) + continue; + df::tiletype_shape_basic basicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + bool uppyDowny; + { + //only needs to change if we need uppy-downiness + df::coord up = df::coord(point.x, point.y, point.z+1); + df::coord down = df::coord(point.x, point.y, point.z-1); + uppyDowny = !(importantPoints.find(up) == importantPoints.end() && importantPoints.find(down) == importantPoints.end()); + } + if ( (basicShape == df::tiletype_shape_basic::Floor || basicShape == df::tiletype_shape_basic::Ramp) && !uppyDowny ) { + continue; + } + + if ( uppyDowny ) { + cache.setTiletypeAt(point, df::tiletype::StoneStairUD); + } else { + cache.setTiletypeAt(point, df::tiletype::StoneFloor1); + } + } + + cache.WriteAll(); return CR_OK; } vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int xMax, int yMax, int zMax) { - vector candidates; - for ( int dx = -1; dx <= 1; dx++ ) { - if ( point.x + dx < 0 ) continue; - for ( int dy = -1; dy <= 1; dy++ ) { - if ( point.y + dy < 0 ) continue; - if ( dx == 0 && dy == 0) - continue; - candidates.push_back(df::coord(point.x+dx,point.y+dy,point.z)); - } - } - for ( int dz = -1; dz <= 1; dz++ ) { - if ( point.z + dz < 0 ) continue; - if ( dz == 0 ) continue; - candidates.push_back(df::coord(point.x, point.y, point.z+dz)); - } - int connectivityType; - { - df::map_block* block = cache.BlockAt(df::coord(point.x/16, point.y/16, point.z))->getRaw(); - if ( block == NULL ) { - return new vector; - } - connectivityType = block->walkable[point.x%16][point.y%16]; - } - if ( connectivityType == 0 ) - return new vector; - for ( int dx = -1; dx <= 1; dx++ ) { - if ( point.x + dx < 0 ) - continue; - for ( int dy = -1; dy <= 1; dy++ ) { - if ( point.y + dy < 0 ) - continue; - for ( int dz = -1; dz <= 1; dz++ ) { - if ( dz == 0 ) continue; - if ( point.z + dz < 0 ) - continue; - if ( dx == 0 && dy == 0 ) - continue; - df::map_block* block = cache.BlockAt(df::coord((point.x+dx)/16, (point.y+dy)/16, point.z+dz))->getRaw(); - if ( block == NULL ) { - continue; - } - if ( block->walkable[(point.x+dx)%16][(point.y+dy)%16] != connectivityType ) { - continue; - } - candidates.push_back(df::coord(point.x+dx, point.y+dy, point.z+dz)); - } - } - } - - //TODO: ramps, buildings - - vector* result = new vector; - df::tiletype_shape_basic basePointBasicShape; - bool basePointIsWall; - { - df::tiletype type = cache.tiletypeAt(point); - df::tiletype_shape shape = tileShape(type); - if ( shape == df::tiletype_shape::EMPTY ) - return result; - basePointBasicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); - //TODO: worry about up stairs vs down stairs vs updown stairs - } - - if ( basePointBasicShape == df::tiletype_shape_basic::Wall && cache.hasConstructionAt(point) ) - return result; - - /*if ( point.z < zMax-1 ) { - //ramps part 1: going up - //if I'm a ramp, and there's a wall in some direction, and there's nothing above me, and that tile is open, I can go there. - df::tiletype_shape_basic upBasicShape; - { - df::tiletype type = cache.tiletypeAt(df::coord(point.x, point.y, point.z+1)); - df::tiletype_shape shape = tileShape(type); - upBasicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); - } - if ( upBasicShape == df::tiletype_shape_basic::Ramp ) { - for ( int dx = -1; dx <= 1; dx++ ) { - for ( int dy = -1; dy <= 1; dy++ ) { - if ( dx == 0 && dy == 0 ) - continue; - df::tiletype type = cache.tiletypeAt(df::coord(point.x+dx, point.y+dy, point.z+1)); - df::tiletype_shape shape = tileShape(type); - df::tiletype_shape_basic basicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); - if ( basicShape == df::tiletype_shape_basic::Floor || - basicShape == df::tiletype_shape_basic::Stair || - basicShape == df::tiletype_shape_basic::Ramp ) { - candidates.push_back(df::coord(point.x+dx, point.y+dy, point.z+1)); - } - } - } - } - } - - if ( point.z >= 1 ) { - //ramps part 2: going down - - }*/ - - for ( size_t a = 0; a < candidates.size(); a++ ) { - if ( candidates[a].x <= 1 || candidates[a].x >= xMax-1 - || candidates[a].y <= 1 || candidates[a].y >= yMax-1 - || candidates[a].z <= 1 || candidates[a].z >= zMax-1 - ) { - continue; - } - df::tiletype type = cache.tiletypeAt(candidates[a]); - df::tiletype_shape shape = tileShape(type); //what namespace? - if ( shape == df::tiletype_shape::EMPTY ) - continue; - df::tiletype_shape_basic basicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); - if ( basicShape == df::tiletype_shape_basic::Wall && cache.hasConstructionAt(candidates[a]) ) { - continue; - } - - //if it's a forbidden door, continue - df::map_block* block = cache.BlockAt(df::coord(candidates[a].x/16, candidates[a].y/16, candidates[a].z))->getRaw(); - if ( block == NULL ) { - continue; - } else { - df::tile_building_occ building_occ = block->occupancy[candidates[a].x%16][candidates[a].y%16].bits.building; - if ( building_occ == df::tile_building_occ::Obstacle ) - continue; - if ( building_occ == df::tile_building_occ::Impassable ) - continue; - if ( building_occ == df::tile_building_occ::Well ) - continue; - if ( building_occ == df::tile_building_occ::Dynamic ) { - //continue; //TODO: check df.map.xml.walkable - } - } - - int cost = 1; - if ( basePointIsWall || basicShape == df::tiletype_shape_basic::Wall ) { - cost += 1000000; //TODO: fancy cost - } - //if ( candidates[a] < point ) { - // result->push_back(Edge(candidates[a], point, cost)); - //} else { - result->push_back(Edge(point, candidates[a], cost)); - //} - } - return result; + vector candidates; + for ( int dx = -1; dx <= 1; dx++ ) { + if ( point.x + dx < 0 ) continue; + for ( int dy = -1; dy <= 1; dy++ ) { + if ( point.y + dy < 0 ) continue; + if ( dx == 0 && dy == 0) + continue; + candidates.push_back(df::coord(point.x+dx,point.y+dy,point.z)); + } + } + for ( int dz = -1; dz <= 1; dz++ ) { + if ( point.z + dz < 0 ) continue; + if ( dz == 0 ) continue; + candidates.push_back(df::coord(point.x, point.y, point.z+dz)); + } + int connectivityType; + { + df::map_block* block = cache.BlockAt(df::coord(point.x/16, point.y/16, point.z))->getRaw(); + if ( block == NULL ) { + return new vector; + } + connectivityType = block->walkable[point.x%16][point.y%16]; + } + if ( connectivityType == 0 ) + return new vector; + for ( int dx = -1; dx <= 1; dx++ ) { + if ( point.x + dx < 0 ) + continue; + for ( int dy = -1; dy <= 1; dy++ ) { + if ( point.y + dy < 0 ) + continue; + for ( int dz = -1; dz <= 1; dz++ ) { + if ( dz == 0 ) continue; + if ( point.z + dz < 0 ) + continue; + if ( dx == 0 && dy == 0 ) + continue; + df::map_block* block = cache.BlockAt(df::coord((point.x+dx)/16, (point.y+dy)/16, point.z+dz))->getRaw(); + if ( block == NULL ) { + continue; + } + if ( block->walkable[(point.x+dx)%16][(point.y+dy)%16] != connectivityType ) { + continue; + } + candidates.push_back(df::coord(point.x+dx, point.y+dy, point.z+dz)); + } + } + } + + //TODO: ramps, buildings + + vector* result = new vector; + df::tiletype_shape_basic basePointBasicShape; + bool basePointIsWall; + { + df::tiletype type = cache.tiletypeAt(point); + df::tiletype_shape shape = tileShape(type); + if ( shape == df::tiletype_shape::EMPTY ) + return result; + basePointBasicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + //TODO: worry about up stairs vs down stairs vs updown stairs + } + + if ( basePointBasicShape == df::tiletype_shape_basic::Wall && cache.hasConstructionAt(point) ) + return result; + + /*if ( point.z < zMax-1 ) { + //ramps part 1: going up + //if I'm a ramp, and there's a wall in some direction, and there's nothing above me, and that tile is open, I can go there. + df::tiletype_shape_basic upBasicShape; + { + df::tiletype type = cache.tiletypeAt(df::coord(point.x, point.y, point.z+1)); + df::tiletype_shape shape = tileShape(type); + upBasicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + } + if ( upBasicShape == df::tiletype_shape_basic::Ramp ) { + for ( int dx = -1; dx <= 1; dx++ ) { + for ( int dy = -1; dy <= 1; dy++ ) { + if ( dx == 0 && dy == 0 ) + continue; + df::tiletype type = cache.tiletypeAt(df::coord(point.x+dx, point.y+dy, point.z+1)); + df::tiletype_shape shape = tileShape(type); + df::tiletype_shape_basic basicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + if ( basicShape == df::tiletype_shape_basic::Floor || + basicShape == df::tiletype_shape_basic::Stair || + basicShape == df::tiletype_shape_basic::Ramp ) { + candidates.push_back(df::coord(point.x+dx, point.y+dy, point.z+1)); + } + } + } + } + } + + if ( point.z >= 1 ) { + //ramps part 2: going down + + }*/ + + for ( size_t a = 0; a < candidates.size(); a++ ) { + if ( candidates[a].x <= 1 || candidates[a].x >= xMax-1 + || candidates[a].y <= 1 || candidates[a].y >= yMax-1 + || candidates[a].z <= 1 || candidates[a].z >= zMax-1 + ) { + continue; + } + df::tiletype type = cache.tiletypeAt(candidates[a]); + df::tiletype_shape shape = tileShape(type); //what namespace? + if ( shape == df::tiletype_shape::EMPTY ) + continue; + df::tiletype_shape_basic basicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); + if ( basicShape == df::tiletype_shape_basic::Wall && cache.hasConstructionAt(candidates[a]) ) { + continue; + } + + //if it's a forbidden door, continue + df::map_block* block = cache.BlockAt(df::coord(candidates[a].x/16, candidates[a].y/16, candidates[a].z))->getRaw(); + if ( block == NULL ) { + continue; + } else { + df::tile_building_occ building_occ = block->occupancy[candidates[a].x%16][candidates[a].y%16].bits.building; + if ( building_occ == df::tile_building_occ::Obstacle ) + continue; + if ( building_occ == df::tile_building_occ::Impassable ) + continue; + if ( building_occ == df::tile_building_occ::Well ) + continue; + if ( building_occ == df::tile_building_occ::Dynamic ) { + //continue; //TODO: check df.map.xml.walkable + } + } + + int cost = 1; + if ( basePointIsWall || basicShape == df::tiletype_shape_basic::Wall ) { + cost += 1000000; //TODO: fancy cost + } + //if ( candidates[a] < point ) { + // result->push_back(Edge(candidates[a], point, cost)); + //} else { + result->push_back(Edge(point, candidates[a], cost)); + //} + } + return result; } df::coord getRoot(df::coord point, map& rootMap) { - map::iterator i = rootMap.find(point); - if ( i == rootMap.end() ) { - rootMap[point] = point; - return point; - } - df::coord parent = (*i).second; - if ( parent == point ) - return parent; - df::coord root = getRoot(parent, rootMap); - rootMap[point] = root; - return root; + map::iterator i = rootMap.find(point); + if ( i == rootMap.end() ) { + rootMap[point] = point; + return point; + } + df::coord parent = (*i).second; + if ( parent == point ) + return parent; + df::coord root = getRoot(parent, rootMap); + rootMap[point] = root; + return root; } From 45efcfc763f8cb4776f8ee6f37033e6c8709b714 Mon Sep 17 00:00:00 2001 From: expwnent Date: Fri, 24 Aug 2012 11:40:51 -0400 Subject: [PATCH 03/51] Warning: does not work. Temp commit! --- library/include/modules/Buildings.h | 5 +++++ library/modules/Buildings.cpp | 25 +++++++++++++++------ plugins/diggingInvaders/diggingInvaders.cpp | 12 ++++++++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 6e0a22052..dfe85a4b8 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -173,5 +173,10 @@ DFHACK_EXPORT bool constructWithFilters(df::building *bld, std::vectorqueueDestroy(); return false; } + + return deconstructImmediately(bld); +} +bool Buildings::deconstructImmediately(df::building *bld) +{ + using df::global::ui; + using df::global::world; + using df::global::ui_look_list; + + CHECK_NULL_POINTER(bld); + /* Immediate destruction code path. Should only happen for abstract and unconstructed buildings.*/ - + if (bld->isSettingOccupancy()) { markBuildingTiles(bld, true); bld->cleanupMap(); } - + bld->removeUses(false, false); // Assume: no parties. unlinkRooms(bld); @@ -997,16 +1008,16 @@ bool Buildings::deconstruct(df::building *bld) // Assume: does not affect pathfinding bld->deconstructItems(false, false); // Don't clear arrows. - + bld->uncategorize(); delete bld; - + if (world->selected_building == bld) { world->selected_building = NULL; world->update_selected_building = true; } - + for (int i = ui_look_list->items.size()-1; i >= 0; i--) { auto item = ui_look_list->items[i]; @@ -1017,10 +1028,10 @@ bool Buildings::deconstruct(df::building *bld) delete item; } } - + Job::checkBuildingsNow(); Job::checkDesignationsNow(); - + return true; } diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index de358399d..a6c4e1752 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -10,8 +10,12 @@ #include "df/ui.h" #include "df/unit.h" #include "df/world.h" -#include "modules/World.h" + +#include "Types.h" +#include "modules/Buildings.h" + //crashes without Types.h #include "modules/MapCache.h" +#include "modules/World.h" #include #include @@ -293,7 +297,11 @@ command_result diggingInvadersFunc(color_ostream &out, std::vector //found it! foundIt = true; //destroy it - building->deconstructItems(false, false); + out.print("deconstructImmediately...\n"); + DFHack::Buildings::deconstructImmediately(building); + out.print("done\n"); + building = NULL; + //building->deconstructItems(false, false); //building->removeUses(false, false); break; } From 2760484c37f20aa448750e13826412f6ab0df421 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sun, 16 Dec 2012 16:53:02 -0500 Subject: [PATCH 04/51] diggingInvaders: cleanup. --- plugins/diggingInvaders/diggingInvaders.cpp | 28 ++++++++------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index a6c4e1752..d106e027f 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -1,9 +1,15 @@ #include "Core.h" -#include -#include -#include - +#include "Console.h" #include "DataDefs.h" +#include "Export.h" +#include "PluginManager.h" +#include "Types.h" + +#include "modules/Buildings.h" +#include "modules/EventManager.h" +#include "modules/MapCache.h" +#include "modules/World.h" + #include "df/building.h" #include "df/coord.h" #include "df/map_block.h" @@ -11,16 +17,10 @@ #include "df/unit.h" #include "df/world.h" -#include "Types.h" -#include "modules/Buildings.h" - //crashes without Types.h -#include "modules/MapCache.h" -#include "modules/World.h" - -#include #include #include #include +#include using namespace std; using namespace DFHack; @@ -30,10 +30,8 @@ command_result diggingInvadersFunc(color_ostream &out, std::vector DFHACK_PLUGIN("diggingInvaders"); -// Mandatory init function. If you have some global state, create it here. DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - //out.print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!\n\n\n\n\n"); // Fill the command list with your commands. commands.push_back(PluginCommand( "diggingInvaders", "Makes invaders dig to your dwarves.", @@ -44,12 +42,8 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector Date: Sun, 16 Dec 2012 17:06:46 -0500 Subject: [PATCH 05/51] diggingInvaders: int -> int32_t --- plugins/diggingInvaders/diggingInvaders.cpp | 71 ++++++++++----------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index d106e027f..9691f5598 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -85,17 +85,17 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) class Edge { public: - //static map pointCost; + //static map pointCost; df::coord p1; df::coord p2; - int cost; - Edge(df::coord p1In, df::coord p2In, int costIn): p1(p1In), p2(p2In), cost(costIn) { + int32_t cost; + Edge(df::coord p1In, df::coord p2In, int32_t costIn): p1(p1In), p2(p2In), cost(costIn) { } /*bool operator<(const Edge e) const { - int pCost = max(pointCost[p1], pointCost[p2]) + cost; - int e_pCost = max(pointCost[e.p1], pointCost[e.p2]) + e.cost; + int32_t pCost = max(pointCost[p1], pointCost[p2]) + cost; + int32_t e_pCost = max(pointCost[e.p1], pointCost[e.p2]) + e.cost; if ( pCost != e_pCost ) return pCost < e_pCost; if ( p1 != e.p1 ) @@ -104,34 +104,33 @@ public: }*/ }; -vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int xMax, int yMax, int zMax); +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, map& rootMap); class PointComp { public: - map *pointCost; - PointComp(map *p): pointCost(p) { + map *pointCost; + PointComp(map *p): pointCost(p) { } - int operator()(df::coord p1, df::coord p2) { - map::iterator i1 = pointCost->find(p1); - map::iterator i2 = pointCost->find(p2); + int32_t operator()(df::coord p1, df::coord p2) { + map::iterator i1 = pointCost->find(p1); + map::iterator i2 = pointCost->find(p2); if ( i1 == pointCost->end() && i2 == pointCost->end() ) return p1 < p2; if ( i1 == pointCost->end() ) return 1; if ( i2 == pointCost->end() ) return -1; - int c1 = (*i1).second; - int c2 = (*i2).second; + int32_t c1 = (*i1).second; + int32_t c2 = (*i2).second; if ( c1 != c2 ) return c1 < c2; return p1 < p2; } }; -// A command! It sits around and looks pretty. And it's nice and friendly. command_result diggingInvadersFunc(color_ostream &out, std::vector & parameters) { if (!parameters.empty()) @@ -151,15 +150,15 @@ command_result diggingInvadersFunc(color_ostream &out, std::vector //TODO: consider whether to pursue hidden dwarf diplomats and merchants vector locals; vector invaders; - map dwarfCount; + map dwarfCount; //map*> edgeSet; map rootMap; map parentMap; - map pointCost; + map pointCost; PointComp comp(&pointCost); set fringe(comp); - for ( size_t a = 0; a < df::global::world->units.all.size(); a++ ) { - df::unit* unit = df::global::world->units.all[a]; + for ( size_t a = 0; a < df::global::world->units.active.size(); a++ ) { + df::unit* unit = df::global::world->units.active[a]; bool isInvader = false; if ( df::global::ui->invasions.next_id > 0 && unit->invasion_id+1 == df::global::ui->invasions.next_id ) { invaders.push_back(unit); @@ -185,11 +184,11 @@ command_result diggingInvadersFunc(color_ostream &out, std::vector return CR_OK; //no invaders, no problem! } set importantPoints; - int a=0; - int dwarvesFound = 1; + int32_t a=0; + int32_t dwarvesFound = 1; while(dwarvesFound < invaders.size()+locals.size() && fringe.size() > 0) { df::coord point = *fringe.begin(); - //out.print("%d: (%d,%d,%d); dwarvesFound = %d\n", a++, (int)point.x, (int)point.y, (int)point.z, dwarvesFound); + //out.print("%d: (%d,%d,%d); dwarvesFound = %d\n", a++, (int32_t)point.x, (int32_t)point.y, (int32_t)point.z, dwarvesFound); //if ( a > 10000 ) break; fringe.erase(fringe.begin()); //dwarfCount[getRoot(point, rootMap)] += dwarfCount[point]; @@ -198,11 +197,11 @@ command_result diggingInvadersFunc(color_ostream &out, std::vector dwarfCount[getRoot(point, rootMap)] += dwarfCount[point]; } - int costSoFar = pointCost[point]; + int32_t costSoFar = pointCost[point]; vector* neighbors = getEdgeSet(out, point, cache, xMax, yMax, zMax); for ( size_t a = 0; a < neighbors->size(); a++ ) { df::coord neighbor = (*neighbors)[a].p2; - int neighborCost; + int32_t neighborCost; if ( pointCost.find(neighbor) == pointCost.end() ) neighborCost = -1; else @@ -248,7 +247,7 @@ command_result diggingInvadersFunc(color_ostream &out, std::vector out.print("Important points:\n"); for ( set::iterator i = importantPoints.begin(); i != importantPoints.end(); i++ ) { df::coord point = *i; - out.print(" (%d, %d, %d)\n", (int)point.x, (int)point.y, (int)point.z); + out.print(" (%d, %d, %d)\n", (int32_t)point.x, (int32_t)point.y, (int32_t)point.z); } //dig out all the important points @@ -265,7 +264,7 @@ command_result diggingInvadersFunc(color_ostream &out, std::vector df::tiletype_shape shape = tileShape(type); df::tiletype_shape_basic basic = ENUM_ATTR(tiletype_shape, basic_shape, shape); df::tile_building_occ building_occ = block->occupancy[point.x%16][point.y%16].bits.building; - int z = point.z; + int32_t z = point.z; if ( basic == df::tiletype_shape_basic::Ramp && building_occ == df::tile_building_occ::None ) { df::map_block* block2 = cache.BlockAt(df::coord(point.x/16, point.y/16, point.z+1))->getRaw(); if ( block2 != NULL ) { @@ -332,23 +331,23 @@ command_result diggingInvadersFunc(color_ostream &out, std::vector return CR_OK; } -vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int xMax, int yMax, int zMax) { +vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax) { vector candidates; - for ( int dx = -1; dx <= 1; dx++ ) { + for ( int32_t dx = -1; dx <= 1; dx++ ) { if ( point.x + dx < 0 ) continue; - for ( int dy = -1; dy <= 1; dy++ ) { + for ( int32_t dy = -1; dy <= 1; dy++ ) { if ( point.y + dy < 0 ) continue; if ( dx == 0 && dy == 0) continue; candidates.push_back(df::coord(point.x+dx,point.y+dy,point.z)); } } - for ( int dz = -1; dz <= 1; dz++ ) { + for ( int32_t dz = -1; dz <= 1; dz++ ) { if ( point.z + dz < 0 ) continue; if ( dz == 0 ) continue; candidates.push_back(df::coord(point.x, point.y, point.z+dz)); } - int connectivityType; + int32_t connectivityType; { df::map_block* block = cache.BlockAt(df::coord(point.x/16, point.y/16, point.z))->getRaw(); if ( block == NULL ) { @@ -358,13 +357,13 @@ vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCach } if ( connectivityType == 0 ) return new vector; - for ( int dx = -1; dx <= 1; dx++ ) { + for ( int32_t dx = -1; dx <= 1; dx++ ) { if ( point.x + dx < 0 ) continue; - for ( int dy = -1; dy <= 1; dy++ ) { + for ( int32_t dy = -1; dy <= 1; dy++ ) { if ( point.y + dy < 0 ) continue; - for ( int dz = -1; dz <= 1; dz++ ) { + for ( int32_t dz = -1; dz <= 1; dz++ ) { if ( dz == 0 ) continue; if ( point.z + dz < 0 ) continue; @@ -409,8 +408,8 @@ vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCach upBasicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); } if ( upBasicShape == df::tiletype_shape_basic::Ramp ) { - for ( int dx = -1; dx <= 1; dx++ ) { - for ( int dy = -1; dy <= 1; dy++ ) { + for ( int32_t dx = -1; dx <= 1; dx++ ) { + for ( int32_t dy = -1; dy <= 1; dy++ ) { if ( dx == 0 && dy == 0 ) continue; df::tiletype type = cache.tiletypeAt(df::coord(point.x+dx, point.y+dy, point.z+1)); @@ -464,7 +463,7 @@ vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCach } } - int cost = 1; + int32_t cost = 1; if ( basePointIsWall || basicShape == df::tiletype_shape_basic::Wall ) { cost += 1000000; //TODO: fancy cost } From a61cbc661b5a90931648f3fbb66d2156597e9184 Mon Sep 17 00:00:00 2001 From: expwnent Date: Sun, 16 Dec 2012 21:44:23 -0500 Subject: [PATCH 06/51] diggingInvaders: starting a rewrite. Unstable --- plugins/diggingInvaders/diggingInvaders.cpp | 509 ++++++++------------ 1 file changed, 205 insertions(+), 304 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index 9691f5598..a0e378c2b 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -7,7 +7,9 @@ #include "modules/Buildings.h" #include "modules/EventManager.h" +#include "modules/Maps.h" #include "modules/MapCache.h" +#include "modules/Units.h" #include "modules/World.h" #include "df/building.h" @@ -26,6 +28,14 @@ using namespace std; using namespace DFHack; using namespace df::enums; +/////////////////////// +color_ostream* glob_out; + +#if 0 +#define DEBUG_PRINT(str) \ +out.print("%s, line %d" STR, __FILE__, __LINE__); +#endif + command_result diggingInvadersFunc(color_ostream &out, std::vector & parameters); DFHACK_PLUGIN("diggingInvaders"); @@ -89,8 +99,26 @@ public: df::coord p1; df::coord p2; int32_t cost; - Edge(df::coord p1In, df::coord p2In, int32_t costIn): p1(p1In), p2(p2In), cost(costIn) { - + Edge(df::coord p1In, df::coord p2In, int32_t costIn): cost(costIn) { + if ( p2In < p1In ) { + p1 = p2In; + p2 = p1In; + } else { + p1 = p1In; + p2 = p2In; + } + } + + bool operator==(const Edge& e) const { + return (p1 == e.p1 && p2 == e.p2); + } + + bool operator<(const Edge& e) const { + if ( p1 != e.p1 ) + return p1 < e.p1; + if ( p2 != e.p2 ) + return p2 < e.p2; + return false; } /*bool operator<(const Edge e) const { @@ -104,7 +132,7 @@ public: }*/ }; -vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax); +vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int32_t yMax, int32_t zMax); df::coord getRoot(df::coord point, map& rootMap); class PointComp { @@ -115,6 +143,7 @@ public: } int32_t operator()(df::coord p1, df::coord p2) { + if ( p1 == p2 ) return 0; map::iterator i1 = pointCost->find(p1); map::iterator i2 = pointCost->find(p2); if ( i1 == pointCost->end() && i2 == pointCost->end() ) @@ -131,348 +160,220 @@ public: } }; -command_result diggingInvadersFunc(color_ostream &out, std::vector & parameters) -{ +bool important(df::coord pos, map >& edges, df::coord prev, set& importantPoints, set& importantEdges); + +command_result diggingInvadersFunc(color_ostream& out, std::vector& parameters) { if (!parameters.empty()) return CR_WRONG_USAGE; CoreSuspender suspend; - //eventually we're going to want a path from each surviving invader to each dwarf, but for now, let's just do from each dwarf to each dwarf - int32_t race_id = df::global::ui->race_id; - int32_t civ_id = df::global::ui->civ_id; - + map > edgeSet; + set roots; + set importantPoints; + map rootMap; uint32_t xMax, yMax, zMax; Maps::getSize(xMax,yMax,zMax); xMax *= 16; yMax *= 16; - MapExtras::MapCache cache; - - //TODO: consider whether to pursue hidden dwarf diplomats and merchants - vector locals; - vector invaders; - map dwarfCount; - //map*> edgeSet; - map rootMap; - map parentMap; - map pointCost; - PointComp comp(&pointCost); - set fringe(comp); + + //find all locals and invaders for ( size_t a = 0; a < df::global::world->units.active.size(); a++ ) { df::unit* unit = df::global::world->units.active[a]; - bool isInvader = false; - if ( df::global::ui->invasions.next_id > 0 && unit->invasion_id+1 == df::global::ui->invasions.next_id ) { - invaders.push_back(unit); - //dwarfCount[unit->pos]++; - isInvader = true; - } - - if ( (!isInvader && (unit->race != race_id || unit->civ_id != civ_id)) || unit->flags1.bits.dead ) + if ( unit->flags1.bits.dead ) continue; - - if ( !isInvader ) - locals.push_back(unit); - dwarfCount[unit->pos]++; - //edgeSet[unit->pos] = getEdgeSet(unit->pos, cache, xMax, yMax, zMax); - rootMap[unit->pos] = unit->pos; - parentMap[unit->pos] = unit->pos; - pointCost[unit->pos] = 0; - fringe.insert(unit->pos); - } - - //TODO: if only one connectivity group, return - if ( invaders.size() == 0 ) { - return CR_OK; //no invaders, no problem! - } - set importantPoints; - int32_t a=0; - int32_t dwarvesFound = 1; - while(dwarvesFound < invaders.size()+locals.size() && fringe.size() > 0) { - df::coord point = *fringe.begin(); - //out.print("%d: (%d,%d,%d); dwarvesFound = %d\n", a++, (int32_t)point.x, (int32_t)point.y, (int32_t)point.z, dwarvesFound); - //if ( a > 10000 ) break; - fringe.erase(fringe.begin()); - //dwarfCount[getRoot(point, rootMap)] += dwarfCount[point]; - - if ( getRoot(point, rootMap) != point && dwarfCount[point] != 0 ) { - dwarfCount[getRoot(point, rootMap)] += dwarfCount[point]; + if ( !Units::isCitizen(unit) && !unit->flags1.bits.active_invader ) + continue; + if ( unit->flags2.bits.resident ) { + out.print("resident\n"); } - int32_t costSoFar = pointCost[point]; - vector* neighbors = getEdgeSet(out, point, cache, xMax, yMax, zMax); - for ( size_t a = 0; a < neighbors->size(); a++ ) { - df::coord neighbor = (*neighbors)[a].p2; - int32_t neighborCost; - if ( pointCost.find(neighbor) == pointCost.end() ) - neighborCost = -1; - else - neighborCost = pointCost[neighbor]; - if ( neighborCost == -1 || neighborCost > costSoFar + (*neighbors)[a].cost ) { - fringe.erase(neighbor); - pointCost[neighbor] = costSoFar + (*neighbors)[a].cost; - parentMap[neighbor] = point; - //if ( getRoot(neighbor, rootMap) == neighbor ) - rootMap[neighbor] = rootMap[point]; - fringe.insert(neighbor); - } - df::coord pointRoot = getRoot(point, rootMap); - df::coord neighborRoot = getRoot(neighbor, rootMap); - //check for unified sections of the map - if ( neighborRoot != neighbor && neighborRoot != pointRoot ) { - //dwarvesFound++; - dwarfCount[pointRoot] += dwarfCount[neighborRoot]; - dwarfCount[neighborRoot] = 0; - dwarvesFound = max(dwarvesFound, dwarfCount[pointRoot]); - rootMap[neighborRoot] = rootMap[pointRoot]; - - df::coord temp = point; - while(true) { - importantPoints.insert(temp); - if ( parentMap[temp] != temp ) - temp = parentMap[temp]; - else break; - } - temp = neighbor; - while(true) { - importantPoints.insert(temp); - if ( parentMap[temp] != temp ) - temp = parentMap[temp]; - else break; - } - } + if ( roots.find(unit->pos) != roots.end() ) + continue; + + roots.insert(unit->pos); + importantPoints.insert(unit->pos); + vector* neighbors = getEdgeSet(out, unit->pos, xMax, yMax, zMax); + set& rootEdges = edgeSet[unit->pos]; + for ( auto i = neighbors->begin(); i != neighbors->end(); i++ ) { + Edge edge = *i; + rootEdges.insert(edge); } delete neighbors; } - out.print("dwarves found: %d\n", dwarvesFound); - - out.print("Important points:\n"); - for ( set::iterator i = importantPoints.begin(); i != importantPoints.end(); i++ ) { - df::coord point = *i; - out.print(" (%d, %d, %d)\n", (int32_t)point.x, (int32_t)point.y, (int32_t)point.z); - } - - //dig out all the important points - for ( set::iterator i = importantPoints.begin(); i != importantPoints.end(); i++ ) { - df::coord point = *i; - - //deal with buildings, hatches, and doors - { - df::map_block* block = cache.BlockAt(df::coord((point.x)/16, (point.y)/16, point.z))->getRaw(); - /*if ( block == NULL ) { + + set importantEdges; + + int32_t dumb = 0; + while(roots.size() > 1) { + if ( dumb >= 1000 ) + break; + dumb++; + set toDelete; + int32_t firstSize = edgeSet[*roots.begin()].size(); + //out.print("%s, %d: root size = %d, first size = %d\n", __FILE__, __LINE__, roots.size(), firstSize); + for ( auto i = roots.begin(); i != roots.end(); i++ ) { + df::coord root = *i; + //out.print(" (%d,%d,%d)\n", root.x, root.y, root.z); + if ( toDelete.find(root) != toDelete.end() ) continue; - }*/ - df::tiletype type = cache.tiletypeAt(point); - df::tiletype_shape shape = tileShape(type); - df::tiletype_shape_basic basic = ENUM_ATTR(tiletype_shape, basic_shape, shape); - df::tile_building_occ building_occ = block->occupancy[point.x%16][point.y%16].bits.building; - int32_t z = point.z; - if ( basic == df::tiletype_shape_basic::Ramp && building_occ == df::tile_building_occ::None ) { - df::map_block* block2 = cache.BlockAt(df::coord(point.x/16, point.y/16, point.z+1))->getRaw(); - if ( block2 != NULL ) { - building_occ = block2->occupancy[point.x%16][point.y%16].bits.building; - z = z+1; - if ( building_occ != df::tile_building_occ::None ) { - //if it doesn't block pathing, don't destroy it - - } - } + if ( edgeSet[root].empty() ) { + out.print("%s, %d: Error: no edges: %d, %d, %d\n", __FILE__, __LINE__, root.x, root.y, root.z); + return CR_FAILURE; + } + set& myEdges = edgeSet[root]; + Edge edge = *myEdges.begin(); + myEdges.erase(myEdges.begin()); + if ( edgeSet[root].size() != myEdges.size() ) { + out.print("DOOOOOM! %s, %d\n", __FILE__, __LINE__); + return CR_FAILURE; } - if ( building_occ != df::tile_building_occ::None ) { - //find the building there - bool foundIt = false; - for( size_t a = 0; a < df::global::world->buildings.all.size(); a++ ) { - df::building* building = df::global::world->buildings.all[a]; - if ( z != building->z ) - continue; - if ( building->x1 < point.x || building->x2 > point.x ) - continue; - if ( building->y1 < point.y || building->y2 > point.y ) - continue; - //found it! - foundIt = true; - //destroy it - out.print("deconstructImmediately...\n"); - DFHack::Buildings::deconstructImmediately(building); - out.print("done\n"); - building = NULL; - //building->deconstructItems(false, false); - //building->removeUses(false, false); - break; + if ( getRoot(edge.p1, rootMap) != root && getRoot(edge.p2, rootMap) != root ) { + out.print("%s, %d: Invalid edge.\n", __FILE__, __LINE__); + return CR_FAILURE; + } + + df::coord other = edge.p1; + if ( getRoot(other, rootMap) == root ) + other = edge.p2; + if ( getRoot(other, rootMap) == root ) { + //out.print("%s, %d: Error: self edge: %d, %d, %d\n", __FILE__, __LINE__, root.x, root.y, root.z); + /*vector badEdges; + for ( auto j = myEdges.begin(); j != myEdges.end(); j++ ) { + Edge e = *j; + if ( getRoot(e.p1, rootMap) == getRoot(e.p2, rootMap) ) + badEdges.push_back(e); } - if ( !foundIt ) { - out.print("Error: could not find building at (%d,%d,%d).\n", point.x, point.y, point.z); + for ( size_t j = 0; j < badEdges.size(); j++ ) { + myEdges.erase(badEdges[j]); + }*/ + continue; + } + + importantEdges.insert(edge); + + df::coord otherRoot = getRoot(other,rootMap); + rootMap[otherRoot] = root; + + //merge his stuff with my stuff + if ( edgeSet.find(other) == edgeSet.end() ) { + set& hisEdges = edgeSet[other]; + vector* neighbors = getEdgeSet(out, other, xMax, yMax, zMax); + for ( auto i = neighbors->begin(); i != neighbors->end(); i++ ) { + Edge edge = *i; + hisEdges.insert(edge); } + delete neighbors; + } + set& hisEdges = edgeSet[other]; + + for ( auto j = hisEdges.begin(); j != hisEdges.end(); j++ ) { + Edge e = *j; + if ( getRoot(e.p1, rootMap) == getRoot(e.p2, rootMap) ) + continue; + df::coord farPt = e.p1; + if ( farPt == other ) + farPt = e.p2; + myEdges.insert(e); + //myEdges.insert(Edge(root, farPt, e.cost)); } + //hisEdges.clear(); + edgeSet.erase(otherRoot); + toDelete.insert(otherRoot); } - - df::tiletype type = cache.tiletypeAt(point); - df::tiletype_shape shape = tileShape(type); - if ( shape == df::tiletype_shape::STAIR_UPDOWN ) - continue; - df::tiletype_shape_basic basicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); - bool uppyDowny; - { - //only needs to change if we need uppy-downiness - df::coord up = df::coord(point.x, point.y, point.z+1); - df::coord down = df::coord(point.x, point.y, point.z-1); - uppyDowny = !(importantPoints.find(up) == importantPoints.end() && importantPoints.find(down) == importantPoints.end()); + for ( auto j = toDelete.begin(); j != toDelete.end(); j++ ) { + df::coord bob = *j; + roots.erase(bob); } - if ( (basicShape == df::tiletype_shape_basic::Floor || basicShape == df::tiletype_shape_basic::Ramp) && !uppyDowny ) { + } + + edgeSet.clear(); + for ( auto i = importantEdges.begin(); i != importantEdges.end(); i++ ) { + Edge e = *i; + edgeSet[e.p1].insert(e); + edgeSet[e.p2].insert(e); + } + + //now we find the edges used along the paths between any two roots + importantEdges.clear(); + glob_out = &out; + { + important(*importantPoints.begin(), edgeSet, df::coord(-1,-1,-1), importantPoints, importantEdges); + } + + //NOW we filter to see edges that require digging/constructing + map actionable; + for ( auto a = importantEdges.begin(); a != importantEdges.end(); a++ ) { + Edge e = *a; + if ( Maps::canWalkBetween(e.p1, e.p2) ) continue; + actionable[e.p1]++; + if( actionable[e.p1] == 0 ) { + out.print("fuck\n"); + return CR_FAILURE; } - - if ( uppyDowny ) { - cache.setTiletypeAt(point, df::tiletype::StoneStairUD); - } else { - cache.setTiletypeAt(point, df::tiletype::StoneFloor1); - } + actionable[e.p2]++; } - - cache.WriteAll(); + + for ( auto a = actionable.begin(); a != actionable.end(); a++ ) { + df::coord pos = (*a).first; + if ( (*a).second < 2 ) + continue; + out.print("Requires action: (%d,%d,%d): %d\n", pos.x,pos.y,pos.z, (*a).second); + } + + /*for ( auto a = importantPoints.begin(); a != importantPoints.end(); a++ ) { + df::coord pos = (*a); + out.print("Important point: (%d,%d,%d)\n", pos.x,pos.y,pos.z); + }*/ + return CR_OK; } -vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax) { - vector candidates; - for ( int32_t dx = -1; dx <= 1; dx++ ) { - if ( point.x + dx < 0 ) continue; - for ( int32_t dy = -1; dy <= 1; dy++ ) { - if ( point.y + dy < 0 ) continue; - if ( dx == 0 && dy == 0) - continue; - candidates.push_back(df::coord(point.x+dx,point.y+dy,point.z)); - } - } - for ( int32_t dz = -1; dz <= 1; dz++ ) { - if ( point.z + dz < 0 ) continue; - if ( dz == 0 ) continue; - candidates.push_back(df::coord(point.x, point.y, point.z+dz)); - } - int32_t connectivityType; - { - df::map_block* block = cache.BlockAt(df::coord(point.x/16, point.y/16, point.z))->getRaw(); - if ( block == NULL ) { - return new vector; +bool important(df::coord pos, map >& edges, df::coord prev, set& importantPoints, set& importantEdges) { + //glob_out->print("oh my glob; (%d,%d,%d)\n", pos.x,pos.y,pos.z); + set& myEdges = edges[pos]; + bool result = importantPoints.find(pos) != importantPoints.end(); + for ( auto i = myEdges.begin(); i != myEdges.end(); i++ ) { + Edge e = *i; + df::coord other = e.p1; + if ( other == pos ) + other = e.p2; + if ( other == prev ) + continue; + if ( important(other, edges, pos, importantPoints, importantEdges) ) { + result = true; + importantEdges.insert(e); } - connectivityType = block->walkable[point.x%16][point.y%16]; } - if ( connectivityType == 0 ) - return new vector; + return result; +} + +vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int32_t yMax, int32_t zMax) { + vector* result = new vector; + for ( int32_t dx = -1; dx <= 1; dx++ ) { - if ( point.x + dx < 0 ) - continue; for ( int32_t dy = -1; dy <= 1; dy++ ) { - if ( point.y + dy < 0 ) - continue; for ( int32_t dz = -1; dz <= 1; dz++ ) { - if ( dz == 0 ) continue; - if ( point.z + dz < 0 ) - continue; - if ( dx == 0 && dy == 0 ) + 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; - df::map_block* block = cache.BlockAt(df::coord((point.x+dx)/16, (point.y+dy)/16, point.z+dz))->getRaw(); - if ( block == NULL ) { + 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 ( block->walkable[(point.x+dx)%16][(point.y+dy)%16] != connectivityType ) { + if ( dx == 0 && dy == 0 && dz == 0 ) continue; + int32_t cost = 0; + if ( dz != 0 ) cost++; + if ( Maps::canWalkBetween(point, neighbor) ) { + Edge edge(point, neighbor, cost+1); + result->push_back(edge); + } else { + Edge edge(point, neighbor, cost+100); + result->push_back(edge); } - candidates.push_back(df::coord(point.x+dx, point.y+dy, point.z+dz)); } } } - - //TODO: ramps, buildings - - vector* result = new vector; - df::tiletype_shape_basic basePointBasicShape; - bool basePointIsWall; - { - df::tiletype type = cache.tiletypeAt(point); - df::tiletype_shape shape = tileShape(type); - if ( shape == df::tiletype_shape::EMPTY ) - return result; - basePointBasicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); - //TODO: worry about up stairs vs down stairs vs updown stairs - } - - if ( basePointBasicShape == df::tiletype_shape_basic::Wall && cache.hasConstructionAt(point) ) - return result; - - /*if ( point.z < zMax-1 ) { - //ramps part 1: going up - //if I'm a ramp, and there's a wall in some direction, and there's nothing above me, and that tile is open, I can go there. - df::tiletype_shape_basic upBasicShape; - { - df::tiletype type = cache.tiletypeAt(df::coord(point.x, point.y, point.z+1)); - df::tiletype_shape shape = tileShape(type); - upBasicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); - } - if ( upBasicShape == df::tiletype_shape_basic::Ramp ) { - for ( int32_t dx = -1; dx <= 1; dx++ ) { - for ( int32_t dy = -1; dy <= 1; dy++ ) { - if ( dx == 0 && dy == 0 ) - continue; - df::tiletype type = cache.tiletypeAt(df::coord(point.x+dx, point.y+dy, point.z+1)); - df::tiletype_shape shape = tileShape(type); - df::tiletype_shape_basic basicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); - if ( basicShape == df::tiletype_shape_basic::Floor || - basicShape == df::tiletype_shape_basic::Stair || - basicShape == df::tiletype_shape_basic::Ramp ) { - candidates.push_back(df::coord(point.x+dx, point.y+dy, point.z+1)); - } - } - } - } - } - - if ( point.z >= 1 ) { - //ramps part 2: going down - - }*/ - - for ( size_t a = 0; a < candidates.size(); a++ ) { - if ( candidates[a].x <= 1 || candidates[a].x >= xMax-1 - || candidates[a].y <= 1 || candidates[a].y >= yMax-1 - || candidates[a].z <= 1 || candidates[a].z >= zMax-1 - ) { - continue; - } - df::tiletype type = cache.tiletypeAt(candidates[a]); - df::tiletype_shape shape = tileShape(type); //what namespace? - if ( shape == df::tiletype_shape::EMPTY ) - continue; - df::tiletype_shape_basic basicShape = ENUM_ATTR(tiletype_shape, basic_shape, shape); - if ( basicShape == df::tiletype_shape_basic::Wall && cache.hasConstructionAt(candidates[a]) ) { - continue; - } - - //if it's a forbidden door, continue - df::map_block* block = cache.BlockAt(df::coord(candidates[a].x/16, candidates[a].y/16, candidates[a].z))->getRaw(); - if ( block == NULL ) { - continue; - } else { - df::tile_building_occ building_occ = block->occupancy[candidates[a].x%16][candidates[a].y%16].bits.building; - if ( building_occ == df::tile_building_occ::Obstacle ) - continue; - if ( building_occ == df::tile_building_occ::Impassable ) - continue; - if ( building_occ == df::tile_building_occ::Well ) - continue; - if ( building_occ == df::tile_building_occ::Dynamic ) { - //continue; //TODO: check df.map.xml.walkable - } - } - - int32_t cost = 1; - if ( basePointIsWall || basicShape == df::tiletype_shape_basic::Wall ) { - cost += 1000000; //TODO: fancy cost - } - //if ( candidates[a] < point ) { - // result->push_back(Edge(candidates[a], point, cost)); - //} else { - result->push_back(Edge(point, candidates[a], cost)); - //} - } + return result; } From bf25ea51298b5361719500e9ae30761e2c85443f Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 17 Dec 2012 00:17:07 -0500 Subject: [PATCH 07/51] diggingInvaders: fixed a major bug with edge sorting. --- plugins/diggingInvaders/diggingInvaders.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index a0e378c2b..20b4a6d85 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -110,10 +110,12 @@ public: } bool operator==(const Edge& e) const { - return (p1 == e.p1 && p2 == e.p2); + 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 != e.p1 ) return p1 < e.p1; if ( p2 != e.p2 ) @@ -183,9 +185,6 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& continue; if ( !Units::isCitizen(unit) && !unit->flags1.bits.active_invader ) continue; - if ( unit->flags2.bits.resident ) { - out.print("resident\n"); - } if ( roots.find(unit->pos) != roots.end() ) continue; @@ -203,11 +202,7 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& set importantEdges; - int32_t dumb = 0; while(roots.size() > 1) { - if ( dumb >= 1000 ) - break; - dumb++; set toDelete; int32_t firstSize = edgeSet[*roots.begin()].size(); //out.print("%s, %d: root size = %d, first size = %d\n", __FILE__, __LINE__, roots.size(), firstSize); @@ -319,6 +314,8 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& if ( (*a).second < 2 ) continue; out.print("Requires action: (%d,%d,%d): %d\n", pos.x,pos.y,pos.z, (*a).second); + df::map_block* block = Maps::getTileBlock(pos); + block->tiletype[pos.x&0x0F][pos.y&0x0F] = df::enums::tiletype::ConstructedStairUD; } /*for ( auto a = importantPoints.begin(); a != importantPoints.end(); a++ ) { From ed1766936bf50374007c77dd3d51c3cd6b54e149 Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 17 Dec 2012 00:54:39 -0500 Subject: [PATCH 08/51] diggingInvaders: sadly, it seems that minimum spanning tree is a terrible heuristic for vertex Steiner tree in this case. --- plugins/diggingInvaders/diggingInvaders.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index 20b4a6d85..15aa94524 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -116,8 +116,12 @@ public: 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; @@ -354,17 +358,17 @@ vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int3 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) ) + 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; int32_t cost = 0; - if ( dz != 0 ) cost++; + //if ( dz != 0 ) cost++; if ( Maps::canWalkBetween(point, neighbor) ) { - Edge edge(point, neighbor, cost+1); + Edge edge(point, neighbor, cost+0); result->push_back(edge); } else { - Edge edge(point, neighbor, cost+100); + Edge edge(point, neighbor, cost+1); result->push_back(edge); } } From 94673e447d4a86d180d3416cb06deb6152d501a0 Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 17 Dec 2012 13:37:02 -0500 Subject: [PATCH 09/51] diggingInvaders: reimplemented path-based digging. Works kind of ok. --- plugins/diggingInvaders/diggingInvaders.cpp | 201 +++++++++----------- 1 file changed, 91 insertions(+), 110 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index 15aa94524..d35255f2e 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -174,27 +174,38 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& CoreSuspender suspend; map > edgeSet; - set roots; - set importantPoints; - map rootMap; + set invaderPts; + set localPts; + map parentMap; + map costMap; + PointComp comp(&costMap); + set fringe(comp); uint32_t xMax, yMax, zMax; Maps::getSize(xMax,yMax,zMax); xMax *= 16; yMax *= 16; + //TODO: look for invaders with buildingdestroyer:3 + //find all locals and invaders for ( size_t a = 0; a < df::global::world->units.active.size(); a++ ) { df::unit* unit = df::global::world->units.active[a]; if ( unit->flags1.bits.dead ) continue; - if ( !Units::isCitizen(unit) && !unit->flags1.bits.active_invader ) + if ( Units::isCitizen(unit) ) { + if ( localPts.find(unit->pos) != localPts.end() ) + continue; + localPts.insert(unit->pos); + } else if ( unit->flags1.bits.active_invader ) { + if ( invaderPts.find(unit->pos) != invaderPts.end() ) + continue; + invaderPts.insert(unit->pos); + costMap[unit->pos] = 0; + fringe.insert(unit->pos); + } else { continue; + } - if ( roots.find(unit->pos) != roots.end() ) - continue; - - roots.insert(unit->pos); - importantPoints.insert(unit->pos); vector* neighbors = getEdgeSet(out, unit->pos, xMax, yMax, zMax); set& rootEdges = edgeSet[unit->pos]; for ( auto i = neighbors->begin(); i != neighbors->end(); i++ ) { @@ -204,128 +215,98 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& delete neighbors; } - set importantEdges; + int32_t localPtsFound = 0; + set closedSet; - while(roots.size() > 1) { - set toDelete; - int32_t firstSize = edgeSet[*roots.begin()].size(); - //out.print("%s, %d: root size = %d, first size = %d\n", __FILE__, __LINE__, roots.size(), firstSize); - for ( auto i = roots.begin(); i != roots.end(); i++ ) { - df::coord root = *i; - //out.print(" (%d,%d,%d)\n", root.x, root.y, root.z); - if ( toDelete.find(root) != toDelete.end() ) - continue; - if ( edgeSet[root].empty() ) { - out.print("%s, %d: Error: no edges: %d, %d, %d\n", __FILE__, __LINE__, root.x, root.y, root.z); - return CR_FAILURE; - } - set& myEdges = edgeSet[root]; - Edge edge = *myEdges.begin(); - myEdges.erase(myEdges.begin()); - if ( edgeSet[root].size() != myEdges.size() ) { - out.print("DOOOOOM! %s, %d\n", __FILE__, __LINE__); - return CR_FAILURE; - } - if ( getRoot(edge.p1, rootMap) != root && getRoot(edge.p2, rootMap) != root ) { - out.print("%s, %d: Invalid edge.\n", __FILE__, __LINE__); - return CR_FAILURE; - } - - df::coord other = edge.p1; - if ( getRoot(other, rootMap) == root ) - other = edge.p2; - if ( getRoot(other, rootMap) == root ) { - //out.print("%s, %d: Error: self edge: %d, %d, %d\n", __FILE__, __LINE__, root.x, root.y, root.z); - /*vector badEdges; - for ( auto j = myEdges.begin(); j != myEdges.end(); j++ ) { - Edge e = *j; - if ( getRoot(e.p1, rootMap) == getRoot(e.p2, rootMap) ) - badEdges.push_back(e); - } - for ( size_t j = 0; j < badEdges.size(); j++ ) { - myEdges.erase(badEdges[j]); - }*/ - continue; - } - - importantEdges.insert(edge); + while(!fringe.empty()) { + df::coord pt = *(fringe.begin()); + fringe.erase(fringe.begin()); + out.print("line %d: fringe size = %d, localPtsFound = %d / %d, closedSetSize = %d\n", __LINE__, fringe.size(), localPtsFound, localPts.size(), closedSet.size()); + if ( closedSet.find(pt) != closedSet.end() ) { + out.print("Double closure! Bad!\n"); + break; + } + closedSet.insert(pt); + + if ( localPts.find(pt) != localPts.end() ) { + localPtsFound++; + if ( localPtsFound >= localPts.size() ) + break; + if ( costMap[pt] > 0 ) + break; + } - df::coord otherRoot = getRoot(other,rootMap); - rootMap[otherRoot] = root; - - //merge his stuff with my stuff - if ( edgeSet.find(other) == edgeSet.end() ) { - set& hisEdges = edgeSet[other]; - vector* neighbors = getEdgeSet(out, other, xMax, yMax, zMax); - for ( auto i = neighbors->begin(); i != neighbors->end(); i++ ) { - Edge edge = *i; - hisEdges.insert(edge); - } - delete neighbors; + if ( edgeSet.find(pt) == edgeSet.end() ) { + set& temp = edgeSet[pt]; + vector* edges = getEdgeSet(out, pt, xMax, yMax, zMax); + for ( auto a = edges->begin(); a != edges->end(); a++ ) { + Edge e = *a; + temp.insert(e); } - set& hisEdges = edgeSet[other]; - - for ( auto j = hisEdges.begin(); j != hisEdges.end(); j++ ) { - Edge e = *j; - if ( getRoot(e.p1, rootMap) == getRoot(e.p2, rootMap) ) - continue; - df::coord farPt = e.p1; - if ( farPt == other ) - farPt = e.p2; - myEdges.insert(e); - //myEdges.insert(Edge(root, farPt, e.cost)); + delete edges; + } + int32_t myCost = costMap[pt]; + set& myEdges = edgeSet[pt]; + for ( auto a = myEdges.begin(); a != myEdges.end(); a++ ) { + Edge e = *a; + df::coord other = e.p1; + if ( other == pt ) + other = e.p2; + if ( costMap.find(other) == costMap.end() || costMap[other] > myCost + e.cost ) { + fringe.erase(other); + costMap[other] = myCost + e.cost; + fringe.insert(other); + parentMap[other] = pt; } - //hisEdges.clear(); - edgeSet.erase(otherRoot); - toDelete.insert(otherRoot); } - for ( auto j = toDelete.begin(); j != toDelete.end(); j++ ) { - df::coord bob = *j; - roots.erase(bob); + edgeSet.erase(pt); + } + + //find important edges + set importantEdges; + map importance; + for ( auto i = localPts.begin(); i != localPts.end(); i++ ) { + df::coord pt = *i; + if ( costMap.find(pt) == costMap.end() ) + continue; + if ( parentMap.find(pt) == parentMap.end() ) + continue; + while ( parentMap.find(pt) != parentMap.end() ) { + df::coord parent = parentMap[pt]; + if ( !Maps::canWalkBetween(pt, parent) ) { + importantEdges.insert(Edge(pt,parent,1)); + } + pt = parent; } } - edgeSet.clear(); for ( auto i = importantEdges.begin(); i != importantEdges.end(); i++ ) { Edge e = *i; - edgeSet[e.p1].insert(e); - edgeSet[e.p2].insert(e); - } - - //now we find the edges used along the paths between any two roots - importantEdges.clear(); - glob_out = &out; - { - important(*importantPoints.begin(), edgeSet, df::coord(-1,-1,-1), importantPoints, importantEdges); - } - - //NOW we filter to see edges that require digging/constructing - map actionable; - for ( auto a = importantEdges.begin(); a != importantEdges.end(); a++ ) { - Edge e = *a; - if ( Maps::canWalkBetween(e.p1, e.p2) ) - continue; - actionable[e.p1]++; - if( actionable[e.p1] == 0 ) { - out.print("fuck\n"); - return CR_FAILURE; - } - actionable[e.p2]++; + /*if ( e.p1.z == e.p2.z ) { + + }*/ + importance[e.p1]++; + importance[e.p2]++; } - for ( auto a = actionable.begin(); a != actionable.end(); a++ ) { + //dig important points + for ( auto a = importance.begin(); a != importance.end(); a++ ) { df::coord pos = (*a).first; - if ( (*a).second < 2 ) + int32_t cost = (*a).second; + if ( cost < 1 ) continue; - out.print("Requires action: (%d,%d,%d): %d\n", pos.x,pos.y,pos.z, (*a).second); + + out.print("Requires action: (%d,%d,%d): %d\n", pos.x,pos.y,pos.z, cost); df::map_block* block = Maps::getTileBlock(pos); block->tiletype[pos.x&0x0F][pos.y&0x0F] = df::enums::tiletype::ConstructedStairUD; } +#if 0 /*for ( auto a = importantPoints.begin(); a != importantPoints.end(); a++ ) { df::coord pos = (*a); out.print("Important point: (%d,%d,%d)\n", pos.x,pos.y,pos.z); }*/ +#endif return CR_OK; } @@ -358,7 +339,7 @@ vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int3 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) ) + 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; From 76fcf1c335c33847fa8b6d713e0c76803f6d1b8d Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 17 Dec 2012 14:22:45 -0500 Subject: [PATCH 10/51] Digging invaders: multi-dimensional edge cost: always prefer walking over digging, no matter how far. --- plugins/diggingInvaders/diggingInvaders.cpp | 105 +++++++++++++++----- 1 file changed, 78 insertions(+), 27 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index d35255f2e..767a98c4f 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -20,6 +20,7 @@ #include "df/world.h" #include +#include #include #include #include @@ -93,13 +94,68 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } };*/ +const size_t costDim = 2; + +struct Cost { + int32_t cost[costDim]; + Cost() { + memset(cost, 0, costDim*sizeof(int32_t)); + } + Cost( int32_t costIn[costDim] ) { + memcpy(cost, costIn, costDim*sizeof(int32_t)); + } + Cost(const Cost& c) { + memcpy(cost, c.cost, costDim*sizeof(int32_t)); + } + Cost( int32_t i ) { + memset(cost, 0, costDim*sizeof(int32_t)); + cost[0] = i; + } + + bool operator>(const Cost& c) const { + for ( size_t a = 0; a < costDim; a++ ) { + if ( cost[costDim-1-a] != c.cost[costDim-1-a] ) + return cost[costDim-1-a] > c.cost[costDim-1-a]; + } + return false; + } + + bool operator<(const Cost& c) const { + for ( size_t a = 0; a < costDim; a++ ) { + if ( cost[costDim-1-a] != c.cost[costDim-1-a] ) + return cost[costDim-1-a] < c.cost[costDim-1-a]; + } + return false; + } + + bool operator==(const Cost& c) const { + for ( size_t a = 0; a < costDim; a++ ) { + if ( cost[a] != c.cost[a] ) + return false; + } + return true; + } + + bool operator!=(const Cost& c) const { + return !( *this == c); + } + + Cost operator+(const Cost& c) const { + Cost result(*this); + for ( size_t a = 0; a < costDim; a++ ) { + result.cost[a] += c.cost[a]; + } + return result; + } +}; + class Edge { public: //static map pointCost; df::coord p1; df::coord p2; - int32_t cost; - Edge(df::coord p1In, df::coord p2In, int32_t costIn): cost(costIn) { + Cost cost; + Edge(df::coord p1In, df::coord p2In, Cost costIn): cost(costIn) { if ( p2In < p1In ) { p1 = p2In; p2 = p1In; @@ -126,16 +182,6 @@ public: return p2 < e.p2; return false; } - - /*bool operator<(const Edge e) const { - int32_t pCost = max(pointCost[p1], pointCost[p2]) + cost; - int32_t e_pCost = max(pointCost[e.p1], pointCost[e.p2]) + e.cost; - if ( pCost != e_pCost ) - return pCost < e_pCost; - if ( p1 != e.p1 ) - return p1 < e.p1; - return p2 < e.p2; - }*/ }; vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int32_t yMax, int32_t zMax); @@ -143,23 +189,23 @@ df::coord getRoot(df::coord point, map& rootMap); class PointComp { public: - map *pointCost; - PointComp(map *p): pointCost(p) { + map *pointCost; + PointComp(map *p): pointCost(p) { } int32_t operator()(df::coord p1, df::coord p2) { if ( p1 == p2 ) return 0; - map::iterator i1 = pointCost->find(p1); - map::iterator i2 = pointCost->find(p2); + auto i1 = pointCost->find(p1); + auto i2 = pointCost->find(p2); if ( i1 == pointCost->end() && i2 == pointCost->end() ) return p1 < p2; if ( i1 == pointCost->end() ) - return 1; + return true; if ( i2 == pointCost->end() ) - return -1; - int32_t c1 = (*i1).second; - int32_t c2 = (*i2).second; + return false; + Cost c1 = (*i1).second; + Cost c2 = (*i2).second; if ( c1 != c2 ) return c1 < c2; return p1 < p2; @@ -177,7 +223,7 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& set invaderPts; set localPts; map parentMap; - map costMap; + map costMap; PointComp comp(&costMap); set fringe(comp); uint32_t xMax, yMax, zMax; @@ -232,7 +278,7 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& localPtsFound++; if ( localPtsFound >= localPts.size() ) break; - if ( costMap[pt] > 0 ) + if ( costMap[pt].cost[1] > 0 ) break; } @@ -245,7 +291,7 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& } delete edges; } - int32_t myCost = costMap[pt]; + Cost myCost = costMap[pt]; set& myEdges = edgeSet[pt]; for ( auto a = myEdges.begin(); a != myEdges.end(); a++ ) { Edge e = *a; @@ -271,13 +317,17 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& continue; if ( parentMap.find(pt) == parentMap.end() ) continue; + if ( costMap[pt].cost[1] == 0 ) + continue; while ( parentMap.find(pt) != parentMap.end() ) { + out.print("(%d,%d,%d)\n", pt.x, pt.y, pt.z); df::coord parent = parentMap[pt]; if ( !Maps::canWalkBetween(pt, parent) ) { - importantEdges.insert(Edge(pt,parent,1)); + importantEdges.insert(Edge(pt,parent,0)); } pt = parent; } + break; } for ( auto i = importantEdges.begin(); i != importantEdges.end(); i++ ) { @@ -343,13 +393,14 @@ vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int3 continue; if ( dx == 0 && dy == 0 && dz == 0 ) continue; - int32_t cost = 0; + Cost cost = 1; //if ( dz != 0 ) cost++; if ( Maps::canWalkBetween(point, neighbor) ) { - Edge edge(point, neighbor, cost+0); + Edge edge(point, neighbor, cost); result->push_back(edge); } else { - Edge edge(point, neighbor, cost+1); + cost.cost[1] = 1; + Edge edge(point, neighbor, cost); result->push_back(edge); } } From ef80dbacde3a759d826f91d0ae5911680e634095 Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 17 Dec 2012 17:36:35 -0500 Subject: [PATCH 11/51] More fiddling. Almost kind of works. --- plugins/diggingInvaders/diggingInvaders.cpp | 132 ++++++++++++++++++-- 1 file changed, 121 insertions(+), 11 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index 767a98c4f..cda841bfe 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -17,9 +17,13 @@ #include "df/map_block.h" #include "df/ui.h" #include "df/unit.h" +#include "df/tiletype.h" +#include "df/tiletype_shape.h" +#include "df/tiletype_shape_basic.h" #include "df/world.h" #include +#include #include #include #include @@ -94,7 +98,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } };*/ -const size_t costDim = 2; +//cost is [path cost, dig cost, construct cost]. Minimize constructions, then minimize dig cost, then minimize path cost. +const size_t costDim = 3; struct Cost { int32_t cost[costDim]; @@ -309,7 +314,7 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& } //find important edges - set importantEdges; + list importantEdges; map importance; for ( auto i = localPts.begin(); i != localPts.end(); i++ ) { df::coord pt = *i; @@ -317,13 +322,13 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& continue; if ( parentMap.find(pt) == parentMap.end() ) continue; - if ( costMap[pt].cost[1] == 0 ) + if ( costMap[pt].cost[1] == 0 && costMap[pt].cost[2] == 0 ) continue; while ( parentMap.find(pt) != parentMap.end() ) { out.print("(%d,%d,%d)\n", pt.x, pt.y, pt.z); df::coord parent = parentMap[pt]; if ( !Maps::canWalkBetween(pt, parent) ) { - importantEdges.insert(Edge(pt,parent,0)); + importantEdges.push_front(Edge(pt,parent,0)); } pt = parent; } @@ -332,6 +337,34 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& for ( auto i = importantEdges.begin(); i != importantEdges.end(); i++ ) { Edge e = *i; + df::coord pt1 = e.p1; + df::coord pt2 = e.p2; + if ( costMap[e.p2] < costMap[e.p1] ) { + pt1 = e.p2; + pt2 = e.p1; + } + df::building* building = Buildings::findAtTile(pt2); + if ( building != NULL ) { + out.print("%d\n", __LINE__); + building->flags.bits.almost_deleted = true; + //Buildings::deconstructImmediately(building); + out.print("%d\n", __LINE__); + } else { + df::map_block* block1 = Maps::getTileBlock(pt1); + df::map_block* block2 = Maps::getTileBlock(pt2); + 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); + + if ( pt1.z != pt2.z && shape1 != df::enums::tiletype_shape::STAIR_DOWN && shape1 != df::enums::tiletype_shape::STAIR_UPDOWN ) { + block1->tiletype[pt2.x&0x0F][pt2.y&0x0F] = df::enums::tiletype::ConstructedStairUD; + } + + if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Wall ) { + block2->tiletype[pt2.x&0x0F][pt2.y&0x0F] = df::enums::tiletype::ConstructedStairUD; + } + } /*if ( e.p1.z == e.p2.z ) { }*/ @@ -339,6 +372,7 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& importance[e.p2]++; } +#if 0 //dig important points for ( auto a = importance.begin(); a != importance.end(); a++ ) { df::coord pos = (*a).first; @@ -350,12 +384,6 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& df::map_block* block = Maps::getTileBlock(pos); block->tiletype[pos.x&0x0F][pos.y&0x0F] = df::enums::tiletype::ConstructedStairUD; } - -#if 0 - /*for ( auto a = importantPoints.begin(); a != importantPoints.end(); a++ ) { - df::coord pos = (*a); - out.print("Important point: (%d,%d,%d)\n", pos.x,pos.y,pos.z); - }*/ #endif return CR_OK; @@ -399,7 +427,89 @@ vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int3 Edge edge(point, neighbor, cost); result->push_back(edge); } else { - cost.cost[1] = 1; + //cost.cost[1] = 1; + //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); + + { + df::building* building1 = Buildings::findAtTile(point); + df::building* building2 = Buildings::findAtTile(neighbor); + if ( building2 != NULL && building2 != building1 ) { + cost.cost[1] += 1; + if ( dz != 0 ) + continue; + } + } + + if ( shape2 == df::enums::tiletype_shape::EMPTY ) { + cost.cost[2] += 1; + } else { + if ( point.z == neighbor.z ) { + if ( ENUM_ATTR(tiletype_shape, walkable, shape2) ) { + if ( ENUM_ATTR(tiletype_shape, walkable, shape1 ) ) { + //exit(1); + //must be building impassible tile or something + //TODO: check + df::building* building = Buildings::findAtTile(neighbor); + if ( building != NULL ) + cost.cost[1]+=1; + else { + building = Buildings::findAtTile(point); + if ( building == NULL ) { + //out.print("%s, %d: (%d,%d,%d), (%d,%d,%d)\n", __FILE__, __LINE__, point.x,point.y,point.z, neighbor.x,neighbor.y,neighbor.z); + //exit(1); + //TODO: deal with the silly RAMP_TOP condition + continue; + } + } + } + //this is fine: only charge once for digging through a wall + } else { + cost.cost[1] += 20; + } + } else { + bool ascending = neighbor.z > point.z; + /*df::tiletype_shape temp; + if ( neighbor.z > point.z ) { + temp = shape1; + shape1 = shape2; + shape2 = temp; + }*/ + if ( point.x == neighbor.x && point.y == neighbor.y ) { + 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)) ) { + //must be a forbidden hatch: TODO: check + cost.cost[1] += 1; + } else { + //too complicated + continue; + } + } else { + //bad! + if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Wall ) { + cost.cost[1] += 20; + } else if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Open ) { + cost.cost[2] += 1; + } else { + continue; + } + } + } else { + //too complicated + continue; + } + } + } + Edge edge(point, neighbor, cost); result->push_back(edge); } From 515eb3b060004c9653f7f832ff5576ee00c8d74d Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 17 Dec 2012 17:38:47 -0500 Subject: [PATCH 12/51] Reverted the silly changes to Buildings module that didn't work anyway. --- library/include/modules/Buildings.h | 5 ----- library/modules/Buildings.cpp | 25 +++++++------------------ 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 91ec8684b..266aadcb8 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -178,10 +178,5 @@ DFHACK_EXPORT bool constructWithFilters(df::building *bld, std::vectorqueueDestroy(); return false; } - - return deconstructImmediately(bld); -} -bool Buildings::deconstructImmediately(df::building *bld) -{ - using df::global::ui; - using df::global::world; - using df::global::ui_look_list; - - CHECK_NULL_POINTER(bld); - /* Immediate destruction code path. Should only happen for abstract and unconstructed buildings.*/ - + if (bld->isSettingOccupancy()) { markBuildingTiles(bld, true); bld->cleanupMap(); } - + bld->removeUses(false, false); // Assume: no parties. unlinkRooms(bld); @@ -1047,16 +1036,16 @@ bool Buildings::deconstructImmediately(df::building *bld) // Assume: does not affect pathfinding bld->deconstructItems(false, false); // Don't clear arrows. - + bld->uncategorize(); delete bld; - + if (world->selected_building == bld) { world->selected_building = NULL; world->update_selected_building = true; } - + for (int i = ui_look_list->items.size()-1; i >= 0; i--) { auto item = ui_look_list->items[i]; @@ -1067,10 +1056,10 @@ bool Buildings::deconstructImmediately(df::building *bld) delete item; } } - + Job::checkBuildingsNow(); Job::checkDesignationsNow(); - + return true; } From 957a4da4748d49fa4059cabf5eed3364bc4eb470 Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 17 Dec 2012 18:05:57 -0500 Subject: [PATCH 13/51] Minor changes. --- plugins/diggingInvaders/diggingInvaders.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index cda841bfe..21bfedfeb 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -98,8 +98,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } };*/ -//cost is [path cost, dig cost, construct cost]. Minimize constructions, then minimize dig cost, then minimize path cost. -const size_t costDim = 3; +//cost is [path cost, building destruction cost, dig cost, construct cost]. Minimize constructions, then minimize dig cost, then minimize path cost. +const size_t costDim = 4; struct Cost { int32_t cost[costDim]; @@ -347,7 +347,6 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& if ( building != NULL ) { out.print("%d\n", __LINE__); building->flags.bits.almost_deleted = true; - //Buildings::deconstructImmediately(building); out.print("%d\n", __LINE__); } else { df::map_block* block1 = Maps::getTileBlock(pt1); @@ -451,7 +450,7 @@ vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int3 } if ( shape2 == df::enums::tiletype_shape::EMPTY ) { - cost.cost[2] += 1; + cost.cost[3] += 1; } else { if ( point.z == neighbor.z ) { if ( ENUM_ATTR(tiletype_shape, walkable, shape2) ) { @@ -474,7 +473,7 @@ vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int3 } //this is fine: only charge once for digging through a wall } else { - cost.cost[1] += 20; + cost.cost[2] += 1; } } else { bool ascending = neighbor.z > point.z; @@ -496,9 +495,9 @@ vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int3 } else { //bad! if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Wall ) { - cost.cost[1] += 20; - } else if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Open ) { cost.cost[2] += 1; + } else if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Open ) { + cost.cost[3] += 1; } else { continue; } From 937769874d6e4ea97184f2df05e4b6e90f0dbb88 Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 17 Dec 2012 20:12:11 -0500 Subject: [PATCH 14/51] Digging Invaders: kind of works, but WAAAAAY too slow. --- plugins/diggingInvaders/diggingInvaders.cpp | 172 +++++++++++--------- 1 file changed, 94 insertions(+), 78 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index 21bfedfeb..cec1e23d2 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -14,10 +14,13 @@ #include "df/building.h" #include "df/coord.h" +#include "df/inorganic_raw.h" #include "df/map_block.h" +#include "df/strain_type.h" #include "df/ui.h" #include "df/unit.h" #include "df/tiletype.h" +#include "df/tiletype_material.h" #include "df/tiletype_shape.h" #include "df/tiletype_shape_basic.h" #include "df/world.h" @@ -25,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -33,13 +37,10 @@ using namespace std; using namespace DFHack; using namespace df::enums; -/////////////////////// -color_ostream* glob_out; - -#if 0 -#define DEBUG_PRINT(str) \ -out.print("%s, line %d" STR, __FILE__, __LINE__); -#endif +int32_t digSpeed = 100; +int32_t destroySpeed = 100; +int32_t deconstructSpeed = 100; +int32_t constructSpeed = 100; command_result diggingInvadersFunc(color_ostream &out, std::vector & parameters); @@ -62,42 +63,6 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) return CR_OK; } -// Called to notify the plugin about important state changes. -// Invoked with DF suspended, and always before the matching plugin_onupdate. -// More event codes may be added in the future. -/* -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) -{ - switch (event) { - case SC_GAME_LOADED: - // initialize from the world just loaded - break; - case SC_GAME_UNLOADED: - // cleanup - break; - default: - break; - } - return CR_OK; -} -*/ - -// Whatever you put here will be done in each game step. Don't abuse it. -// It's optional, so you can just comment it out like this if you don't need it. -/* -DFhackCExport command_result plugin_onupdate ( color_ostream &out ) -{ - // whetever. You don't need to suspend DF execution here. - return CR_OK; -} -*/ - -/*class CompareEdge { - bool operator()(edge e1, edge e2) { - - } -};*/ - //cost is [path cost, building destruction cost, dig cost, construct cost]. Minimize constructions, then minimize dig cost, then minimize path cost. const size_t costDim = 4; @@ -189,7 +154,7 @@ public: } }; -vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int32_t yMax, int32_t zMax); +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, map& rootMap); class PointComp { @@ -218,10 +183,16 @@ public: }; bool important(df::coord pos, map >& edges, df::coord prev, set& importantPoints, set& importantEdges); +void doDiggingInvaders(color_ostream& out, void* ptr); command_result diggingInvadersFunc(color_ostream& out, std::vector& parameters) { if (!parameters.empty()) return CR_WRONG_USAGE; + doDiggingInvaders(out, NULL); + return CR_OK; +} + +void doDiggingInvaders(color_ostream& out, void* ptr) { CoreSuspender suspend; map > edgeSet; @@ -235,6 +206,7 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& Maps::getSize(xMax,yMax,zMax); xMax *= 16; yMax *= 16; + MapExtras::MapCache cache; //TODO: look for invaders with buildingdestroyer:3 @@ -257,7 +229,7 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& continue; } - vector* neighbors = getEdgeSet(out, unit->pos, xMax, yMax, zMax); + vector* neighbors = getEdgeSet(out, unit->pos, cache, xMax, yMax, zMax); set& rootEdges = edgeSet[unit->pos]; for ( auto i = neighbors->begin(); i != neighbors->end(); i++ ) { Edge edge = *i; @@ -272,7 +244,7 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& while(!fringe.empty()) { df::coord pt = *(fringe.begin()); fringe.erase(fringe.begin()); - out.print("line %d: fringe size = %d, localPtsFound = %d / %d, closedSetSize = %d\n", __LINE__, fringe.size(), localPtsFound, localPts.size(), closedSet.size()); + //out.print("line %d: fringe size = %d, localPtsFound = %d / %d, closedSetSize = %d\n", __LINE__, fringe.size(), localPtsFound, localPts.size(), closedSet.size()); if ( closedSet.find(pt) != closedSet.end() ) { out.print("Double closure! Bad!\n"); break; @@ -289,7 +261,7 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& if ( edgeSet.find(pt) == edgeSet.end() ) { set& temp = edgeSet[pt]; - vector* edges = getEdgeSet(out, pt, xMax, yMax, zMax); + vector* edges = getEdgeSet(out, pt, cache, xMax, yMax, zMax); for ( auto a = edges->begin(); a != edges->end(); a++ ) { Edge e = *a; temp.insert(e); @@ -315,7 +287,6 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& //find important edges list importantEdges; - map importance; for ( auto i = localPts.begin(); i != localPts.end(); i++ ) { df::coord pt = *i; if ( costMap.find(pt) == costMap.end() ) @@ -325,7 +296,7 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& if ( costMap[pt].cost[1] == 0 && costMap[pt].cost[2] == 0 ) continue; while ( parentMap.find(pt) != parentMap.end() ) { - out.print("(%d,%d,%d)\n", pt.x, pt.y, pt.z); + //out.print("(%d,%d,%d)\n", pt.x, pt.y, pt.z); df::coord parent = parentMap[pt]; if ( !Maps::canWalkBetween(pt, parent) ) { importantEdges.push_front(Edge(pt,parent,0)); @@ -335,6 +306,8 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& break; } + bool didSomething = false; + df::coord where; for ( auto i = importantEdges.begin(); i != importantEdges.end(); i++ ) { Edge e = *i; df::coord pt1 = e.p1; @@ -345,9 +318,10 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& } df::building* building = Buildings::findAtTile(pt2); if ( building != NULL ) { - out.print("%d\n", __LINE__); building->flags.bits.almost_deleted = true; - out.print("%d\n", __LINE__); + didSomething = true; + where = pt2; + break; } else { df::map_block* block1 = Maps::getTileBlock(pt1); df::map_block* block2 = Maps::getTileBlock(pt2); @@ -358,34 +332,33 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& if ( pt1.z != pt2.z && shape1 != df::enums::tiletype_shape::STAIR_DOWN && shape1 != df::enums::tiletype_shape::STAIR_UPDOWN ) { block1->tiletype[pt2.x&0x0F][pt2.y&0x0F] = df::enums::tiletype::ConstructedStairUD; + where = pt2; + didSomething = true; + break; } if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Wall ) { block2->tiletype[pt2.x&0x0F][pt2.y&0x0F] = df::enums::tiletype::ConstructedStairUD; + didSomething = true; + where = pt2; + break; } } - /*if ( e.p1.z == e.p2.z ) { - - }*/ - importance[e.p1]++; - importance[e.p2]++; } -#if 0 - //dig important points - for ( auto a = importance.begin(); a != importance.end(); a++ ) { - df::coord pos = (*a).first; - int32_t cost = (*a).second; - if ( cost < 1 ) - continue; - - out.print("Requires action: (%d,%d,%d): %d\n", pos.x,pos.y,pos.z, cost); - df::map_block* block = Maps::getTileBlock(pos); - block->tiletype[pos.x&0x0F][pos.y&0x0F] = df::enums::tiletype::ConstructedStairUD; + if ( !didSomething ) + return; + + Cost cost = costMap[where]; + int32_t cost_tick = 0; + for ( size_t a = 0; a < costDim; a++ ) { + cost_tick += cost.cost[a]; } -#endif - - return CR_OK; + + EventManager::EventHandler handle(doDiggingInvaders); + Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("diggingInvaders"); + EventManager::registerTick(handle, cost_tick, me); + df::global::world->reindex_pathfinding = true; } bool important(df::coord pos, map >& edges, df::coord prev, set& importantPoints, set& importantEdges) { @@ -407,7 +380,45 @@ bool important(df::coord pos, map >& edges, df::coord prev, return result; } -vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int32_t yMax, int32_t zMax) { +int32_t getDestroyCost(df::building* building) { + return 1000 / destroySpeed; +#if 0 + if ( building->mat_type != 0 ) { + cerr << "Error " << __FILE__ << ", " << __LINE__ << endl; + exit(1); + } + df::inorganic_raw* mat = df::global::world->raws.inorganics[building->mat_index]; + int32_t str = mat->material.strength.fracture[df::enums::strain_type::IMPACT]; + return str / destroySpeed; +#endif +} + +int32_t getDigCost(MapExtras::MapCache& cache, df::coord point) { + //TODO: check for constructions + return 10000 / digSpeed; +#if 0 + df::tiletype* type = Maps::getTileType(point); + df::enums::tiletype_material::tiletype_material ttmat = ENUM_ATTR(tiletype, material, *type); + if ( ttmat == df::enums::tiletype_material::CONSTRUCTION ) { + //construction stuff + return 1; + } else if ( ttmat == df::enums::tiletype_material::SOIL ) { + + } + + return 1; +#endif +} + +int32_t getDeconstructCost(df::coord point) { + return 10000 / deconstructSpeed; +} + +int32_t getConstructCost(df::coord point) { + return 100000 / constructSpeed; +} + +vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax) { vector* result = new vector; for ( int32_t dx = -1; dx <= 1; dx++ ) { @@ -443,14 +454,14 @@ vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int3 df::building* building1 = Buildings::findAtTile(point); df::building* building2 = Buildings::findAtTile(neighbor); if ( building2 != NULL && building2 != building1 ) { - cost.cost[1] += 1; + cost.cost[1] += getDestroyCost(building2); if ( dz != 0 ) continue; } } if ( shape2 == df::enums::tiletype_shape::EMPTY ) { - cost.cost[3] += 1; + cost.cost[3] += getConstructCost(neighbor); } else { if ( point.z == neighbor.z ) { if ( ENUM_ATTR(tiletype_shape, walkable, shape2) ) { @@ -460,7 +471,7 @@ vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int3 //TODO: check df::building* building = Buildings::findAtTile(neighbor); if ( building != NULL ) - cost.cost[1]+=1; + cost.cost[1]+=getDestroyCost(building); else { building = Buildings::findAtTile(point); if ( building == NULL ) { @@ -473,7 +484,7 @@ vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int3 } //this is fine: only charge once for digging through a wall } else { - cost.cost[2] += 1; + cost.cost[2] += getDigCost(cache, neighbor); } } else { bool ascending = neighbor.z > point.z; @@ -487,7 +498,12 @@ vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int3 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)) ) { //must be a forbidden hatch: TODO: check - cost.cost[1] += 1; + df::building* building = Buildings::findAtTile(dz < 0 ? point : neighbor); + if ( building != NULL ) { + cost.cost[1] += getDestroyCost(building); + } else { + 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); + } } else { //too complicated continue; @@ -495,9 +511,9 @@ vector* getEdgeSet(color_ostream &out, df::coord point, int32_t xMax, int3 } else { //bad! if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Wall ) { - cost.cost[2] += 1; + cost.cost[2] += getDigCost(cache, neighbor); } else if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Open ) { - cost.cost[3] += 1; + cost.cost[3] += getConstructCost(neighbor); } else { continue; } From 110a9a55799767275bf57e25d8cf8e44afd64256 Mon Sep 17 00:00:00 2001 From: expwnent Date: Mon, 17 Dec 2012 20:44:35 -0500 Subject: [PATCH 15/51] Digging Invaders: a bit faster. --- plugins/diggingInvaders/diggingInvaders.cpp | 41 ++++++++------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index cec1e23d2..5a15351d9 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -26,6 +26,7 @@ #include "df/world.h" #include +#include #include #include #include @@ -195,7 +196,6 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& void doDiggingInvaders(color_ostream& out, void* ptr) { CoreSuspender suspend; - map > edgeSet; set invaderPts; set localPts; map parentMap; @@ -228,19 +228,13 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { } else { continue; } - - vector* neighbors = getEdgeSet(out, unit->pos, cache, xMax, yMax, zMax); - set& rootEdges = edgeSet[unit->pos]; - for ( auto i = neighbors->begin(); i != neighbors->end(); i++ ) { - Edge edge = *i; - rootEdges.insert(edge); - } - delete neighbors; } int32_t localPtsFound = 0; set closedSet; + clock_t t0 = clock(); + clock_t totalEdgeTime = 0; while(!fringe.empty()) { df::coord pt = *(fringe.begin()); fringe.erase(fringe.begin()); @@ -249,30 +243,23 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { out.print("Double closure! Bad!\n"); break; } - closedSet.insert(pt); + //closedSet.insert(pt); if ( localPts.find(pt) != localPts.end() ) { localPtsFound++; if ( localPtsFound >= localPts.size() ) break; - if ( costMap[pt].cost[1] > 0 ) + if ( costMap[pt].cost[1] > 0 || costMap[pt].cost[2] > 0 || costMap[pt].cost[3] > 0 ) break; } - if ( edgeSet.find(pt) == edgeSet.end() ) { - set& temp = edgeSet[pt]; - vector* edges = getEdgeSet(out, pt, cache, xMax, yMax, zMax); - for ( auto a = edges->begin(); a != edges->end(); a++ ) { - Edge e = *a; - temp.insert(e); - } - delete edges; - } - Cost myCost = costMap[pt]; - set& myEdges = edgeSet[pt]; - for ( auto a = myEdges.begin(); a != myEdges.end(); a++ ) { - Edge e = *a; - df::coord other = e.p1; + Cost& myCost = costMap[pt]; + clock_t edgeTime = clock(); + vector* myEdges = getEdgeSet(out, pt, cache, xMax, yMax, zMax); + totalEdgeTime += (clock() - edgeTime); + for ( auto a = myEdges->begin(); a != myEdges->end(); a++ ) { + Edge &e = *a; + df::coord& other = e.p1; if ( other == pt ) other = e.p2; if ( costMap.find(other) == costMap.end() || costMap[other] > myCost + e.cost ) { @@ -282,8 +269,10 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { parentMap[other] = pt; } } - edgeSet.erase(pt); + delete myEdges; } + clock_t time = clock() - t0; + out.print("time = %d, totalEdgeTime = %d\n", time, totalEdgeTime); //find important edges list importantEdges; From a09764e77bcb92c65e9c0f7ada7231c3ebb13f43 Mon Sep 17 00:00:00 2001 From: expwnent Date: Tue, 18 Dec 2012 15:22:21 -0500 Subject: [PATCH 16/51] Digging Invaders: more optimizations. --- plugins/diggingInvaders/diggingInvaders.cpp | 112 +++++++++++++------- 1 file changed, 76 insertions(+), 36 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index 5a15351d9..4f4f317d3 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -33,6 +33,14 @@ #include #include #include + +#define HASHMAP 1 +#if HASHMAP +#include +#include + +#endif + using namespace std; using namespace DFHack; @@ -65,7 +73,7 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) } //cost is [path cost, building destruction cost, dig cost, construct cost]. Minimize constructions, then minimize dig cost, then minimize path cost. -const size_t costDim = 4; +const size_t costDim = 3; struct Cost { int32_t cost[costDim]; @@ -107,6 +115,10 @@ struct Cost { return true; } + bool operator<=(const Cost& c) const { + return *this == c || *this < c; + } + bool operator!=(const Cost& c) const { return !( *this == c); } @@ -156,14 +168,32 @@ public: }; vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax); + +#if HASHMAP +df::coord getRoot(df::coord point, unordered_map& rootMap); +#else df::coord getRoot(df::coord point, map& rootMap); +#endif + +struct PointHash { + size_t operator()(const df::coord c) const { + return c.x * 65537 + c.y * 17 + c.z; + } +}; class PointComp { public: +#if HASHMAP + unordered_map *pointCost; + PointComp(unordered_map *p): pointCost(p) { + + } +#else map *pointCost; PointComp(map *p): pointCost(p) { } +#endif int32_t operator()(df::coord p1, df::coord p2) { if ( p1 == p2 ) return 0; @@ -183,7 +213,7 @@ public: } }; -bool important(df::coord pos, map >& edges, df::coord prev, set& importantPoints, set& importantEdges); +//bool important(df::coord pos, map >& edges, df::coord prev, set& importantPoints, set& importantEdges); void doDiggingInvaders(color_ostream& out, void* ptr); command_result diggingInvadersFunc(color_ostream& out, std::vector& parameters) { @@ -196,10 +226,18 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& void doDiggingInvaders(color_ostream& out, void* ptr) { CoreSuspender suspend; +#if HASHMAP + unordered_set invaderPts; + unordered_set localPts; + unordered_map parentMap; + unordered_map costMap; +#else set invaderPts; set localPts; map parentMap; map costMap; +#endif + PointComp comp(&costMap); set fringe(comp); uint32_t xMax, yMax, zMax; @@ -231,7 +269,11 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { } int32_t localPtsFound = 0; +#if HASHMAP + unordered_set closedSet; +#else set closedSet; +#endif clock_t t0 = clock(); clock_t totalEdgeTime = 0; @@ -249,7 +291,7 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { localPtsFound++; if ( localPtsFound >= localPts.size() ) break; - if ( costMap[pt].cost[1] > 0 || costMap[pt].cost[2] > 0 || costMap[pt].cost[3] > 0 ) + if ( costMap[pt].cost[1] > 0 || costMap[pt].cost[2] > 0 /*|| costMap[pt].cost[3] > 0*/ ) break; } @@ -262,12 +304,19 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { df::coord& other = e.p1; if ( other == pt ) other = e.p2; - if ( costMap.find(other) == costMap.end() || costMap[other] > myCost + e.cost ) { - fringe.erase(other); - costMap[other] = myCost + e.cost; - fringe.insert(other); - parentMap[other] = pt; + //if ( closedSet.find(other) != closedSet.end() ) + // continue; + auto i = costMap.find(other); + if ( i != costMap.end() ) { + Cost& cost = (*i).second; + if ( cost <= myCost + e.cost ) { + continue; + } + fringe.erase((*i).first); } + costMap[other] = myCost + e.cost; + fringe.insert(other); + parentMap[other] = pt; } delete myEdges; } @@ -339,38 +388,20 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { return; Cost cost = costMap[where]; - int32_t cost_tick = 0; - for ( size_t a = 0; a < costDim; a++ ) { - cost_tick += cost.cost[a]; - } + float cost_tick = 0; + cost_tick += cost.cost[0]; + cost_tick += cost.cost[1] / (float)destroySpeed; + cost_tick += cost.cost[2] / (float)digSpeed; EventManager::EventHandler handle(doDiggingInvaders); Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("diggingInvaders"); - EventManager::registerTick(handle, cost_tick, me); + EventManager::registerTick(handle, (int32_t)cost_tick, me); df::global::world->reindex_pathfinding = true; } -bool important(df::coord pos, map >& edges, df::coord prev, set& importantPoints, set& importantEdges) { - //glob_out->print("oh my glob; (%d,%d,%d)\n", pos.x,pos.y,pos.z); - set& myEdges = edges[pos]; - bool result = importantPoints.find(pos) != importantPoints.end(); - for ( auto i = myEdges.begin(); i != myEdges.end(); i++ ) { - Edge e = *i; - df::coord other = e.p1; - if ( other == pos ) - other = e.p2; - if ( other == prev ) - continue; - if ( important(other, edges, pos, importantPoints, importantEdges) ) { - result = true; - importantEdges.insert(e); - } - } - return result; -} int32_t getDestroyCost(df::building* building) { - return 1000 / destroySpeed; + return 10000; #if 0 if ( building->mat_type != 0 ) { cerr << "Error " << __FILE__ << ", " << __LINE__ << endl; @@ -384,7 +415,7 @@ int32_t getDestroyCost(df::building* building) { int32_t getDigCost(MapExtras::MapCache& cache, df::coord point) { //TODO: check for constructions - return 10000 / digSpeed; + return 1000000; #if 0 df::tiletype* type = Maps::getTileType(point); df::enums::tiletype_material::tiletype_material ttmat = ENUM_ATTR(tiletype, material, *type); @@ -400,15 +431,16 @@ int32_t getDigCost(MapExtras::MapCache& cache, df::coord point) { } int32_t getDeconstructCost(df::coord point) { - return 10000 / deconstructSpeed; + return 1000000; } int32_t getConstructCost(df::coord point) { - return 100000 / constructSpeed; + return 10000000; } 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++ ) { @@ -426,6 +458,13 @@ vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCach Edge edge(point, neighbor, cost); result->push_back(edge); } else { +#if 0 + { + cost.cost[1] = 1; + result->push_back(Edge(point,neighbor,cost)); + continue; + } +#endif //cost.cost[1] = 1; //find out WHY we can't walk there //make it simple: don't deal with unallocated blocks @@ -450,7 +489,8 @@ vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCach } if ( shape2 == df::enums::tiletype_shape::EMPTY ) { - cost.cost[3] += getConstructCost(neighbor); + //cost.cost[3] += getConstructCost(neighbor); + continue; } else { if ( point.z == neighbor.z ) { if ( ENUM_ATTR(tiletype_shape, walkable, shape2) ) { From fcbc5d1cb072051b90cb46e6d36b29b2633ed306 Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 19 Dec 2012 18:46:46 -0500 Subject: [PATCH 17/51] Digging invaders. Temp commit. Unstable. --- plugins/diggingInvaders/diggingInvaders.cpp | 489 ++++++++++++++++---- 1 file changed, 402 insertions(+), 87 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index 4f4f317d3..c4f9614a8 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -7,22 +7,41 @@ #include "modules/Buildings.h" #include "modules/EventManager.h" +#include "modules/Job.h" #include "modules/Maps.h" #include "modules/MapCache.h" #include "modules/Units.h" #include "modules/World.h" +#include "df/body_part_raw_flags.h" #include "df/building.h" +#include "df/building_type.h" +#include "df/caste_body_info.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/inorganic_raw.h" +#include "df/job.h" +#include "df/job_skill.h" +#include "df/job_type.h" #include "df/map_block.h" #include "df/strain_type.h" -#include "df/ui.h" -#include "df/unit.h" +#include "df/tile_building_occ.h" +#include "df/tile_occupancy.h" #include "df/tiletype.h" #include "df/tiletype_material.h" #include "df/tiletype_shape.h" #include "df/tiletype_shape_basic.h" +#include "df/ui.h" +#include "df/unit.h" +#include "df/unit_inventory_item.h" #include "df/world.h" #include @@ -73,7 +92,25 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) } //cost is [path cost, building destruction cost, dig cost, construct cost]. Minimize constructions, then minimize dig cost, then minimize path cost. -const size_t costDim = 3; +enum CostDimension { + Distance, + DestroyBuilding, + Dig, + DestroyConstruction, + //Construct, + costDim +}; + +const int32_t costBucket[] = { +//Distance +0, +//DestroyBuilding +1, +//Dig +2, +//DestroyConstruction +3, +}; struct Cost { int32_t cost[costDim]; @@ -88,39 +125,73 @@ struct Cost { } Cost( int32_t i ) { memset(cost, 0, costDim*sizeof(int32_t)); - cost[0] = i; + cost[CostDimension::Distance] = i; + } + + /* + int32_t workNeeded() const { + int32_t result = 0; + for ( int32_t a = 1; a < costDim; a++ ) { + result += cost[a]; + } + return result; + } + */ + + int32_t compare(const Cost& c) const { + int32_t table[costDim]; + memset(table, 0, costDim*sizeof(int32_t)); + for ( size_t a = 0; a < costDim; a++ ) { + table[costBucket[a]] += cost[a] - c.cost[a]; + } + for ( size_t a = 0; a < costDim; a++ ) { + if ( table[costDim-1-a] > 0 ) + return 1; + if ( table[costDim-1-a] < 0 ) + return -1; + } + return 0; } bool operator>(const Cost& c) const { + return compare(c) > 0; + /* for ( size_t a = 0; a < costDim; a++ ) { if ( cost[costDim-1-a] != c.cost[costDim-1-a] ) return cost[costDim-1-a] > c.cost[costDim-1-a]; } return false; + */ } bool operator<(const Cost& c) const { + return compare(c) < 0; + /* for ( size_t a = 0; a < costDim; a++ ) { if ( cost[costDim-1-a] != c.cost[costDim-1-a] ) return cost[costDim-1-a] < c.cost[costDim-1-a]; } return false; + */ } bool operator==(const Cost& c) const { + return compare(c) == 0; + /* for ( size_t a = 0; a < costDim; a++ ) { if ( cost[a] != c.cost[a] ) return false; } return true; + */ } bool operator<=(const Cost& c) const { - return *this == c || *this < c; + return compare(c) <= 0; } bool operator!=(const Cost& c) const { - return !( *this == c); + return compare(c) != 0; } Cost operator+(const Cost& c) const { @@ -245,6 +316,7 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { xMax *= 16; yMax *= 16; MapExtras::MapCache cache; + df::unit* firstInvader = NULL; //TODO: look for invaders with buildingdestroyer:3 @@ -263,10 +335,14 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { invaderPts.insert(unit->pos); costMap[unit->pos] = 0; fringe.insert(unit->pos); + if ( firstInvader == NULL ) + firstInvader = unit; } else { continue; } } + out << firstInvader->id << endl; + out << firstInvader->pos.x << ", " << firstInvader->pos.y << ", " << firstInvader->pos.z << endl; int32_t localPtsFound = 0; #if HASHMAP @@ -285,13 +361,20 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { out.print("Double closure! Bad!\n"); break; } - //closedSet.insert(pt); + closedSet.insert(pt); if ( localPts.find(pt) != localPts.end() ) { localPtsFound++; if ( localPtsFound >= localPts.size() ) break; - if ( costMap[pt].cost[1] > 0 || costMap[pt].cost[2] > 0 /*|| costMap[pt].cost[3] > 0*/ ) + bool doBreak = false; + for ( int32_t a = 1; a < costDim; a++ ) { + if ( costMap[pt].cost[a] > 0 ) { + doBreak = true; + break; + } + } + if ( doBreak ) break; } @@ -323,6 +406,9 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { clock_t time = clock() - t0; out.print("time = %d, totalEdgeTime = %d\n", time, totalEdgeTime); + unordered_set requiresZNeg; + unordered_set requiresZPos; + //find important edges list importantEdges; for ( auto i = localPts.begin(); i != localPts.end(); i++ ) { @@ -331,12 +417,30 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { continue; if ( parentMap.find(pt) == parentMap.end() ) continue; - if ( costMap[pt].cost[1] == 0 && costMap[pt].cost[2] == 0 ) + bool requireAction = false; + for ( int32_t a = 1; a < costDim; a++ ) { + if ( costMap[pt].cost[a] != 0 ) { + requireAction = true; + break; + } + } + if ( !requireAction ) continue; while ( parentMap.find(pt) != parentMap.end() ) { //out.print("(%d,%d,%d)\n", pt.x, pt.y, pt.z); df::coord parent = parentMap[pt]; - if ( !Maps::canWalkBetween(pt, parent) ) { + if ( pt.z < parent.z ) { + requiresZNeg.insert(parent); + requiresZPos.insert(pt); + } else if ( pt.z > parent.z ) { + requiresZNeg.insert(pt); + requiresZPos.insert(parent); + } + Cost cost1 = costMap[pt]; + Cost cost2 = costMap[parent]; + cost1.cost[0] = 0; + cost2.cost[0] = 0; + if ( true || cost1 != cost2 ) { importantEdges.push_front(Edge(pt,parent,0)); } pt = parent; @@ -344,6 +448,28 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { break; } + unordered_set toDelete; + for ( auto a = requiresZNeg.begin(); a != requiresZNeg.end(); a++ ) { + df::coord pos = *a; + df::tiletype* type = Maps::getTileType(pos); + df::tiletype_shape shape = ENUM_ATTR(tiletype, shape, *type); + if ( ENUM_ATTR(tiletype_shape, passable_low, shape) ) { + toDelete.insert(pos); + } + } + requiresZNeg.erase(toDelete.begin(), toDelete.end()); + toDelete.clear(); + for ( auto a = requiresZPos.begin(); a != requiresZPos.end(); a++ ) { + df::coord pos = *a; + df::tiletype* type = Maps::getTileType(pos); + df::tiletype_shape shape = ENUM_ATTR(tiletype, shape, *type); + if ( ENUM_ATTR(tiletype_shape, passable_high, shape) ) { + toDelete.insert(pos); + } + } + requiresZPos.erase(toDelete.begin(), toDelete.end()); + toDelete.clear(); + bool didSomething = false; df::coord where; for ( auto i = importantEdges.begin(); i != importantEdges.end(); i++ ) { @@ -354,54 +480,199 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { pt1 = e.p2; pt2 = e.p1; } + bool important = //requireZNeg.find(pt1) != requireZNeg.end() || + //requireZPos.find(pt1) != requireZPos.end() || + requiresZNeg.find(pt2) != requiresZNeg.end() || + requiresZPos.find(pt2) != requiresZPos.end(); + if ( !important ) { + Cost c1 = costMap[pt1]; + Cost c2 = costMap[pt2]; + c1.cost[0] = 0; + c2.cost[0] = 0; + if ( c1 == c2 ) { + //definitely not important + continue; + } + } + + 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]; + + //TODO: if actions required > 1, continue df::building* building = Buildings::findAtTile(pt2); + if ( building != NULL && passable2 ) { + building = NULL; + } if ( building != NULL ) { - building->flags.bits.almost_deleted = true; + out.print("%s, line %d: Destroying building %d\n", __FILE__, __LINE__, building->id); + //building->flags.bits.almost_deleted = true; + + 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->references.push_back(buildingRef); + df::general_ref_unit_workerst* workerRef = new df::general_ref_unit_workerst; + workerRef->unit_id = firstInvader->id; + job->references.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 = pt1; + firstInvader->job.hunt_target = NULL; + firstInvader->job.destroy_target = NULL; + + building->jobs.clear(); + building->jobs.push_back(job); + Job::linkIntoWorld(job); + didSomething = true; where = pt2; - break; + continue; } else { - df::map_block* block1 = Maps::getTileBlock(pt1); - df::map_block* block2 = Maps::getTileBlock(pt2); 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); - - if ( pt1.z != pt2.z && shape1 != df::enums::tiletype_shape::STAIR_DOWN && shape1 != df::enums::tiletype_shape::STAIR_UPDOWN ) { - block1->tiletype[pt2.x&0x0F][pt2.y&0x0F] = df::enums::tiletype::ConstructedStairUD; - where = pt2; + bool construction2 = ENUM_ATTR(tiletype, material, *type1) == df::enums::tiletype_material::CONSTRUCTION; + if ( construction2 ) { + out.print("%s, line %d. Removing construction (%d,%d,%d)\n", __FILE__, __LINE__, pt2.x,pt2.y,pt2.z); + 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->references.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); didSomething = true; + where = pt2; break; } - if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Wall ) { - block2->tiletype[pt2.x&0x0F][pt2.y&0x0F] = df::enums::tiletype::ConstructedStairUD; - didSomething = true; + /*if ( pt1.z != pt2.z && shape1 != df::enums::tiletype_shape::STAIR_DOWN && shape1 != df::enums::tiletype_shape::STAIR_UPDOWN ) { + block1->tiletype[pt2.x&0x0F][pt2.y&0x0F] = df::enums::tiletype::ConstructedStairUD; where = pt2; + didSomething = true; break; + }*/ + + 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; + } else if ( up && !down ) { + job->job_type = df::enums::job_type::CarveUpwardStaircase; + job->pos = pt2; + } else if ( !up && down ) { + job->job_type = df::enums::job_type::CarveDownwardStaircase; + job->pos = pt2; + } else { + job->job_type = df::enums::job_type::Dig; + job->pos = pt1; + } + df::general_ref_unit_workerst* ref = new df::general_ref_unit_workerst; + ref->unit_id = firstInvader->id; + job->references.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(); + firstInvader->path.dest = pt2; + Job::linkIntoWorld(job); + + //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 = 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; + } + pick->subtype = itemdef; + pick->sharpness = 5000; + + int32_t part = -1; + part = firstInvader->body.unk_3c8; //weapon_bp + if ( part == -1 ) { + out.print("%s, %d: no grasp part.\n", __FILE__, __LINE__); + return; } + //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); + didSomething = true; + where = pt2; + break; } } + out << "didSomething = " << didSomething << endl; if ( !didSomething ) return; Cost cost = costMap[where]; float cost_tick = 0; - cost_tick += cost.cost[0]; - cost_tick += cost.cost[1] / (float)destroySpeed; - cost_tick += cost.cost[2] / (float)digSpeed; + cost_tick += cost.cost[CostDimension::Distance]; + cost_tick += cost.cost[CostDimension::DestroyBuilding] / (float)destroySpeed; + cost_tick += cost.cost[CostDimension::Dig] / (float)digSpeed; EventManager::EventHandler handle(doDiggingInvaders); Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("diggingInvaders"); - EventManager::registerTick(handle, (int32_t)cost_tick, me); + //EventManager::registerTick(handle, (int32_t)cost_tick, me); df::global::world->reindex_pathfinding = true; } int32_t getDestroyCost(df::building* building) { - return 10000; + return 1; #if 0 if ( building->mat_type != 0 ) { cerr << "Error " << __FILE__ << ", " << __LINE__ << endl; @@ -415,7 +686,7 @@ int32_t getDestroyCost(df::building* building) { int32_t getDigCost(MapExtras::MapCache& cache, df::coord point) { //TODO: check for constructions - return 1000000; + return 1; #if 0 df::tiletype* type = Maps::getTileType(point); df::enums::tiletype_material::tiletype_material ttmat = ENUM_ATTR(tiletype, material, *type); @@ -431,11 +702,11 @@ int32_t getDigCost(MapExtras::MapCache& cache, df::coord point) { } int32_t getDeconstructCost(df::coord point) { - return 1000000; + return 1; } int32_t getConstructCost(df::coord point) { - return 10000000; + return 1; } vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax) { @@ -455,6 +726,18 @@ vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCach Cost cost = 1; //if ( dz != 0 ) cost++; if ( Maps::canWalkBetween(point, neighbor) ) { + df::map_block* block2 = Maps::getTileBlock(neighbor); + bool building2 = block2->occupancy[point.x&0x0F][point.y&0x0F].bits.building == df::enums::tile_building_occ::Obstacle || block2->occupancy[point.x&0x0F][point.y&0x0F].bits.building == df::enums::tile_building_occ::Impassable; + if ( building2 ) { + df::building* building = Buildings::findAtTile(neighbor); + if ( building->getType() == df::enums::building_type::Hatch ) { + if ( building->isForbidden() ) { + //TODO: worry about destroying hatches with nowhere to stand + cost.cost[CostDimension::DestroyBuilding] += getDestroyCost(building); + } + } + } + Edge edge(point, neighbor, cost); result->push_back(edge); } else { @@ -474,86 +757,118 @@ vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCach 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::building* building1 = Buildings::findAtTile(point); - df::building* building2 = Buildings::findAtTile(neighbor); - if ( building2 != NULL && building2 != building1 ) { - cost.cost[1] += getDestroyCost(building2); - if ( dz != 0 ) - continue; + 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 ( shape2 == df::enums::tiletype_shape::EMPTY ) { - //cost.cost[3] += getConstructCost(neighbor); - continue; + if ( !passable2 && building2 && !sameBuilding ) { + df::building* b2 = Buildings::findAtTile(neighbor); + cost.cost[CostDimension::DestroyBuilding] += getDestroyCost(b2); + if ( dz != 0 ) + continue; } else { - if ( point.z == neighbor.z ) { - if ( ENUM_ATTR(tiletype_shape, walkable, shape2) ) { - if ( ENUM_ATTR(tiletype_shape, walkable, shape1 ) ) { - //exit(1); - //must be building impassible tile or something - //TODO: check - df::building* building = Buildings::findAtTile(neighbor); - if ( building != NULL ) - cost.cost[1]+=getDestroyCost(building); - else { - building = Buildings::findAtTile(point); - if ( building == NULL ) { - //out.print("%s, %d: (%d,%d,%d), (%d,%d,%d)\n", __FILE__, __LINE__, point.x,point.y,point.z, neighbor.x,neighbor.y,neighbor.z); - //exit(1); - //TODO: deal with the silly RAMP_TOP condition + + 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.cost[CostDimension::DestroyConstruction] += getDeconstructCost(neighbor); + } else { + cost.cost[CostDimension::Dig] += getDigCost(cache, neighbor); } } - //this is fine: only charge once for digging through a wall } else { - cost.cost[2] += getDigCost(cache, neighbor); - } - } else { - bool ascending = neighbor.z > point.z; - /*df::tiletype_shape temp; - if ( neighbor.z > point.z ) { - temp = shape1; - shape1 = shape2; - shape2 = temp; - }*/ - if ( point.x == neighbor.x && point.y == neighbor.y ) { - 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)) ) { - //must be a forbidden hatch: TODO: check - df::building* building = Buildings::findAtTile(dz < 0 ? point : neighbor); - if ( building != NULL ) { - cost.cost[1] += getDestroyCost(building); + //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 { - 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); + //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.cost[CostDimension::Dig] += 1; + } else { + continue; + } + } else { + //descending + if ( construction2 ) + continue; + if ( basic == df::enums::tiletype_shape_basic::Wall ) { + cost.cost[CostDimension::Dig] += 1; + } else { + continue; + } + } } } else { - //too complicated - continue; + //shape2 is not a stair + if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Wall ) { + if ( construction2 ) + continue; + cost.cost[CostDimension::Dig] += getDigCost(cache, neighbor); + } else if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Open ) { + continue; + //cost.cost[CostDimension::Construct] += getConstructCost(neighbor); + } else { + if ( ascending && ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Floor && !construction2 ) { + cost.cost[CostDimension::Dig] += 1; + } + continue; + } } } else { - //bad! - if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Wall ) { - cost.cost[2] += getDigCost(cache, neighbor); - } else if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Open ) { - cost.cost[3] += getConstructCost(neighbor); - } else { - continue; - } + //now we're talking about moving up and down nonvertically, ie with ramps + continue; } - } else { - //too complicated - continue; } } } + if ( cost.cost[CostDimension::DestroyBuilding] != 0 ) { + //out.print("Line %d: Destroy building from (%d,%d,%d), (%d,%d,%d)\n", __LINE__, point.x,point.y,point.z, neighbor.x,neighbor.y,neighbor.z); + } Edge edge(point, neighbor, cost); result->push_back(edge); } From 9cb3f196309457244133379c088addd0cb76fd5d Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 19 Dec 2012 20:44:22 -0500 Subject: [PATCH 18/51] Digging invaders: Minor changes. Still unstable. --- plugins/diggingInvaders/diggingInvaders.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index c4f9614a8..d72516c37 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -725,7 +725,7 @@ vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCach continue; Cost cost = 1; //if ( dz != 0 ) cost++; - if ( Maps::canWalkBetween(point, neighbor) ) { + if ( Maps::canStepBetween(point, neighbor) ) { df::map_block* block2 = Maps::getTileBlock(neighbor); bool building2 = block2->occupancy[point.x&0x0F][point.y&0x0F].bits.building == df::enums::tile_building_occ::Obstacle || block2->occupancy[point.x&0x0F][point.y&0x0F].bits.building == df::enums::tile_building_occ::Impassable; if ( building2 ) { From 46b9148277f256fe70128c275f080a89e593c3ca Mon Sep 17 00:00:00 2001 From: expwnent Date: Thu, 20 Dec 2012 00:35:45 -0500 Subject: [PATCH 19/51] Digging invaders: it still doesn't work, but now it's a lot simpler. --- plugins/diggingInvaders/diggingInvaders.cpp | 670 ++++++++++---------- 1 file changed, 323 insertions(+), 347 deletions(-) diff --git a/plugins/diggingInvaders/diggingInvaders.cpp b/plugins/diggingInvaders/diggingInvaders.cpp index d72516c37..b4d5ec438 100644 --- a/plugins/diggingInvaders/diggingInvaders.cpp +++ b/plugins/diggingInvaders/diggingInvaders.cpp @@ -52,24 +52,14 @@ #include #include #include - -#define HASHMAP 1 -#if HASHMAP #include #include -#endif - using namespace std; using namespace DFHack; using namespace df::enums; -int32_t digSpeed = 100; -int32_t destroySpeed = 100; -int32_t deconstructSpeed = 100; -int32_t constructSpeed = 100; - command_result diggingInvadersFunc(color_ostream &out, std::vector & parameters); DFHACK_PLUGIN("diggingInvaders"); @@ -101,106 +91,15 @@ enum CostDimension { costDim }; -const int32_t costBucket[] = { +const int64_t costWeight[] = { //Distance -0, -//DestroyBuilding 1, -//Dig +//Destroy Building 2, +//Dig +100, //DestroyConstruction -3, -}; - -struct Cost { - int32_t cost[costDim]; - Cost() { - memset(cost, 0, costDim*sizeof(int32_t)); - } - Cost( int32_t costIn[costDim] ) { - memcpy(cost, costIn, costDim*sizeof(int32_t)); - } - Cost(const Cost& c) { - memcpy(cost, c.cost, costDim*sizeof(int32_t)); - } - Cost( int32_t i ) { - memset(cost, 0, costDim*sizeof(int32_t)); - cost[CostDimension::Distance] = i; - } - - /* - int32_t workNeeded() const { - int32_t result = 0; - for ( int32_t a = 1; a < costDim; a++ ) { - result += cost[a]; - } - return result; - } - */ - - int32_t compare(const Cost& c) const { - int32_t table[costDim]; - memset(table, 0, costDim*sizeof(int32_t)); - for ( size_t a = 0; a < costDim; a++ ) { - table[costBucket[a]] += cost[a] - c.cost[a]; - } - for ( size_t a = 0; a < costDim; a++ ) { - if ( table[costDim-1-a] > 0 ) - return 1; - if ( table[costDim-1-a] < 0 ) - return -1; - } - return 0; - } - - bool operator>(const Cost& c) const { - return compare(c) > 0; - /* - for ( size_t a = 0; a < costDim; a++ ) { - if ( cost[costDim-1-a] != c.cost[costDim-1-a] ) - return cost[costDim-1-a] > c.cost[costDim-1-a]; - } - return false; - */ - } - - bool operator<(const Cost& c) const { - return compare(c) < 0; - /* - for ( size_t a = 0; a < costDim; a++ ) { - if ( cost[costDim-1-a] != c.cost[costDim-1-a] ) - return cost[costDim-1-a] < c.cost[costDim-1-a]; - } - return false; - */ - } - - bool operator==(const Cost& c) const { - return compare(c) == 0; - /* - for ( size_t a = 0; a < costDim; a++ ) { - if ( cost[a] != c.cost[a] ) - return false; - } - return true; - */ - } - - bool operator<=(const Cost& c) const { - return compare(c) <= 0; - } - - bool operator!=(const Cost& c) const { - return compare(c) != 0; - } - - Cost operator+(const Cost& c) const { - Cost result(*this); - for ( size_t a = 0; a < costDim; a++ ) { - result.cost[a] += c.cost[a]; - } - return result; - } +100, }; class Edge { @@ -208,8 +107,8 @@ public: //static map pointCost; df::coord p1; df::coord p2; - Cost cost; - Edge(df::coord p1In, df::coord p2In, Cost costIn): cost(costIn) { + int64_t cost; + Edge(df::coord p1In, df::coord p2In, int64_t costIn): cost(costIn) { if ( p2In < p1In ) { p1 = p2In; p2 = p1In; @@ -240,11 +139,7 @@ public: vector* getEdgeSet(color_ostream &out, df::coord point, MapExtras::MapCache& cache, int32_t xMax, int32_t yMax, int32_t zMax); -#if HASHMAP df::coord getRoot(df::coord point, unordered_map& rootMap); -#else -df::coord getRoot(df::coord point, map& rootMap); -#endif struct PointHash { size_t operator()(const df::coord c) const { @@ -254,17 +149,10 @@ struct PointHash { class PointComp { public: -#if HASHMAP - unordered_map *pointCost; - PointComp(unordered_map *p): pointCost(p) { - - } -#else - map *pointCost; - PointComp(map *p): pointCost(p) { + unordered_map *pointCost; + PointComp(unordered_map *p): pointCost(p) { } -#endif int32_t operator()(df::coord p1, df::coord p2) { if ( p1 == p2 ) return 0; @@ -276,8 +164,8 @@ public: return true; if ( i2 == pointCost->end() ) return false; - Cost c1 = (*i1).second; - Cost c2 = (*i2).second; + int64_t c1 = (*i1).second; + int64_t c2 = (*i2).second; if ( c1 != c2 ) return c1 < c2; return p1 < p2; @@ -297,17 +185,10 @@ command_result diggingInvadersFunc(color_ostream& out, std::vector& void doDiggingInvaders(color_ostream& out, void* ptr) { CoreSuspender suspend; -#if HASHMAP unordered_set invaderPts; unordered_set localPts; unordered_map parentMap; - unordered_map costMap; -#else - set invaderPts; - set localPts; - map parentMap; - map costMap; -#endif + unordered_map costMap; PointComp comp(&costMap); set fringe(comp); @@ -345,11 +226,8 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { out << firstInvader->pos.x << ", " << firstInvader->pos.y << ", " << firstInvader->pos.z << endl; int32_t localPtsFound = 0; -#if HASHMAP unordered_set closedSet; -#else - set closedSet; -#endif + unordered_map workNeeded; //non-walking work needed to get there clock_t t0 = clock(); clock_t totalEdgeTime = 0; @@ -367,18 +245,11 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { localPtsFound++; if ( localPtsFound >= localPts.size() ) break; - bool doBreak = false; - for ( int32_t a = 1; a < costDim; a++ ) { - if ( costMap[pt].cost[a] > 0 ) { - doBreak = true; - break; - } - } - if ( doBreak ) + if ( workNeeded.find(pt) != workNeeded.end() && workNeeded[pt] > 0 ) break; } - Cost& myCost = costMap[pt]; + int64_t myCost = costMap[pt]; clock_t edgeTime = clock(); vector* myEdges = getEdgeSet(out, pt, cache, xMax, yMax, zMax); totalEdgeTime += (clock() - edgeTime); @@ -391,7 +262,7 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { // continue; auto i = costMap.find(other); if ( i != costMap.end() ) { - Cost& cost = (*i).second; + int64_t cost = (*i).second; if ( cost <= myCost + e.cost ) { continue; } @@ -400,6 +271,7 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { costMap[other] = myCost + e.cost; fringe.insert(other); parentMap[other] = pt; + workNeeded[other] = (e.cost > 1 ? 1 : 0) + workNeeded[pt]; } delete myEdges; } @@ -417,17 +289,10 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { continue; if ( parentMap.find(pt) == parentMap.end() ) continue; - bool requireAction = false; - for ( int32_t a = 1; a < costDim; a++ ) { - if ( costMap[pt].cost[a] != 0 ) { - requireAction = true; - break; - } - } - if ( !requireAction ) + if ( workNeeded[pt] == 0 ) continue; while ( parentMap.find(pt) != parentMap.end() ) { - //out.print("(%d,%d,%d)\n", pt.x, pt.y, pt.z); + out.print("(%d,%d,%d)\n", pt.x, pt.y, pt.z); df::coord parent = parentMap[pt]; if ( pt.z < parent.z ) { requiresZNeg.insert(parent); @@ -436,13 +301,9 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { requiresZNeg.insert(pt); requiresZPos.insert(parent); } - Cost cost1 = costMap[pt]; - Cost cost2 = costMap[parent]; - cost1.cost[0] = 0; - cost2.cost[0] = 0; - if ( true || cost1 != cost2 ) { + //if ( workNeeded[pt] > workNeeded[parent] ) { importantEdges.push_front(Edge(pt,parent,0)); - } + //} pt = parent; } break; @@ -472,41 +333,57 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { bool didSomething = false; df::coord where; +out << __LINE__ << endl; for ( auto i = importantEdges.begin(); i != importantEdges.end(); i++ ) { +out << __LINE__ << endl; Edge e = *i; df::coord pt1 = e.p1; df::coord pt2 = e.p2; if ( costMap[e.p2] < costMap[e.p1] ) { +out << __LINE__ << endl; pt1 = e.p2; pt2 = e.p1; } +out << __LINE__ << endl; bool important = //requireZNeg.find(pt1) != requireZNeg.end() || //requireZPos.find(pt1) != requireZPos.end() || requiresZNeg.find(pt2) != requiresZNeg.end() || - requiresZPos.find(pt2) != requiresZPos.end(); + requiresZPos.find(pt2) != requiresZPos.end() || + Buildings::findAtTile(pt2) != NULL; if ( !important ) { - Cost c1 = costMap[pt1]; - Cost c2 = costMap[pt2]; - c1.cost[0] = 0; - c2.cost[0] = 0; - if ( c1 == c2 ) { +out << __LINE__ << endl; + if ( workNeeded[pt2] <= workNeeded[pt1] ) { +out << __LINE__ << endl; //definitely not important continue; } } +out << __LINE__ << endl; + if ( workNeeded[pt1] > 0 ) + continue; + df::map_block* block1 = Maps::getTileBlock(pt1); +out << __LINE__ << endl; 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]; //TODO: if actions required > 1, continue df::building* building = Buildings::findAtTile(pt2); +out << __LINE__ << endl; + if ( building != NULL && building->getType() == df::enums::building_type::Stockpile ) { +out << __LINE__ << endl; + continue; + } +out << __LINE__ << endl; if ( building != NULL && passable2 ) { - building = NULL; + //building = NULL; } +out << __LINE__ << endl; if ( building != NULL ) { - out.print("%s, line %d: Destroying building %d\n", __FILE__, __LINE__, building->id); +out << __LINE__ << endl; + out.print("%s, line %d: Destroying building %d at (%d,%d,%d)\n", __FILE__, __LINE__, building->id, pt2.x,pt2.y,pt2.z); //building->flags.bits.almost_deleted = true; df::job* job = new df::job; @@ -532,14 +409,17 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { didSomething = true; where = pt2; - continue; +out << __LINE__ << endl; + break; } else { +out << __LINE__ << endl; 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, *type1) == df::enums::tiletype_material::CONSTRUCTION; if ( construction2 ) { +out << __LINE__ << endl; out.print("%s, line %d. Removing construction (%d,%d,%d)\n", __FILE__, __LINE__, pt2.x,pt2.y,pt2.z); df::job* job = new df::job(); job->job_type = df::enums::job_type::RemoveConstruction; @@ -557,6 +437,7 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { Job::linkIntoWorld(job); didSomething = true; where = pt2; +out << __LINE__ << endl; break; } @@ -567,6 +448,7 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { break; }*/ +out << __LINE__ << endl; bool up = requiresZPos.find(pt2) != requiresZPos.end(); bool down = requiresZNeg.find(pt2) != requiresZNeg.end(); df::job* job = new df::job; @@ -619,6 +501,7 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { pick->skill_used = (df::enums::job_skill::job_skill)0; pick->maker = -1; df::itemdef_weaponst* itemdef = NULL; +out << __LINE__ << endl; 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" ) { @@ -626,16 +509,20 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { break; } } +out << __LINE__ << endl; if ( itemdef == NULL ) { out.print("%s, %d: null itemdef.\n", __FILE__, __LINE__); return; } +out << __LINE__ << endl; pick->subtype = itemdef; pick->sharpness = 5000; int32_t part = -1; part = firstInvader->body.unk_3c8; //weapon_bp +out << __LINE__ << endl; if ( part == -1 ) { +out << __LINE__ << endl; out.print("%s, %d: no grasp part.\n", __FILE__, __LINE__); return; } @@ -650,14 +537,17 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { Items::moveToInventory(cache, pick, firstInvader, df::unit_inventory_item::T_mode::Weapon, part); didSomething = true; where = pt2; +out << __LINE__ << endl; break; } } +out << __LINE__ << endl; out << "didSomething = " << didSomething << endl; if ( !didSomething ) return; +/* Cost cost = costMap[where]; float cost_tick = 0; cost_tick += cost.cost[CostDimension::Distance]; @@ -668,212 +558,298 @@ void doDiggingInvaders(color_ostream& out, void* ptr) { Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("diggingInvaders"); //EventManager::registerTick(handle, (int32_t)cost_tick, me); df::global::world->reindex_pathfinding = true; +*/ } +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; + } + } -int32_t getDestroyCost(df::building* building) { - return 1; -#if 0 - if ( building->mat_type != 0 ) { - cerr << "Error " << __FILE__ << ", " << __LINE__ << endl; - exit(1); + if ( Maps::canStepBetween(pt1, pt2) ) { + if ( building2 && !sameBuilding ) { + cost += costWeight[CostDimension::DestroyBuilding]; + } + return cost; } - df::inorganic_raw* mat = df::global::world->raws.inorganics[building->mat_index]; - int32_t str = mat->material.strength.fracture[df::enums::strain_type::IMPACT]; - return str / destroySpeed; -#endif -} -int32_t getDigCost(MapExtras::MapCache& cache, df::coord point) { - //TODO: check for constructions - return 1; -#if 0 - df::tiletype* type = Maps::getTileType(point); - df::enums::tiletype_material::tiletype_material ttmat = ENUM_ATTR(tiletype, material, *type); - if ( ttmat == df::enums::tiletype_material::CONSTRUCTION ) { - //construction stuff - return 1; - } else if ( ttmat == df::enums::tiletype_material::SOIL ) { - + if ( shape2 == df::enums::tiletype_shape::EMPTY ) { + return -1; } - - return 1; -#endif -} -int32_t getDeconstructCost(df::coord point) { - 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; + } -int32_t getConstructCost(df::coord point) { - return 1; -} + if ( shape2 == df::enums::tiletype_shape::TREE ) { + 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); + 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; + } - 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; - Cost cost = 1; - //if ( dz != 0 ) cost++; - if ( Maps::canStepBetween(point, neighbor) ) { - df::map_block* block2 = Maps::getTileBlock(neighbor); - bool building2 = block2->occupancy[point.x&0x0F][point.y&0x0F].bits.building == df::enums::tile_building_occ::Obstacle || block2->occupancy[point.x&0x0F][point.y&0x0F].bits.building == df::enums::tile_building_occ::Impassable; - if ( building2 ) { - df::building* building = Buildings::findAtTile(neighbor); - if ( building->getType() == df::enums::building_type::Hatch ) { - if ( building->isForbidden() ) { - //TODO: worry about destroying hatches with nowhere to stand - cost.cost[CostDimension::DestroyBuilding] += getDestroyCost(building); - } - } - } - - Edge edge(point, neighbor, cost); - result->push_back(edge); - } else { + //moving diagonally + return -1; #if 0 - { - cost.cost[1] = 1; - result->push_back(Edge(point,neighbor,cost)); - continue; - } -#endif - //cost.cost[1] = 1; - //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 ( 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.cost[CostDimension::DestroyBuilding] += getDestroyCost(b2); - if ( dz != 0 ) - continue; - } else { + 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; + 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 { - 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 + 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 { - //this is fine: only charge once for digging through a wall, etc + continue; } } else { - //passable2 is false - if ( construction2 ) { - //must deconstruct orthogonally - if ( dx*dy != 0 ) - continue; - cost.cost[CostDimension::DestroyConstruction] += getDeconstructCost(neighbor); + //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 { - cost.cost[CostDimension::Dig] += getDigCost(cache, neighbor); + continue; } } - } 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.cost[CostDimension::Dig] += 1; - } else { - continue; - } - } else { - //descending - if ( construction2 ) - continue; - if ( basic == df::enums::tiletype_shape_basic::Wall ) { - cost.cost[CostDimension::Dig] += 1; - } 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.cost[CostDimension::Dig] += getDigCost(cache, neighbor); - } else if ( ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Open ) { - continue; - //cost.cost[CostDimension::Construct] += getConstructCost(neighbor); - } else { - if ( ascending && ENUM_ATTR(tiletype_shape, basic_shape, shape2) == df::enums::tiletype_shape_basic::Floor && !construction2 ) { - cost.cost[CostDimension::Dig] += 1; - } - continue; - } - } - } else { - //now we're talking about moving up and down nonvertically, ie with ramps + } + } 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; } - - if ( cost.cost[CostDimension::DestroyBuilding] != 0 ) { - //out.print("Line %d: Destroy building from (%d,%d,%d), (%d,%d,%d)\n", __LINE__, point.x,point.y,point.z, neighbor.x,neighbor.y,neighbor.z); - } - Edge edge(point, neighbor, cost); - result->push_back(edge); } } } + } + 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; From 144e0b4dcb91bdb0d6787c4b9c5aad0eaf9b8856 Mon Sep 17 00:00:00 2001 From: expwnent Date: Wed, 2 Jan 2013 11:26:30 -0500 Subject: [PATCH 20/51] Digging Invaders: merged eventManager and recent. --- CMakeLists.txt | 13 +- Lua API.html | 81 +- Lua API.rst | 87 +- NEWS | 37 +- Readme.html | 961 +++++++++++------ Readme.rst | 414 +++++++- dfhack.init-example | 57 +- images/assign-rack.png | Bin 0 -> 3892 bytes images/automaterial-mat.png | Bin 0 -> 5187 bytes images/automaterial-pos.png | Bin 0 -> 3007 bytes images/guide-path.png | Bin 0 -> 4871 bytes images/liquids.png | Bin 0 -> 3676 bytes images/manipulator.png | Bin 0 -> 7724 bytes images/manipulator2.png | Bin 0 -> 7558 bytes images/mechanisms.png | Bin 0 -> 3741 bytes images/power-meter.png | Bin 0 -> 4435 bytes images/rename-bld.png | Bin 0 -> 6667 bytes images/rename-prof.png | Bin 0 -> 6190 bytes images/room-list.png | Bin 0 -> 4839 bytes images/search-stockpile.png | Bin 0 -> 5971 bytes images/search.png | Bin 0 -> 4509 bytes images/siege-engine.png | Bin 0 -> 4023 bytes images/tweak-mil-color.png | Bin 0 -> 6967 bytes images/tweak-plate.png | Bin 0 -> 3365 bytes images/workflow-new1.png | Bin 0 -> 6775 bytes images/workflow-new2.png | Bin 0 -> 7793 bytes images/workflow-status.png | Bin 0 -> 5118 bytes images/workflow.png | Bin 0 -> 5779 bytes images/workshop-job-item.png | Bin 0 -> 4806 bytes images/workshop-job-material.png | Bin 0 -> 5553 bytes images/workshop-job.png | Bin 0 -> 6461 bytes library/CMakeLists.txt | 44 +- library/Core.cpp | 57 +- library/Hooks-windows.cpp | 66 +- library/LuaApi.cpp | 22 + library/LuaTools.cpp | 12 + library/MacPool.mm | 14 +- library/Process-darwin.cpp | 96 +- library/Process-linux.cpp | 36 +- library/Process-windows.cpp | 78 +- library/Types.cpp | 18 + library/VersionInfoFactory.cpp | 4 +- library/include/Console.h | 14 +- library/include/DFHack.h | 1 - library/include/DataDefs.h | 2 +- library/include/LuaTools.h | 15 + library/include/MemAccess.h | 25 +- library/include/ModuleFactory.h | 1 - library/include/TileTypes.h | 8 +- library/include/Types.h | 26 + library/include/VersionInfo.h | 2 +- library/include/modules/Buildings.h | 3 + library/include/modules/Graphic.h | 90 +- library/include/modules/Gui.h | 5 + library/include/modules/Job.h | 5 + library/include/modules/MapCache.h | 16 +- library/include/modules/Maps.h | 1 - library/include/modules/Materials.h | 8 +- library/include/modules/Screen.h | 132 +++ library/include/modules/Units.h | 5 + library/include/modules/Vegetation.h | 70 -- library/include/modules/World.h | 49 + library/lua/binpatch.lua | 121 +++ library/lua/dfhack.lua | 43 +- library/lua/gui.lua | 20 +- library/lua/gui/dialogs.lua | 22 +- library/lua/gui/materials.lua | 22 +- library/lua/gui/widgets.lua | 119 ++- library/lua/memscan.lua | 22 +- library/modules/Buildings.cpp | 16 +- library/modules/Graphic.cpp | 74 +- library/modules/Items.cpp | 48 +- library/modules/Job.cpp | 35 +- library/modules/Maps.cpp | 21 +- library/modules/Materials.cpp | 6 +- library/modules/Screen.cpp | 74 +- library/modules/Translation.cpp | 15 + library/modules/Units.cpp | 61 +- library/modules/Vegetation.cpp | 85 -- library/modules/World.cpp | 6 +- library/xml | 2 +- patches/v0.34.11 SDL/armorstand-capacity.dif | 142 +++ patches/v0.34.11 SDL/custom-reagent-size.dif | 91 ++ patches/v0.34.11 SDL/deconstruct-heapfall.dif | 61 ++ patches/v0.34.11 SDL/deconstruct-teleport.dif | 104 ++ .../v0.34.11 SDL/hospital-overstocking.dif | 62 ++ patches/v0.34.11 SDL/training-ammo.dif | 83 ++ patches/v0.34.11 SDL/weaponrack-unassign.dif | 61 ++ .../v0.34.11 linux/armorstand-capacity.dif | 147 +++ .../v0.34.11 linux/custom-reagent-size.dif | 40 + .../v0.34.11 linux/deconstruct-heapfall.dif | 83 ++ .../v0.34.11 linux/deconstruct-teleport.dif | 139 +++ .../v0.34.11 linux/hospital-overstocking.dif | 60 ++ patches/v0.34.11 linux/training-ammo.dif | 85 ++ .../v0.34.11 linux/weaponrack-unassign.dif | 45 + plugins/CMakeLists.txt | 2 + plugins/Dfusion/CMakeLists.txt | 5 +- plugins/Dfusion/dfusion.cpp | 227 +--- plugins/Dfusion/include/OutFile.h | 2 +- plugins/Dfusion/include/functioncall.h | 26 - plugins/Dfusion/include/lua_FunctionCall.h | 14 - plugins/Dfusion/include/lua_Misc.h | 1 - plugins/Dfusion/include/lua_Offsets.h | 13 - plugins/Dfusion/include/lua_VersionInfo.h | 12 - plugins/Dfusion/luafiles/adv_tools/init.lua | 133 --- plugins/Dfusion/luafiles/adv_tools/plugin.lua | 3 - plugins/Dfusion/luafiles/buildingpatterns.lua | 24 - plugins/Dfusion/luafiles/common.lua | 548 ---------- plugins/Dfusion/luafiles/editor.lua | 150 --- plugins/Dfusion/luafiles/embark/a.out | 0 plugins/Dfusion/luafiles/embark/build.bat | 1 - plugins/Dfusion/luafiles/embark/init.lua | 83 -- plugins/Dfusion/luafiles/embark/plugin.lua | 5 - plugins/Dfusion/luafiles/embark/races.txt | 9 - .../Dfusion/luafiles/friendship/friendship.o | Bin 722 -> 0 bytes plugins/Dfusion/luafiles/friendship/init.lua | 45 - .../Dfusion/luafiles/friendship/install.lua | 35 - plugins/Dfusion/luafiles/friendship/patch.lua | 57 - .../Dfusion/luafiles/friendship/plugin.lua | 18 - plugins/Dfusion/luafiles/friendship/races.txt | 8 - .../luafiles/friendship_civ/compile.bat | 1 - .../luafiles/friendship_civ/friendship_c.asm | 41 - .../luafiles/friendship_civ/friendship_c.o | Bin 462 -> 0 bytes .../Dfusion/luafiles/friendship_civ/init.lua | 89 -- .../luafiles/friendship_civ/plugin.lua | 57 - plugins/Dfusion/luafiles/init.lua | 97 -- plugins/Dfusion/luafiles/itempatterns.lua | 62 -- plugins/Dfusion/luafiles/items/plugin.lua | 212 ---- plugins/Dfusion/luafiles/migrants/compile.bat | 1 - plugins/Dfusion/luafiles/migrants/init.lua | 62 -- .../Dfusion/luafiles/migrants/migrants.asm | 20 - plugins/Dfusion/luafiles/migrants/migrants.o | Bin 336 -> 0 bytes plugins/Dfusion/luafiles/migrants/plugin.lua | 5 - plugins/Dfusion/luafiles/migrants/races.txt | 29 - plugins/Dfusion/luafiles/offsets/plugin.lua | 2 - plugins/Dfusion/luafiles/offsets_misc.lua | 48 - plugins/Dfusion/luafiles/patterns.lua | 248 ----- .../luafiles/patterns/supplementary.xml | 7 - .../luafiles/patterns/xml_angavrilov.lua | 55 - plugins/Dfusion/luafiles/patterns2.lua | 29 - .../Dfusion/luafiles/simple_embark/plugin.lua | 15 - plugins/Dfusion/luafiles/tools/init.lua | 497 --------- plugins/Dfusion/luafiles/tools/plugin.lua | 8 - plugins/Dfusion/luafiles/triggers/compile.bat | 1 - .../Dfusion/luafiles/triggers/functions.lua | 20 - .../luafiles/triggers/functions_menu.lua | 12 - plugins/Dfusion/luafiles/triggers/plugin.lua | 107 -- .../Dfusion/luafiles/triggers/triggers.asm | 68 -- plugins/Dfusion/luafiles/triggers/triggers.o | Bin 720 -> 0 bytes .../luafiles/triggers/universalfunc.asm | 14 - plugins/Dfusion/luafiles/utils.lua | 1 - plugins/Dfusion/luafiles/xml_struct.lua | 151 --- plugins/Dfusion/luafiles/xml_types.lua | 734 ------------- .../Dfusion/luafiles/xml_types_windows.lua | 159 --- plugins/Dfusion/src/OutFile.cpp | 18 +- plugins/Dfusion/src/functioncall.cpp | 121 --- plugins/Dfusion/src/lua_FunctionCall.cpp | 39 - plugins/Dfusion/src/lua_Misc.cpp | 99 +- plugins/Dfusion/src/lua_Offsets.cpp | 190 ---- plugins/Dfusion/src/lua_VersionInfo.cpp | 115 -- plugins/advtools.cpp | 8 +- plugins/autodump.cpp | 8 +- plugins/autolabor.cpp | 12 +- plugins/automaterial.cpp | 378 +++++++ plugins/cleaners.cpp | 1 + plugins/cleanowned.cpp | 2 +- plugins/devel/memview.cpp | 242 +++-- plugins/devel/nestboxes.cpp | 74 +- plugins/devel/rprobe.cpp | 24 +- plugins/devel/stockcheck.cpp | 366 +++---- plugins/devel/stripcaged.cpp | 68 +- plugins/devel/tiles.cpp | 1 - plugins/dfstream.cpp | 2 +- plugins/diggingInvaders/diggingInvaders.cpp | 14 +- plugins/filltraffic.cpp | 2 +- plugins/fix-armory.cpp | 38 +- plugins/forceequip.cpp | 682 ++++++------ plugins/getplants.cpp | 2 +- plugins/liquids.cpp | 1 - plugins/lua/dfusion.lua | 240 +++++ plugins/lua/dfusion/adv_tools.lua | 116 ++ plugins/lua/dfusion/embark.lua | 124 +++ .../luafiles/embark => lua/dfusion}/embark.o | Bin 348 -> 369 bytes plugins/lua/dfusion/friendship.lua | 112 ++ plugins/lua/dfusion/friendship.o | Bin 0 -> 854 bytes .../dfusion/srcs}/compile.bat | 0 .../embark => lua/dfusion/srcs}/embark.asm | 6 +- .../dfusion/srcs}/friendship.asm | 10 +- plugins/lua/dfusion/tools.lua | 243 +++++ plugins/lua/reactionhooks.lua | 13 + plugins/lua/workflow.lua | 41 +- plugins/manipulator.cpp | 78 +- plugins/mapexport/mapexport.cpp | 1 + plugins/plants.cpp | 12 +- plugins/prospector.cpp | 1 + plugins/reactionhooks.cpp | 322 ++++++ plugins/ruby/README | 5 +- plugins/ruby/building.rb | 4 +- plugins/ruby/codegen.pl | 18 +- plugins/ruby/item.rb | 2 +- plugins/ruby/map.rb | 62 +- plugins/ruby/ruby-autogen-defs.rb | 71 +- plugins/ruby/ruby.cpp | 110 +- plugins/ruby/ruby.rb | 67 +- plugins/ruby/unit.rb | 210 +++- plugins/search.cpp | 133 ++- plugins/showmood.cpp | 4 +- plugins/steam-engine.cpp | 16 +- plugins/tiletypes.cpp | 1 - plugins/tweak.cpp | 289 ++++- plugins/workflow.cpp | 301 +++++- plugins/zone.cpp | 202 ++-- scripts/autofarm.rb | 165 +++ scripts/autounsuspend.rb | 58 + scripts/binpatch.lua | 44 + scripts/create-items.rb | 161 +++ scripts/deathcause.rb | 45 +- scripts/devel/inspect-screen.lua | 103 ++ scripts/dfusion.lua | 14 + scripts/embark.lua | 53 + scripts/fix/loyaltycascade.rb | 6 +- scripts/fix/stable-temp.lua | 10 +- scripts/growcrops.rb | 4 +- scripts/gui/assign-rack.lua | 26 +- scripts/gui/companion-order.lua | 470 +++++++++ scripts/gui/gm-editor.lua | 245 +++++ scripts/gui/workflow.lua | 998 +++++++++++++++--- scripts/lever.rb | 120 +++ scripts/lua.lua | 46 + scripts/magmasource.rb | 2 +- scripts/region-pops.lua | 34 + scripts/slayrace.rb | 46 +- scripts/soundsense-season.lua | 26 + scripts/stripcaged.rb | 204 ++++ scripts/superdwarf.rb | 2 +- scripts/unsuspend.rb | 17 + 236 files changed, 10121 insertions(+), 6962 deletions(-) create mode 100644 images/assign-rack.png create mode 100644 images/automaterial-mat.png create mode 100644 images/automaterial-pos.png create mode 100644 images/guide-path.png create mode 100644 images/liquids.png create mode 100644 images/manipulator.png create mode 100644 images/manipulator2.png create mode 100644 images/mechanisms.png create mode 100644 images/power-meter.png create mode 100644 images/rename-bld.png create mode 100644 images/rename-prof.png create mode 100644 images/room-list.png create mode 100644 images/search-stockpile.png create mode 100644 images/search.png create mode 100644 images/siege-engine.png create mode 100644 images/tweak-mil-color.png create mode 100644 images/tweak-plate.png create mode 100644 images/workflow-new1.png create mode 100644 images/workflow-new2.png create mode 100644 images/workflow-status.png create mode 100644 images/workflow.png create mode 100644 images/workshop-job-item.png create mode 100644 images/workshop-job-material.png create mode 100644 images/workshop-job.png delete mode 100644 library/include/modules/Vegetation.h create mode 100644 library/lua/binpatch.lua delete mode 100644 library/modules/Vegetation.cpp create mode 100644 patches/v0.34.11 SDL/armorstand-capacity.dif create mode 100644 patches/v0.34.11 SDL/custom-reagent-size.dif create mode 100644 patches/v0.34.11 SDL/deconstruct-heapfall.dif create mode 100644 patches/v0.34.11 SDL/deconstruct-teleport.dif create mode 100644 patches/v0.34.11 SDL/hospital-overstocking.dif create mode 100644 patches/v0.34.11 SDL/training-ammo.dif create mode 100644 patches/v0.34.11 SDL/weaponrack-unassign.dif create mode 100644 patches/v0.34.11 linux/armorstand-capacity.dif create mode 100644 patches/v0.34.11 linux/custom-reagent-size.dif create mode 100644 patches/v0.34.11 linux/deconstruct-heapfall.dif create mode 100644 patches/v0.34.11 linux/deconstruct-teleport.dif create mode 100644 patches/v0.34.11 linux/hospital-overstocking.dif create mode 100644 patches/v0.34.11 linux/training-ammo.dif create mode 100644 patches/v0.34.11 linux/weaponrack-unassign.dif delete mode 100644 plugins/Dfusion/include/functioncall.h delete mode 100644 plugins/Dfusion/include/lua_FunctionCall.h delete mode 100644 plugins/Dfusion/include/lua_Offsets.h delete mode 100644 plugins/Dfusion/include/lua_VersionInfo.h delete mode 100644 plugins/Dfusion/luafiles/adv_tools/init.lua delete mode 100644 plugins/Dfusion/luafiles/adv_tools/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/buildingpatterns.lua delete mode 100644 plugins/Dfusion/luafiles/common.lua delete mode 100644 plugins/Dfusion/luafiles/editor.lua delete mode 100644 plugins/Dfusion/luafiles/embark/a.out delete mode 100644 plugins/Dfusion/luafiles/embark/build.bat delete mode 100644 plugins/Dfusion/luafiles/embark/init.lua delete mode 100644 plugins/Dfusion/luafiles/embark/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/embark/races.txt delete mode 100644 plugins/Dfusion/luafiles/friendship/friendship.o delete mode 100644 plugins/Dfusion/luafiles/friendship/init.lua delete mode 100644 plugins/Dfusion/luafiles/friendship/install.lua delete mode 100644 plugins/Dfusion/luafiles/friendship/patch.lua delete mode 100644 plugins/Dfusion/luafiles/friendship/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/friendship/races.txt delete mode 100644 plugins/Dfusion/luafiles/friendship_civ/compile.bat delete mode 100644 plugins/Dfusion/luafiles/friendship_civ/friendship_c.asm delete mode 100644 plugins/Dfusion/luafiles/friendship_civ/friendship_c.o delete mode 100644 plugins/Dfusion/luafiles/friendship_civ/init.lua delete mode 100644 plugins/Dfusion/luafiles/friendship_civ/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/init.lua delete mode 100644 plugins/Dfusion/luafiles/itempatterns.lua delete mode 100644 plugins/Dfusion/luafiles/items/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/migrants/compile.bat delete mode 100644 plugins/Dfusion/luafiles/migrants/init.lua delete mode 100644 plugins/Dfusion/luafiles/migrants/migrants.asm delete mode 100644 plugins/Dfusion/luafiles/migrants/migrants.o delete mode 100644 plugins/Dfusion/luafiles/migrants/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/migrants/races.txt delete mode 100644 plugins/Dfusion/luafiles/offsets/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/offsets_misc.lua delete mode 100644 plugins/Dfusion/luafiles/patterns.lua delete mode 100644 plugins/Dfusion/luafiles/patterns/supplementary.xml delete mode 100644 plugins/Dfusion/luafiles/patterns/xml_angavrilov.lua delete mode 100644 plugins/Dfusion/luafiles/patterns2.lua delete mode 100644 plugins/Dfusion/luafiles/simple_embark/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/tools/init.lua delete mode 100644 plugins/Dfusion/luafiles/tools/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/triggers/compile.bat delete mode 100644 plugins/Dfusion/luafiles/triggers/functions.lua delete mode 100644 plugins/Dfusion/luafiles/triggers/functions_menu.lua delete mode 100644 plugins/Dfusion/luafiles/triggers/plugin.lua delete mode 100644 plugins/Dfusion/luafiles/triggers/triggers.asm delete mode 100644 plugins/Dfusion/luafiles/triggers/triggers.o delete mode 100644 plugins/Dfusion/luafiles/triggers/universalfunc.asm delete mode 100644 plugins/Dfusion/luafiles/utils.lua delete mode 100644 plugins/Dfusion/luafiles/xml_struct.lua delete mode 100644 plugins/Dfusion/luafiles/xml_types.lua delete mode 100644 plugins/Dfusion/luafiles/xml_types_windows.lua delete mode 100644 plugins/Dfusion/src/functioncall.cpp delete mode 100644 plugins/Dfusion/src/lua_FunctionCall.cpp delete mode 100644 plugins/Dfusion/src/lua_Offsets.cpp delete mode 100644 plugins/Dfusion/src/lua_VersionInfo.cpp create mode 100644 plugins/automaterial.cpp create mode 100644 plugins/lua/dfusion.lua create mode 100644 plugins/lua/dfusion/adv_tools.lua create mode 100644 plugins/lua/dfusion/embark.lua rename plugins/{Dfusion/luafiles/embark => lua/dfusion}/embark.o (55%) create mode 100644 plugins/lua/dfusion/friendship.lua create mode 100644 plugins/lua/dfusion/friendship.o rename plugins/{Dfusion/luafiles/friendship => lua/dfusion/srcs}/compile.bat (100%) rename plugins/{Dfusion/luafiles/embark => lua/dfusion/srcs}/embark.asm (61%) rename plugins/{Dfusion/luafiles/friendship => lua/dfusion/srcs}/friendship.asm (83%) create mode 100644 plugins/lua/dfusion/tools.lua create mode 100644 plugins/lua/reactionhooks.lua create mode 100644 plugins/reactionhooks.cpp create mode 100644 scripts/autofarm.rb create mode 100644 scripts/autounsuspend.rb create mode 100644 scripts/binpatch.lua create mode 100644 scripts/create-items.rb create mode 100644 scripts/devel/inspect-screen.lua create mode 100644 scripts/dfusion.lua create mode 100644 scripts/embark.lua create mode 100644 scripts/gui/companion-order.lua create mode 100644 scripts/gui/gm-editor.lua create mode 100644 scripts/lever.rb create mode 100644 scripts/lua.lua create mode 100644 scripts/soundsense-season.lua create mode 100644 scripts/stripcaged.rb create mode 100644 scripts/unsuspend.rb diff --git a/CMakeLists.txt b/CMakeLists.txt index 41c38bd44..50d50d18d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,7 +121,7 @@ ADD_DEFINITIONS(-DPROTOBUF_USE_DLLS) ADD_DEFINITIONS(-DLUA_BUILD_AS_DLL) if(APPLE) - add_definitions(-D_DARWIN) + add_definitions(-D_DARWIN) elseif(UNIX) add_definitions(-D_LINUX) elseif(WIN32) @@ -172,6 +172,7 @@ IF(BUILD_LIBRARY) add_subdirectory (library) ## install the default documentation files install(FILES LICENSE "Lua API.html" Readme.html Compile.html Contributors.html DESTINATION ${DFHACK_USERDOC_DESTINATION}) + install(DIRECTORY images DESTINATION ${DFHACK_USERDOC_DESTINATION}) endif() #build the plugins @@ -181,11 +182,11 @@ endif() # Packaging with CPack! IF(UNIX) - if(APPLE) - SET(CPACK_GENERATOR "ZIP") - else() - SET(CPACK_GENERATOR "TGZ") - endif() + if(APPLE) + SET(CPACK_GENERATOR "ZIP") + else() + SET(CPACK_GENERATOR "TGZ") + endif() ELSEIF(WIN32) SET(CPACK_GENERATOR "ZIP") ENDIF() diff --git a/Lua API.html b/Lua API.html index 226afa98a..e5318165d 100644 --- a/Lua API.html +++ b/Lua API.html @@ -404,7 +404,10 @@ ul.auto-toc {
  • sort
  • -
  • Scripts
  • +
  • Scripts +
  • The current version of DFHack has extensive support for @@ -1034,6 +1037,9 @@ can be omitted.

  • dfhack.getHackPath()

    Returns the dfhack directory path, i.e. ".../df/hack/".

  • +
  • dfhack.getSavePath()

    +

    Returns the path to the current save directory, or nil if no save loaded.

    +
  • dfhack.getTickCount()

    Returns the tick count in ms, exactly as DF ui uses.

  • @@ -1109,6 +1115,12 @@ above operations accordingly. If enabled, pauses and zooms to position.

  • dfhack.job.printItemDetails(jobitem,idx)

    Prints info about the job item.

  • +
  • dfhack.job.getGeneralRef(job, type)

    +

    Searches for a general_ref with the given type.

    +
  • +
  • dfhack.job.getSpecificRef(job, type)

    +

    Searches for a specific_ref with the given type.

    +
  • dfhack.job.getHolder(job)

    Returns the building holding the job.

  • @@ -1147,6 +1159,12 @@ the flags in the job item.

  • dfhack.units.getPosition(unit)

    Returns true x,y,z of the unit, or nil if invalid; may be not equal to unit.pos if caged.

  • +
  • dfhack.units.getGeneralRef(unit, type)

    +

    Searches for a general_ref with the given type.

    +
  • +
  • dfhack.units.getSpecificRef(unit, type)

    +

    Searches for a specific_ref with the given type.

    +
  • dfhack.units.getContainer(unit)

    Returns the container (cage) item or nil.

  • @@ -1209,6 +1227,9 @@ is true, subtracts the rust penalty.

  • dfhack.units.getEffectiveSkill(unit, skill)

    Computes the effective rating for the given skill, taking into account exhaustion, pain etc.

  • +
  • dfhack.units.getExperience(unit, skill[, total])

    +

    Returns the experience value for the given skill. If total is true, adds experience implied by the current rating.

    +
  • dfhack.units.computeMovementSpeed(unit)

    Computes number of frames * 100 it takes the unit to move in its current state of mind and body.

  • @@ -1403,6 +1424,12 @@ burrows, or the presence of invaders.

    Buildings module

      +
    • dfhack.buildings.getGeneralRef(building, type)

      +

      Searches for a general_ref with the given type.

      +
    • +
    • dfhack.buildings.getSpecificRef(building, type)

      +

      Searches for a specific_ref with the given type.

      +
    • dfhack.buildings.setOwner(item,unit)

      Replaces the owner of the building. If unit is nil, removes ownership. Returns false in case of error.

      @@ -1728,7 +1755,10 @@ options; if multiple interpretations exist, the table will contain multiple keys

      Maps to an integer in range 0-255. Duplicates a separate "STRING_A???" code for convenience.

      _MOUSE_L, _MOUSE_R
      -

      If the left or right mouse button is pressed.

      +

      If the left or right mouse button is being pressed.

      +
      +
      _MOUSE_L_DOWN, _MOUSE_R_DOWN
      +

      If the left or right mouse button was just pressed.

      If this method is omitted, the screen is dismissed on receival of the LEAVESCREEN key.

      @@ -1763,9 +1793,17 @@ global environment, persistent between calls to the script.

    • dfhack.internal.getVTable(name)

      Returns the pre-extracted vtable address name, or nil.

    • +
    • dfhack.internal.getImageBase()

      +

      Returns the mmap base of the executable.

      +
    • dfhack.internal.getRebaseDelta()

      Returns the ASLR rebase offset of the DF executable.

    • +
    • dfhack.internal.adjustOffset(offset[,to_file])

      +

      Returns the re-aligned offset, or nil if invalid. +If to_file is true, the offset is adjusted from memory to file. +This function returns the original value everywhere except windows.

      +
    • dfhack.internal.getMemRanges()

      Returns a sequence of tables describing virtual memory ranges of the process.

    • @@ -2758,6 +2796,14 @@ before rendering the token.

    • token.tile = pen

      Specifies a pen to paint as one tile before the main part of the token.

    • +
    • token.width = ...

      +

      If specified either as a value or a callback, the text field is padded +or truncated to the specified number.

      +
    • +
    • token.pad_char = '?'

      +

      If specified together with width, the padding area is filled with +this character instead of just being skipped over.

      +
    • token.key = '...'

      Specifies the keycode associated with the token. The string description of the key binding is added to the text content of the token.

      @@ -2819,11 +2865,16 @@ this may be extended with mouse click support.

      icon_pen:Default pen for icons. -on_select:Selection change callback; called as on_select(index,choice). +on_select:Selection change callback; called as on_select(index,choice). +This is also called with nil arguments if setChoices is called +with an empty list. on_submit:Enter key callback; if specified, the list reacts to the key and calls it as on_submit(index,choice). +on_submit2:Shift-Enter key callback; if specified, the list reacts to the key +and calls it as on_submit2(index,choice). + row_height:Height of every row in text lines. icon_width:If not nil, the specified number of character columns @@ -2879,6 +2930,9 @@ with the following fields:

    • list:submit()

      Call the on_submit callback, as if the Enter key was handled.

    • +
    • list:submit2()

      +

      Call the on_submit2 callback, as if the Shift-Enter key was handled.

      +
    @@ -2893,6 +2947,8 @@ supports:

    edit_pen:If specified, used instead of cursor_pen for the edit field. +edit_below:If true, the edit field is placed below the list instead of above. + not_found_label:  Specifies the text of the label shown when no items match the filter. @@ -3014,6 +3070,25 @@ The name argument should be the name stem, as

    Note that this function lets errors propagate to the caller.

    +
    +

    Save init script

    +

    If a save directory contains a file called raw/init.lua, it is +automatically loaded and executed every time the save is loaded. It +can also define the following functions to be called by dfhack:

    +
      +
    • function onStateChange(op) ... end

      +

      Automatically called from the regular onStateChange event as long +as the save is still loaded. This avoids the need to install a hook +into the global dfhack.onStateChange table, with associated +cleanup concerns.

      +
    • +
    • function onUnload() ... end

      +

      Called when the save containing the script is unloaded. This function +should clean up any global hooks installed by the script.

      +
    • +
    +

    Within the init script, the path to the save directory is available as SAVE_PATH.

    +
    diff --git a/Lua API.rst b/Lua API.rst index d06e3d2e6..0ec464f76 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -741,6 +741,10 @@ can be omitted. Returns the dfhack directory path, i.e. ``".../df/hack/"``. +* ``dfhack.getSavePath()`` + + Returns the path to the current save directory, or *nil* if no save loaded. + * ``dfhack.getTickCount()`` Returns the tick count in ms, exactly as DF ui uses. @@ -833,6 +837,14 @@ Job module Prints info about the job item. +* ``dfhack.job.getGeneralRef(job, type)`` + + Searches for a general_ref with the given type. + +* ``dfhack.job.getSpecificRef(job, type)`` + + Searches for a specific_ref with the given type. + * ``dfhack.job.getHolder(job)`` Returns the building holding the job. @@ -879,6 +891,14 @@ Units module Returns true *x,y,z* of the unit, or *nil* if invalid; may be not equal to unit.pos if caged. +* ``dfhack.units.getGeneralRef(unit, type)`` + + Searches for a general_ref with the given type. + +* ``dfhack.units.getSpecificRef(unit, type)`` + + Searches for a specific_ref with the given type. + * ``dfhack.units.getContainer(unit)`` Returns the container (cage) item or *nil*. @@ -954,6 +974,10 @@ Units module Computes the effective rating for the given skill, taking into account exhaustion, pain etc. +* ``dfhack.units.getExperience(unit, skill[, total])`` + + Returns the experience value for the given skill. If ``total`` is true, adds experience implied by the current rating. + * ``dfhack.units.computeMovementSpeed(unit)`` Computes number of frames * 100 it takes the unit to move in its current state of mind and body. @@ -1198,6 +1222,14 @@ Burrows module Buildings module ---------------- +* ``dfhack.buildings.getGeneralRef(building, type)`` + + Searches for a general_ref with the given type. + +* ``dfhack.buildings.getSpecificRef(building, type)`` + + Searches for a specific_ref with the given type. + * ``dfhack.buildings.setOwner(item,unit)`` Replaces the owner of the building. If unit is *nil*, removes ownership. @@ -1582,7 +1614,10 @@ Supported callbacks and fields are: Maps to an integer in range 0-255. Duplicates a separate "STRING_A???" code for convenience. ``_MOUSE_L, _MOUSE_R`` - If the left or right mouse button is pressed. + If the left or right mouse button is being pressed. + + ``_MOUSE_L_DOWN, _MOUSE_R_DOWN`` + If the left or right mouse button was just pressed. If this method is omitted, the screen is dismissed on receival of the ``LEAVESCREEN`` key. @@ -1618,10 +1653,20 @@ and are only documented here for completeness: Returns the pre-extracted vtable address ``name``, or *nil*. +* ``dfhack.internal.getImageBase()`` + + Returns the mmap base of the executable. + * ``dfhack.internal.getRebaseDelta()`` Returns the ASLR rebase offset of the DF executable. +* ``dfhack.internal.adjustOffset(offset[,to_file])`` + + Returns the re-aligned offset, or *nil* if invalid. + If ``to_file`` is true, the offset is adjusted from memory to file. + This function returns the original value everywhere except windows. + * ``dfhack.internal.getMemRanges()`` Returns a sequence of tables describing virtual memory ranges of the process. @@ -2672,6 +2717,16 @@ containing newlines, or a table with the following possible fields: Specifies a pen to paint as one tile before the main part of the token. +* ``token.width = ...`` + + If specified either as a value or a callback, the text field is padded + or truncated to the specified number. + +* ``token.pad_char = '?'`` + + If specified together with ``width``, the padding area is filled with + this character instead of just being skipped over. + * ``token.key = '...'`` Specifies the keycode associated with the token. The string description @@ -2737,8 +2792,12 @@ It has the following attributes: :inactive_pen: If specified, used for the cursor when the widget is not active. :icon_pen: Default pen for icons. :on_select: Selection change callback; called as ``on_select(index,choice)``. + This is also called with *nil* arguments if ``setChoices`` is called + with an empty list. :on_submit: Enter key callback; if specified, the list reacts to the key and calls it as ``on_submit(index,choice)``. +:on_submit2: Shift-Enter key callback; if specified, the list reacts to the key + and calls it as ``on_submit2(index,choice)``. :row_height: Height of every row in text lines. :icon_width: If not *nil*, the specified number of character columns are reserved to the left of the list item for the icons. @@ -2788,6 +2847,10 @@ The list supports the following methods: Call the ``on_submit`` callback, as if the Enter key was handled. +* ``list:submit2()`` + + Call the ``on_submit2`` callback, as if the Shift-Enter key was handled. + FilteredList class ------------------ @@ -2798,6 +2861,7 @@ In addition to passing through all attributes supported by List, it supports: :edit_pen: If specified, used instead of ``cursor_pen`` for the edit field. +:edit_below: If true, the edit field is placed below the list instead of above. :not_found_label: Specifies the text of the label shown when no items match the filter. The list choices may include the following attributes: @@ -2933,3 +2997,24 @@ from other scripts) in any context, via the same function the core uses: The ``name`` argument should be the name stem, as would be used on the command line. Note that this function lets errors propagate to the caller. + +Save init script +================ + +If a save directory contains a file called ``raw/init.lua``, it is +automatically loaded and executed every time the save is loaded. It +can also define the following functions to be called by dfhack: + +* ``function onStateChange(op) ... end`` + + Automatically called from the regular onStateChange event as long + as the save is still loaded. This avoids the need to install a hook + into the global ``dfhack.onStateChange`` table, with associated + cleanup concerns. + +* ``function onUnload() ... end`` + + Called when the save containing the script is unloaded. This function + should clean up any global hooks installed by the script. + +Within the init script, the path to the save directory is available as ``SAVE_PATH``. diff --git a/NEWS b/NEWS index f2237cab9..ed6290261 100644 --- a/NEWS +++ b/NEWS @@ -10,24 +10,55 @@ DFHack future - fastdwarf: new mode using debug flags, and some internal consistency fixes. - added a small stand-alone utility for applying and removing binary patches. - removebadthoughts: add --dry-run option + - superdwarf: work in adventure mode too + - tweak stable-cursor: carries cursor location from/to Build menu. + - deathcause: allow selection from the unitlist screen + - slayrace: allow targetting undeads + New tweaks: + - tweak military-training: speed up melee squad training up to 10x (normally 3-5x). New scripts: + - binpatch: the same as the stand-alone binpatch.exe, but works at runtime. - region-pops: displays animal populations of the region and allows tweaking them. + - lua: lua interpreter front-end converted to a script from a native command. + - dfusion: misc scripts with a text based menu. + - embark: lets you embark anywhere. + - lever: list and pull fort levers from the dfhack console. + - stripcaged: mark items inside cages for dumping, eg caged goblin weapons. + - soundsense-season: writes the correct season to gamelog.txt on world load. + - create-items: spawn items New GUI scripts: - gui/guide-path: displays the cached path for minecart Guide orders. - gui/workshop-job: displays inputs of a workshop job and allows tweaking them. - - gui/workflow: a front-end for the workflow plugin. + - gui/workflow: a front-end for the workflow plugin (part inspired by falconne). - gui/assign-rack: works together with a binary patch to fix weapon racks. + - gui/gm-editor: an universal editor for lots of dfhack things. + - gui/companion-order: a adventure mode command interface for your companions. + New binary patches (for use with binpatch): + - armorstand-capacity: doubles the capacity of armor stands. + - custom-reagent-size: lets custom reactions use small amounts of inputs. + - deconstruct-heapfall: stops some items still falling on head when deconstructing. + - deconstruct-teleport: stops items from 16x16 block teleporting when deconstructing. + - hospital-overstocking: stops hospital overstocking with supplies. + - training-ammo: lets dwarves with quiver full of combat-only ammo train. + - weaponrack-unassign: fixes bug that negates work done by gui/assign-rack. Workflow plugin: - properly considers minecarts assigned to routes busy. - code for deducing job outputs rewritten in lua for flexibility. - logic fix: collecting webs produces silk, and ungathered webs are not thread. + - items assigned to squads are considered busy, even if not in inventory. + - shearing and milking jobs are supported, but only with generic MILK or YARN outputs. + - workflow announces when the stock level gets very low once a season. New Fix Armory plugin: Together with a couple of binary patches and the gui/assign-rack script, this plugin makes weapon racks, armor stands, chests and cabinets in properly designated barracks be used again for storage of squad equipment. New Search plugin by falconne: - Adds an incremental search function to the Stocks, Trading and Unit List screens. - + Adds an incremental search function to the Stocks, Trading, Stockpile and Unit List screens. + New AutoMaterial plugin by falconne: + Makes building constructions (walls, floors, fortifications, etc) a little bit easier by + saving you from having to trawl through long lists of materials each time you place one. + Dfusion plugin: + Reworked to make use of lua modules, now all the scripts can be used from other scripts. DFHack v0.34.11-r2 diff --git a/Readme.html b/Readme.html index cd073459c..54deb013f 100644 --- a/Readme.html +++ b/Readme.html @@ -337,197 +337,210 @@ access DF memory and allow for easier development of new tools.

  • Introduction
  • Getting DFHack
  • Compatibility
  • -
  • Installation/Removal
  • -
  • Using DFHack