From 9e1e34f69b63bf74fbe73b73e6f0837b9dd14940 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Wed, 18 Apr 2012 00:16:44 -0500 Subject: [PATCH 01/53] Created new stockcheck plugin --- plugins/devel/CMakeLists.txt | 1 + plugins/devel/stockcheck.cpp | 121 +++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 plugins/devel/stockcheck.cpp diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 9e7e7af6f..736a5194f 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -15,4 +15,5 @@ DFHACK_PLUGIN(dumpmats dumpmats.cpp) #DFHACK_PLUGIN(tiles tiles.cpp) DFHACK_PLUGIN(regrass regrass.cpp) DFHACK_PLUGIN(counters counters.cpp) +DFHACK_PLUGIN(stockcheck stockcheck.cpp) diff --git a/plugins/devel/stockcheck.cpp b/plugins/devel/stockcheck.cpp new file mode 100644 index 000000000..ab301a9dc --- /dev/null +++ b/plugins/devel/stockcheck.cpp @@ -0,0 +1,121 @@ +#include "Core.h" +#include "Console.h" +#include "Export.h" +#include "PluginManager.h" + +#include "DataDefs.h" +#include "df/world.h" +#include "df/ui.h" +#include "df/building_stockpilest.h" +#include "df/global_objects.h" +#include "df/viewscreen_dwarfmodest.h" +#include "df/item.h" +#include "df/items_other_id.h" +#include "modules/MapCache.h" + +using std::vector; +using std::string; +using std::endl; +using namespace DFHack; +using namespace df::enums; + +using df::global::world; +using df::global::ui; +using df::global::selection_rect; + +using df::building_stockpilest; + +static command_result copystock(color_ostream &out, vector & parameters); +static command_result stockcheck(color_ostream &out, vector & parameters); +static bool copystock_guard(df::viewscreen *top); + +DFHACK_PLUGIN("stockcheck"); + +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) +{ + if (world && ui) { + commands.push_back( + PluginCommand("stockcheck", "Check for stockpile adequacy.", + stockcheck, false, + "Scan world for unstockpiled items and verify stockpiles exist for them.\n" + ) + ); + } + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +} + +static command_result stockcheck(color_ostream &out, vector & parameters) +{ + CoreSuspender suspend; + MapExtras::MapCache mc; + + for (int i = 0; i < world->buildings.all.size(); ++i) + { + df::building *build = world->buildings.all[i]; + auto type = build->getType(); + if (df::enums::building_type::Stockpile == type) + { + building_stockpilest *sp = virtual_cast(build); + df::stockpile_settings st = sp->settings; + out << "Stockpile " << sp->stockpile_number << ":\n"; + out << " width=" << build->room.width << " height= " << build->room.height << endl; + + int x1 = build->room.x; + int x2 = build->room.x + build->room.width; + int y1 = build->room.y; + int y2 = build->room.y + build->room.height; + int e = 0; + int size = 0, free = 0; + for (int x = x1; x < x2; x++) + for (int y = y1; y < y2; y++) + if (build->room.extents[e++] == 1) + { + size++; + DFCoord cursor (x,y,build->z); + uint32_t blockX = x / 16; + uint32_t tileX = x % 16; + uint32_t blockY = y / 16; + uint32_t tileY = y % 16; + MapExtras::Block * b = mc.BlockAt(cursor/16); + if(b && b->is_valid()) + { + auto &block = *b->getRaw(); + df::tile_occupancy &occ = block.occupancy[tileX][tileY]; + if (!occ.bits.item) + free++; + } + } + + out << " size=" << size << " free= " << free << endl; + } + } + + std::vector &items = world->items.other[items_other_id::ANY_FREE]; + + // Precompute a bitmask with the bad flags + df::item_flags bad_flags; + bad_flags.whole = 0; + +#define F(x) bad_flags.bits.x = true; + F(dump); F(forbid); F(garbage_collect); + F(hostile); F(on_fire); F(rotten); F(trader); + F(in_building); F(construction); F(artifact1); +#undef F + + for (size_t i = 0; i < items.size(); i++) + { + df::item *item = items[i]; + if (item->flags.whole & bad_flags.whole) + continue; + + + } + out << "Stockcheck done" << endl; + + return CR_OK; +} From 2dc3aa1b892ade4caf19eb264b6599b1deed09ff Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Wed, 18 Apr 2012 11:33:17 -0500 Subject: [PATCH 02/53] danger of rotting) --- plugins/devel/stockcheck.cpp | 229 +++++++++++++++++++++++++++++------ 1 file changed, 193 insertions(+), 36 deletions(-) diff --git a/plugins/devel/stockcheck.cpp b/plugins/devel/stockcheck.cpp index ab301a9dc..8dc44e98a 100644 --- a/plugins/devel/stockcheck.cpp +++ b/plugins/devel/stockcheck.cpp @@ -8,10 +8,14 @@ #include "df/ui.h" #include "df/building_stockpilest.h" #include "df/global_objects.h" -#include "df/viewscreen_dwarfmodest.h" #include "df/item.h" +#include "df/unit.h" +#include "df/building.h" #include "df/items_other_id.h" +#include "df/item_stockpile_ref.h" #include "modules/MapCache.h" +#include "modules/Items.h" + using std::vector; using std::string; @@ -35,9 +39,9 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector z; + x1 = sp_->room.x; + x2 = sp_->room.x + sp_->room.width; + y1 = sp_->room.y; + y2 = sp_->room.y + sp_->room.height; + int e = 0; + size = 0; + free = 0; + for (int y = y1; y < y2; y++) + for (int x = x1; x < x2; x++) + if (sp_->room.extents[e++] == 1) + { + size++; + DFCoord cursor (x,y,z); + uint32_t blockX = x / 16; + uint32_t tileX = x % 16; + uint32_t blockY = y / 16; + uint32_t tileY = y % 16; + MapExtras::Block * b = mc.BlockAt(cursor/16); + if(b && b->is_valid()) + { + auto &block = *b->getRaw(); + df::tile_occupancy &occ = block.occupancy[tileX][tileY]; + if (!occ.bits.item) + free++; + } + } + } + + bool isFull() { return free == 0; } + + bool canHold(df::item *i) + { + return false; + } + + bool inStockpile(df::item *i) + { + df::item *container = Items::getContainer(i); + if (container) + return inStockpile(container); + + if (i->pos.z != z) return false; + if (i->pos.x < x1 || i->pos.x >= x2 || + i->pos.y < y1 || i->pos.y >= y2) return false; + int e = (i->pos.x - x1) + (i->pos.y - y1) * sp->room.width; + return sp->room.extents[e] == 1; + } + + int getId() { return sp->id; } +}; + static command_result stockcheck(color_ostream &out, vector & parameters) { CoreSuspender suspend; - MapExtras::MapCache mc; + + std::vector stockpiles; for (int i = 0; i < world->buildings.all.size(); ++i) { @@ -61,38 +129,10 @@ static command_result stockcheck(color_ostream &out, vector & parameter if (df::enums::building_type::Stockpile == type) { building_stockpilest *sp = virtual_cast(build); - df::stockpile_settings st = sp->settings; - out << "Stockpile " << sp->stockpile_number << ":\n"; - out << " width=" << build->room.width << " height= " << build->room.height << endl; - - int x1 = build->room.x; - int x2 = build->room.x + build->room.width; - int y1 = build->room.y; - int y2 = build->room.y + build->room.height; - int e = 0; - int size = 0, free = 0; - for (int x = x1; x < x2; x++) - for (int y = y1; y < y2; y++) - if (build->room.extents[e++] == 1) - { - size++; - DFCoord cursor (x,y,build->z); - uint32_t blockX = x / 16; - uint32_t tileX = x % 16; - uint32_t blockY = y / 16; - uint32_t tileY = y % 16; - MapExtras::Block * b = mc.BlockAt(cursor/16); - if(b && b->is_valid()) - { - auto &block = *b->getRaw(); - df::tile_occupancy &occ = block.occupancy[tileX][tileY]; - if (!occ.bits.item) - free++; - } - } - - out << " size=" << size << " free= " << free << endl; + StockpileInfo *spi = new StockpileInfo(sp); + stockpiles.push_back(spi); } + } std::vector &items = world->items.other[items_other_id::ANY_FREE]; @@ -105,6 +145,7 @@ static command_result stockcheck(color_ostream &out, vector & parameter F(dump); F(forbid); F(garbage_collect); F(hostile); F(on_fire); F(rotten); F(trader); F(in_building); F(construction); F(artifact1); + F(spider_web); F(owned); F(in_job); #undef F for (size_t i = 0; i < items.size(); i++) @@ -113,9 +154,125 @@ static command_result stockcheck(color_ostream &out, vector & parameter if (item->flags.whole & bad_flags.whole) continue; + // we really only care about MEAT, FISH, FISH_RAW, PLANT, CHEESE, FOOD, and EGG + + df::item_type typ = item->getType(); + if (typ != df::enums::item_type::MEAT && + typ != df::enums::item_type::FISH && + typ != df::enums::item_type::FISH_RAW && + typ != df::enums::item_type::PLANT && + typ != df::enums::item_type::CHEESE && + typ != df::enums::item_type::FOOD && + typ != df::enums::item_type::EGG) + continue; + + df::item *container = 0; + df::unit *holder = 0; + df::building *building = 0; + + for (size_t i = 0; i < item->itemrefs.size(); i++) + { + df::general_ref *ref = item->itemrefs[i]; + + switch (ref->getType()) + { + case general_ref_type::CONTAINED_IN_ITEM: + container = ref->getItem(); + break; + + case general_ref_type::UNIT_HOLDER: + holder = ref->getUnit(); + break; + + case general_ref_type::BUILDING_HOLDER: + building = ref->getBuilding(); + break; + + } + } + + df::item *nextcontainer = container; + df::item *lastcontainer = 0; + + while(nextcontainer) { + df::item *thiscontainer = nextcontainer; + nextcontainer = 0; + for (size_t i = 0; i < thiscontainer->itemrefs.size(); i++) + { + df::general_ref *ref = thiscontainer->itemrefs[i]; + + switch (ref->getType()) + { + case general_ref_type::CONTAINED_IN_ITEM: + lastcontainer = nextcontainer = ref->getItem(); + break; + + case general_ref_type::UNIT_HOLDER: + holder = ref->getUnit(); + break; + + case general_ref_type::BUILDING_HOLDER: + building = ref->getBuilding(); + break; + + } + } + } + + if (holder) + continue; // carried items do not rot as far as i know + + if (building) { + df::building_type btype = building->getType(); + if (btype == df::enums::building_type::TradeDepot || + btype == df::enums::building_type::Wagon) + continue; // items in trade depot or the embark wagon do not rot + + if (typ == df::enums::item_type::EGG && btype ==df::enums::building_type::NestBox) + continue; // eggs in nest box do not rot + } + + int canHoldCount = 0; + StockpileInfo *current = 0; + + for (int idx = 0; idx < stockpiles.size(); idx++) + { + StockpileInfo *spi = stockpiles[idx]; + if (spi->canHold(item)) canHoldCount++; + if (spi->inStockpile(item)) current=spi; + } + + if (current) + continue; + + std::string description; + item->getItemDescription(&description, 0); + out << " * " << description; + + if (container) { + std::string containerDescription; + container->getItemDescription(&containerDescription, 0); + out << ", in container " << containerDescription; + if (lastcontainer) { + std::string lastcontainerDescription; + lastcontainer->getItemDescription(&lastcontainerDescription, 0); + out << ", in container " << lastcontainerDescription; + } + } + + if (holder) { + out << ", carried"; + } + + if (building) { + out << ", in building " << building->id << " (type=" << building->getType() << ")"; + } + + out << ", flags=" << std::hex << item->flags.whole << std::dec; + out << endl; } - out << "Stockcheck done" << endl; return CR_OK; } + From f655a0986dde21796fe00613912845a04ec61082 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 19 Apr 2012 11:03:29 +0400 Subject: [PATCH 03/53] Sync to changes in the data structure definitions. --- .../df/custom/block_burrow.methods.inc | 17 +++------ .../block_square_event_mineralst.methods.inc | 16 +++----- .../df/custom/tile_bitmask.methods.inc | 38 +++++++++++++++++++ library/modules/Maps.cpp | 2 +- library/xml | 2 +- plugins/devel/buildprobe.cpp | 2 +- 6 files changed, 52 insertions(+), 25 deletions(-) create mode 100644 library/include/df/custom/tile_bitmask.methods.inc diff --git a/library/include/df/custom/block_burrow.methods.inc b/library/include/df/custom/block_burrow.methods.inc index 216a58662..3959fcf35 100644 --- a/library/include/df/custom/block_burrow.methods.inc +++ b/library/include/df/custom/block_burrow.methods.inc @@ -1,26 +1,21 @@ inline bool getassignment( const df::coord2d &xy ) { - return getassignment(xy.x,xy.y); + return tile_bitmask.getassignment(xy); } inline bool getassignment( int x, int y ) { - return (tile_bitmask[y] & (1 << x)); + return tile_bitmask.getassignment(x,y); } inline void setassignment( const df::coord2d &xy, bool bit ) { - return setassignment(xy.x,xy.y, bit); + return tile_bitmask.setassignment(xy, bit); } inline void setassignment( int x, int y, bool bit ) { - if(bit) - tile_bitmask[y] |= (1 << x); - else - tile_bitmask[y] &= ~(1 << x); + return tile_bitmask.setassignment(x, y, bit); } bool has_assignments() { - for (int i = 0; i < 16; i++) - if (tile_bitmask[i]) - return true; - return false; + return tile_bitmask.has_assignments(); } + diff --git a/library/include/df/custom/block_square_event_mineralst.methods.inc b/library/include/df/custom/block_square_event_mineralst.methods.inc index 216a58662..4da4c65be 100644 --- a/library/include/df/custom/block_square_event_mineralst.methods.inc +++ b/library/include/df/custom/block_square_event_mineralst.methods.inc @@ -1,26 +1,20 @@ inline bool getassignment( const df::coord2d &xy ) { - return getassignment(xy.x,xy.y); + return tile_bitmask.getassignment(xy); } inline bool getassignment( int x, int y ) { - return (tile_bitmask[y] & (1 << x)); + return tile_bitmask.getassignment(x,y); } inline void setassignment( const df::coord2d &xy, bool bit ) { - return setassignment(xy.x,xy.y, bit); + return tile_bitmask.setassignment(xy, bit); } inline void setassignment( int x, int y, bool bit ) { - if(bit) - tile_bitmask[y] |= (1 << x); - else - tile_bitmask[y] &= ~(1 << x); + return tile_bitmask.setassignment(x, y, bit); } bool has_assignments() { - for (int i = 0; i < 16; i++) - if (tile_bitmask[i]) - return true; - return false; + return tile_bitmask.has_assignments(); } diff --git a/library/include/df/custom/tile_bitmask.methods.inc b/library/include/df/custom/tile_bitmask.methods.inc new file mode 100644 index 000000000..18b312a08 --- /dev/null +++ b/library/include/df/custom/tile_bitmask.methods.inc @@ -0,0 +1,38 @@ +inline uint16_t &operator[] (int y) +{ + return bits[y]; +} +void clear() +{ + memset(bits,0,sizeof(bits)); +} +void set_all() +{ + memset(bits,0xFF,sizeof(bits)); +} +inline bool getassignment( const df::coord2d &xy ) +{ + return getassignment(xy.x,xy.y); +} +inline bool getassignment( int x, int y ) +{ + return (bits[y] & (1 << x)); +} +inline void setassignment( const df::coord2d &xy, bool bit ) +{ + return setassignment(xy.x,xy.y, bit); +} +inline void setassignment( int x, int y, bool bit ) +{ + if(bit) + bits[y] |= (1 << x); + else + bits[y] &= ~(1 << x); +} +bool has_assignments() +{ + for (int i = 0; i < 16; i++) + if (bits[i]) + return true; + return false; +} diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 327e26986..e795659ca 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -770,7 +770,7 @@ df::block_burrow *Maps::getBlockBurrowMask(df::burrow *burrow, df::map_block *bl link->item = new df::block_burrow; link->item->id = burrow->id; - memset(link->item->tile_bitmask,0,sizeof(link->item->tile_bitmask)); + link->item->tile_bitmask.clear(); link->item->link = link; link->next = NULL; diff --git a/library/xml b/library/xml index e8036d3f1..a1e342afe 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit e8036d3f13c6be0141899baae90f605ad11d5385 +Subproject commit a1e342afe5a5e1e07672cd8b6553953bc251a05d diff --git a/plugins/devel/buildprobe.cpp b/plugins/devel/buildprobe.cpp index 6c360455a..17e95237f 100644 --- a/plugins/devel/buildprobe.cpp +++ b/plugins/devel/buildprobe.cpp @@ -119,7 +119,7 @@ command_result writeFlag (color_ostream &out, vector & parameters) MapExtras::MapCache * MCache = new MapExtras::MapCache(); t_occupancy oc = MCache->occupancyAt(cursor); - oc.bits.building = value; + oc.bits.building = df::tile_building_occ(value); MCache->setOccupancyAt(cursor, oc); MCache->WriteAll(); From 4b87f1bcaca65b7fa7542dfa6e638413c5b6d48f Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 19 Apr 2012 19:17:07 +0400 Subject: [PATCH 04/53] Refactor MapCache: make it parse everything that is known re tiles & mats. --- library/include/modules/MapCache.h | 249 +++++++++++++---- library/include/modules/Materials.h | 10 + library/modules/Maps.cpp | 410 ++++++++++++++++++++++++---- library/xml | 2 +- plugins/dig.cpp | 8 +- plugins/mapexport/mapexport.cpp | 4 +- plugins/probe.cpp | 58 ++-- plugins/prospector.cpp | 4 +- 8 files changed, 601 insertions(+), 144 deletions(-) diff --git a/library/include/modules/MapCache.h b/library/include/modules/MapCache.h index 8eac514be..4ddf0c984 100644 --- a/library/include/modules/MapCache.h +++ b/library/include/modules/MapCache.h @@ -31,12 +31,17 @@ distribution. #include #include #include "df/map_block.h" +#include "df/tile_bitmask.h" #include "df/block_square_event_mineralst.h" #include "df/construction.h" #include "df/item.h" using namespace DFHack; +namespace df { + struct world_region_details; +} + namespace MapExtras { @@ -50,6 +55,38 @@ inline bool is_valid_tile_coord(df::coord2d p) { return (p.x & ~15) == 0 && (p.y & ~15) == 0; } +class Block; + +class BlockInfo +{ + Block *mblock; + MapCache *parent; + df::map_block *block; + +public: + t_blockmaterials veinmats; + t_blockmaterials basemats; + t_blockmaterials grass; + std::map plants; + + df::feature_init *global_feature; + df::feature_init *local_feature; + + BlockInfo() + : mblock(NULL), parent(NULL), block(NULL), + global_feature(NULL), local_feature(NULL) {} + + void prepare(Block *mblock); + + t_matpair getBaseMaterial(df::tiletype tt, df::coord2d pos); + + static void SquashVeins(df::map_block *mb, t_blockmaterials & materials); + static void SquashFrozenLiquids (df::map_block *mb, tiletypes40d & frozen); + static void SquashRocks (df::map_block *mb, t_blockmaterials & materials, + std::vector< std::vector > * layerassign); + static void SquashGrass(df::map_block *mb, t_blockmaterials &materials); +}; + class DFHACK_EXPORT Block { public: @@ -62,39 +99,81 @@ public: //Arbitrary tag field for flood fills etc. int16_t &tag(df::coord2d p) { + if (!tags) init_tags(); return index_tile(tags, p); } + // Base layer + df::tiletype baseTiletypeAt(df::coord2d p) + { + if (!tiles) init_tiles(); + return index_tile(tiles->base_tiles,p); + } + t_matpair baseMaterialAt(df::coord2d p) + { + if (!basemats) init_tiles(true); + return t_matpair( + index_tile(basemats->mattype,p), + index_tile(basemats->matindex,p) + ); + } + bool isVeinAt(df::coord2d p) + { + using namespace df::enums::tiletype_material; + auto tm = tileMaterial(baseTiletypeAt(p)); + return tm == MINERAL; + } + bool isLayerAt(df::coord2d p) + { + using namespace df::enums::tiletype_material; + auto tm = tileMaterial(baseTiletypeAt(p)); + return tm == STONE || tm == SOIL; + } + int16_t veinMaterialAt(df::coord2d p) { - return index_tile(veinmats,p); + return isVeinAt(p) ? baseMaterialAt(p).mat_index : -1; } - int16_t baseMaterialAt(df::coord2d p) + int16_t layerMaterialAt(df::coord2d p) { - return index_tile(basemats,p); + if (!basemats) init_tiles(true); + return index_tile(basemats->layermat,p); } - df::tiletype BaseTileTypeAt(df::coord2d p) + // Static layer (base + constructions) + df::tiletype staticTiletypeAt(df::coord2d p) { - auto tt = index_tile(contiles,p); - if (tt != tiletype::Void) return tt; - tt = index_tile(icetiles,p); - if (tt != tiletype::Void) return tt; - return index_tile(rawtiles,p); + if (!tiles) init_tiles(); + if (tiles->con_info) + return index_tile(tiles->con_info->tiles,p); + return baseTiletypeAt(p); } - df::tiletype TileTypeAt(df::coord2d p) + t_matpair staticMaterialAt(df::coord2d p) { - return index_tile(rawtiles,p); + if (!basemats) init_tiles(true); + if (tiles->con_info) + return t_matpair( + index_tile(tiles->con_info->mattype,p), + index_tile(tiles->con_info->matindex,p) + ); + return baseMaterialAt(p); } - bool setTiletypeAt(df::coord2d p, df::tiletype tiletype) + bool hasConstructionAt(df::coord2d p) { - if(!valid) return false; - dirty_tiletypes = true; - //printf("setting block %d/%d/%d , %d %d\n",x,y,z, p.x, p.y); - index_tile(rawtiles,p) = tiletype; - return true; + if (!tiles) init_tiles(); + return tiles->con_info && + tiles->con_info->constructed.getassignment(p); } + df::tiletype tiletypeAt(df::coord2d p) + { + if (!block) return tiletype::Void; + if (tiles) + return index_tile(tiles->raw_tiles,p); + return index_tile(block->tiletype,p); + } + bool setTiletypeAt(df::coord2d, df::tiletype tt, bool force = false); + uint16_t temperature1At(df::coord2d p) { return index_tile(temp1,p); @@ -179,29 +258,29 @@ public: bool is_valid() { return valid; } df::map_block *getRaw() { return block; } + MapCache *getParent() { return parent; } + private: friend class MapCache; MapCache *parent; df::map_block *block; - static void SquashVeins(df::map_block *mb, t_blockmaterials & materials); - static void SquashFrozenLiquids (df::map_block *mb, tiletypes40d & frozen); - static void SquashConstructions (df::map_block *mb, tiletypes40d & constructions); - static void SquashRocks (df::map_block *mb, t_blockmaterials & materials, - std::vector< std::vector > * layerassign); - bool valid; bool dirty_designations:1; - bool dirty_tiletypes:1; + bool dirty_tiles:1; bool dirty_temperatures:1; bool dirty_blockflags:1; bool dirty_occupancies:1; DFCoord bcoord; - int16_t tags[16][16]; + // Custom tags for floodfill + typedef int16_t T_tags[16]; + T_tags *tags; + void init_tags(); + // On-ground item count info typedef int T_item_counts[16]; T_item_counts *item_counts; void init_item_counts(); @@ -209,30 +288,53 @@ private: bool addItemOnGround(df::item *item); bool removeItemOnGround(df::item *item); - tiletypes40d rawtiles; + struct ConInfo { + df::tile_bitmask constructed; + df::tiletype tiles[16][16]; + t_blockmaterials mattype; + t_blockmaterials matindex; + }; + struct TileInfo { + df::tile_bitmask frozen; + df::tile_bitmask dirty_raw; + df::tiletype raw_tiles[16][16]; + + ConInfo *con_info; + + df::tile_bitmask dirty_base; + df::tiletype base_tiles[16][16]; + + TileInfo(); + ~TileInfo(); + + void init_coninfo(); + }; + struct BasematInfo { + df::tile_bitmask dirty; + t_blockmaterials mattype; + t_blockmaterials matindex; + t_blockmaterials layermat; + + BasematInfo(); + }; + TileInfo *tiles; + BasematInfo *basemats; + void init_tiles(bool basemat = false); + void ParseTiles(TileInfo *tiles); + void ParseBasemats(TileInfo *tiles, BasematInfo *bmats); + designations40d designation; occupancies40d occupancy; t_blockflags blockflags; - t_blockmaterials veinmats; - t_blockmaterials basemats; t_temperatures temp1; t_temperatures temp2; - tiletypes40d contiles; // what's underneath constructions - tiletypes40d icetiles; // what's underneath ice }; class DFHACK_EXPORT MapCache { public: - MapCache() - { - valid = 0; - Maps::getSize(x_bmax, y_bmax, z_max); - x_tmax = x_bmax*16; y_tmax = y_bmax*16; - validgeo = Maps::ReadGeology(&layer_mats, &geoidx); - valid = true; - }; + MapCache(); ~MapCache() { trash(); @@ -251,19 +353,60 @@ class DFHACK_EXPORT MapCache df::tiletype baseTiletypeAt (DFCoord tilecoord) { - Block * b= BlockAtTile(tilecoord); - return b ? b->BaseTileTypeAt(tilecoord) : tiletype::Void; + Block *b = BlockAtTile(tilecoord); + return b ? b->baseTiletypeAt(tilecoord) : tiletype::Void; + } + t_matpair baseMaterialAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->baseMaterialAt(tilecoord) : t_matpair(); + } + int16_t veinMaterialAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->veinMaterialAt(tilecoord) : -1; + } + int16_t layerMaterialAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->layerMaterialAt(tilecoord) : -1; + } + bool isVeinAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b && b->isVeinAt(tilecoord); } + bool isLayerAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b && b->isLayerAt(tilecoord); + } + + df::tiletype staticTiletypeAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->staticTiletypeAt(tilecoord) : tiletype::Void; + } + t_matpair staticMaterialAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->staticMaterialAt(tilecoord) : t_matpair(); + } + bool hasConstructionAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b && b->hasConstructionAt(tilecoord); + } + df::tiletype tiletypeAt (DFCoord tilecoord) { - Block * b= BlockAtTile(tilecoord); - return b ? b->TileTypeAt(tilecoord) : tiletype::Void; + Block *b = BlockAtTile(tilecoord); + return b ? b->tiletypeAt(tilecoord) : tiletype::Void; } - bool setTiletypeAt(DFCoord tilecoord, df::tiletype tiletype) + bool setTiletypeAt (DFCoord tilecoord, df::tiletype tt, bool force = false) { - if (Block * b= BlockAtTile(tilecoord)) - return b->setTiletypeAt(tilecoord, tiletype); - return false; + Block *b = BlockAtTile(tilecoord); + return b && b->setTiletypeAt(tilecoord, tt, force); } uint16_t temperature1At (DFCoord tilecoord) @@ -290,17 +433,6 @@ class DFHACK_EXPORT MapCache return false; } - int16_t veinMaterialAt (DFCoord tilecoord) - { - Block * b= BlockAtTile(tilecoord); - return b ? b->veinMaterialAt(tilecoord) : -1; - } - int16_t baseMaterialAt (DFCoord tilecoord) - { - Block * b= BlockAtTile(tilecoord); - return b ? b->baseMaterialAt(tilecoord) : -1; - } - int16_t tagAt(DFCoord tilecoord) { Block * b= BlockAtTile(tilecoord); @@ -311,6 +443,7 @@ class DFHACK_EXPORT MapCache Block * b= BlockAtTile(tilecoord); if (b) b->tag(tilecoord) = val; } + void resetTags(); df::tile_designation designationAt (DFCoord tilecoord) { @@ -378,6 +511,7 @@ class DFHACK_EXPORT MapCache private: friend class Block; + friend class BlockInfo; bool valid; bool validgeo; @@ -388,6 +522,7 @@ private: uint32_t z_max; std::vector geoidx; std::vector< std::vector > layer_mats; + std::map region_details; std::map blocks; }; } diff --git a/library/include/modules/Materials.h b/library/include/modules/Materials.h index 3d70dc9fa..76c89de30 100644 --- a/library/include/modules/Materials.h +++ b/library/include/modules/Materials.h @@ -60,6 +60,14 @@ namespace df namespace DFHack { + struct t_matpair { + int16_t mat_type; + int32_t mat_index; + + t_matpair(int16_t type = -1, int32_t index = -1) + : mat_type(type), mat_index(index) {} + }; + struct DFHACK_EXPORT MaterialInfo { static const int NUM_BUILTIN = 19; static const int GROUP_SIZE = 200; @@ -91,6 +99,7 @@ namespace DFHack public: MaterialInfo(int16_t type = -1, int32_t index = -1) { decode(type, index); } + MaterialInfo(const t_matpair &mp) { decode(mp.mat_type, mp.mat_index); } template MaterialInfo(T *ptr) { decode(ptr); } bool isValid() const { return material != NULL; } @@ -107,6 +116,7 @@ namespace DFHack bool decode(int16_t type, int32_t index = -1); bool decode(df::item *item); bool decode(const df::material_vec_ref &vr, int idx); + bool decode(const t_matpair &mp) { return decode(mp.mat_type, mp.mat_index); } template bool decode(T *ptr) { // Assume and exploit a certain naming convention diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index e795659ca..06e525fea 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -51,6 +51,9 @@ using namespace std; #include "df/burrow.h" #include "df/block_burrow.h" #include "df/block_burrow_link.h" +#include "df/world_region_details.h" +#include "df/builtin_mats.h" +#include "df/block_square_event_grassst.h" using namespace DFHack; using namespace df::enums; @@ -158,7 +161,7 @@ df::world_data::T_region_map *Maps::getRegionBiome(df::coord2d rgn_pos) df::feature_init *Maps::getGlobalInitFeature(int32_t index) { auto data = world->world_data; - if (!data) + if (!data || index < 0) return NULL; auto rgn = vector_get(data->underground_regions, index); @@ -186,7 +189,7 @@ bool Maps::GetGlobalFeature(t_feature &feature, int32_t index) df::feature_init *Maps::getLocalInitFeature(df::coord2d rgn_pos, int32_t index) { auto data = world->world_data; - if (!data) + if (!data || index < 0) return NULL; if (rgn_pos.x < 0 || rgn_pos.x >= data->world_width || @@ -353,7 +356,7 @@ bool Maps::ReadGeology(vector > *layer_mats, vector int bioRX = world->map.region_x / 16 + ((i % 3) - 1); int bioRY = world->map.region_y / 16 + ((i / 3) - 1); - df::coord2d rgn_pos(clip_range(bioRX,0,world_width-1),clip_range(bioRX,0,world_height-1)); + df::coord2d rgn_pos(clip_range(bioRX,0,world_width-1),clip_range(bioRY,0,world_height-1)); (*geoidx)[i] = rgn_pos; @@ -389,7 +392,7 @@ bool Maps::ReadGeology(vector > *layer_mats, vector MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) { dirty_designations = false; - dirty_tiletypes = false; + dirty_tiles = false; dirty_temperatures = false; dirty_blockflags = false; dirty_occupancies = false; @@ -397,12 +400,12 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) bcoord = _bcoord; block = Maps::getBlock(bcoord); item_counts = NULL; - - memset(tags,0,sizeof(tags)); + tags = NULL; + tiles = NULL; + basemats = NULL; if(block) { - COPY(rawtiles, block->tiletype); COPY(designation, block->designation); COPY(occupancy, block->occupancy); blockflags = block->flags; @@ -410,33 +413,189 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) COPY(temp1, block->temperature_1); COPY(temp2, block->temperature_2); - SquashVeins(block,veinmats); - SquashConstructions(block, contiles); - SquashFrozenLiquids(block, icetiles); - if(parent->validgeo) - SquashRocks(block,basemats,&parent->layer_mats); - else - memset(basemats,-1,sizeof(basemats)); valid = true; } else { blockflags.whole = 0; - memset(rawtiles,0,sizeof(rawtiles)); memset(designation,0,sizeof(designation)); memset(occupancy,0,sizeof(occupancy)); memset(temp1,0,sizeof(temp1)); memset(temp2,0,sizeof(temp2)); - memset(veinmats,-1,sizeof(veinmats)); - memset(contiles,0,sizeof(contiles)); - memset(icetiles,0,sizeof(icetiles)); - memset(basemats,-1,sizeof(basemats)); } } MapExtras::Block::~Block() { delete[] item_counts; + delete[] tags; + delete tiles; + delete basemats; +} + +void MapExtras::Block::init_tags() +{ + if (!tags) + tags = new T_tags[16]; + memset(tags,0,sizeof(T_tags)*16); +} + +void MapExtras::Block::init_tiles(bool basemat) +{ + if (!tiles) + { + tiles = new TileInfo(); + + if (block) + ParseTiles(tiles); + } + + if (basemat && !basemats) + { + basemats = new BasematInfo(); + + if (block) + ParseBasemats(tiles, basemats); + } +} + +MapExtras::Block::TileInfo::TileInfo() +{ + frozen.clear(); + dirty_raw.clear(); + memset(raw_tiles,0,sizeof(raw_tiles)); + con_info = NULL; + dirty_base.clear(); + memset(base_tiles,0,sizeof(base_tiles)); +} + +MapExtras::Block::TileInfo::~TileInfo() +{ + delete con_info; +} + +void MapExtras::Block::TileInfo::init_coninfo() +{ + if (con_info) + return; + + con_info = new ConInfo(); + con_info->constructed.clear(); + COPY(con_info->tiles, base_tiles); + memset(con_info->mattype, -1, sizeof(con_info->mattype)); + memset(con_info->matindex, -1, sizeof(con_info->matindex)); +} + +MapExtras::Block::BasematInfo::BasematInfo() +{ + dirty.clear(); + memset(mattype,0,sizeof(mattype)); + memset(matindex,-1,sizeof(matindex)); + memset(layermat,-1,sizeof(layermat)); +} + +bool MapExtras::Block::setTiletypeAt(df::coord2d pos, df::tiletype tt, bool force) +{ + if (!block) + return false; + + if (!basemats) + init_tiles(true); + + pos = pos & 15; + + dirty_tiles = true; + tiles->raw_tiles[pos.x][pos.y] = tt; + tiles->dirty_raw.setassignment(pos, true); + + return true; +} + +void MapExtras::Block::ParseTiles(TileInfo *tiles) +{ + tiletypes40d icetiles; + BlockInfo::SquashFrozenLiquids(block, icetiles); + + COPY(tiles->raw_tiles, block->tiletype); + + for (int x = 0; x < 16; x++) + { + for (int y = 0; y < 16; y++) + { + using namespace df::enums::tiletype_material; + + df::tiletype tt = tiles->raw_tiles[x][y]; + df::coord coord = block->map_pos + df::coord(x,y,0); + + // Frozen liquid comes topmost + if (tileMaterial(tt) == FROZEN_LIQUID) + { + tiles->frozen.setassignment(x,y,true); + if (icetiles[x][y] != tiletype::Void) + { + tt = icetiles[x][y]; + } + } + + // The next layer may be construction + bool is_con = false; + + if (tileMaterial(tt) == CONSTRUCTION) + { + df::construction *con = df::construction::find(coord); + if (con) + { + if (!tiles->con_info) + tiles->init_coninfo(); + + is_con = true; + tiles->con_info->constructed.setassignment(x,y,true); + tiles->con_info->tiles[x][y] = tt; + tiles->con_info->mattype[x][y] = con->mat_type; + tiles->con_info->matindex[x][y] = con->mat_index; + + tt = con->original_tile; + } + } + + // Finally, base material + tiles->base_tiles[x][y] = tt; + + // Copy base info back to construction layer + if (tiles->con_info && !is_con) + tiles->con_info->tiles[x][y] = tt; + } + } +} + +void MapExtras::Block::ParseBasemats(TileInfo *tiles, BasematInfo *bmats) +{ + BlockInfo info; + + info.prepare(this); + + COPY(bmats->layermat, info.basemats); + + for (int x = 0; x < 16; x++) + { + for (int y = 0; y < 16; y++) + { + using namespace df::enums::tiletype_material; + + auto tt = tiles->base_tiles[x][y]; + auto mat = info.getBaseMaterial(tt, df::coord2d(x,y)); + + bmats->mattype[x][y] = mat.mat_type; + bmats->matindex[x][y] = mat.mat_index; + + // Copy base info back to construction layer + if (tiles->con_info && !tiles->con_info->constructed.getassignment(x,y)) + { + tiles->con_info->mattype[x][y] = mat.mat_type; + tiles->con_info->matindex[x][y] = mat.mat_index; + } + } + } } bool MapExtras::Block::Write () @@ -454,10 +613,21 @@ bool MapExtras::Block::Write () block->flags.bits.designated = true; dirty_designations = false; } - if(dirty_tiletypes) + if(dirty_tiles && tiles) { - COPY(block->tiletype, rawtiles); - dirty_tiletypes = false; + dirty_tiles = false; + + for (int x = 0; x < 16; x++) + { + for (int y = 0; y < 16; y++) + { + if (tiles->dirty_raw.getassignment(x,y)) + block->tiletype[x][y] = tiles->raw_tiles[x][y]; + } + } + + delete tiles; tiles = NULL; + delete basemats; basemats = NULL; } if(dirty_temperatures) { @@ -473,65 +643,145 @@ bool MapExtras::Block::Write () return true; } -void MapExtras::Block::SquashVeins(df::map_block *mb, t_blockmaterials & materials) +void MapExtras::BlockInfo::prepare(Block *mblock) { - memset(materials,-1,sizeof(materials)); - std::vector veins; - Maps::SortBlockEvents(mb,&veins); - for (uint32_t x = 0;x<16;x++) for (uint32_t y = 0; y< 16;y++) + this->mblock = mblock; + + block = mblock->getRaw(); + parent = mblock->getParent(); + + SquashVeins(block,veinmats); + SquashGrass(block, grass); + + if (parent->validgeo) + SquashRocks(block,basemats,&parent->layer_mats); + else + memset(basemats,-1,sizeof(basemats)); + + for (size_t i = 0; i < block->plants.size(); i++) { - df::tiletype tt = mb->tiletype[x][y]; - if (tileMaterial(tt) == tiletype_material::MINERAL) + auto pp = block->plants[i]; + plants[pp->pos] = pp; + } + + global_feature = Maps::getGlobalInitFeature(block->global_feature); + local_feature = Maps::getLocalInitFeature(block->region_pos, block->local_feature); +} + +t_matpair MapExtras::BlockInfo::getBaseMaterial(df::tiletype tt, df::coord2d pos) +{ + using namespace df::enums::tiletype_material; + + t_matpair rv(0,-1); + int x = pos.x, y = pos.y; + + switch (tileMaterial(tt)) { + case CONSTRUCTION: // just a fallback + case SOIL: + case STONE: + rv.mat_index = basemats[x][y]; + break; + + case MINERAL: + rv.mat_index = veinmats[x][y]; + break; + + case LAVA_STONE: + if (auto details = parent->region_details[mblock->biomeRegionAt(pos)]) + rv.mat_index = details->lava_stone; + break; + + case PLANT: + rv.mat_type = MaterialInfo::PLANT_BASE; + if (auto plant = plants[block->map_pos + df::coord(x,y,0)]) { - for (size_t i = 0; i < veins.size(); i++) + if (auto raw = df::plant_raw::find(plant->material)) { - if (veins[i]->getassignment(x,y)) - materials[x][y] = veins[i]->inorganic_mat; + rv.mat_type = raw->material_defs.type_basic_mat; + rv.mat_index = raw->material_defs.idx_basic_mat; } } + break; + + case GRASS_LIGHT: + case GRASS_DARK: + case GRASS_DRY: + case GRASS_DEAD: + rv.mat_type = MaterialInfo::PLANT_BASE; + if (auto raw = df::plant_raw::find(grass[x][y])) + { + rv.mat_type = raw->material_defs.type_basic_mat; + rv.mat_index = raw->material_defs.idx_basic_mat; + } + break; + + case FEATURE: + { + auto dsgn = block->designation[x][y]; + + if (dsgn.bits.feature_local && local_feature) + local_feature->getMaterial(&rv.mat_type, &rv.mat_index); + else if (dsgn.bits.feature_global && global_feature) + global_feature->getMaterial(&rv.mat_type, &rv.mat_index); + + break; } + + case FROZEN_LIQUID: + case POOL: + case BROOK: + case RIVER: + rv.mat_type = builtin_mats::WATER; + break; + + case ASHES: + case FIRE: + case CAMPFIRE: + rv.mat_type = builtin_mats::ASH; + break; + + default: + rv.mat_type = -1; + } + + return rv; } -void MapExtras::Block::SquashFrozenLiquids(df::map_block *mb, tiletypes40d & frozen) +void MapExtras::BlockInfo::SquashVeins(df::map_block *mb, t_blockmaterials & materials) { - std::vector ices; - Maps::SortBlockEvents(mb,NULL,&ices); - for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++) + std::vector veins; + Maps::SortBlockEvents(mb,&veins); + memset(materials,-1,sizeof(materials)); + for (uint32_t x = 0;x<16;x++) for (uint32_t y = 0; y< 16;y++) { - df::tiletype tt = mb->tiletype[x][y]; - frozen[x][y] = tiletype::Void; - if (tileMaterial(tt) == tiletype_material::FROZEN_LIQUID) + for (size_t i = 0; i < veins.size(); i++) { - for (size_t i = 0; i < ices.size(); i++) - { - df::tiletype tt2 = ices[i]->tiles[x][y]; - if (tt2 != tiletype::Void) - { - frozen[x][y] = tt2; - break; - } - } + if (veins[i]->getassignment(x,y)) + materials[x][y] = veins[i]->inorganic_mat; } } } -void MapExtras::Block::SquashConstructions (df::map_block *mb, tiletypes40d & constructions) +void MapExtras::BlockInfo::SquashFrozenLiquids(df::map_block *mb, tiletypes40d & frozen) { + std::vector ices; + Maps::SortBlockEvents(mb,NULL,&ices); + memset(frozen,0,sizeof(frozen)); for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++) { - df::tiletype tt = mb->tiletype[x][y]; - constructions[x][y] = tiletype::Void; - if (tileMaterial(tt) == tiletype_material::CONSTRUCTION) + for (size_t i = 0; i < ices.size(); i++) { - DFCoord coord = mb->map_pos + df::coord(x,y,0); - df::construction *con = df::construction::find(coord); - if (con) - constructions[x][y] = con->original_tile; + df::tiletype tt2 = ices[i]->tiles[x][y]; + if (tt2 != tiletype::Void) + { + frozen[x][y] = tt2; + break; + } } } } -void MapExtras::Block::SquashRocks (df::map_block *mb, t_blockmaterials & materials, +void MapExtras::BlockInfo::SquashRocks (df::map_block *mb, t_blockmaterials & materials, std::vector< std::vector > * layerassign) { // get the layer materials @@ -547,6 +797,25 @@ void MapExtras::Block::SquashRocks (df::map_block *mb, t_blockmaterials & materi } } +void MapExtras::BlockInfo::SquashGrass(df::map_block *mb, t_blockmaterials &materials) +{ + std::vector grasses; + Maps::SortBlockEvents(mb, NULL, NULL, NULL, &grasses); + memset(materials,-1,sizeof(materials)); + for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++) + { + int amount = 0; + for (size_t i = 0; i < grasses.size(); i++) + { + if (grasses[i]->amount[x][y] >= amount) + { + amount = grasses[i]->amount[x][y]; + materials[x][y] = grasses[i]->plant_index; + } + } + } +} + df::coord2d MapExtras::Block::biomeRegionAt(df::coord2d p) { if (!block) @@ -662,6 +931,24 @@ bool MapExtras::Block::removeItemOnGround(df::item *item) return true; } +MapExtras::MapCache::MapCache() +{ + valid = 0; + Maps::getSize(x_bmax, y_bmax, z_max); + x_tmax = x_bmax*16; y_tmax = y_bmax*16; + validgeo = Maps::ReadGeology(&layer_mats, &geoidx); + valid = true; + + if (auto data = df::global::world->world_data) + { + for (size_t i = 0; i < data->region_details.size(); i++) + { + auto info = data->region_details[i]; + region_details[info->pos] = info; + } + } +} + MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord) { if(!valid) @@ -685,6 +972,15 @@ MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord) } } +void MapExtras::MapCache::resetTags() +{ + for (auto it = blocks.begin(); it != blocks.end(); ++it) + { + delete[] it->second->tags; + it->second->tags = NULL; + } +} + df::burrow *Maps::findBurrowByName(std::string name) { auto &vec = df::burrow::get_vector(); diff --git a/library/xml b/library/xml index a1e342afe..25c2a3dad 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit a1e342afe5a5e1e07672cd8b6553953bc251a05d +Subproject commit 25c2a3dad964abbcceb5abd41558b71fb113e83b diff --git a/plugins/dig.cpp b/plugins/dig.cpp index 64161a3aa..21c199102 100644 --- a/plugins/dig.cpp +++ b/plugins/dig.cpp @@ -1196,7 +1196,7 @@ command_result digl (color_ostream &out, vector & parameters) df::tile_designation des = MCache->designationAt(xy); df::tiletype tt = MCache->tiletypeAt(xy); int16_t veinmat = MCache->veinMaterialAt(xy); - int16_t basemat = MCache->baseMaterialAt(xy); + int16_t basemat = MCache->layerMaterialAt(xy); if( veinmat != -1 ) { con.printerr("This is a vein. Use vdig instead!\n"); @@ -1215,7 +1215,7 @@ command_result digl (color_ostream &out, vector & parameters) if (MCache->tagAt(current)) continue; int16_t vmat2 = MCache->veinMaterialAt(current); - int16_t bmat2 = MCache->baseMaterialAt(current); + int16_t bmat2 = MCache->layerMaterialAt(current); tt = MCache->tiletypeAt(current); if(!DFHack::isWallTerrain(tt)) @@ -1282,7 +1282,7 @@ command_result digl (color_ostream &out, vector & parameters) //below = 1; des_minus = MCache->designationAt(current-1); vmat_minus = MCache->veinMaterialAt(current-1); - bmat_minus = MCache->baseMaterialAt(current-1); + bmat_minus = MCache->layerMaterialAt(current-1); tt_minus = MCache->tiletypeAt(current-1); if ( tileMaterial(tt_minus)==tiletype_material::STONE || tileMaterial(tt_minus)==tiletype_material::SOIL) @@ -1293,7 +1293,7 @@ command_result digl (color_ostream &out, vector & parameters) //above = 1; des_plus = MCache->designationAt(current+1); vmat_plus = MCache->veinMaterialAt(current+1); - bmat_plus = MCache->baseMaterialAt(current+1); + bmat_plus = MCache->layerMaterialAt(current+1); tt_plus = MCache->tiletypeAt(current+1); if ( tileMaterial(tt_plus)==tiletype_material::STONE || tileMaterial(tt_plus)==tiletype_material::SOIL) diff --git a/plugins/mapexport/mapexport.cpp b/plugins/mapexport/mapexport.cpp index e0a7e5e69..592c526db 100644 --- a/plugins/mapexport/mapexport.cpp +++ b/plugins/mapexport/mapexport.cpp @@ -193,7 +193,7 @@ command_result mapexport (color_ostream &out, std::vector & parame prototile->set_flow_size(des.bits.flow_size); } - df::tiletype type = b->TileTypeAt(coord); + df::tiletype type = b->tiletypeAt(coord); prototile->set_type((dfproto::Tile::TileType)tileShape(type)); prototile->set_tile_material((dfproto::Tile::TileMaterialType)tileMaterial(type)); @@ -204,7 +204,7 @@ command_result mapexport (color_ostream &out, std::vector & parame case tiletype_material::SOIL: case tiletype_material::STONE: prototile->set_material_type(0); - prototile->set_material_index(b->baseMaterialAt(coord)); + prototile->set_material_index(b->layerMaterialAt(coord)); break; case tiletype_material::MINERAL: prototile->set_material_type(0); diff --git a/plugins/probe.cpp b/plugins/probe.cpp index 4e041f180..8240d91f3 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -103,6 +103,30 @@ command_result df_cprobe (color_ostream &out, vector & parameters) return CR_OK; } +void describeTile(color_ostream &out, df::tiletype tiletype) +{ + out.print("%d", tiletype); + if(tileName(tiletype)) + out.print(" = %s",tileName(tiletype)); + out.print("\n"); + + df::tiletype_shape shape = tileShape(tiletype); + df::tiletype_material material = tileMaterial(tiletype); + df::tiletype_special special = tileSpecial(tiletype); + df::tiletype_variant variant = tileVariant(tiletype); + out.print("%-10s: %4d %s\n","Class" ,shape, + ENUM_KEY_STR(tiletype_shape, shape).c_str()); + out.print("%-10s: %4d %s\n","Material" , + material, ENUM_KEY_STR(tiletype_material, material).c_str()); + out.print("%-10s: %4d %s\n","Special" , + special, ENUM_KEY_STR(tiletype_special, special).c_str()); + out.print("%-10s: %4d %s\n" ,"Variant" , + variant, ENUM_KEY_STR(tiletype_variant, variant).c_str()); + out.print("%-10s: %s\n" ,"Direction", + tileDirection(tiletype).getStr()); + out.print("\n"); +} + command_result df_probe (color_ostream &out, vector & parameters) { //bool showBlock, showDesig, showOccup, showTile, showMisc; @@ -186,26 +210,12 @@ command_result df_probe (color_ostream &out, vector & parameters) */ // tiletype - out.print("tiletype: %d", tiletype); - if(tileName(tiletype)) - out.print(" = %s",tileName(tiletype)); - out.print("\n"); - - df::tiletype_shape shape = tileShape(tiletype); - df::tiletype_material material = tileMaterial(tiletype); - df::tiletype_special special = tileSpecial(tiletype); - df::tiletype_variant variant = tileVariant(tiletype); - out.print("%-10s: %4d %s\n","Class" ,shape, - ENUM_KEY_STR(tiletype_shape, shape).c_str()); - out.print("%-10s: %4d %s\n","Material" , - material, ENUM_KEY_STR(tiletype_material, material).c_str()); - out.print("%-10s: %4d %s\n","Special" , - special, ENUM_KEY_STR(tiletype_special, special).c_str()); - out.print("%-10s: %4d %s\n" ,"Variant" , - variant, ENUM_KEY_STR(tiletype_variant, variant).c_str()); - out.print("%-10s: %s\n" ,"Direction", - tileDirection(tiletype).getStr()); - out.print("\n"); + out.print("tiletype: "); + describeTile(out, tiletype); + out.print("static: "); + describeTile(out, mc.staticTiletypeAt(cursor)); + out.print("base: "); + describeTile(out, mc.baseTiletypeAt(cursor)); out.print("temperature1: %d U\n",mc.temperature1At(cursor)); out.print("temperature2: %d U\n",mc.temperature2At(cursor)); @@ -214,7 +224,7 @@ command_result df_probe (color_ostream &out, vector & parameters) out << "biome: " << des.bits.biome << std::endl; out << "geolayer: " << des.bits.geolayer_index << std::endl; - int16_t base_rock = mc.baseMaterialAt(cursor); + int16_t base_rock = mc.layerMaterialAt(cursor); if(base_rock != -1) { out << "Layer material: " << dec << base_rock; @@ -238,6 +248,12 @@ command_result df_probe (color_ostream &out, vector & parameters) else out << endl; } + MaterialInfo minfo(mc.baseMaterialAt(cursor)); + if (minfo.isValid()) + out << "Base material: " << minfo.getToken() << " / " << minfo.toString() << endl; + minfo.decode(mc.staticMaterialAt(cursor)); + if (minfo.isValid()) + out << "Static material: " << minfo.getToken() << " / " << minfo.toString() << endl; // liquids if(des.bits.flow_size) { diff --git a/plugins/prospector.cpp b/plugins/prospector.cpp index c90a66c5a..e2f1e9534 100644 --- a/plugins/prospector.cpp +++ b/plugins/prospector.cpp @@ -474,7 +474,7 @@ command_result prospector (color_ostream &con, vector & parameters) liquidWater.add(global_z); } - df::tiletype type = b->TileTypeAt(coord); + df::tiletype type = b->tiletypeAt(coord); df::tiletype_shape tileshape = tileShape(type); df::tiletype_material tilemat = tileMaterial(type); @@ -506,7 +506,7 @@ command_result prospector (color_ostream &con, vector & parameters) { case tiletype_material::SOIL: case tiletype_material::STONE: - layerMats[b->baseMaterialAt(coord)].add(global_z); + layerMats[b->layerMaterialAt(coord)].add(global_z); break; case tiletype_material::MINERAL: veinMats[b->veinMaterialAt(coord)].add(global_z); From 0b32d374db8a58547d554d758e4e77ad850f54c5 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 19 Apr 2012 23:02:30 +0400 Subject: [PATCH 05/53] Implement SOIL/STONE substitution logic, and add compat in mapexport. --- library/include/modules/MapCache.h | 5 ++ library/modules/Maps.cpp | 81 ++++++++++++++++++++++++++++-- plugins/mapexport/mapexport.cpp | 44 +++++++++++++++- 3 files changed, 123 insertions(+), 7 deletions(-) diff --git a/library/include/modules/MapCache.h b/library/include/modules/MapCache.h index 4ddf0c984..0b4e78b21 100644 --- a/library/include/modules/MapCache.h +++ b/library/include/modules/MapCache.h @@ -262,10 +262,13 @@ public: private: friend class MapCache; + friend class BlockInfo; MapCache *parent; df::map_block *block; + int biomeIndexAt(df::coord2d p); + bool valid; bool dirty_designations:1; bool dirty_tiles:1; @@ -521,6 +524,8 @@ private: uint32_t y_tmax; uint32_t z_max; std::vector geoidx; + std::vector default_soil; + std::vector default_stone; std::vector< std::vector > layer_mats; std::map region_details; std::map blocks; diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 06e525fea..e130915d6 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -676,11 +676,42 @@ t_matpair MapExtras::BlockInfo::getBaseMaterial(df::tiletype tt, df::coord2d pos int x = pos.x, y = pos.y; switch (tileMaterial(tt)) { - case CONSTRUCTION: // just a fallback + case DRIFTWOOD: case SOIL: + { + rv.mat_index = basemats[x][y]; + + if (auto raw = df::inorganic_raw::find(rv.mat_index)) + { + if (raw->flags.is_set(inorganic_flags::SOIL_ANY)) + break; + + int biome = mblock->biomeIndexAt(pos); + int idx = vector_get(parent->default_soil, biome, -1); + if (idx >= 0) + rv.mat_index = idx; + } + + break; + } + case STONE: + { rv.mat_index = basemats[x][y]; + + if (auto raw = df::inorganic_raw::find(rv.mat_index)) + { + if (!raw->flags.is_set(inorganic_flags::SOIL_ANY)) + break; + + int biome = mblock->biomeIndexAt(pos); + int idx = vector_get(parent->default_stone, biome, -1); + if (idx >= 0) + rv.mat_index = idx; + } + break; + } case MINERAL: rv.mat_index = veinmats[x][y]; @@ -727,11 +758,17 @@ t_matpair MapExtras::BlockInfo::getBaseMaterial(df::tiletype tt, df::coord2d pos break; } + case CONSTRUCTION: // just a fallback + break; + case FROZEN_LIQUID: + rv.mat_type = builtin_mats::WATER; + break; + case POOL: case BROOK: case RIVER: - rv.mat_type = builtin_mats::WATER; + rv.mat_index = basemats[x][y]; break; case ASHES: @@ -816,18 +853,30 @@ void MapExtras::BlockInfo::SquashGrass(df::map_block *mb, t_blockmaterials &mate } } -df::coord2d MapExtras::Block::biomeRegionAt(df::coord2d p) +int MapExtras::Block::biomeIndexAt(df::coord2d p) { if (!block) - return df::coord2d(-30000,-30000); + return -1; auto des = index_tile(designation,p); uint8_t idx = des.bits.biome; if (idx >= 9) - return block->region_pos; + return -1; idx = block->region_offset[idx]; if (idx >= parent->geoidx.size()) + return -1; + return idx; +} + +df::coord2d MapExtras::Block::biomeRegionAt(df::coord2d p) +{ + if (!block) + return df::coord2d(-30000,-30000); + + int idx = biomeIndexAt(p); + if (idx < 0) return block->region_pos; + return parent->geoidx[idx]; } @@ -947,6 +996,28 @@ MapExtras::MapCache::MapCache() region_details[info->pos] = info; } } + + default_soil.resize(layer_mats.size()); + default_stone.resize(layer_mats.size()); + + for (size_t i = 0; i < layer_mats.size(); i++) + { + default_soil[i] = -1; + default_stone[i] = -1; + + for (size_t j = 0; j < layer_mats[i].size(); j++) + { + auto raw = df::inorganic_raw::find(layer_mats[i][j]); + if (!raw) + continue; + + bool is_soil = raw->flags.is_set(inorganic_flags::SOIL_ANY); + if (is_soil) + default_soil[i] = layer_mats[i][j]; + else if (default_stone[i] == -1) + default_stone[i] = layer_mats[i][j]; + } + } } MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord) diff --git a/plugins/mapexport/mapexport.cpp b/plugins/mapexport/mapexport.cpp index 592c526db..fa1736ed7 100644 --- a/plugins/mapexport/mapexport.cpp +++ b/plugins/mapexport/mapexport.cpp @@ -2,7 +2,7 @@ #include "Console.h" #include "Export.h" #include "PluginManager.h" -#include "modules/MapCache.h" +#include "modules/MapCache.h"f using namespace DFHack; #include @@ -39,6 +39,46 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) return CR_OK; } +static dfproto::Tile::TileMaterialType toProto(df::tiletype_material mat) +{ + /* + * This is surely ugly, but casting enums without officially + * defined numerical values to protobuf enums is against the + * way protobufs are supposed to be used, because it defeats + * the backward compatible nature of the protocols. + */ + switch (mat) + { +#define CONVERT(name) case tiletype_material::name: return dfproto::Tile::name; + CONVERT(AIR) + case tiletype_material::PLANT: + CONVERT(SOIL) + CONVERT(STONE) + CONVERT(FEATURE) + CONVERT(LAVA_STONE) + CONVERT(MINERAL) + CONVERT(FROZEN_LIQUID) + CONVERT(CONSTRUCTION) + CONVERT(GRASS_LIGHT) + CONVERT(GRASS_DARK) + CONVERT(GRASS_DRY) + CONVERT(GRASS_DEAD) + CONVERT(HFS) + CONVERT(CAMPFIRE) + CONVERT(FIRE) + CONVERT(ASHES) + case tiletype_material::MAGMA: + return dfproto::Tile::MAGMA_TYPE; + CONVERT(DRIFTWOOD) + CONVERT(POOL) + CONVERT(BROOK) + CONVERT(RIVER) +#undef CONVERT + default: + return dfproto::Tile::AIR; + } +} + command_result mapexport (color_ostream &out, std::vector & parameters) { bool showHidden = false; @@ -195,7 +235,7 @@ command_result mapexport (color_ostream &out, std::vector & parame df::tiletype type = b->tiletypeAt(coord); prototile->set_type((dfproto::Tile::TileType)tileShape(type)); - prototile->set_tile_material((dfproto::Tile::TileMaterialType)tileMaterial(type)); + prototile->set_tile_material(toProto(tileMaterial(type))); df::coord map_pos = df::coord(b_x*16+x,b_y*16+y,z); From d95cc3435fe249e1c86e5af36b8a10e9009edd97 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 20 Apr 2012 13:04:03 +0400 Subject: [PATCH 06/53] Fix lua wrapper sizeof for static arrays. Since it actually depends on the element type, it is more tricky. --- library/DataDefs.cpp | 12 +++++++----- library/LuaWrapper.cpp | 17 +++++++++++++++-- library/include/DataDefs.h | 2 +- library/include/DataIdentity.h | 4 +++- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 05988e419..062635182 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -42,13 +42,14 @@ using namespace DFHack; void *type_identity::do_allocate_pod() { - void *p = malloc(size); - memset(p, 0, size); + size_t sz = byte_size(); + void *p = malloc(sz); + memset(p, 0, sz); return p; } void type_identity::do_copy_pod(void *tgt, const void *src) { - memmove(tgt, src, size); + memmove(tgt, src, byte_size()); }; bool type_identity::do_destroy_pod(void *obj) { @@ -81,8 +82,9 @@ bool type_identity::destroy(void *obj) { } void *enum_identity::do_allocate() { - void *p = malloc(byte_size()); - memcpy(p, &first_item_value, std::min(byte_size(), sizeof(int64_t))); + size_t sz = byte_size(); + void *p = malloc(sz); + memcpy(p, &first_item_value, std::min(sz, sizeof(int64_t))); return p; } diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 1c3b61c15..0b958f37c 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -542,10 +542,23 @@ static int meta_sizeof(lua_State *state) return 2; } - type_identity *id = get_object_identity(state, 1, "df.sizeof()", true); + type_identity *id = get_object_identity(state, 1, "df.sizeof()", true, true); - lua_pushinteger(state, id->byte_size()); + // Static arrays need special handling + if (id->type() == IDTYPE_BUFFER) + { + auto buf = (df::buffer_container_identity*)id; + type_identity *item = buf->getItemType(); + int count = buf->getSize(); + + fetch_container_details(state, lua_gettop(state), &item, &count); + + lua_pushinteger(state, item->byte_size() * count); + } + else + lua_pushinteger(state, id->byte_size()); + // Add the address if (lua_isuserdata(state, 1)) { lua_pushnumber(state, (size_t)get_object_ref(state, 1)); diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index d4d757d94..02f988248 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -83,7 +83,7 @@ namespace DFHack public: virtual ~type_identity() {} - size_t byte_size() { return size; } + virtual size_t byte_size() { return size; } virtual identity_type type() = 0; diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index a775c85b2..e4197a506 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -289,9 +289,11 @@ namespace df {} buffer_container_identity(int size, type_identity *item, enum_identity *ienum = NULL) - : container_identity(item->byte_size()*size, NULL, item, ienum), size(size) + : container_identity(0, NULL, item, ienum), size(size) {} + size_t byte_size() { return getItemType()->byte_size()*size; } + std::string getFullName(type_identity *item); int getSize() { return size; } From 0a6982f4041f43d571bd2690f148ff369f2d8038 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 20 Apr 2012 13:30:37 +0400 Subject: [PATCH 07/53] Enable warnings correctly on linux and fix a lot of them. --- CMakeLists.txt | 2 +- depends/protobuf/CMakeLists.txt | 2 ++ library/Console-linux.cpp | 11 ++++++----- library/DataDefs.cpp | 8 ++++---- library/LuaApi.cpp | 1 + library/LuaTypes.cpp | 29 ----------------------------- library/RemoteTools.cpp | 7 +++++-- library/TileTypes.cpp | 2 ++ library/include/DataDefs.h | 4 ++-- library/include/RemoteTools.h | 2 +- library/include/modules/Windows.h | 12 ++++++------ library/modules/Constructions.cpp | 4 ++-- library/modules/Engravings.cpp | 4 ++-- library/modules/Gui.cpp | 2 +- library/modules/Items.cpp | 3 +++ library/modules/Maps.cpp | 17 +++++++++++------ library/modules/Materials.cpp | 10 +++++----- library/modules/Units.cpp | 2 +- library/modules/Vegetation.cpp | 4 ++-- library/modules/Windows.cpp | 2 +- library/xml | 2 +- plugins/burrows.cpp | 10 +++++----- plugins/jobutils.cpp | 4 ++-- plugins/mapexport/mapexport.cpp | 6 +++--- plugins/workflow.cpp | 6 ++++++ 25 files changed, 75 insertions(+), 81 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e83f20f54..d16390c45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,7 +108,7 @@ OPTION(BUILD_PLUGINS "Build the plugins." ON) # enable C++11 features IF(UNIX) add_definitions(-DLINUX_BUILD) - SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall") + SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g -Wall -Wno-unused-variable") SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic -std=c++0x") SET(CMAKE_C_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic") ENDIF() diff --git a/depends/protobuf/CMakeLists.txt b/depends/protobuf/CMakeLists.txt index f4a3b6d19..92504f059 100644 --- a/depends/protobuf/CMakeLists.txt +++ b/depends/protobuf/CMakeLists.txt @@ -200,6 +200,8 @@ google/protobuf/compiler/zip_writer.cc LIST(APPEND LIBPROTOBUF_FULL_SRCS ${LIBPROTOBUF_LITE_SRCS}) +SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -Wno-sign-compare") + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) SET(PROTOBUF_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS}) diff --git a/library/Console-linux.cpp b/library/Console-linux.cpp index 9a0905647..3c0ad8938 100644 --- a/library/Console-linux.cpp +++ b/library/Console-linux.cpp @@ -163,6 +163,7 @@ namespace DFHack return false; return true; } + return false; } public: @@ -489,7 +490,7 @@ namespace DFHack { right_arrow: /* right arrow */ - if (raw_cursor != raw_buffer.size()) + if (size_t(raw_cursor) != raw_buffer.size()) { raw_cursor++; prompt_refresh(); @@ -510,7 +511,7 @@ namespace DFHack history_index = 0; break; } - else if (history_index >= history.size()) + else if (size_t(history_index) >= history.size()) { history_index = history.size()-1; break; @@ -545,7 +546,7 @@ namespace DFHack if (seq[1] == '3' && seq2 == '~' ) { // delete - if (raw_buffer.size() > 0 && raw_cursor < raw_buffer.size()) + if (raw_buffer.size() > 0 && size_t(raw_cursor) < raw_buffer.size()) { raw_buffer.erase(raw_cursor,1); prompt_refresh(); @@ -555,11 +556,11 @@ namespace DFHack } break; default: - if (raw_buffer.size() == raw_cursor) + if (raw_buffer.size() == size_t(raw_cursor)) { raw_buffer.append(1,c); raw_cursor++; - if (plen+raw_buffer.size() < get_columns()) + if (plen+raw_buffer.size() < size_t(get_columns())) { /* Avoid a full update of the line in the * trivial case. */ diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 062635182..061110ecc 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -98,7 +98,7 @@ std::vector compound_identity::top_scope; compound_identity::compound_identity(size_t size, TAllocateFn alloc, compound_identity *scope_parent, const char *dfhack_name) - : constructed_identity(size, alloc), scope_parent(scope_parent), dfhack_name(dfhack_name) + : constructed_identity(size, alloc), dfhack_name(dfhack_name), scope_parent(scope_parent) { next = list; list = this; } @@ -146,8 +146,8 @@ enum_identity::enum_identity(size_t size, const char *const *keys, const void *attrs, struct_identity *attr_type) : compound_identity(size, NULL, scope_parent, dfhack_name), - first_item_value(first_item_value), last_item_value(last_item_value), - keys(keys), base_type(base_type), attrs(attrs), attr_type(attr_type) + keys(keys), first_item_value(first_item_value), last_item_value(last_item_value), + base_type(base_type), attrs(attrs), attr_type(attr_type) { } @@ -382,7 +382,7 @@ int DFHack::findEnumItem(const std::string &name, int size, const char *const *i void DFHack::flagarrayToString(std::vector *pvec, const void *p, int bytes, int base, int size, const char *const *items) { - for (unsigned i = 0; i < bytes*8; i++) { + for (int i = 0; i < bytes*8; i++) { int value = getBitfieldField(p, i, 1); if (value) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 5f2195760..ccd057c9c 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -353,6 +353,7 @@ static void push_matinfo(lua_State *state, MaterialInfo &info) case MaterialInfo::Plant: id = "plant"; break; case MaterialInfo::Creature: id = "creature"; break; case MaterialInfo::Inorganic: id = "inorganic"; break; + default: break; } lua_pushstring(state, id); diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp index 2cfe8a84e..b4a4f5d4f 100644 --- a/library/LuaTypes.cpp +++ b/library/LuaTypes.cpp @@ -576,35 +576,6 @@ static void write_field(lua_State *state, const struct_field_info *field, void * } } -/** - * Metamethod: represent a type node as string. - */ -static int meta_type_tostring(lua_State *state) -{ - if (!lua_getmetatable(state, 1)) - return 0; - - lua_getfield(state, -1, "__metatable"); - const char *cname = lua_tostring(state, -1); - - lua_pushstring(state, stl_sprintf("", cname).c_str()); - return 1; -} - -/** - * Metamethod: represent a DF object reference as string. - */ -static int meta_ptr_tostring(lua_State *state) -{ - uint8_t *ptr = get_object_addr(state, 1, 0, "access"); - - lua_getfield(state, UPVAL_METATABLE, "__metatable"); - const char *cname = lua_tostring(state, -1); - - lua_pushstring(state, stl_sprintf("<%s: 0x%08x>", cname, (unsigned)ptr).c_str()); - return 1; -} - /** * Metamethod: __index for structures. */ diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp index c8af46cbf..5e11e0621 100644 --- a/library/RemoteTools.cpp +++ b/library/RemoteTools.cpp @@ -216,6 +216,9 @@ void DFHack::describeMaterial(BasicMaterialInfo *info, const MaterialInfo &mat, case MaterialInfo::Plant: info->set_plant_id(mat.index); break; + + default: + break; } } @@ -298,7 +301,7 @@ void DFHack::describeUnit(BasicUnitInfo *info, df::unit *unit, if (mask && mask->labors()) { - for (int i = 0; i < sizeof(unit->status.labors)/sizeof(bool); i++) + for (size_t i = 0; i < sizeof(unit->status.labors)/sizeof(bool); i++) if (unit->status.labors[i]) info->add_labors(i); } @@ -630,7 +633,7 @@ static command_result ListSquads(color_ostream &stream, static command_result SetUnitLabors(color_ostream &stream, const SetUnitLaborsIn *in) { - for (size_t i = 0; i < in->change_size(); i++) + for (int i = 0; i < in->change_size(); i++) { auto change = in->change(i); auto unit = df::unit::find(change.unit_id()); diff --git a/library/TileTypes.cpp b/library/TileTypes.cpp index fa5d99555..5564e5e3f 100644 --- a/library/TileTypes.cpp +++ b/library/TileTypes.cpp @@ -68,6 +68,8 @@ namespace DFHack return tiletype::LavaPillar; case tiletype_material::STONE: return tiletype::StonePillar; + default: + break; } } diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 02f988248..a411ae667 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -372,14 +372,14 @@ namespace DFHack template int linear_index(const DFHack::enum_list_attr &lst, T val) { - for (int i = 0; i < lst.size; i++) + for (size_t i = 0; i < lst.size; i++) if (lst.items[i] == val) return i; return -1; } inline int linear_index(const DFHack::enum_list_attr &lst, const std::string &val) { - for (int i = 0; i < lst.size; i++) + for (size_t i = 0; i < lst.size; i++) if (lst.items[i] == val) return i; return -1; diff --git a/library/include/RemoteTools.h b/library/include/RemoteTools.h index bccbb5e62..65884badc 100644 --- a/library/include/RemoteTools.h +++ b/library/include/RemoteTools.h @@ -73,7 +73,7 @@ namespace DFHack */ template void flagarray_to_ints(RepeatedField *pf, const BitArray &val) { - for (int i = 0; i < val.size*8; i++) + for (size_t i = 0; i < val.size*8; i++) if (val.is_set(T(i))) pf->Add(i); } diff --git a/library/include/modules/Windows.h b/library/include/modules/Windows.h index 314b6e292..7dbc9f1ad 100644 --- a/library/include/modules/Windows.h +++ b/library/include/modules/Windows.h @@ -117,11 +117,11 @@ namespace Windows for ( auto iter = str.begin(); iter != str.end(); iter++) { auto elem = *iter; - if(cursor_y >= height) + if(cursor_y >= (int)height) break; if(wrap) { - if(cursor_x >= width) + if(cursor_x >= (int)width) cursor_x = wrap_column; } df_screentile & tile = buffer[cursor_x * height + cursor_y]; @@ -224,12 +224,12 @@ namespace Windows virtual void blit_to_parent () { df_tilebuf par = parent->getBuffer(); - for(int xi = 0; xi < width; xi++) + for(unsigned xi = 0; xi < width; xi++) { - for(int yi = 0; yi < height; yi++) + for(unsigned yi = 0; yi < height; yi++) { - int parx = left + xi; - int pary = top + yi; + unsigned parx = left + xi; + unsigned pary = top + yi; if(pary >= par.height) continue; if(parx >= par.width) continue; par.data[parx * par.height + pary] = buffer[xi * height + yi]; diff --git a/library/modules/Constructions.cpp b/library/modules/Constructions.cpp index 8dc81911c..4c2bf8ec1 100644 --- a/library/modules/Constructions.cpp +++ b/library/modules/Constructions.cpp @@ -52,14 +52,14 @@ uint32_t Constructions::getCount() df::construction * Constructions::getConstruction(const int32_t index) { - if (index < 0 || index >= getCount()) + if (uint32_t(index) >= getCount()) return NULL; return world->constructions[index]; } bool Constructions::copyConstruction(const int32_t index, t_construction &out) { - if (index < 0 || index >= getCount()) + if (uint32_t(index) >= getCount()) return false; out.origin = world->constructions[index]; diff --git a/library/modules/Engravings.cpp b/library/modules/Engravings.cpp index 3621bc736..bff17ef5f 100644 --- a/library/modules/Engravings.cpp +++ b/library/modules/Engravings.cpp @@ -53,14 +53,14 @@ uint32_t Engravings::getCount() df::engraving * Engravings::getEngraving(int index) { - if (index < 0 || index >= getCount()) + if (uint32_t(index) >= getCount()) return NULL; return world->engravings[index]; } bool Engravings::copyEngraving(const int32_t index, t_engraving &out) { - if (index < 0 || index >= getCount()) + if (uint32_t(index) >= getCount()) return false; out.origin = world->engravings[index]; diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 592f27c0b..2eaa4c27e 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -223,7 +223,7 @@ df::job *Gui::getSelectedWorkshopJob(color_ostream &out, bool quiet) df::building *selected = world->selected_building; int idx = *ui_workshop_job_cursor; - if (idx < 0 || idx >= selected->jobs.size()) + if (size_t(idx) >= selected->jobs.size()) { out.printerr("Invalid job cursor index: %d\n", idx); return NULL; diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 9d0657e19..96fcc19ce 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -536,6 +536,9 @@ df::coord Items::getPosition(df::item *item) if (auto bld = ref->getBuilding()) return df::coord(bld->centerx, bld->centery, bld->z); break; + + default: + break; } } } diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index e130915d6..3c4933fb7 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -676,6 +676,11 @@ t_matpair MapExtras::BlockInfo::getBaseMaterial(df::tiletype tt, df::coord2d pos int x = pos.x, y = pos.y; switch (tileMaterial(tt)) { + case NONE: + case AIR: + rv.mat_type = -1; + break; + case DRIFTWOOD: case SOIL: { @@ -759,6 +764,9 @@ t_matpair MapExtras::BlockInfo::getBaseMaterial(df::tiletype tt, df::coord2d pos } case CONSTRUCTION: // just a fallback + case MAGMA: + case HFS: + // use generic 'rock' break; case FROZEN_LIQUID: @@ -776,9 +784,6 @@ t_matpair MapExtras::BlockInfo::getBaseMaterial(df::tiletype tt, df::coord2d pos case CAMPFIRE: rv.mat_type = builtin_mats::ASH; break; - - default: - rv.mat_type = -1; } return rv; @@ -1031,9 +1036,9 @@ MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord) } else { - if(blockcoord.x >= 0 && blockcoord.x < x_bmax && - blockcoord.y >= 0 && blockcoord.y < y_bmax && - blockcoord.z >= 0 && blockcoord.z < z_max) + if(unsigned(blockcoord.x) < x_bmax && + unsigned(blockcoord.y) < y_bmax && + unsigned(blockcoord.z) < z_max) { Block * nblo = new Block(this, blockcoord); blocks[blockcoord] = nblo; diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index 0172e24f8..f49dd82e2 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -80,7 +80,7 @@ bool MaterialInfo::decode(df::item *item) bool MaterialInfo::decode(const df::material_vec_ref &vr, int idx) { - if (idx < 0 || idx >= vr.mat_type.size() || idx >= vr.mat_index.size()) + if (size_t(idx) >= vr.mat_type.size() || size_t(idx) >= vr.mat_index.size()) return decode(-1); else return decode(vr.mat_type[idx], vr.mat_index[idx]); @@ -103,7 +103,7 @@ bool MaterialInfo::decode(int16_t type, int32_t index) df::world_raws &raws = world->raws; - if (type >= sizeof(raws.mat_table.builtin)/sizeof(void*)) + if (size_t(type) >= sizeof(raws.mat_table.builtin)/sizeof(void*)) return false; if (index < 0) @@ -127,7 +127,7 @@ bool MaterialInfo::decode(int16_t type, int32_t index) mode = Creature; subtype = type-CREATURE_BASE; creature = df::creature_raw::find(index); - if (!creature || subtype >= creature->material.size()) + if (!creature || size_t(subtype) >= creature->material.size()) return false; material = creature->material[subtype]; } @@ -139,7 +139,7 @@ bool MaterialInfo::decode(int16_t type, int32_t index) if (!figure) return false; creature = df::creature_raw::find(figure->race); - if (!creature || subtype >= creature->material.size()) + if (!creature || size_t(subtype) >= creature->material.size()) return false; material = creature->material[subtype]; } @@ -148,7 +148,7 @@ bool MaterialInfo::decode(int16_t type, int32_t index) mode = Plant; subtype = type-PLANT_BASE; plant = df::plant_raw::find(index); - if (!plant || subtype >= plant->material.size()) + if (!plant || size_t(subtype) >= plant->material.size()) return false; material = plant->material[subtype]; } diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 8b44d4cf9..55bd98dec 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -78,7 +78,7 @@ df::unit * Units::GetCreature (const int32_t index) if (!isValid()) return NULL; // read pointer from vector at position - if(index > world->units.all.size()) + if(size_t(index) > world->units.all.size()) return 0; return world->units.all[index]; } diff --git a/library/modules/Vegetation.cpp b/library/modules/Vegetation.cpp index bd0eeb656..a76c3a2cd 100644 --- a/library/modules/Vegetation.cpp +++ b/library/modules/Vegetation.cpp @@ -54,14 +54,14 @@ uint32_t Vegetation::getCount() df::plant * Vegetation::getPlant(const int32_t index) { - if (index < 0 || index >= getCount()) + if (uint32_t(index) >= getCount()) return NULL; return world->plants.all[index]; } bool Vegetation::copyPlant(const int32_t index, t_plant &out) { - if (index < 0 || index >= getCount()) + if (uint32_t(index) >= getCount()) return false; out.origin = world->plants.all[index]; diff --git a/library/modules/Windows.cpp b/library/modules/Windows.cpp index 0cdd7e831..e069d964e 100644 --- a/library/modules/Windows.cpp +++ b/library/modules/Windows.cpp @@ -41,7 +41,7 @@ Windows::df_screentile *Windows::getScreenBuffer() } Windows::df_window::df_window(int x, int y, unsigned int width, unsigned int height) -:buffer(0), parent(0), left(x), top(y), width(width), height(height), current_painter(NULL) +:buffer(0), width(width), height(height), parent(0), left(x), top(y), current_painter(NULL) { buffer = 0; }; diff --git a/library/xml b/library/xml index 25c2a3dad..521aad437 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 25c2a3dad964abbcceb5abd41558b71fb113e83b +Subproject commit 521aad43799ff65d772e5314972b8bc675500fb3 diff --git a/plugins/burrows.cpp b/plugins/burrows.cpp index a629e30c1..8080710c3 100644 --- a/plugins/burrows.cpp +++ b/plugins/burrows.cpp @@ -604,7 +604,7 @@ static command_result burrow(color_ostream &out, vector ¶meters) bool state = (cmd == "enable"); - for (int i = 1; i < parameters.size(); i++) + for (size_t i = 1; i < parameters.size(); i++) { string &option = parameters[i]; @@ -619,7 +619,7 @@ static command_result burrow(color_ostream &out, vector ¶meters) if (parameters.size() < 2) return CR_WRONG_USAGE; - for (int i = 1; i < parameters.size(); i++) + for (size_t i = 1; i < parameters.size(); i++) { auto target = findByName(out, parameters[i]); if (!target) @@ -642,7 +642,7 @@ static command_result burrow(color_ostream &out, vector ¶meters) bool enable = (cmd != "remove-units"); - for (int i = 2; i < parameters.size(); i++) + for (size_t i = 2; i < parameters.size(); i++) { auto source = findByName(out, parameters[i]); if (!source) @@ -656,7 +656,7 @@ static command_result burrow(color_ostream &out, vector ¶meters) if (parameters.size() < 2) return CR_WRONG_USAGE; - for (int i = 1; i < parameters.size(); i++) + for (size_t i = 1; i < parameters.size(); i++) { auto target = findByName(out, parameters[i]); if (!target) @@ -679,7 +679,7 @@ static command_result burrow(color_ostream &out, vector ¶meters) bool enable = (cmd != "remove-tiles"); - for (int i = 2; i < parameters.size(); i++) + for (size_t i = 2; i < parameters.size(); i++) { if (setTilesByKeyword(target, parameters[i], enable)) continue; diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp index 511095943..de7f81366 100644 --- a/plugins/jobutils.cpp +++ b/plugins/jobutils.cpp @@ -193,7 +193,7 @@ static bool build_choice_matches(df::ui_build_item_req *req, df::build_req_choic { if (gen->mat_type == new_mat.type && gen->mat_index == new_mat.index && - (ignore_select || gen->used_count < gen->candidates.size())) + (ignore_select || size_t(gen->used_count) < gen->candidates.size())) { return true; } @@ -305,7 +305,7 @@ static df::job_item *getJobItem(color_ostream &out, df::job *job, std::string id return NULL; int v = atoi(idx.c_str()); - if (v < 1 || v > job->job_items.size()) { + if (v < 1 || size_t(v) > job->job_items.size()) { out.printerr("Invalid item index.\n"); return NULL; } diff --git a/plugins/mapexport/mapexport.cpp b/plugins/mapexport/mapexport.cpp index fa1736ed7..625f4a74d 100644 --- a/plugins/mapexport/mapexport.cpp +++ b/plugins/mapexport/mapexport.cpp @@ -2,7 +2,7 @@ #include "Console.h" #include "Export.h" #include "PluginManager.h" -#include "modules/MapCache.h"f +#include "modules/MapCache.h" using namespace DFHack; #include @@ -50,6 +50,7 @@ static dfproto::Tile::TileMaterialType toProto(df::tiletype_material mat) switch (mat) { #define CONVERT(name) case tiletype_material::name: return dfproto::Tile::name; + case tiletype_material::NONE: CONVERT(AIR) case tiletype_material::PLANT: CONVERT(SOIL) @@ -74,9 +75,8 @@ static dfproto::Tile::TileMaterialType toProto(df::tiletype_material mat) CONVERT(BROOK) CONVERT(RIVER) #undef CONVERT - default: - return dfproto::Tile::AIR; } + return dfproto::Tile::AIR; } command_result mapexport (color_ostream &out, std::vector & parameters) diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index aa3c94a55..5e5ca84bc 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -892,6 +892,9 @@ static void guess_job_material(df::job *job, MaterialInfo &mat, df::dfhack_mater case item_type::WOOD: mat_mask.bits.wood = mat_mask.bits.wood2 = true; break; + + default: + break; } } } @@ -1148,6 +1151,9 @@ static void map_job_items(color_ostream &out) if (item->getTotalDimension() < 10000) is_invalid = true; break; + + default: + break; } if (item->flags.bits.melt && !item->flags.bits.owned && !itemBusy(item)) From e0bb5bf53b7466542413328c36f82cfef709d0fe Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Fri, 20 Apr 2012 14:22:20 -0500 Subject: [PATCH 08/53] Add "naked" (has no inventory) predicate to zone (useful for mass pitting) --- plugins/zone.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/plugins/zone.cpp b/plugins/zone.cpp index ee6abf327..fe8a4f759 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -366,6 +366,7 @@ void cageInfo(color_ostream & out, df::building* building, bool verbose); void chainInfo(color_ostream & out, df::building* building, bool verbose); bool isBuiltCageAtPos(df::coord pos); bool isInBuiltCageRoom(df::unit*); +bool isNaked(df::unit *); int32_t getUnitAge(df::unit* unit) { @@ -644,6 +645,12 @@ bool hasValidMapPos(df::unit* unit) return false; } +bool isNaked(df::unit* unit) +{ + return (unit->inventory.empty()); +} + + int getUnitIndexFromId(df::unit* unit_) { for (size_t i=0; i < world->units.all.size(); i++) @@ -1748,6 +1755,8 @@ command_result df_zone (color_ostream &out, vector & parameters) bool find_not_milkable = false; bool find_named = false; bool find_not_named = false; + bool find_naked = false; + bool find_not_naked = false; bool find_agemin = false; bool find_agemax = false; @@ -2126,6 +2135,15 @@ command_result df_zone (color_ostream &out, vector & parameters) find_not_egglayer = true; invert_filter=false; } + else if(p == "naked" && !invert_filter) + { + find_naked = true; + } + else if(p == "naked" && invert_filter) + { + find_not_naked = true; + invert_filter=false; + } else if(p == "grazer" && !invert_filter) { find_grazer = true; @@ -2393,6 +2411,8 @@ command_result df_zone (color_ostream &out, vector & parameters) || (find_not_female && isFemale(unit)) || (find_named && !unit->name.has_name) || (find_not_named && unit->name.has_name) + || (find_naked && !isNaked(unit)) + || (find_not_naked && isNaked(unit)) || (find_trainable_war && (isWar(unit) || isHunter(unit) || !isTrainableWar(unit))) || (find_not_trainable_war && isTrainableWar(unit)) // hm, is this check enough? || (find_trainable_hunting && (isWar(unit) || isHunter(unit) || !isTrainableHunting(unit))) From ad73b8c737190d98797e88be4a121deb3bae3a5f Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Fri, 20 Apr 2012 14:38:50 -0500 Subject: [PATCH 09/53] Add "tamable" for animals that can be tamed (PET or PET_EXOTIC in raws) --- plugins/zone.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/plugins/zone.cpp b/plugins/zone.cpp index fe8a4f759..4fbf9b69b 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -367,6 +367,7 @@ void chainInfo(color_ostream & out, df::building* building, bool verbose); bool isBuiltCageAtPos(df::coord pos); bool isInBuiltCageRoom(df::unit*); bool isNaked(df::unit *); +bool isTamable(df::unit *); int32_t getUnitAge(df::unit* unit) { @@ -621,6 +622,20 @@ bool isTrainableHunting(df::unit* unit) return false; } +bool isTamable(df::unit* unit) +{ + df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race]; + size_t sizecas = raw->caste.size(); + for (size_t j = 0; j < sizecas;j++) + { + df::caste_raw *caste = raw->caste[j]; + if(caste->flags.is_set(caste_raw_flags::PET) || + caste->flags.is_set(caste_raw_flags::PET_EXOTIC)) + return true; + } + return false; +} + bool isMale(df::unit* unit) { return unit->sex == 1; @@ -1757,6 +1772,8 @@ command_result df_zone (color_ostream &out, vector & parameters) bool find_not_named = false; bool find_naked = false; bool find_not_naked = false; + bool find_tamable = false; + bool find_not_tamable = false; bool find_agemin = false; bool find_agemax = false; @@ -2144,6 +2161,15 @@ command_result df_zone (color_ostream &out, vector & parameters) find_not_naked = true; invert_filter=false; } + else if(p == "tamable" && !invert_filter) + { + find_tamable = true; + } + else if(p == "tamable" && invert_filter) + { + find_not_tamable = true; + invert_filter=false; + } else if(p == "grazer" && !invert_filter) { find_grazer = true; @@ -2413,6 +2439,8 @@ command_result df_zone (color_ostream &out, vector & parameters) || (find_not_named && unit->name.has_name) || (find_naked && !isNaked(unit)) || (find_not_naked && isNaked(unit)) + || (find_tamable && !isTamable(unit)) + || (find_not_tamable && isTamable(unit)) || (find_trainable_war && (isWar(unit) || isHunter(unit) || !isTrainableWar(unit))) || (find_not_trainable_war && isTrainableWar(unit)) // hm, is this check enough? || (find_trainable_hunting && (isWar(unit) || isHunter(unit) || !isTrainableHunting(unit))) From adbd351462f71db6a98b681eb9336524cc530092 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 21 Apr 2012 12:46:55 +0400 Subject: [PATCH 10/53] Rename units.other[0] to units.active. --- library/RemoteTools.cpp | 2 +- library/modules/Gui.cpp | 4 ++-- library/xml | 2 +- plugins/Dfusion/luafiles/adv_tools/init.lua | 2 +- plugins/Dfusion/luafiles/common.lua | 4 ++-- plugins/Dfusion/luafiles/tools/init.lua | 4 ++-- plugins/advtools.cpp | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp index 5e11e0621..b72c0c91d 100644 --- a/library/RemoteTools.cpp +++ b/library/RemoteTools.cpp @@ -402,7 +402,7 @@ static command_result GetWorldInfo(color_ostream &stream, case GAMETYPE_ADVENTURE_MAIN: out->set_mode(GetWorldInfoOut::MODE_ADVENTURE); - if (auto unit = vector_get(world->units.other[0], 0)) + if (auto unit = vector_get(world->units.active, 0)) out->set_player_unit_id(unit->id); if (!ui_advmode) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 2eaa4c27e..6841421b6 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -194,7 +194,7 @@ bool Gui::view_unit_hotkey(df::viewscreen *top) if (!ui_selected_unit) // allow missing return false; - return vector_get(world->units.other[0], *ui_selected_unit) != NULL; + return vector_get(world->units.active, *ui_selected_unit) != NULL; } bool Gui::unit_inventory_hotkey(df::viewscreen *top) @@ -303,7 +303,7 @@ static df::unit *getAnyUnit(df::viewscreen *top) if (!ui_selected_unit) return NULL; - return vector_get(world->units.other[0], *ui_selected_unit); + return vector_get(world->units.active, *ui_selected_unit); } case LookAround: { diff --git a/library/xml b/library/xml index 521aad437..ee172f69f 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 521aad43799ff65d772e5314972b8bc675500fb3 +Subproject commit ee172f69f613716c8d740bbd22054f48b1a22d5f diff --git a/plugins/Dfusion/luafiles/adv_tools/init.lua b/plugins/Dfusion/luafiles/adv_tools/init.lua index f5fd9a473..5504f32bc 100644 --- a/plugins/Dfusion/luafiles/adv_tools/init.lua +++ b/plugins/Dfusion/luafiles/adv_tools/init.lua @@ -5,7 +5,7 @@ function adv_tools.reincarnate(swap_soul) --only for adventurer i guess if swap_soul==nil then swap_soul=true end - local adv=df.global.world.units.other[0][0] + local adv=df.global.world.units.active[0] if adv.flags1.dead==false then error("You are not dead (yet)!") end diff --git a/plugins/Dfusion/luafiles/common.lua b/plugins/Dfusion/luafiles/common.lua index 7e41dc4e5..951468bc7 100644 --- a/plugins/Dfusion/luafiles/common.lua +++ b/plugins/Dfusion/luafiles/common.lua @@ -472,8 +472,8 @@ function getSelectedUnit() return nil end local unit_indx=df.global.ui_selected_unit - if unit_indx<#df.global.world.units.other[0]-1 then - return df.global.world.units.other[0][unit_indx] + if unit_indx<#df.global.world.units.active-1 then + return df.global.world.units.active[unit_indx] else return nil end diff --git a/plugins/Dfusion/luafiles/tools/init.lua b/plugins/Dfusion/luafiles/tools/init.lua index b01157c87..e3a4607cf 100644 --- a/plugins/Dfusion/luafiles/tools/init.lua +++ b/plugins/Dfusion/luafiles/tools/init.lua @@ -113,7 +113,7 @@ function tools.change_adv(unit,nemesis) if unit==nil then error("Invalid unit!") end - local other=df.global.world.units.other[0] + local other=df.global.world.units.active local unit_indx for k,v in pairs(other) do if v==unit then @@ -157,7 +157,7 @@ function tools.MakeFollow(unit,trgunit) error("Invalid creature") end if trgunit==nil then - trgunit=df.global.world.units.other[0][0] + trgunit=df.global.world.units.active[0] end unit.relations.group_leader_id=trgunit.id local u_nem=getNemesis(unit) diff --git a/plugins/advtools.cpp b/plugins/advtools.cpp index 69841849a..4823d362c 100644 --- a/plugins/advtools.cpp +++ b/plugins/advtools.cpp @@ -169,7 +169,7 @@ bool bodySwap(color_ostream &out, df::unit *player) return false; } - auto &vec = world->units.other[0]; + auto &vec = world->units.active; int idx = linear_index(vec, player); if (idx < 0) @@ -195,7 +195,7 @@ df::nemesis_record *getPlayerNemesis(color_ostream &out, bool restore_swap) if (restore_swap) { - df::unit *ctl = world->units.other[0][0]; + df::unit *ctl = world->units.active[0]; auto ctl_nemesis = Units::getNemesis(ctl); if (ctl_nemesis != real_nemesis) From 2d19514fbc3c0a31f0aa96c4db3380767271e514 Mon Sep 17 00:00:00 2001 From: Robert Heinrich Date: Sat, 21 Apr 2012 11:52:03 +0200 Subject: [PATCH 11/53] probe: minor change --- plugins/probe.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/probe.cpp b/plugins/probe.cpp index 4e041f180..6524bd06b 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -383,14 +383,10 @@ command_result df_bprobe (color_ostream &out, vector & parameters) break; } if(building.origin->is_room) //isRoom()) - out << ", is room"; - else - out << ", not a room"; + out << ", room"; if(building.origin->getBuildStage()!=building.origin->getMaxBuildStage()) out << ", in construction"; out.print("\n"); - - } return CR_OK; } From 3282ac3db216ef43cab5744631b57ed05bcf8a12 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 21 Apr 2012 15:43:52 +0400 Subject: [PATCH 12/53] Add a hotkey command that sorts units in lists using lua comparators. --- library/DataStaticsFields.cpp | 1 + library/LuaApi.cpp | 1 + library/include/DataIdentity.h | 1 + library/include/modules/Units.h | 2 + library/lua/utils.lua | 106 ++++++++++++++ library/modules/Units.cpp | 16 +++ library/xml | 2 +- plugins/CMakeLists.txt | 1 + plugins/lua/sort.lua | 32 +++++ plugins/lua/sort/units.lua | 20 +++ plugins/sort.cpp | 235 ++++++++++++++++++++++++++++++++ 11 files changed, 416 insertions(+), 1 deletion(-) create mode 100644 library/lua/utils.lua create mode 100644 plugins/lua/sort.lua create mode 100644 plugins/lua/sort/units.lua create mode 100644 plugins/sort.cpp diff --git a/library/DataStaticsFields.cpp b/library/DataStaticsFields.cpp index a34938f95..79aa3bcf1 100644 --- a/library/DataStaticsFields.cpp +++ b/library/DataStaticsFields.cpp @@ -28,6 +28,7 @@ namespace df { NUMBER_IDENTITY_TRAITS(int64_t); NUMBER_IDENTITY_TRAITS(uint64_t); NUMBER_IDENTITY_TRAITS(float); + NUMBER_IDENTITY_TRAITS(double); bool_identity identity_traits::identity; stl_string_identity identity_traits::identity; diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index ccd057c9c..00192c058 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -615,6 +615,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, clearBurrowMembers), WRAPM(Units, isInBurrow), WRAPM(Units, setInBurrow), + WRAPM(Units, getAge), { NULL, NULL } }; diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index e4197a506..dcd0ae979 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -447,6 +447,7 @@ namespace df NUMBER_IDENTITY_TRAITS(int64_t); NUMBER_IDENTITY_TRAITS(uint64_t); NUMBER_IDENTITY_TRAITS(float); + NUMBER_IDENTITY_TRAITS(double); template<> struct DFHACK_EXPORT identity_traits { static bool_identity identity; diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index e093ed1ef..775cc5181 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -214,6 +214,8 @@ DFHACK_EXPORT void clearBurrowMembers(df::burrow *burrow); DFHACK_EXPORT bool isInBurrow(df::unit *unit, df::burrow *burrow); DFHACK_EXPORT void setInBurrow(df::unit *unit, df::burrow *burrow, bool enable); +DFHACK_EXPORT double getAge(df::unit *unit); + } } #endif diff --git a/library/lua/utils.lua b/library/lua/utils.lua new file mode 100644 index 000000000..d884f2f62 --- /dev/null +++ b/library/lua/utils.lua @@ -0,0 +1,106 @@ +local _ENV = mkmodule('utils') + +-- Comparator function +function compare(a,b) + if a < b then + return -1 + elseif a > b then + return 1 + else + return 0 + end +end + +-- Sort strings; compare empty last +function compare_name(a,b) + if a == '' then + if b == '' then + return 0 + else + return 1 + end + elseif b == '' then + return -1 + else + return compare(a,b) + end +end + +--[[ + Sort items in data according to ordering. + + Each ordering spec is a table with possible fields: + + * key = function(value) + Computes comparison key from a data value. Not called on nil. + * key_table = function(data) + Computes a key table from the data table in one go. + * compare = function(a,b) + Comparison function. Defaults to compare above. + Called on non-nil keys; nil sorts last. + + Returns a table of integer indices into data. +--]] +function make_sort_order(data,ordering) + -- Compute sort keys and comparators + local keys = {} + local cmps = {} + + for i=1,#ordering do + local order = ordering[i] + + if order.key_table then + keys[i] = order.key_table(data) + elseif order.key then + local kt = {} + local kf = order.key + for j=1,#data do + if data[j] == nil then + kt[j] = nil + else + kt[j] = kf(data[j]) + end + end + keys[i] = kt + else + keys[i] = data + end + + cmps[i] = order.compare or compare + end + + -- Make an order table + local index = {} + for i=1,#data do + index[i] = i + end + + -- Sort the ordering table + table.sort(index, function(ia,ib) + for i=1,#keys do + local ka = keys[i][ia] + local kb = keys[i][ib] + + -- Sort nil keys to the end + if ka == nil then + if kb ~= nil then + return false + end + elseif kb == nil then + return true + else + local cmpv = cmps[i](ka,kb) + if cmpv < 0 then + return true + elseif cmpv > 0 then + return false + end + end + end + return ia < ib -- this should ensure stable sort + end) + + return index +end + +return _ENV \ No newline at end of file diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 55bd98dec..e9699edbe 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -734,3 +734,19 @@ void DFHack::Units::setInBurrow(df::unit *unit, df::burrow *burrow, bool enable) } } +double DFHack::Units::getAge(df::unit *unit) +{ + using df::global::cur_year; + using df::global::cur_year_tick; + + CHECK_NULL_POINTER(unit); + + if (!cur_year || !cur_year_tick) + return -1; + + double year_ticks = 403200.0; + double birth_time = unit->relations.birth_year + unit->relations.birth_time/year_ticks; + double cur_time = *cur_year + *cur_year_tick / year_ticks; + + return cur_time - birth_time; +} diff --git a/library/xml b/library/xml index ee172f69f..e217d28c4 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit ee172f69f613716c8d740bbd22054f48b1a22d5f +Subproject commit e217d28c4800fadd3b37e153a363656dc7beb3e3 diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 4bfb392a2..70b40cedb 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -106,6 +106,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(regrass regrass.cpp) # this one exports functions to lua DFHACK_PLUGIN(burrows burrows.cpp LINK_LIBRARIES lua) + DFHACK_PLUGIN(sort sort.cpp LINK_LIBRARIES lua) # not yet. busy with other crud again... #DFHACK_PLUGIN(versionosd versionosd.cpp) endif() diff --git a/plugins/lua/sort.lua b/plugins/lua/sort.lua new file mode 100644 index 000000000..2318d7e07 --- /dev/null +++ b/plugins/lua/sort.lua @@ -0,0 +1,32 @@ +local _ENV = mkmodule('plugins.sort') + +local utils = require('utils') +local units = require('plugins.sort.units') + +orders = orders or {} +orders.units = units.orders + +function parse_ordering_spec(type,...) + local group = orders[type] + if group == nil then + dfhack.printerr('Invalid ordering class: '..tostring(type)) + return nil + end + + local specs = table.pack(...) + local rv = { } + for _,spec in ipairs(specs) do + local cm = group[spec] + if cm == nil then + dfhack.printerr('Unknown order for '..type..': '..tostring(spec)) + return nil + end + rv[#rv+1] = cm + end + + return rv +end + +make_sort_order = utils.make_sort_order + +return _ENV \ No newline at end of file diff --git a/plugins/lua/sort/units.lua b/plugins/lua/sort/units.lua new file mode 100644 index 000000000..872e49429 --- /dev/null +++ b/plugins/lua/sort/units.lua @@ -0,0 +1,20 @@ +local _ENV = mkmodule('plugins.sort.units') + +local utils = require('utils') + +orders = orders or {} + +orders.name = { + key = function(unit) + return dfhack.TranslateName(unit.name) + end, + compare = utils.compare_name +} + +orders.age = { + key = function(unit) + return dfhack.units.getAge(unit) + end +} + +return _ENV \ No newline at end of file diff --git a/plugins/sort.cpp b/plugins/sort.cpp new file mode 100644 index 000000000..a373d54c3 --- /dev/null +++ b/plugins/sort.cpp @@ -0,0 +1,235 @@ +#include "Core.h" +#include "Console.h" +#include "Export.h" +#include "PluginManager.h" + +#include "modules/Gui.h" +#include "modules/Translation.h" +#include "modules/Units.h" + +#include "LuaTools.h" + +#include "DataDefs.h" +#include "df/ui.h" +#include "df/world.h" +#include "df/viewscreen_joblistst.h" +#include "df/viewscreen_unitlistst.h" +#include "df/viewscreen_dwarfmodest.h" + +#include "MiscUtils.h" + +#include + +using std::vector; +using std::string; +using std::endl; +using namespace DFHack; +using namespace df::enums; + +using df::global::ui; +using df::global::world; + +static bool unit_list_hotkey(df::viewscreen *top); + +static command_result sort_units(color_ostream &out, vector & parameters); + +DFHACK_PLUGIN("sort"); + +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) +{ + commands.push_back(PluginCommand( + "sort-units", "Sort the visible unit list.", sort_units, unit_list_hotkey, + " sort-units filter...\n" + " Sort the unit list using the given sequence of comparisons.\n" + )); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +} + +template +void reorder_vector(std::vector *pvec, const std::vector &order) +{ + assert(pvec->size() == order.size()); + + std::vector tmp(*pvec); + for (size_t i = 0; i < order.size(); i++) + (*pvec)[i] = tmp[order[i]]; +} + +bool parse_ordering_spec(color_ostream &out, lua_State *L, std::string type, const std::vector ¶ms) +{ + if (!lua_checkstack(L, params.size() + 2)) + return false; + + if (!Lua::PushModulePublic(out, L, "plugins.sort", "parse_ordering_spec")) + return false; + + Lua::Push(L, type); + for (size_t i = 0; i < params.size(); i++) + Lua::Push(L, params[i]); + + if (!Lua::SafeCall(out, L, params.size()+1, 1)) + return false; + + if (!lua_istable(L, -1)) + { + lua_pop(L, 1); + return false; + } + + return true; +} + +bool read_order(color_ostream &out, lua_State *L, std::vector *order, size_t size) +{ + std::vector found; + + if (!lua_istable(L, -1)) + { + out.printerr("Not a table returned as ordering.\n"); + goto fail; + } + + if (lua_rawlen(L, -1) != size) + { + out.printerr("Invalid ordering size: expected %d, actual %d\n", size, lua_rawlen(L, -1)); + goto fail; + } + + order->clear(); + order->resize(size); + found.resize(size); + + for (size_t i = 1; i <= size; i++) + { + lua_rawgeti(L, -1, i); + int v = lua_tointeger(L, -1); + lua_pop(L, 1); + + if (v < 1 || size_t(v) > size) + { + out.printerr("Order value out of range: %d\n", v); + goto fail; + } + + if (found[v-1]) + { + out.printerr("Duplicate order value: %d\n", v); + goto fail; + } + + found[v-1] = 1; + (*order)[i-1] = v-1; + } + + lua_pop(L, 1); + return true; +fail: + lua_pop(L, 1); + return false; +} + +template +bool compute_order(color_ostream &out, lua_State *L, int base, std::vector *order, const std::vector &key) +{ + lua_pushvalue(L, base+1); + Lua::PushVector(L, key); + lua_pushvalue(L, base+2); + + if (!Lua::SafeCall(out, L, 2, 1)) + return false; + + return read_order(out, L, order, key.size()); +} + + +static bool unit_list_hotkey(df::viewscreen *screen) +{ + if (strict_virtual_cast(screen)) + return true; + if (strict_virtual_cast(screen)) + return true; + + if (strict_virtual_cast(screen)) + { + using namespace df::enums::ui_sidebar_mode; + + switch (ui->main.mode) + { + case Burrows: + return ui->burrows.in_add_units_mode; + default: + return false; + } + } + + return false; +} + +static command_result sort_units(color_ostream &out, vector ¶meters) +{ + if (parameters.empty()) + return CR_WRONG_USAGE; + + auto L = Lua::Core::State; + int top = lua_gettop(L); + + if (!Lua::Core::PushModulePublic(out, "plugins.sort", "make_sort_order")) + { + out.printerr("Cannot access the sorter function.\n"); + return CR_WRONG_USAGE; + } + + if (!parse_ordering_spec(out, L, "units", parameters)) + { + out.printerr("Invalid unit ordering specification.\n"); + lua_settop(L, top); + return CR_WRONG_USAGE; + } + + auto screen = Core::getInstance().getTopViewscreen(); + std::vector order; + + if (auto units = strict_virtual_cast(screen)) + { + for (int i = 0; i < 4; i++) + { + if (compute_order(out, L, top, &order, units->units[i])) + { + reorder_vector(&units->units[i], order); + reorder_vector(&units->jobs[i], order); + } + } + } + else if (auto jobs = strict_virtual_cast(screen)) + { + if (compute_order(out, L, top, &order, jobs->units)) + { + reorder_vector(&jobs->units, order); + reorder_vector(&jobs->jobs, order); + } + } + else if (strict_virtual_cast(screen)) + { + switch (ui->main.mode) + { + case ui_sidebar_mode::Burrows: + if (compute_order(out, L, top, &order, ui->burrows.list_units)) + { + reorder_vector(&ui->burrows.list_units, order); + reorder_vector(&ui->burrows.sel_units, order); + } + break; + + default: + break;; + } + } + + lua_settop(L, top); + return CR_OK; +} From 4af051bab3e455a9115a44e43b8c3da0cd189f43 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 21 Apr 2012 16:53:17 +0400 Subject: [PATCH 13/53] Add a few more unit orderings, and a way to reverse direction. --- library/lua/utils.lua | 11 ++++++-- plugins/Dfusion/dfusion.cpp | 22 +++++++++++++--- plugins/lua/sort.lua | 21 ++++++++++++++++ plugins/lua/sort/units.lua | 50 ++++++++++++++++++++++++++++++++++++- plugins/sort.cpp | 7 +++++- 5 files changed, 104 insertions(+), 7 deletions(-) diff --git a/library/lua/utils.lua b/library/lua/utils.lua index d884f2f62..3becdbb6d 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -38,6 +38,10 @@ end * compare = function(a,b) Comparison function. Defaults to compare above. Called on non-nil keys; nil sorts last. + * nil_first + If true, nil keys are sorted first instead of last. + * reverse + If true, sort non-nil keys in descending order. Returns a table of integer indices into data. --]] @@ -84,12 +88,15 @@ function make_sort_order(data,ordering) -- Sort nil keys to the end if ka == nil then if kb ~= nil then - return false + return ordering[i].nil_first end elseif kb == nil then - return true + return not ordering[i].nil_first else local cmpv = cmps[i](ka,kb) + if ordering[i].reverse then + cmpv = -cmpv + end if cmpv < 0 then return true elseif cmpv > 0 then diff --git a/plugins/Dfusion/dfusion.cpp b/plugins/Dfusion/dfusion.cpp index d8710c2db..2b36a9747 100644 --- a/plugins/Dfusion/dfusion.cpp +++ b/plugins/Dfusion/dfusion.cpp @@ -121,10 +121,26 @@ command_result lua_run_file (color_ostream &out, std::vector ¶ } command_result lua_run (color_ostream &out, std::vector ¶meters) { - if (!parameters.empty() && parameters[0] == "--core-context") + if (!parameters.empty()) { - Lua::InterpreterLoop(out, Lua::Core::State, "core lua"); - return CR_OK; + if (parameters[0] == "--core-context") + { + Lua::InterpreterLoop(out, Lua::Core::State, "core lua"); + return CR_OK; + } + else if (parameters[0] == "--core-reload") + { + CoreSuspender suspend; + + for (size_t i = 1; i < parameters.size(); i++) + { + lua_getglobal(Lua::Core::State, "reload"); + lua_pushstring(Lua::Core::State, parameters[i].c_str()); + Lua::SafeCall(out, Lua::Core::State, 1, 0); + } + + return CR_OK; + } } mymutex->lock(); diff --git a/plugins/lua/sort.lua b/plugins/lua/sort.lua index 2318d7e07..f042e85cd 100644 --- a/plugins/lua/sort.lua +++ b/plugins/lua/sort.lua @@ -15,12 +15,33 @@ function parse_ordering_spec(type,...) local specs = table.pack(...) local rv = { } + for _,spec in ipairs(specs) do + local nil_first = false + if string.sub(spec,1,1) == '<' then + nil_first = true + spec = string.sub(spec,2) + end + + local reverse = false + if string.sub(spec,1,1) == '>' then + reverse = true + spec = string.sub(spec,2) + end + local cm = group[spec] + if cm == nil then dfhack.printerr('Unknown order for '..type..': '..tostring(spec)) return nil end + + if nil_first or reverse then + cm = copyall(cm) + cm.nil_first = nil_first + cm.reverse = reverse + end + rv[#rv+1] = cm end diff --git a/plugins/lua/sort/units.lua b/plugins/lua/sort/units.lua index 872e49429..b92fc6c82 100644 --- a/plugins/lua/sort/units.lua +++ b/plugins/lua/sort/units.lua @@ -6,7 +6,9 @@ orders = orders or {} orders.name = { key = function(unit) - return dfhack.TranslateName(unit.name) + if unit.name.has_name then + return dfhack.TranslateName(unit.name) + end end, compare = utils.compare_name } @@ -17,4 +19,50 @@ orders.age = { end } +-- This assumes that units are added to active in arrival order +orders.arrival = { + key_table = function(units) + local tmp={} + for i,v in ipairs(units) do + tmp[v.id] = i + end + local idx={} + for i,v in ipairs(df.global.world.units.active) do + local ix = tmp[v.id] + if ix ~= nil then + idx[ix] = i + end + end + return idx + end +} + +orders.profession = { + key = function(unit) + local cp = unit.custom_profession + if cp == '' then + cp = df.profession.attrs[unit.profession].caption + end + return cp + end +} + +orders.squad = { + key = function(unit) + local sidx = unit.military.squad_index + if sidx >= 0 then + return sidx + end + end +} + +orders.squad_position = { + key = function(unit) + local sidx = unit.military.squad_index + if sidx >= 0 then + return sidx * 1000 + unit.military.squad_position + end + end +} + return _ENV \ No newline at end of file diff --git a/plugins/sort.cpp b/plugins/sort.cpp index a373d54c3..48e4bcaca 100644 --- a/plugins/sort.cpp +++ b/plugins/sort.cpp @@ -39,8 +39,13 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector ' prefix reverses the sort order for defined values.\n" + " Unit order examples:\n" + " name, age, arrival, squad, squad_position, profession\n" + "The orderings are defined in hack/lua/plugins/sort/*.lua\n" )); return CR_OK; } From 2ef321a2086f03de30d184c06c527f504893cd07 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 21 Apr 2012 20:15:57 +0400 Subject: [PATCH 14/53] Preserve the original lua global environment for modules. The intent is to prevent accidental pollution of module namespaces by globals defined from careless scripts running in the _G environment. --- library/LuaTools.cpp | 45 ++++++++++++++++++++++++++++++++++++------ library/lua/dfhack.lua | 11 ++++++++++- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 469caa65f..253af099c 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -162,11 +162,13 @@ static Console *get_console(lua_State *state) return static_cast(pstream); } +static int DFHACK_TOSTRING_TOKEN = 0; + static std::string lua_print_fmt(lua_State *L) { /* Copied from lua source to fully replicate builtin print */ int n = lua_gettop(L); /* number of arguments */ - lua_getglobal(L, "tostring"); + lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_TOSTRING_TOKEN); std::stringstream ss; @@ -319,7 +321,7 @@ static int DFHACK_EXCEPTION_META_TOKEN = 0; static void error_tostring(lua_State *L, bool keep_old = false) { - lua_getglobal(L, "tostring"); + lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_TOSTRING_TOKEN); if (keep_old) lua_pushvalue(L, -2); else @@ -682,6 +684,9 @@ int DFHack::Lua::SafeResume(color_ostream &out, lua_State *from, int nargs, int */ static int DFHACK_LOADED_TOKEN = 0; +static int DFHACK_DFHACK_TOKEN = 0; +static int DFHACK_BASE_G_TOKEN = 0; +static int DFHACK_REQUIRE_TOKEN = 0; bool DFHack::Lua::PushModule(color_ostream &out, lua_State *state, const char *module) { @@ -699,7 +704,7 @@ bool DFHack::Lua::PushModule(color_ostream &out, lua_State *state, const char *m } lua_pop(state, 2); - lua_getglobal(state, "require"); + lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_REQUIRE_TOKEN); lua_pushstring(state, module); return Lua::SafeCall(out, state, 1, 1); @@ -730,7 +735,11 @@ bool DFHack::Lua::Require(color_ostream &out, lua_State *state, return false; if (setglobal) - lua_setglobal(state, module.c_str()); + { + lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_BASE_G_TOKEN); + lua_swap(state); + lua_setfield(state, -2, module.c_str()); + } else lua_pop(state, 1); @@ -900,7 +909,7 @@ namespace { static bool init_interpreter(color_ostream &out, lua_State *state, void *info) { auto args = (InterpreterArgs*)info; - lua_getglobal(state, "dfhack"); + lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_DFHACK_TOKEN); lua_getfield(state, -1, "interpreter"); lua_remove(state, -2); lua_pushstring(state, args->prompt); @@ -1252,9 +1261,22 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state) lua_pushcfunction(state, lua_dfhack_println); lua_setglobal(state, "print"); + lua_getglobal(state, "require"); + lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_REQUIRE_TOKEN); + lua_getglobal(state, "tostring"); + lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_TOSTRING_TOKEN); + // Create the dfhack global lua_newtable(state); + lua_dup(state); + lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_DFHACK_TOKEN); + + lua_rawgeti(state, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); + lua_dup(state); + lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_BASE_G_TOKEN); + lua_setfield(state, -2, "BASE_G"); + lua_pushboolean(state, IsCoreContext(state)); lua_setfield(state, -2, "is_core_context"); @@ -1295,6 +1317,17 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state) luaL_setfuncs(state, dfhack_coro_funcs, 0); lua_pop(state, 1); + // split the global environment + lua_newtable(state); + lua_newtable(state); + lua_rawgeti(state, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); + lua_setfield(state, -2, "__index"); + lua_setmetatable(state, -2); + lua_dup(state); + lua_setglobal(state, "_G"); + lua_dup(state); + lua_rawseti(state, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); + // load dfhack.lua Require(out, state, "dfhack"); @@ -1322,7 +1355,7 @@ void DFHack::Lua::Core::Init(color_ostream &out) Lua::Open(out, State); // Register events - lua_getglobal(State, "dfhack"); + lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_DFHACK_TOKEN); MakeEvent(State, (void*)onStateChange); lua_setfield(State, -2, "onStateChange"); diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index c7e2669c5..5c10b002c 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -1,6 +1,15 @@ -- Common startup file for all dfhack plugins with lua support -- The global dfhack table is already created by C++ init code. +-- Setup the global environment. +-- BASE_G is the original lua global environment, +-- preserved as a common denominator for all modules. +-- This file uses it instead of the new default one. + +local dfhack = dfhack +local base_env = dfhack.BASE_G +local _ENV = base_env + -- Console color constants COLOR_RESET = -1 @@ -70,7 +79,7 @@ function mkmodule(module,env) if plugname then dfhack.open_plugin(pkg,plugname) end - setmetatable(pkg, { __index = (env or _G) }) + setmetatable(pkg, { __index = (env or base_env) }) return pkg end From c104f822a4ed01324ce98bfbeffbbf026dbc2770 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Sat, 21 Apr 2012 12:53:46 -0500 Subject: [PATCH 15/53] Move stripcaged to master branch, works fine. --- plugins/devel/CMakeLists.txt | 1 + plugins/devel/stripcaged.cpp | 103 +++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 plugins/devel/stripcaged.cpp diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index fd25962b2..ef2816608 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -14,4 +14,5 @@ DFHACK_PLUGIN(dumpmats dumpmats.cpp) #DFHACK_PLUGIN(tiles tiles.cpp) DFHACK_PLUGIN(counters counters.cpp) DFHACK_PLUGIN(stockcheck stockcheck.cpp) +DFHACK_PLUGIN(stripcaged stripcaged.cpp) diff --git a/plugins/devel/stripcaged.cpp b/plugins/devel/stripcaged.cpp new file mode 100644 index 000000000..7e492cb01 --- /dev/null +++ b/plugins/devel/stripcaged.cpp @@ -0,0 +1,103 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +#include "Core.h" +#include "Console.h" +#include "Export.h" +#include "PluginManager.h" +#include "modules/Units.h" +#include "modules/Maps.h" +#include "modules/Gui.h" +#include "modules/World.h" +#include "MiscUtils.h" + +#include +#include "df/world.h" +#include "df/world_raws.h" +#include "df/building_def.h" +#include "df/unit_inventory_item.h" +#include +#include + +using std::vector; +using std::string; +using namespace DFHack; +using namespace df::enums; +using df::global::world; +using df::global::cursor; +using df::global::ui; + +using namespace DFHack::Gui; + +command_result df_stripcaged(color_ostream &out, vector & parameters); + +DFHACK_PLUGIN("stripcaged"); + +// check if contained in item (e.g. animals in cages) +bool isContainedInItem(df::unit* unit) +{ + bool contained = false; + for (size_t r=0; r < unit->refs.size(); r++) + { + df::general_ref * ref = unit->refs[r]; + auto rtype = ref->getType(); + if(rtype == df::general_ref_type::CONTAINED_IN_ITEM) + { + contained = true; + break; + } + } + return contained; +} + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + commands.push_back(PluginCommand( + "stripcaged", "strip caged units of all items", + df_stripcaged, false, + "Clears forbid and sets dump for the inventories of all caged units." + )); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +} + +command_result df_stripcaged(color_ostream &out, vector & parameters) +{ + CoreSuspender suspend; + + size_t count = 0; + for (size_t i=0; i < world->units.all.size(); i++) + { + df::unit* unit = world->units.all[i]; + if (isContainedInItem(unit)) + { + for (size_t j=0; j < unit->inventory.size(); j++) + { + df::unit_inventory_item* uii = unit->inventory[j]; + if (uii->item) + { + uii->item->flags.bits.forbid = 0; + uii->item->flags.bits.dump = 1; + count++; + } + } + } + } + + out << count << " items marked for dumping" << endl; + + return CR_OK; +} From f5644f385bb5bf101444729cbaa6cd64ae343007 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Sat, 21 Apr 2012 23:22:21 -0500 Subject: [PATCH 16/53] Probe now displays biome savagery and evilness. Devel plugin bprobe (also in this commit) goes digging around in region data at embark screen, not really useful for public consumption. --- plugins/devel/CMakeLists.txt | 1 + plugins/devel/rprobe.cpp | 107 +++++++++++++++++++++++++++++++++++ plugins/probe.cpp | 11 +++- 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 plugins/devel/rprobe.cpp diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index ef2816608..70daa7b2c 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -15,4 +15,5 @@ DFHACK_PLUGIN(dumpmats dumpmats.cpp) DFHACK_PLUGIN(counters counters.cpp) DFHACK_PLUGIN(stockcheck stockcheck.cpp) DFHACK_PLUGIN(stripcaged stripcaged.cpp) +DFHACK_PLUGIN(rprobe rprobe.cpp) diff --git a/plugins/devel/rprobe.cpp b/plugins/devel/rprobe.cpp new file mode 100644 index 000000000..4378c547c --- /dev/null +++ b/plugins/devel/rprobe.cpp @@ -0,0 +1,107 @@ +// Produces a list of materials available on the map. +// Options: +// -a : show unrevealed tiles +// -p : don't show plants +// -s : don't show slade +// -t : don't show demon temple + +//#include +#include +#include +#include +#include +#include + +using namespace std; +#include "Core.h" +#include "Console.h" +#include "Export.h" +#include "PluginManager.h" +#include "modules/MapCache.h" + +#include "MiscUtils.h" + +#include "DataDefs.h" +#include "df/world.h" +#include "df/world_data.h" +#include "df/world_region_details.h" +#include "df/world_geo_biome.h" +#include "df/world_geo_layer.h" +#include "df/inclusion_type.h" +#include "df/viewscreen_choose_start_sitest.h" + +using namespace DFHack; +using namespace df::enums; +using df::global::world; +using df::coord2d; + + + +command_result rprobe (color_ostream &out, vector & parameters); + +DFHACK_PLUGIN("rprobe"); + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + commands.push_back(PluginCommand( + "rprobe", "Display assorted region information from embark screen", + rprobe, false, + "Display assorted region information from embark screen\n" + )); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +} + + +command_result rprobe (color_ostream &out, vector & parameters) +{ + CoreSuspender suspend; + + // Embark screen active: estimate using world geology data + VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, Core::getTopViewscreen()); + + if (!screen) + return CR_WRONG_USAGE; + + if (!world || !world->world_data) + { + out.printerr("World data is not available.\n"); + return CR_FAILURE; + } + + df::world_data *data = world->world_data; + coord2d cur_region = screen->region_pos; + + // Compute biomes + for (int i = 0; i < screen->biome_rgn.size(); i++) + { + coord2d rg = screen->biome_rgn[i]; + out << i << ": x = " << rg.x << ", y = " << rg.y; + + df::world_data::T_region_map* rd = &data->region_map[rg.x][rg.y]; + + out << + " region_id: " << rd->region_id << + " geo_index: " << rd->geo_index << + " landmass_id: " << rd->landmass_id << + " flags: " << hex << rd->flags.as_int() << dec << + " sav: " << rd->savagery << + " evil: " << rd->evilness; + + int32_t *p = (int32_t *)rd; + int c = sizeof(*rd) / sizeof(int32_t); + for (int j = 0; j < c; j++) { + if (j % 8 == 0) + out << endl << setfill('0') << setw(8) << hex << (int)(rd+j) << ": "; + out << " " << setfill('0') << setw(8) << hex << p[j]; + } + out << dec << endl; + + } + + return CR_OK; +} diff --git a/plugins/probe.cpp b/plugins/probe.cpp index 4e041f180..058a9e5ca 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -210,8 +210,17 @@ command_result df_probe (color_ostream &out, vector & parameters) out.print("temperature1: %d U\n",mc.temperature1At(cursor)); out.print("temperature2: %d U\n",mc.temperature2At(cursor)); + int offset = block.region_offset[des.bits.biome]; + df::coord2d region_pos = block.region_pos + df::coord2d ((offset % 3) - 1, (offset / 3) -1); + + df::world_data::T_region_map* biome = + &world->world_data->region_map[region_pos.x][region_pos.y]; + // biome, geolayer - out << "biome: " << des.bits.biome << std::endl; + out << "biome: " << des.bits.biome << " (" << + "region id=" << biome->region_id << ", " << + "savagery " << biome->savagery << ", " << + "evilness " << biome->evilness << ")" << std::endl; out << "geolayer: " << des.bits.geolayer_index << std::endl; int16_t base_rock = mc.baseMaterialAt(cursor); From 26cec5f60c120b7ea4ce4592ef926bd11f2c2cc4 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Sun, 22 Apr 2012 10:17:14 -0500 Subject: [PATCH 17/53] Probe now displays the "surroundings" name for the biome (e.g. Joyous Wilds, or Terrifying). Rprobe can be used from the embark screen to set the evilness of the selected biome; use with due care. --- plugins/devel/rprobe.cpp | 22 +++++++++++++++++++++- plugins/probe.cpp | 9 +++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/plugins/devel/rprobe.cpp b/plugins/devel/rprobe.cpp index 4378c547c..8829b9a4f 100644 --- a/plugins/devel/rprobe.cpp +++ b/plugins/devel/rprobe.cpp @@ -61,6 +61,9 @@ command_result rprobe (color_ostream &out, vector & parameters) { CoreSuspender suspend; + bool set = false; + int to_set, set_val; + // Embark screen active: estimate using world geology data VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, Core::getTopViewscreen()); @@ -73,6 +76,17 @@ command_result rprobe (color_ostream &out, vector & parameters) return CR_FAILURE; } + if (parameters.size() == 1) + { + if (!screen->biome_highlighted) + { + return CR_WRONG_USAGE; + } + set = true; + to_set = screen->biome_idx; + set_val = atoi(parameters[0].c_str()); + } + df::world_data *data = world->world_data; coord2d cur_region = screen->region_pos; @@ -80,10 +94,16 @@ command_result rprobe (color_ostream &out, vector & parameters) for (int i = 0; i < screen->biome_rgn.size(); i++) { coord2d rg = screen->biome_rgn[i]; - out << i << ": x = " << rg.x << ", y = " << rg.y; df::world_data::T_region_map* rd = &data->region_map[rg.x][rg.y]; + if (set && i == to_set) { + rd->evilness = set_val; + out << "* Set evilness to " << set_val << endl; + } + + out << i << ": x = " << rg.x << ", y = " << rg.y; + out << " region_id: " << rd->region_id << " geo_index: " << rd->geo_index << diff --git a/plugins/probe.cpp b/plugins/probe.cpp index 058a9e5ca..06b1f968e 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -216,9 +216,18 @@ command_result df_probe (color_ostream &out, vector & parameters) df::world_data::T_region_map* biome = &world->world_data->region_map[region_pos.x][region_pos.y]; + int sav = biome->savagery; + int evi = biome->evilness; + int sindex = sav > 65 ? 2 : sav < 33 ? 0 : 1; + int eindex = evi > 65 ? 2 : evi < 33 ? 0 : 1; + int surr = sindex + eindex * 3; + + char* surroundings[] = { "Serene", "Mirthful", "Joyous Wilds", "Calm", "Wilderness", "Untamed Wilds", "Sinister", "Haunted", "Terrifying" }; + // biome, geolayer out << "biome: " << des.bits.biome << " (" << "region id=" << biome->region_id << ", " << + surroundings[surr] << ", " << "savagery " << biome->savagery << ", " << "evilness " << biome->evilness << ")" << std::endl; out << "geolayer: " << des.bits.geolayer_index From 125cd6622a1d2ce7a8dfa7caf40a85049baf4977 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 22 Apr 2012 19:22:00 +0400 Subject: [PATCH 18/53] Support sorting units in many more ui contexts. --- library/include/LuaTools.h | 30 ++- library/lua/utils.lua | 5 +- library/xml | 2 +- plugins/lua/sort/units.lua | 7 + plugins/sort.cpp | 367 +++++++++++++++++++++++++++++++------ 5 files changed, 354 insertions(+), 57 deletions(-) diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index b2d440a6b..a41b58d1c 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -248,9 +248,15 @@ namespace DFHack {namespace Lua { } template - void PushVector(lua_State *state, const T &pvec) + void PushVector(lua_State *state, const T &pvec, bool addn = false) { - lua_createtable(state,pvec.size(),0); + lua_createtable(state,pvec.size(), addn?1:0); + + if (addn) + { + lua_pushinteger(state, pvec.size()); + lua_setfield(state, -2, "n"); + } for (size_t i = 0; i < pvec.size(); i++) { @@ -267,6 +273,26 @@ namespace DFHack {namespace Lua { DFHACK_EXPORT void MakeEvent(lua_State *state, void *key); DFHACK_EXPORT void InvokeEvent(color_ostream &out, lua_State *state, void *key, int num_args); + class StackUnwinder { + lua_State *state; + int top; + public: + StackUnwinder(lua_State *state, int bias = 0) : state(state), top(0) { + if (state) top = lua_gettop(state) - bias; + } + ~StackUnwinder() { + if (state) lua_settop(state, top); + } + operator int () { return top; } + int operator+ (int v) { return top + v; } + int operator- (int v) { return top + v; } + int operator[] (int v) { return top + v; } + StackUnwinder &operator += (int v) { top += v; return *this; } + StackUnwinder &operator -= (int v) { top += v; return *this; } + StackUnwinder &operator ++ () { top++; return *this; } + StackUnwinder &operator -- () { top--; return *this; } + }; + /** * Namespace for the common lua interpreter state. * All accesses must be done under CoreSuspender. diff --git a/library/lua/utils.lua b/library/lua/utils.lua index 3becdbb6d..00e42f9da 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -49,6 +49,7 @@ function make_sort_order(data,ordering) -- Compute sort keys and comparators local keys = {} local cmps = {} + local size = data.n or #data for i=1,#ordering do local order = ordering[i] @@ -58,7 +59,7 @@ function make_sort_order(data,ordering) elseif order.key then local kt = {} local kf = order.key - for j=1,#data do + for j=1,size do if data[j] == nil then kt[j] = nil else @@ -75,7 +76,7 @@ function make_sort_order(data,ordering) -- Make an order table local index = {} - for i=1,#data do + for i=1,size do index[i] = i end diff --git a/library/xml b/library/xml index e217d28c4..0ec109ce5 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit e217d28c4800fadd3b37e153a363656dc7beb3e3 +Subproject commit 0ec109ce5643be2c3ebcd4d652aeafaf78f33da7 diff --git a/plugins/lua/sort/units.lua b/plugins/lua/sort/units.lua index b92fc6c82..84beff8ed 100644 --- a/plugins/lua/sort/units.lua +++ b/plugins/lua/sort/units.lua @@ -4,6 +4,13 @@ local utils = require('utils') orders = orders or {} +-- Relies on NULL being auto-translated to NULL, and then sorted +orders.exists = { + key = function(unit) + return 1 + end +} + orders.name = { key = function(unit) if unit.name.has_name then diff --git a/plugins/sort.cpp b/plugins/sort.cpp index 48e4bcaca..965702b57 100644 --- a/plugins/sort.cpp +++ b/plugins/sort.cpp @@ -14,7 +14,13 @@ #include "df/world.h" #include "df/viewscreen_joblistst.h" #include "df/viewscreen_unitlistst.h" +#include "df/viewscreen_layer_militaryst.h" +#include "df/viewscreen_layer_workshop_profilest.h" +#include "df/viewscreen_layer_noblelistst.h" +#include "df/viewscreen_layer_overall_healthst.h" #include "df/viewscreen_dwarfmodest.h" +#include "df/viewscreen_petst.h" +#include "df/layer_object_listst.h" #include "MiscUtils.h" @@ -28,6 +34,9 @@ using namespace df::enums; using df::global::ui; using df::global::world; +using df::global::ui_building_in_owner; +using df::global::ui_building_item_cursor; +using df::global::ui_owner_candidates; static bool unit_list_hotkey(df::viewscreen *top); @@ -65,6 +74,22 @@ void reorder_vector(std::vector *pvec, const std::vector &order) (*pvec)[i] = tmp[order[i]]; } +template +void reorder_cursor(T *cursor, const std::vector &order) +{ + if (*cursor == 0) + return; + + for (size_t i = 0; i < order.size(); i++) + { + if (unsigned(*cursor) == order[i]) + { + *cursor = T(i); + break; + } + } +} + bool parse_ordering_spec(color_ostream &out, lua_State *L, std::string type, const std::vector ¶ms) { if (!lua_checkstack(L, params.size() + 2)) @@ -93,16 +118,18 @@ bool read_order(color_ostream &out, lua_State *L, std::vector *order, { std::vector found; + Lua::StackUnwinder frame(L, 1); + if (!lua_istable(L, -1)) { out.printerr("Not a table returned as ordering.\n"); - goto fail; + return false; } if (lua_rawlen(L, -1) != size) { out.printerr("Invalid ordering size: expected %d, actual %d\n", size, lua_rawlen(L, -1)); - goto fail; + return false; } order->clear(); @@ -111,38 +138,34 @@ bool read_order(color_ostream &out, lua_State *L, std::vector *order, for (size_t i = 1; i <= size; i++) { - lua_rawgeti(L, -1, i); + lua_rawgeti(L, frame[1], i); int v = lua_tointeger(L, -1); lua_pop(L, 1); if (v < 1 || size_t(v) > size) { out.printerr("Order value out of range: %d\n", v); - goto fail; + return false; } if (found[v-1]) { out.printerr("Duplicate order value: %d\n", v); - goto fail; + return false; } found[v-1] = 1; (*order)[i-1] = v-1; } - lua_pop(L, 1); return true; -fail: - lua_pop(L, 1); - return false; } template bool compute_order(color_ostream &out, lua_State *L, int base, std::vector *order, const std::vector &key) { lua_pushvalue(L, base+1); - Lua::PushVector(L, key); + Lua::PushVector(L, key, true); lua_pushvalue(L, base+2); if (!Lua::SafeCall(out, L, 2, 1)) @@ -151,90 +174,330 @@ bool compute_order(color_ostream &out, lua_State *L, int base, std::vector ¶ms) +{ + if (!parse_ordering_spec(out, L, "units", params)) + { + out.printerr("Invalid ordering specification for %s.\n", type); + return false; + } + + return true; +} + +#define PARSE_SPEC(type, params) \ + if (!ParseSpec(*pout, L, type, params)) return false; -static bool unit_list_hotkey(df::viewscreen *screen) +static void sort_null_first(vector ¶meters) { - if (strict_virtual_cast(screen)) - return true; - if (strict_virtual_cast(screen)) - return true; + vector_insert_at(parameters, 0, std::string("(screen)) - { - using namespace df::enums::ui_sidebar_mode; +static df::layer_object_listst *getLayerList(df::viewscreen_layerst *layer, int idx) +{ + return virtual_cast(vector_get(layer->layer_objects,idx)); +} - switch (ui->main.mode) +static bool maybe_sort_units(color_ostream *pout, lua_State *L, + df::viewscreen *screen, vector ¶meters) +{ + Lua::StackUnwinder top(L); + + if (L) + { + if (!Lua::PushModulePublic(*pout, L, "plugins.sort", "make_sort_order")) { - case Burrows: - return ui->burrows.in_add_units_mode; - default: + pout->printerr("Cannot access the sorter function.\n"); return false; } } - return false; -} + std::vector order; -static command_result sort_units(color_ostream &out, vector ¶meters) -{ - if (parameters.empty()) - return CR_WRONG_USAGE; + if (auto units = strict_virtual_cast(screen)) + { + if (!L) return true; - auto L = Lua::Core::State; - int top = lua_gettop(L); + /* + * Sort units in the 'u'nit list screen. + */ + + PARSE_SPEC("units", parameters); + + int page = units->page; + + if (compute_order(*pout, L, top, &order, units->units[page])) + { + reorder_cursor(&units->cursor_pos[page], order); + reorder_vector(&units->units[page], order); + reorder_vector(&units->jobs[page], order); + } - if (!Lua::Core::PushModulePublic(out, "plugins.sort", "make_sort_order")) + return true; + } + else if (auto jobs = strict_virtual_cast(screen)) { - out.printerr("Cannot access the sorter function.\n"); - return CR_WRONG_USAGE; + if (!L) return true; + + /* + * Sort units in the 'j'ob list screen. + */ + + PARSE_SPEC("units", parameters); + + if (compute_order(*pout, L, top, &order, jobs->units)) + { + reorder_cursor(&jobs->cursor_pos, order); + reorder_vector(&jobs->units, order); + reorder_vector(&jobs->jobs, order); + } + + return true; } + else if (auto military = strict_virtual_cast(screen)) + { + switch (military->page) + { + case df::viewscreen_layer_militaryst::Positions: + { + auto &candidates = military->positions.candidates; + auto list3 = getLayerList(military, 2); + + /* + * Sort candidate units in the 'p'osition page of the 'm'ilitary screen. + */ + + if (list3 && !candidates.empty() && list3->bright) + { + if (!L) return true; + + PARSE_SPEC("units", parameters); + + if (compute_order(*pout, L, top, &order, candidates)) + { + reorder_cursor(&list3->cursor, order); + reorder_vector(&candidates, order); + } - if (!parse_ordering_spec(out, L, "units", parameters)) + return true; + } + + return false; + } + + default: + return false; + } + } + else if (auto profile = strict_virtual_cast(screen)) { - out.printerr("Invalid unit ordering specification.\n"); - lua_settop(L, top); - return CR_WRONG_USAGE; + auto list1 = getLayerList(profile, 0); + + if (!list1) return false; + if (!L) return true; + + /* + * Sort units in the workshop 'q'uery 'P'rofile modification screen. + */ + + PARSE_SPEC("units", parameters); + + if (compute_order(*pout, L, top, &order, profile->workers)) + { + reorder_cursor(&list1->cursor, order); + reorder_vector(&profile->workers, order); + } + + return true; } + else if (auto nobles = strict_virtual_cast(screen)) + { + switch (nobles->mode) + { + case df::viewscreen_layer_noblelistst::Appoint: + { + auto list2 = getLayerList(nobles, 1); - auto screen = Core::getInstance().getTopViewscreen(); - std::vector order; + /* + * Sort units in the appointment candidate list of the 'n'obles screen. + */ - if (auto units = strict_virtual_cast(screen)) + if (list2) + { + if (!L) return true; + + sort_null_first(parameters); + PARSE_SPEC("units", parameters); + + std::vector units; + for (size_t i = 0; i < nobles->candidates.size(); i++) + units.push_back(nobles->candidates[i]->unit); + + if (compute_order(*pout, L, top, &order, units)) + { + reorder_cursor(&list2->cursor, order); + reorder_vector(&nobles->candidates, order); + } + + return true; + } + + return false; + } + + default: + return false; + } + } + else if (auto animals = strict_virtual_cast(screen)) { - for (int i = 0; i < 4; i++) + switch (animals->mode) { - if (compute_order(out, L, top, &order, units->units[i])) + case df::viewscreen_petst::List: { - reorder_vector(&units->units[i], order); - reorder_vector(&units->jobs[i], order); + if (!L) return true; + + /* + * Sort animal units in the Animal page of the 'z' status screen. + */ + + PARSE_SPEC("units", parameters); + + std::vector units; + for (size_t i = 0; i < animals->animal.size(); i++) + units.push_back(animals->is_vermin[i] ? NULL : (df::unit*)animals->animal[i]); + + if (compute_order(*pout, L, top, &order, units)) + { + reorder_cursor(&animals->cursor, order); + reorder_vector(&animals->animal, order); + reorder_vector(&animals->is_vermin, order); + reorder_vector(&animals->pet_info, order); + reorder_vector(&animals->is_tame, order); + reorder_vector(&animals->is_adopting, order); + } + + return true; } + + case df::viewscreen_petst::SelectTrainer: + { + if (!L) return true; + + /* + * Sort candidate trainers in the Animal page of the 'z' status screen. + */ + + sort_null_first(parameters); + PARSE_SPEC("units", parameters); + + if (compute_order(*pout, L, top, &order, animals->trainer_unit)) + { + reorder_cursor(&animals->trainer_cursor, order); + reorder_vector(&animals->trainer_unit, order); + reorder_vector(&animals->trainer_mode, order); + } + + return true; + } + + default: + return false; } } - else if (auto jobs = strict_virtual_cast(screen)) + else if (auto health = strict_virtual_cast(screen)) { - if (compute_order(out, L, top, &order, jobs->units)) + auto list1 = getLayerList(health, 0); + + if (!list1) return false; + if (!L) return true; + + /* + * Sort units in the Health page of the 'z' status screen. + */ + + PARSE_SPEC("units", parameters); + + if (compute_order(*pout, L, top, &order, health->unit)) { - reorder_vector(&jobs->units, order); - reorder_vector(&jobs->jobs, order); + reorder_cursor(&list1->cursor, order); + reorder_vector(&health->unit, order); + reorder_vector(&health->bits1, order); + reorder_vector(&health->bits2, order); + reorder_vector(&health->bits3, order); } + + return true; } else if (strict_virtual_cast(screen)) { switch (ui->main.mode) { case ui_sidebar_mode::Burrows: - if (compute_order(out, L, top, &order, ui->burrows.list_units)) + if (!L) return true; + + /* + * Sort burrow member candidate units in the 'w' sidebar mode. + */ + + PARSE_SPEC("units", parameters); + + if (compute_order(*pout, L, top, &order, ui->burrows.list_units)) { + reorder_cursor(&ui->burrows.unit_cursor_pos, order); reorder_vector(&ui->burrows.list_units, order); reorder_vector(&ui->burrows.sel_units, order); } - break; + + return true; + + case ui_sidebar_mode::QueryBuilding: + if (ui_building_in_owner && *ui_building_in_owner && + ui_owner_candidates && ui_building_item_cursor) + { + if (!L) return true; + + /* + * Sort building owner candidate units in the 'q' sidebar mode. + */ + + sort_null_first(parameters); + PARSE_SPEC("units", parameters); + + if (compute_order(*pout, L, top, &order, *ui_owner_candidates)) + { + reorder_cursor(ui_building_item_cursor, order); + reorder_vector(ui_owner_candidates, order); + } + + return true; + } + return false; default: - break;; + return false; } } + else + return false; +} + +static bool unit_list_hotkey(df::viewscreen *screen) +{ + vector dummy; + return maybe_sort_units(NULL, NULL, screen, dummy); +} + +static command_result sort_units(color_ostream &out, vector ¶meters) +{ + if (parameters.empty()) + return CR_WRONG_USAGE; + + auto L = Lua::Core::State; + auto screen = Core::getInstance().getTopViewscreen(); + + if (!maybe_sort_units(&out, L, screen, parameters)) + return CR_WRONG_USAGE; - lua_settop(L, top); return CR_OK; } From 760bc8b09f55413873a611a5c5f86344476f479a Mon Sep 17 00:00:00 2001 From: Robert Heinrich Date: Sun, 22 Apr 2012 20:25:10 +0200 Subject: [PATCH 19/53] regrass: ignore furrowed tiles (dirt roads) --- plugins/regrass.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/regrass.cpp b/plugins/regrass.cpp index dec091a92..64aa3a472 100644 --- a/plugins/regrass.cpp +++ b/plugins/regrass.cpp @@ -86,6 +86,10 @@ command_result df_regrass (color_ostream &out, vector & parameters) || cur->occupancy[x][y].bits.building) continue; + // don't touch furrowed tiles (dirt roads made on soil) + if(tileSpecial(cur->tiletype[x][y]) == tiletype_special::FURROWED) + continue; + int mat = tileMaterial(cur->tiletype[x][y]); if ( mat != tiletype_material::SOIL && mat != tiletype_material::GRASS_DARK // refill existing grass, too @@ -93,6 +97,7 @@ command_result df_regrass (color_ostream &out, vector & parameters) ) continue; + // max = set amounts of all grass events on that tile to 100 if(max) { From e1770fa62bebf1fe3fc4af7a6dbd9c7260b51c6e Mon Sep 17 00:00:00 2001 From: Robert Heinrich Date: Mon, 23 Apr 2012 00:34:19 +0200 Subject: [PATCH 20/53] regrass: leaves tiles with the no_grow flag alone (avoids regrassing roads etc you embark on) --- plugins/probe.cpp | 33 +++++++++++++++++++++++++++++++++ plugins/regrass.cpp | 3 ++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/plugins/probe.cpp b/plugins/probe.cpp index b3b288374..4c8179e52 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -314,6 +314,39 @@ command_result df_probe (color_ostream &out, vector & parameters) out << "global feature idx: " << block.global_feature << endl; out << std::endl; + + if(block.occupancy[tileX][tileY].bits.no_grow) + out << "no grow" << endl; + + for(size_t e=0; egetType(); + switch(blevtype) + { + case df::block_square_event_type::grass: + { + df::block_square_event_grassst * gr_ev = (df::block_square_event_grassst *)blev; + if(gr_ev->amount[tileX][tileY] > 0) + { + out << "amount of grass: " << (int)gr_ev->amount[tileX][tileY] << endl; + } + break; + } + case df::block_square_event_type::world_construction: + { + df::block_square_event_world_constructionst * co_ev = (df::block_square_event_world_constructionst*)blev; + uint16_t bits = co_ev->tile_bitmask[tileY]; + out << "construction bits: " << bits << endl; + break; + } + default: + //out << "unhandled block event type!" << endl; + break; + } + } + + return CR_OK; } diff --git a/plugins/regrass.cpp b/plugins/regrass.cpp index 64aa3a472..5475862ae 100644 --- a/plugins/regrass.cpp +++ b/plugins/regrass.cpp @@ -83,7 +83,8 @@ command_result df_regrass (color_ostream &out, vector & parameters) { if ( tileShape(cur->tiletype[x][y]) != tiletype_shape::FLOOR || cur->designation[x][y].bits.subterranean - || cur->occupancy[x][y].bits.building) + || cur->occupancy[x][y].bits.building + || cur->occupancy[x][y].bits.no_grow) continue; // don't touch furrowed tiles (dirt roads made on soil) From e06c80f69025850b0c6e6443759be2dc32db9d04 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Sun, 22 Apr 2012 23:36:13 -0500 Subject: [PATCH 21/53] Fix minor ostream munging glitch in rprobe. --- plugins/devel/rprobe.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/devel/rprobe.cpp b/plugins/devel/rprobe.cpp index 8829b9a4f..c8d7e1b1a 100644 --- a/plugins/devel/rprobe.cpp +++ b/plugins/devel/rprobe.cpp @@ -119,7 +119,7 @@ command_result rprobe (color_ostream &out, vector & parameters) out << endl << setfill('0') << setw(8) << hex << (int)(rd+j) << ": "; out << " " << setfill('0') << setw(8) << hex << p[j]; } - out << dec << endl; + out << setfill(' ') << setw(0) << dec << endl; } From 8dcf2db316da4a64d6bb5207fdfd2546071fad41 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Mon, 23 Apr 2012 09:45:00 -0500 Subject: [PATCH 22/53] rs. Use at your own peril, as strangeness may ensue. --- plugins/devel/rprobe.cpp | 62 +++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/plugins/devel/rprobe.cpp b/plugins/devel/rprobe.cpp index c8d7e1b1a..7a091a962 100644 --- a/plugins/devel/rprobe.cpp +++ b/plugins/devel/rprobe.cpp @@ -62,7 +62,7 @@ command_result rprobe (color_ostream &out, vector & parameters) CoreSuspender suspend; bool set = false; - int to_set, set_val; + int to_set, set_field, set_val; // Embark screen active: estimate using world geology data VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, Core::getTopViewscreen()); @@ -76,15 +76,33 @@ command_result rprobe (color_ostream &out, vector & parameters) return CR_FAILURE; } - if (parameters.size() == 1) + + if (parameters.size() == 2) { - if (!screen->biome_highlighted) - { + if (parameters[0] == "wet") + set_field = 0; + else if (parameters[0] == "veg") + set_field = 1; + else if (parameters[0] == "tem") + set_field = 2; + else if (parameters[0] == "evi") + set_field = 3; + else if (parameters[0] == "hil") + set_field = 4; + else if (parameters[0] == "sav") + set_field = 5; + else if (parameters[0] == "sal") + set_field = 6; + else return CR_WRONG_USAGE; - } + + if (screen->biome_highlighted) + to_set = screen->biome_idx; + else + to_set = 0; + set = true; - to_set = screen->biome_idx; - set_val = atoi(parameters[0].c_str()); + set_val = atoi(parameters[1].c_str()); } df::world_data *data = world->world_data; @@ -98,8 +116,20 @@ command_result rprobe (color_ostream &out, vector & parameters) df::world_data::T_region_map* rd = &data->region_map[rg.x][rg.y]; if (set && i == to_set) { - rd->evilness = set_val; - out << "* Set evilness to " << set_val << endl; + if (set_field == 0) + rd->wetness = set_val; + else if (set_field == 1) + rd->vegetation = set_val; + else if (set_field == 2) + rd->temperature = set_val; + else if (set_field == 3) + rd->evilness = set_val; + else if (set_field == 4) + rd->hilliness = set_val; + else if (set_field == 5) + rd->savagery = set_val; + else if (set_field == 6) + rd->saltiness = set_val; } out << i << ": x = " << rg.x << ", y = " << rg.y; @@ -108,10 +138,16 @@ command_result rprobe (color_ostream &out, vector & parameters) " region_id: " << rd->region_id << " geo_index: " << rd->geo_index << " landmass_id: " << rd->landmass_id << - " flags: " << hex << rd->flags.as_int() << dec << - " sav: " << rd->savagery << - " evil: " << rd->evilness; - + " flags: " << hex << rd->flags.as_int() << dec << endl; + out << + "wet: " << rd->wetness << " " << + "veg: " << rd->vegetation << " " << + "tem: " << rd->temperature << " " << + "evi: " << rd->evilness << " " << + "hil: " << rd->hilliness << " " << + "sav: " << rd->savagery << " " << + "sal: " << rd->saltiness; + int32_t *p = (int32_t *)rd; int c = sizeof(*rd) / sizeof(int32_t); for (int j = 0; j < c; j++) { From ac0e4a15e8ed403c8f41dba695df196960a26820 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Mon, 23 Apr 2012 09:46:57 -0500 Subject: [PATCH 23/53] Rprobe can now be used to change any of the region_map paremeters. Use at your own peril, as strangeness may ensue. --- plugins/devel/rprobe.cpp | 62 +++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/plugins/devel/rprobe.cpp b/plugins/devel/rprobe.cpp index c8d7e1b1a..7a091a962 100644 --- a/plugins/devel/rprobe.cpp +++ b/plugins/devel/rprobe.cpp @@ -62,7 +62,7 @@ command_result rprobe (color_ostream &out, vector & parameters) CoreSuspender suspend; bool set = false; - int to_set, set_val; + int to_set, set_field, set_val; // Embark screen active: estimate using world geology data VIRTUAL_CAST_VAR(screen, df::viewscreen_choose_start_sitest, Core::getTopViewscreen()); @@ -76,15 +76,33 @@ command_result rprobe (color_ostream &out, vector & parameters) return CR_FAILURE; } - if (parameters.size() == 1) + + if (parameters.size() == 2) { - if (!screen->biome_highlighted) - { + if (parameters[0] == "wet") + set_field = 0; + else if (parameters[0] == "veg") + set_field = 1; + else if (parameters[0] == "tem") + set_field = 2; + else if (parameters[0] == "evi") + set_field = 3; + else if (parameters[0] == "hil") + set_field = 4; + else if (parameters[0] == "sav") + set_field = 5; + else if (parameters[0] == "sal") + set_field = 6; + else return CR_WRONG_USAGE; - } + + if (screen->biome_highlighted) + to_set = screen->biome_idx; + else + to_set = 0; + set = true; - to_set = screen->biome_idx; - set_val = atoi(parameters[0].c_str()); + set_val = atoi(parameters[1].c_str()); } df::world_data *data = world->world_data; @@ -98,8 +116,20 @@ command_result rprobe (color_ostream &out, vector & parameters) df::world_data::T_region_map* rd = &data->region_map[rg.x][rg.y]; if (set && i == to_set) { - rd->evilness = set_val; - out << "* Set evilness to " << set_val << endl; + if (set_field == 0) + rd->wetness = set_val; + else if (set_field == 1) + rd->vegetation = set_val; + else if (set_field == 2) + rd->temperature = set_val; + else if (set_field == 3) + rd->evilness = set_val; + else if (set_field == 4) + rd->hilliness = set_val; + else if (set_field == 5) + rd->savagery = set_val; + else if (set_field == 6) + rd->saltiness = set_val; } out << i << ": x = " << rg.x << ", y = " << rg.y; @@ -108,10 +138,16 @@ command_result rprobe (color_ostream &out, vector & parameters) " region_id: " << rd->region_id << " geo_index: " << rd->geo_index << " landmass_id: " << rd->landmass_id << - " flags: " << hex << rd->flags.as_int() << dec << - " sav: " << rd->savagery << - " evil: " << rd->evilness; - + " flags: " << hex << rd->flags.as_int() << dec << endl; + out << + "wet: " << rd->wetness << " " << + "veg: " << rd->vegetation << " " << + "tem: " << rd->temperature << " " << + "evi: " << rd->evilness << " " << + "hil: " << rd->hilliness << " " << + "sav: " << rd->savagery << " " << + "sal: " << rd->saltiness; + int32_t *p = (int32_t *)rd; int c = sizeof(*rd) / sizeof(int32_t); for (int j = 0; j < c; j++) { From 763a301b4f928bf20c78fb67f864e1623409c710 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 23 Apr 2012 21:30:53 +0400 Subject: [PATCH 24/53] Add a few more lua api functions, documentation, and unit sort orders. Units::getProfessionName appears to work correctly for everything except nobles. --- LUA_API.rst | 97 ++++++++++++- Lua API.html | 89 ++++++++++-- library/LuaApi.cpp | 4 + library/include/modules/Translation.h | 2 + library/include/modules/Units.h | 6 +- library/lua/dfhack.lua | 56 +++++--- library/modules/Translation.cpp | 23 ++- library/modules/Units.cpp | 200 +++++++++++++++++++++----- plugins/lua/sort/units.lua | 52 +++++-- 9 files changed, 449 insertions(+), 80 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 050714c72..32716fbf8 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -594,41 +594,44 @@ C++ function wrappers ===================== Thin wrappers around C++ functions, similar to the ones for virtual methods. +One notable difference is that these explicit wrappers allow argument count +adjustment according to the usual lua rules, so trailing false/nil arguments +can be omitted. -* ``dfhack.TranslateName(name,in_english,only_last_name)`` +* ``dfhack.TranslateName(name[,in_english,only_last_name])`` Convert a language_name or only the last name part to string. Gui module ---------- -* ``dfhack.gui.getSelectedWorkshopJob(silent)`` +* ``dfhack.gui.getSelectedWorkshopJob([silent])`` When a job is selected in *'q'* mode, returns the job, else prints error unless silent and returns *nil*. -* ``dfhack.gui.getSelectedJob(silent)`` +* ``dfhack.gui.getSelectedJob([silent])`` Returns the job selected in a workshop or unit/jobs screen. -* ``dfhack.gui.getSelectedUnit(silent)`` +* ``dfhack.gui.getSelectedUnit([silent])`` Returns the unit selected via *'v'*, *'k'*, unit/jobs, or a full-screen item view of a cage or suchlike. -* ``dfhack.gui.getSelectedItem(silent)`` +* ``dfhack.gui.getSelectedItem([silent])`` Returns the item selected via *'v'* ->inventory, *'k'*, *'t'*, or a full-screen item view of a container. Note that in the last case, the highlighted *contained item* is returned, not the container itself. -* ``dfhack.gui.showAnnouncement(text,color,is_bright)`` +* ``dfhack.gui.showAnnouncement(text,color[,is_bright])`` Adds a regular announcement with given text, color, and brightness. The is_bright boolean actually seems to invert the brightness. -* ``dfhack.gui.showPopupAnnouncement(text,color,is_bright)`` +* ``dfhack.gui.showPopupAnnouncement(text,color[,is_bright])`` Pops up a titan-style modal announcement window. @@ -688,6 +691,10 @@ Units module Returns the language_name object visible in game, accounting for false identities. +* ``dfhack.units.getIdentity(unit)`` + + Returns the false identity of the unit if it has one, or *nil*. + * ``dfhack.units.getNemesis(unit)`` Returns the nemesis record of the unit if it has one, or *nil*. @@ -716,6 +723,19 @@ Units module Adds or removes the unit from the burrow. +* ``dfhack.units.getAge(unit[,true_age])`` + + Returns the age of the unit in years as a floating-point value. + If ``true_age`` is true, ignores false identities. + +* ``dfhack.units.getProfessionName(unit[,plural])`` + + Retrieves the profession name using custom profession or raws. + +* ``dfhack.units.getCasteProfessionName(race,caste,prof_id[,plural])`` + + Retrieves the profession name for the given race/caste using raws. + Items module ------------ @@ -856,3 +876,66 @@ Features: Invokes all listeners contained in the event in an arbitrary order using ``dfhack.safecall``. + +======= +Plugins +======= + +DFHack plugins may export native functions and events +to lua contexts. They are automatically imported by +``mkmodule('plugins.')``; this means that a lua +module file is still necessary for ``require`` to read. + +The following plugins have lua support. + +burrows +======= + +Implements extended burrow manipulations. + +Events: + +* ``onBurrowRename.foo = function(burrow)`` + + Emitted when a burrow might have been renamed either through + the game UI, or ``renameBurrow()``. + +* ``onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype)`` + + Emitted when a tile might have been dug out. Only tracked if the + auto-growing burrows feature is enabled. + +Native functions: + +* ``renameBurrow(burrow,name)`` + + Renames the burrow, emitting ``onBurrowRename`` and updating auto-grow state properly. + +* ``findByName(burrow,name)`` + + Finds a burrow by name, using the same rules as the plugin command line interface. + Namely, trailing ``'+'`` characters marking auto-grow burrows are ignored. + +* ``copyUnits(target,source,enable)`` + + Applies units from ``source`` burrow to ``target``. The ``enable`` + parameter specifies if they are to be added or removed. + +* ``copyTiles(target,source,enable)`` + + Applies tiles from ``source`` burrow to ``target``. The ``enable`` + parameter specifies if they are to be added or removed. + +* ``setTilesByKeyword(target,keyword,enable)`` + + Adds or removes tiles matching a predefined keyword. The keyword + set is the same as used by the command line. + +The lua module file also re-exports or wraps some of the +functions implemented by the dfhack core for convenience. + +sort +==== + +Does not export any native functions as of now. Instead, it +calls lua code to perform the actual ordering of list items. diff --git a/Lua API.html b/Lua API.html index cbf35f0eb..916c6eef8 100644 --- a/Lua API.html +++ b/Lua API.html @@ -350,6 +350,11 @@ ul.auto-toc { +
  • Plugins +
  • @@ -842,37 +847,40 @@ Accept dfhack_material_category auto-assign table.

    C++ function wrappers

    -

    Thin wrappers around C++ functions, similar to the ones for virtual methods.

    +

    Thin wrappers around C++ functions, similar to the ones for virtual methods. +One notable difference is that these explicit wrappers allow argument count +adjustment according to the usual lua rules, so trailing false/nil arguments +can be omitted.

      -
    • dfhack.TranslateName(name,in_english,only_last_name)

      +
    • dfhack.TranslateName(name[,in_english,only_last_name])

      Convert a language_name or only the last name part to string.

    Gui module

      -
    • dfhack.gui.getSelectedWorkshopJob(silent)

      +
    • dfhack.gui.getSelectedWorkshopJob([silent])

      When a job is selected in 'q' mode, returns the job, else prints error unless silent and returns nil.

    • -
    • dfhack.gui.getSelectedJob(silent)

      +
    • dfhack.gui.getSelectedJob([silent])

      Returns the job selected in a workshop or unit/jobs screen.

    • -
    • dfhack.gui.getSelectedUnit(silent)

      +
    • dfhack.gui.getSelectedUnit([silent])

      Returns the unit selected via 'v', 'k', unit/jobs, or a full-screen item view of a cage or suchlike.

    • -
    • dfhack.gui.getSelectedItem(silent)

      +
    • dfhack.gui.getSelectedItem([silent])

      Returns the item selected via 'v' ->inventory, 'k', 't', or a full-screen item view of a container. Note that in the last case, the highlighted contained item is returned, not the container itself.

    • -
    • dfhack.gui.showAnnouncement(text,color,is_bright)

      +
    • dfhack.gui.showAnnouncement(text,color[,is_bright])

      Adds a regular announcement with given text, color, and brightness. The is_bright boolean actually seems to invert the brightness.

    • -
    • dfhack.gui.showPopupAnnouncement(text,color,is_bright)

      +
    • dfhack.gui.showPopupAnnouncement(text,color[,is_bright])

      Pops up a titan-style modal announcement window.

    @@ -923,6 +931,9 @@ a lua list containing them.

  • dfhack.units.getVisibleName(unit)

    Returns the language_name object visible in game, accounting for false identities.

  • +
  • dfhack.units.getIdentity(unit)

    +

    Returns the false identity of the unit if it has one, or nil.

    +
  • dfhack.units.getNemesis(unit)

    Returns the nemesis record of the unit if it has one, or nil.

  • @@ -944,6 +955,16 @@ a lua list containing them.

  • dfhack.units.setInBurrow(unit,burrow,enable)

    Adds or removes the unit from the burrow.

  • +
  • dfhack.units.getAge(unit[,true_age])

    +

    Returns the age of the unit in years as a floating-point value. +If true_age is true, ignores false identities.

    +
  • +
  • dfhack.units.getProfessionName(unit[,plural])

    +

    Retrieves the profession name using custom profession or raws.

    +
  • +
  • dfhack.units.getCasteProfessionName(race,caste,prof_id[,plural])

    +

    Retrieves the profession name for the given race/caste using raws.

    +
  • @@ -1061,6 +1082,58 @@ order using dfhack.safecall.

    +
    +

    Plugins

    +

    DFHack plugins may export native functions and events +to lua contexts. They are automatically imported by +mkmodule('plugins.<name>'); this means that a lua +module file is still necessary for require to read.

    +

    The following plugins have lua support.

    +
    +

    burrows

    +

    Implements extended burrow manipulations.

    +

    Events:

    +
      +
    • onBurrowRename.foo = function(burrow)

      +

      Emitted when a burrow might have been renamed either through +the game UI, or renameBurrow().

      +
    • +
    • onDigComplete.foo = function(job_type,pos,old_tiletype,new_tiletype)

      +

      Emitted when a tile might have been dug out. Only tracked if the +auto-growing burrows feature is enabled.

      +
    • +
    +

    Native functions:

    +
      +
    • renameBurrow(burrow,name)

      +

      Renames the burrow, emitting onBurrowRename and updating auto-grow state properly.

      +
    • +
    • findByName(burrow,name)

      +

      Finds a burrow by name, using the same rules as the plugin command line interface. +Namely, trailing '+' characters marking auto-grow burrows are ignored.

      +
    • +
    • copyUnits(target,source,enable)

      +

      Applies units from source burrow to target. The enable +parameter specifies if they are to be added or removed.

      +
    • +
    • copyTiles(target,source,enable)

      +

      Applies tiles from source burrow to target. The enable +parameter specifies if they are to be added or removed.

      +
    • +
    • setTilesByKeyword(target,keyword,enable)

      +

      Adds or removes tiles matching a predefined keyword. The keyword +set is the same as used by the command line.

      +
    • +
    +

    The lua module file also re-exports or wraps some of the +functions implemented by the dfhack core for convenience.

    +
    +
    +

    sort

    +

    Does not export any native functions as of now. Instead, it +calls lua code to perform the actual ordering of list items.

    +
    +
    diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 00192c058..71df50e22 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -58,6 +58,7 @@ distribution. #include "df/unit.h" #include "df/item.h" #include "df/material.h" +#include "df/assumed_identity.h" #include "df/nemesis_record.h" #include "df/historical_figure.h" #include "df/plant_raw.h" @@ -608,6 +609,7 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, getContainer), WRAPM(Units, setNickname), WRAPM(Units, getVisibleName), + WRAPM(Units, getIdentity), WRAPM(Units, getNemesis), WRAPM(Units, isDead), WRAPM(Units, isAlive), @@ -616,6 +618,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, isInBurrow), WRAPM(Units, setInBurrow), WRAPM(Units, getAge), + WRAPM(Units, getProfessionName), + WRAPM(Units, getCasteProfessionName), { NULL, NULL } }; diff --git a/library/include/modules/Translation.h b/library/include/modules/Translation.h index 6d74431e3..f75bf6339 100644 --- a/library/include/modules/Translation.h +++ b/library/include/modules/Translation.h @@ -52,6 +52,8 @@ DFHACK_EXPORT bool copyName(df::language_name * address, df::language_name * tar DFHACK_EXPORT void setNickname(df::language_name *name, std::string nick); +DFHACK_EXPORT std::string capitalize(const std::string &str, bool all_words = false); + // translate a name using the loaded dictionaries DFHACK_EXPORT std::string TranslateName (const df::language_name * name, bool inEnglish = true, bool onlyLastPart = false); diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 775cc5181..8e4fe2ade 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -37,6 +37,7 @@ namespace df { struct nemesis_record; struct burrow; + struct assumed_identity; } /** @@ -201,6 +202,7 @@ DFHACK_EXPORT df::item *getContainer(df::unit *unit); DFHACK_EXPORT void setNickname(df::unit *unit, std::string nick); DFHACK_EXPORT df::language_name *getVisibleName(df::unit *unit); +DFHACK_EXPORT df::assumed_identity *getIdentity(df::unit *unit); DFHACK_EXPORT df::nemesis_record *getNemesis(df::unit *unit); DFHACK_EXPORT bool isDead(df::unit *unit); @@ -214,8 +216,10 @@ DFHACK_EXPORT void clearBurrowMembers(df::burrow *burrow); DFHACK_EXPORT bool isInBurrow(df::unit *unit, df::burrow *burrow); DFHACK_EXPORT void setInBurrow(df::unit *unit, df::burrow *burrow, bool enable); -DFHACK_EXPORT double getAge(df::unit *unit); +DFHACK_EXPORT double getAge(df::unit *unit, bool true_age = false); +DFHACK_EXPORT std::string getProfessionName(df::unit *unit, bool plural = false); +DFHACK_EXPORT std::string getCasteProfessionName(int race, int caste, df::profession pid, bool plural = false); } } #endif diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 5c10b002c..100aea59c 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -125,6 +125,23 @@ function xyz2pos(x,y,z) end end +function safe_index(obj,idx,...) + if obj == nil or idx == nil then + return nil + end + if type(idx) == 'number' and (idx < 0 or idx >= #obj) then + return nil + end + obj = obj[idx] + if select('#',...) > 0 then + return safe_index(obj,...) + else + return obj + end +end + +-- String conversions + function dfhack.event:__tostring() return "" end @@ -168,12 +185,14 @@ function dfhack.interpreter(prompt,hfile,env) end local prompt_str = "["..(prompt or 'lua').."]# " + local prompt_cont = string.rep(' ',#prompt_str-4)..">>> " local prompt_env = {} - local t_prompt + local cmdlinelist = {} + local t_prompt = nil local vcnt = 1 setmetatable(prompt_env, { __index = env or _G }) - local cmdlinelist={} + while true do local cmdline = dfhack.lineedit(t_prompt or prompt_str, hfile) @@ -182,26 +201,29 @@ function dfhack.interpreter(prompt,hfile,env) elseif cmdline ~= '' then local pfix = string.sub(cmdline,1,1) - if pfix == '!' or pfix == '=' then + if not t_prompt and (pfix == '!' or pfix == '=') then cmdline = 'return '..string.sub(cmdline,2) + else + pfix = nil end - table.insert(cmdlinelist,cmdline) - local code,err = load(table.concat(cmdlinelist,'\n'), '=(interactive)', 't', prompt_env) + + table.insert(cmdlinelist,cmdline) + cmdline = table.concat(cmdlinelist,'\n') + + local code,err = load(cmdline, '=(interactive)', 't', prompt_env) if code == nil then - if err:sub(-5)=="" then - t_prompt="[cont]" - - else - dfhack.printerr(err) - cmdlinelist={} - t_prompt=nil - end + if not pfix and err:sub(-5)=="" then + t_prompt=prompt_cont + else + dfhack.printerr(err) + cmdlinelist={} + t_prompt=nil + end else - - cmdlinelist={} - t_prompt=nil - + cmdlinelist={} + t_prompt=nil + local data = table.pack(safecall(code)) if data[1] and data.n > 1 then diff --git a/library/modules/Translation.cpp b/library/modules/Translation.cpp index 6b055a4ac..db1305161 100644 --- a/library/modules/Translation.cpp +++ b/library/modules/Translation.cpp @@ -81,15 +81,32 @@ bool Translation::copyName(df::language_name * source, df::language_name * targe return true; } +std::string Translation::capitalize(const std::string &str, bool all_words) +{ + string upper = str; + + if (!upper.empty()) + { + upper[0] = toupper(upper[0]); + + if (all_words) + { + for (size_t i = 1; i < upper.size(); i++) + if (isspace(upper[i-1])) + upper[i] = toupper(upper[i]); + } + } + + return upper; +} + void addNameWord (string &out, const string &word) { if (word.empty()) return; - string upper = word; - upper[0] = toupper(upper[0]); if (out.length() > 0) out.append(" "); - out.append(upper); + out.append(Translation::capitalize(word)); } void Translation::setNickname(df::language_name *name, std::string nick) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index e9699edbe..a691dc5d9 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -57,6 +57,8 @@ using namespace std; #include "df/historical_figure_info.h" #include "df/assumed_identity.h" #include "df/burrow.h" +#include "df/creature_raw.h" +#include "df/caste_raw.h" using namespace DFHack; using namespace df::enums; @@ -529,6 +531,23 @@ df::item *Units::getContainer(df::unit *unit) return NULL; } +static df::assumed_identity *getFigureIdentity(df::historical_figure *figure) +{ + if (figure && figure->info && figure->info->reputation) + return df::assumed_identity::find(figure->info->reputation->cur_identity); + + return NULL; +} + +df::assumed_identity *Units::getIdentity(df::unit *unit) +{ + CHECK_NULL_POINTER(unit); + + df::historical_figure *figure = df::historical_figure::find(unit->hist_figure_id); + + return getFigureIdentity(figure); +} + void Units::setNickname(df::unit *unit, std::string nick) { CHECK_NULL_POINTER(unit); @@ -547,25 +566,19 @@ void Units::setNickname(df::unit *unit, std::string nick) { Translation::setNickname(&figure->name, nick); - // v0.34.01: added the vampire's assumed identity - if (figure->info && figure->info->reputation) + if (auto identity = getFigureIdentity(figure)) { - auto identity = df::assumed_identity::find(figure->info->reputation->cur_identity); + auto id_hfig = df::historical_figure::find(identity->histfig_id); - if (identity) + if (id_hfig) { - auto id_hfig = df::historical_figure::find(identity->histfig_id); - - if (id_hfig) - { - // Even DF doesn't do this bit, because it's apparently - // only used for demons masquerading as gods, so you - // can't ever change their nickname in-game. - Translation::setNickname(&id_hfig->name, nick); - } - else - Translation::setNickname(&identity->name, nick); + // Even DF doesn't do this bit, because it's apparently + // only used for demons masquerading as gods, so you + // can't ever change their nickname in-game. + Translation::setNickname(&id_hfig->name, nick); } + else + Translation::setNickname(&identity->name, nick); } } } @@ -574,25 +587,14 @@ df::language_name *Units::getVisibleName(df::unit *unit) { CHECK_NULL_POINTER(unit); - df::historical_figure *figure = df::historical_figure::find(unit->hist_figure_id); - - if (figure) + if (auto identity = getIdentity(unit)) { - // v0.34.01: added the vampire's assumed identity - if (figure->info && figure->info->reputation) - { - auto identity = df::assumed_identity::find(figure->info->reputation->cur_identity); + auto id_hfig = df::historical_figure::find(identity->histfig_id); - if (identity) - { - auto id_hfig = df::historical_figure::find(identity->histfig_id); + if (id_hfig) + return &id_hfig->name; - if (id_hfig) - return &id_hfig->name; - - return &identity->name; - } - } + return &identity->name; } return &unit->name; @@ -734,7 +736,7 @@ void DFHack::Units::setInBurrow(df::unit *unit, df::burrow *burrow, bool enable) } } -double DFHack::Units::getAge(df::unit *unit) +double DFHack::Units::getAge(df::unit *unit, bool true_age) { using df::global::cur_year; using df::global::cur_year_tick; @@ -748,5 +750,139 @@ double DFHack::Units::getAge(df::unit *unit) double birth_time = unit->relations.birth_year + unit->relations.birth_time/year_ticks; double cur_time = *cur_year + *cur_year_tick / year_ticks; + if (!true_age && unit->relations.curse_year >= 0) + { + if (auto identity = getIdentity(unit)) + { + if (identity->histfig_id < 0) + birth_time = identity->birth_year + identity->birth_second/year_ticks; + } + } + return cur_time - birth_time; } + +std::string DFHack::Units::getProfessionName(df::unit *unit, bool plural) +{ + std::string prof = unit->custom_profession; + + if (prof.empty()) + prof = getCasteProfessionName(unit->race, unit->caste, unit->profession, plural); + + return prof; +} + +std::string DFHack::Units::getCasteProfessionName(int race, int casteid, df::profession pid, bool plural) +{ + std::string prof, race_prefix; + + if (pid < 0 || !is_valid_enum_item(pid)) + return ""; + + bool use_race_prefix = (race >= 0 && race != df::global::ui->race_id); + + if (auto creature = df::creature_raw::find(race)) + { + if (auto caste = vector_get(creature->caste, casteid)) + { + race_prefix = caste->caste_name[0]; + + if (plural) + prof = caste->caste_profession_name.plural[pid]; + else + prof = caste->caste_profession_name.singular[pid]; + + if (prof.empty()) + { + switch (pid) + { + case profession::CHILD: + prof = caste->child_name[plural ? 1 : 0]; + if (!prof.empty()) + use_race_prefix = false; + break; + + case profession::BABY: + prof = caste->baby_name[plural ? 1 : 0]; + if (!prof.empty()) + use_race_prefix = false; + break; + + default: + break; + } + } + } + + if (race_prefix.empty()) + race_prefix = creature->name[0]; + + if (prof.empty()) + { + if (plural) + prof = creature->profession_name.plural[pid]; + else + prof = creature->profession_name.singular[pid]; + + if (prof.empty()) + { + switch (pid) + { + case profession::CHILD: + prof = creature->general_child_name[plural ? 1 : 0]; + if (!prof.empty()) + use_race_prefix = false; + break; + + case profession::BABY: + prof = creature->general_baby_name[plural ? 1 : 0]; + if (!prof.empty()) + use_race_prefix = false; + break; + + default: + break; + } + } + } + } + + if (race_prefix.empty()) + race_prefix = "Animal"; + + if (prof.empty()) + { + switch (pid) + { + case profession::TRAINED_WAR: + prof = "War " + (use_race_prefix ? race_prefix : "Peasant"); + use_race_prefix = false; + break; + + case profession::TRAINED_HUNTER: + prof = "Hunting " + (use_race_prefix ? race_prefix : "Peasant"); + use_race_prefix = false; + break; + + case profession::STANDARD: + if (!use_race_prefix) + prof = "Peasant"; + break; + + default: + if (auto caption = ENUM_ATTR(profession, caption, pid)) + prof = caption; + else + prof = ENUM_KEY_STR(profession, pid); + } + } + + if (use_race_prefix) + { + if (!prof.empty()) + race_prefix += " "; + prof = race_prefix + prof; + } + + return Translation::capitalize(prof, true); +} diff --git a/plugins/lua/sort/units.lua b/plugins/lua/sort/units.lua index 84beff8ed..7a332d094 100644 --- a/plugins/lua/sort/units.lua +++ b/plugins/lua/sort/units.lua @@ -13,8 +13,9 @@ orders.exists = { orders.name = { key = function(unit) - if unit.name.has_name then - return dfhack.TranslateName(unit.name) + local name = dfhack.units.getVisibleName(unit) + if name and name.has_name then + return dfhack.TranslateName(name) end end, compare = utils.compare_name @@ -29,28 +30,55 @@ orders.age = { -- This assumes that units are added to active in arrival order orders.arrival = { key_table = function(units) - local tmp={} - for i,v in ipairs(units) do - tmp[v.id] = i + local size = units.n or #units + local lookup={} + for i=1,size do + if units[i] then + lookup[units[i].id] = i + end end local idx={} for i,v in ipairs(df.global.world.units.active) do - local ix = tmp[v.id] - if ix ~= nil then - idx[ix] = i + if lookup[v.id] then + idx[lookup[v.id]] = i end end return idx end } +local function findRaceCaste(unit) + local rraw = df.creature_raw.find(unit.race) + return rraw, safe_index(rraw, 'caste', unit.caste) +end + orders.profession = { key = function(unit) - local cp = unit.custom_profession - if cp == '' then - cp = df.profession.attrs[unit.profession].caption + local cp = dfhack.units.getProfessionName(unit) + if cp and cp ~= '' then + return string.lower(cp) + end + end +} + +orders.profession_class = { + key = function(unit) + local pid = unit.profession + local parent = df.profession.attrs[pid].parent + if parent >= 0 then + return parent + else + return pid + end + end +} + +orders.race = { + key = function(unit) + local rraw = findRaceCaste(unit) + if rraw then + return rraw.name[0] end - return cp end } From 1cd802d426831d1f1939648bf0e8d224a7c4b862 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 25 Apr 2012 18:28:00 +0400 Subject: [PATCH 25/53] Implement unit sorting for the pen zone assignment interface. --- library/xml | 2 +- plugins/sort.cpp | 41 +++++++++++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/library/xml b/library/xml index 0ec109ce5..f649d3100 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 0ec109ce5643be2c3ebcd4d652aeafaf78f33da7 +Subproject commit f649d31001e6023a9df5fe83c7971c17afe0d87d diff --git a/plugins/sort.cpp b/plugins/sort.cpp index 965702b57..9d63b2c6c 100644 --- a/plugins/sort.cpp +++ b/plugins/sort.cpp @@ -34,9 +34,12 @@ using namespace df::enums; using df::global::ui; using df::global::world; -using df::global::ui_building_in_owner; +using df::global::ui_building_in_assign; using df::global::ui_building_item_cursor; -using df::global::ui_owner_candidates; +using df::global::ui_building_assign_type; +using df::global::ui_building_assign_is_marked; +using df::global::ui_building_assign_units; +using df::global::ui_building_assign_items; static bool unit_list_hotkey(df::viewscreen *top); @@ -452,22 +455,44 @@ static bool maybe_sort_units(color_ostream *pout, lua_State *L, return true; case ui_sidebar_mode::QueryBuilding: - if (ui_building_in_owner && *ui_building_in_owner && - ui_owner_candidates && ui_building_item_cursor) + if (!ui_building_in_assign || !*ui_building_in_assign) + return false; + // fall through for building owner / chain assign animal + + case ui_sidebar_mode::ZonesPenInfo: + if (ui_building_item_cursor && + ui_building_assign_type && + ui_building_assign_is_marked && + ui_building_assign_units && + ui_building_assign_items && + ui_building_assign_type->size() == ui_building_assign_units->size() && + !ui_building_assign_type->empty()) { if (!L) return true; /* - * Sort building owner candidate units in the 'q' sidebar mode. + * Sort building owner candidate units in the 'q' sidebar mode, + * or pen assignment candidate units in 'z'->'N', or cage assignment. */ - sort_null_first(parameters); + // TODO: better way + bool is_assign_owner = ((*ui_building_assign_type)[0] == -1); + + if (is_assign_owner) + sort_null_first(parameters); + PARSE_SPEC("units", parameters); - if (compute_order(*pout, L, top, &order, *ui_owner_candidates)) + if (compute_order(*pout, L, top, &order, *ui_building_assign_units)) { reorder_cursor(ui_building_item_cursor, order); - reorder_vector(ui_owner_candidates, order); + reorder_vector(ui_building_assign_type, order); + reorder_vector(ui_building_assign_units, order); + + if (ui_building_assign_units->size() == ui_building_assign_items->size()) + reorder_vector(ui_building_assign_items, order); + if (ui_building_assign_units->size() == ui_building_assign_is_marked->size()) + reorder_vector(ui_building_assign_is_marked, order); } return true; From 23a0b17ff35261d5aca4bf9329250b6e544bb24b Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 25 Apr 2012 18:38:43 +0400 Subject: [PATCH 26/53] Make liquids and tiletypes force rebuild of pathfinding data. --- plugins/liquids.cpp | 4 ++++ plugins/tiletypes.cpp | 3 +++ 2 files changed, 7 insertions(+) diff --git a/plugins/liquids.cpp b/plugins/liquids.cpp index 167e51ce8..8ca66daf5 100644 --- a/plugins/liquids.cpp +++ b/plugins/liquids.cpp @@ -374,6 +374,10 @@ command_result df_liquids_execute(color_ostream &out) DFHack::DFCoord cursor(x,y,z); coord_vec all_tiles = brush->points(mcache,cursor); out << "working..." << endl; + + // Force the game to recompute its walkability cache + df::global::world->reindex_pathfinding = true; + if(mode == "obsidian") { coord_vec::iterator iter = all_tiles.begin(); diff --git a/plugins/tiletypes.cpp b/plugins/tiletypes.cpp index 73ec9f963..ae2dd7b3d 100644 --- a/plugins/tiletypes.cpp +++ b/plugins/tiletypes.cpp @@ -614,6 +614,9 @@ command_result executePaintJob(color_ostream &out) coord_vec all_tiles = brush->points(map, cursor); out.print("working...\n"); + // Force the game to recompute its walkability cache + df::global::world->reindex_pathfinding = true; + for (coord_vec::iterator iter = all_tiles.begin(); iter != all_tiles.end(); ++iter) { const df::tiletype source = map.tiletypeAt(*iter); From 9489c6ed1afd33790adc2e0b4e3c0152da8203e0 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 26 Apr 2012 11:05:35 +0400 Subject: [PATCH 27/53] Add support for a few more viewscreens to Units::getSelectedUnit. --- library/modules/Gui.cpp | 67 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 6841421b6..2d9402fca 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -50,6 +50,11 @@ using namespace DFHack; #include "df/viewscreen_joblistst.h" #include "df/viewscreen_unitlistst.h" #include "df/viewscreen_itemst.h" +#include "df/viewscreen_layerst.h" +#include "df/viewscreen_layer_workshop_profilest.h" +#include "df/viewscreen_layer_noblelistst.h" +#include "df/viewscreen_layer_overall_healthst.h" +#include "df/viewscreen_petst.h" #include "df/ui_unit_view_mode.h" #include "df/ui_sidebar_menus.h" #include "df/ui_look_list.h" @@ -63,12 +68,18 @@ using namespace DFHack; #include "df/popup_message.h" #include "df/interfacest.h" #include "df/graphic.h" +#include "df/layer_object_listst.h" using namespace df::enums; using df::global::gview; using df::global::init; using df::global::gps; +static df::layer_object_listst *getLayerList(df::viewscreen_layerst *layer, int idx) +{ + return virtual_cast(vector_get(layer->layer_objects,idx)); +} + // Predefined common guard functions bool Gui::default_hotkey(df::viewscreen *top) @@ -294,6 +305,62 @@ static df::unit *getAnyUnit(df::viewscreen *top) return ref ? ref->getUnit() : NULL; } + if (VIRTUAL_CAST_VAR(screen, df::viewscreen_layer_workshop_profilest, top)) + { + if (auto list1 = getLayerList(screen, 0)) + return vector_get(screen->workers, list1->cursor); + return NULL; + } + + if (VIRTUAL_CAST_VAR(screen, df::viewscreen_layer_noblelistst, top)) + { + switch (screen->mode) + { + case df::viewscreen_layer_noblelistst::List: + if (auto list1 = getLayerList(screen, 0)) + { + if (auto info = vector_get(screen->info, list1->cursor)) + return info->unit; + } + return NULL; + + case df::viewscreen_layer_noblelistst::Appoint: + if (auto list2 = getLayerList(screen, 1)) + { + if (auto info = vector_get(screen->candidates, list2->cursor)) + return info->unit; + } + return NULL; + + default: + return NULL; + } + } + + if (VIRTUAL_CAST_VAR(screen, df::viewscreen_petst, top)) + { + switch (screen->mode) + { + case df::viewscreen_petst::List: + if (!vector_get(screen->is_vermin, screen->cursor)) + return (df::unit*)vector_get(screen->animal, screen->cursor); + return NULL; + + case df::viewscreen_petst::SelectTrainer: + return vector_get(screen->trainer_unit, screen->trainer_cursor); + + default: + return NULL; + } + } + + if (VIRTUAL_CAST_VAR(screen, df::viewscreen_layer_overall_healthst, top)) + { + if (auto list1 = getLayerList(screen, 0)) + return vector_get(screen->unit, list1->cursor); + return NULL; + } + if (!Gui::dwarfmode_hotkey(top)) return NULL; From 6ab270d129e098f0200824a798ad2117a6ec33c8 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 26 Apr 2012 12:03:56 +0400 Subject: [PATCH 28/53] Retrieve unit noble position info, and use it in getProfessionName. --- LUA_API.rst | 10 +++- Lua API.html | 9 +++- library/LuaApi.cpp | 31 +++++++++++++ library/include/modules/Units.h | 13 +++++- library/modules/Units.cpp | 82 +++++++++++++++++++++++++++++++-- plugins/lua/sort/units.lua | 9 ++++ 6 files changed, 145 insertions(+), 9 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 32716fbf8..acbba3272 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -728,9 +728,15 @@ Units module Returns the age of the unit in years as a floating-point value. If ``true_age`` is true, ignores false identities. -* ``dfhack.units.getProfessionName(unit[,plural])`` +* ``dfhack.units.getNoblePositions(unit)`` - Retrieves the profession name using custom profession or raws. + Returns a list of tables describing noble position assignments, or *nil*. + Every table has fields ``entity``, ``assignment`` and ``position``. + +* ``dfhack.units.getProfessionName(unit[,ignore_noble,plural])`` + + Retrieves the profession name using custom profession, noble assignments + or raws. The ``ignore_noble`` boolean disables the use of noble positions. * ``dfhack.units.getCasteProfessionName(race,caste,prof_id[,plural])`` diff --git a/Lua API.html b/Lua API.html index 916c6eef8..def3790c9 100644 --- a/Lua API.html +++ b/Lua API.html @@ -959,8 +959,13 @@ a lua list containing them.

    Returns the age of the unit in years as a floating-point value. If true_age is true, ignores false identities.

    -
  • dfhack.units.getProfessionName(unit[,plural])

    -

    Retrieves the profession name using custom profession or raws.

    +
  • dfhack.units.getNoblePositions(unit)

    +

    Returns a list of tables describing noble position assignments, or nil. +Every table has fields entity, assignment and position.

    +
  • +
  • dfhack.units.getProfessionName(unit[,ignore_noble,plural])

    +

    Retrieves the profession name using custom profession, noble assignments +or raws. The ignore_noble boolean disables the use of noble positions.

  • dfhack.units.getCasteProfessionName(race,caste,prof_id[,plural])

    Retrieves the profession name for the given race/caste using raws.

    diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 71df50e22..8c56379a7 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -61,6 +61,10 @@ distribution. #include "df/assumed_identity.h" #include "df/nemesis_record.h" #include "df/historical_figure.h" +#include "df/historical_entity.h" +#include "df/entity_position.h" +#include "df/entity_position_assignment.h" +#include "df/histfig_entity_link_positionst.h" #include "df/plant_raw.h" #include "df/creature_raw.h" #include "df/inorganic_raw.h" @@ -628,8 +632,35 @@ static int units_getPosition(lua_State *state) return Lua::PushPosXYZ(state, Units::getPosition(Lua::CheckDFObject(state,1))); } +static int units_getNoblePositions(lua_State *state) +{ + std::vector np; + + if (Units::getNoblePositions(&np, Lua::CheckDFObject(state,1))) + { + lua_createtable(state, np.size(), 0); + + for (size_t i = 0; i < np.size(); i++) + { + lua_createtable(state, 0, 3); + Lua::PushDFObject(state, np[i].entity); + lua_setfield(state, -2, "entity"); + Lua::PushDFObject(state, np[i].assignment); + lua_setfield(state, -2, "assignment"); + Lua::PushDFObject(state, np[i].position); + lua_setfield(state, -2, "position"); + lua_rawseti(state, -2, i+1); + } + } + else + lua_pushnil(state); + + return 1; +} + static const luaL_Reg dfhack_units_funcs[] = { { "getPosition", units_getPosition }, + { "getNoblePositions", units_getNoblePositions }, { NULL, NULL } }; diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index 8e4fe2ade..e1f8baa12 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -38,6 +38,9 @@ namespace df struct nemesis_record; struct burrow; struct assumed_identity; + struct historical_entity; + struct entity_position_assignment; + struct entity_position; } /** @@ -218,7 +221,15 @@ DFHACK_EXPORT void setInBurrow(df::unit *unit, df::burrow *burrow, bool enable); DFHACK_EXPORT double getAge(df::unit *unit, bool true_age = false); -DFHACK_EXPORT std::string getProfessionName(df::unit *unit, bool plural = false); +struct NoblePosition { + df::historical_entity *entity; + df::entity_position_assignment *assignment; + df::entity_position *position; +}; + +DFHACK_EXPORT bool getNoblePositions(std::vector *pvec, df::unit *unit); + +DFHACK_EXPORT std::string getProfessionName(df::unit *unit, bool ignore_noble = false, bool plural = false); DFHACK_EXPORT std::string getCasteProfessionName(int race, int caste, df::profession pid, bool plural = false); } } diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index a691dc5d9..4ea225ade 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -55,6 +55,9 @@ using namespace std; #include "df/historical_entity.h" #include "df/historical_figure.h" #include "df/historical_figure_info.h" +#include "df/entity_position.h" +#include "df/entity_position_assignment.h" +#include "df/histfig_entity_link_positionst.h" #include "df/assumed_identity.h" #include "df/burrow.h" #include "df/creature_raw.h" @@ -762,14 +765,85 @@ double DFHack::Units::getAge(df::unit *unit, bool true_age) return cur_time - birth_time; } -std::string DFHack::Units::getProfessionName(df::unit *unit, bool plural) +static bool noble_pos_compare(const Units::NoblePosition &a, const Units::NoblePosition &b) +{ + if (a.position->precedence < b.position->precedence) + return true; + if (a.position->precedence > b.position->precedence) + return false; + return a.position->id < b.position->id; +} + +bool DFHack::Units::getNoblePositions(std::vector *pvec, df::unit *unit) +{ + CHECK_NULL_POINTER(unit); + + pvec->clear(); + + auto histfig = df::historical_figure::find(unit->hist_figure_id); + if (!histfig) + return false; + + for (size_t i = 0; i < histfig->entity_links.size(); i++) + { + auto link = histfig->entity_links[i]; + auto epos = strict_virtual_cast(link); + if (!epos) + continue; + + NoblePosition pos; + + pos.entity = df::historical_entity::find(epos->entity_id); + if (!pos.entity) + continue; + + pos.assignment = binsearch_in_vector(pos.entity->positions.assignments, epos->assignment_id); + if (!pos.assignment) + continue; + + pos.position = binsearch_in_vector(pos.entity->positions.own, pos.assignment->position_id); + if (!pos.position) + continue; + + pvec->push_back(pos); + } + + if (pvec->empty()) + return false; + + std::sort(pvec->begin(), pvec->end(), noble_pos_compare); + return true; +} + +std::string DFHack::Units::getProfessionName(df::unit *unit, bool ignore_noble, bool plural) { std::string prof = unit->custom_profession; + if (!prof.empty()) + return prof; - if (prof.empty()) - prof = getCasteProfessionName(unit->race, unit->caste, unit->profession, plural); + std::vector np; + + if (!ignore_noble && getNoblePositions(&np, unit)) + { + switch (unit->sex) + { + case 0: + prof = np[0].position->name_female[plural ? 1 : 0]; + break; + case 1: + prof = np[0].position->name_male[plural ? 1 : 0]; + break; + default: + break; + } + + if (prof.empty()) + prof = np[0].position->name[plural ? 1 : 0]; + if (!prof.empty()) + return prof; + } - return prof; + return getCasteProfessionName(unit->race, unit->caste, unit->profession, plural); } std::string DFHack::Units::getCasteProfessionName(int race, int casteid, df::profession pid, bool plural) diff --git a/plugins/lua/sort/units.lua b/plugins/lua/sort/units.lua index 7a332d094..35795502d 100644 --- a/plugins/lua/sort/units.lua +++ b/plugins/lua/sort/units.lua @@ -52,6 +52,15 @@ local function findRaceCaste(unit) return rraw, safe_index(rraw, 'caste', unit.caste) end +orders.noble = { + key = function(unit) + local info = dfhack.units.getNoblePositions(unit) + if info then + return info[1].position.precedence + end + end +} + orders.profession = { key = function(unit) local cp = dfhack.units.getProfessionName(unit) From 16ee049664765f2644d2ef83ad15d5ab101cd4e2 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 26 Apr 2012 12:56:28 +0400 Subject: [PATCH 29/53] Split off the burrows api from Maps and Units. --- LUA_API.rst | 47 ++--- Lua API.html | 63 +++---- library/CMakeLists.txt | 2 + library/LuaApi.cpp | 55 +++--- library/include/modules/Burrows.h | 78 +++++++++ library/include/modules/Maps.h | 29 ---- library/include/modules/Units.h | 5 - library/lua/dfhack.lua | 8 + library/modules/Burrows.cpp | 278 ++++++++++++++++++++++++++++++ library/modules/Maps.cpp | 166 ------------------ library/modules/Units.cpp | 62 ------- plugins/burrows.cpp | 31 ++-- plugins/lua/burrows.lua | 22 +-- 13 files changed, 479 insertions(+), 367 deletions(-) create mode 100644 library/include/modules/Burrows.h create mode 100644 library/modules/Burrows.cpp diff --git a/LUA_API.rst b/LUA_API.rst index acbba3272..38b0e2025 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -711,18 +711,6 @@ Units module The unit is capable of rational action, i.e. not dead, insane or zombie. -* ``dfhack.units.clearBurrowMembers(burrow)`` - - Removes all units from the burrow. - -* ``dfhack.units.isInBurrow(unit,burrow)`` - - Checks if the unit is in the burrow. - -* ``dfhack.units.setInBurrow(unit,burrow,enable)`` - - Adds or removes the unit from the burrow. - * ``dfhack.units.getAge(unit[,true_age])`` Returns the age of the unit in years as a floating-point value. @@ -807,31 +795,47 @@ Maps module Returns the local feature object with the given region coords and index. -* ``dfhack.maps.findBurrowByName(name)`` + +Burrows module +-------------- + +* ``dfhack.burrows.findByName(name)`` Returns the burrow pointer or *nil*. -* ``dfhack.maps.listBurrowBlocks(burrow)`` +* ``dfhack.burrows.clearUnits(burrow)`` - Returns a table of map block pointers. + Removes all units from the burrow. + +* ``dfhack.burrows.isAssignedUnit(burrow,unit)`` -* ``dfhack.maps.clearBurrowTiles(burrow)`` + Checks if the unit is in the burrow. + +* ``dfhack.burrows.setAssignedUnit(burrow,unit,enable)`` + + Adds or removes the unit from the burrow. + +* ``dfhack.burrows.clearTiles(burrow)`` Removes all tiles from the burrow. -* ``dfhack.maps.isBurrowTile(burrow,tile_coord)`` +* ``dfhack.burrows.listBlocks(burrow)`` + + Returns a table of map block pointers. + +* ``dfhack.burrows.isAssignedTile(burrow,tile_coord)`` Checks if the tile is in burrow. -* ``dfhack.maps.setBurrowTile(burrow,tile_coord,enable)`` +* ``dfhack.burrows.setAssignedTile(burrow,tile_coord,enable)`` Adds or removes the tile from the burrow. Returns *false* if invalid coords. -* ``dfhack.maps.isBlockBurrowTile(burrow,block,x,y)`` +* ``dfhack.burrows.isAssignedBlockTile(burrow,block,x,y)`` Checks if the tile within the block is in burrow. -* ``dfhack.maps.setBlockBurrowTile(burrow,block,x,y,enable)`` +* ``dfhack.burrows.setAssignedBlockTile(burrow,block,x,y,enable)`` Adds or removes the tile from the burrow. Returns *false* if invalid coords. @@ -937,8 +941,7 @@ Native functions: Adds or removes tiles matching a predefined keyword. The keyword set is the same as used by the command line. -The lua module file also re-exports or wraps some of the -functions implemented by the dfhack core for convenience. +The lua module file also re-exports functions from ``dfhack.burrows``. sort ==== diff --git a/Lua API.html b/Lua API.html index def3790c9..3e08b3bef 100644 --- a/Lua API.html +++ b/Lua API.html @@ -342,17 +342,18 @@ ul.auto-toc {
  • Units module
  • Items module
  • Maps module
  • +
  • Burrows module
  • -
  • Core interpreter context
  • -
  • Plugins @@ -946,15 +947,6 @@ a lua list containing them.

  • dfhack.units.isSane(unit)

    The unit is capable of rational action, i.e. not dead, insane or zombie.

  • -
  • dfhack.units.clearBurrowMembers(burrow)

    -

    Removes all units from the burrow.

    -
  • -
  • dfhack.units.isInBurrow(unit,burrow)

    -

    Checks if the unit is in the burrow.

    -
  • -
  • dfhack.units.setInBurrow(unit,burrow,enable)

    -

    Adds or removes the unit from the burrow.

    -
  • dfhack.units.getAge(unit[,true_age])

    Returns the age of the unit in years as a floating-point value. If true_age is true, ignores false identities.

    @@ -1023,32 +1015,46 @@ Returns false in case of error.

  • dfhack.maps.getLocalInitFeature(region_coord2d,index)

    Returns the local feature object with the given region coords and index.

  • -
  • dfhack.maps.findBurrowByName(name)

    + + +
    +

    Burrows module

    +
      +
    • dfhack.burrows.findByName(name)

      Returns the burrow pointer or nil.

    • -
    • dfhack.maps.listBurrowBlocks(burrow)

      -

      Returns a table of map block pointers.

      +
    • dfhack.burrows.clearUnits(burrow)

      +

      Removes all units from the burrow.

      +
    • +
    • dfhack.burrows.isAssignedUnit(burrow,unit)

      +

      Checks if the unit is in the burrow.

    • -
    • dfhack.maps.clearBurrowTiles(burrow)

      +
    • dfhack.burrows.setAssignedUnit(burrow,unit,enable)

      +

      Adds or removes the unit from the burrow.

      +
    • +
    • dfhack.burrows.clearTiles(burrow)

      Removes all tiles from the burrow.

    • -
    • dfhack.maps.isBurrowTile(burrow,tile_coord)

      +
    • dfhack.burrows.listBlocks(burrow)

      +

      Returns a table of map block pointers.

      +
    • +
    • dfhack.burrows.isAssignedTile(burrow,tile_coord)

      Checks if the tile is in burrow.

    • -
    • dfhack.maps.setBurrowTile(burrow,tile_coord,enable)

      +
    • dfhack.burrows.setAssignedTile(burrow,tile_coord,enable)

      Adds or removes the tile from the burrow. Returns false if invalid coords.

    • -
    • dfhack.maps.isBlockBurrowTile(burrow,block,x,y)

      +
    • dfhack.burrows.isAssignedBlockTile(burrow,block,x,y)

      Checks if the tile within the block is in burrow.

    • -
    • dfhack.maps.setBlockBurrowTile(burrow,block,x,y,enable)

      +
    • dfhack.burrows.setAssignedBlockTile(burrow,block,x,y,enable)

      Adds or removes the tile from the burrow. Returns false if invalid coords.

    -

    Core interpreter context

    +

    Core interpreter context

    While plugins can create any number of interpreter instances, there is one special context managed by dfhack core. It is the only context that can receive events from DF and plugins.

    @@ -1062,7 +1068,7 @@ only context that can receive events from DF and plugins.

  • -

    Event type

    +

    Event type

    An event is just a lua table with a predefined metatable that contains a __call metamethod. When it is invoked, it loops through the table with next and calls all contained values. @@ -1088,14 +1094,14 @@ order using dfhack.safecall.

    -

    Plugins

    +

    Plugins

    DFHack plugins may export native functions and events to lua contexts. They are automatically imported by mkmodule('plugins.<name>'); this means that a lua module file is still necessary for require to read.

    The following plugins have lua support.

    -

    burrows

    +

    burrows

    Implements extended burrow manipulations.

    Events:

      @@ -1130,11 +1136,10 @@ parameter specifies if they are to be added or removed.

      set is the same as used by the command line.

    -

    The lua module file also re-exports or wraps some of the -functions implemented by the dfhack core for convenience.

    +

    The lua module file also re-exports functions from dfhack.burrows.

    -

    sort

    +

    sort

    Does not export any native functions as of now. Instead, it calls lua code to perform the actual ordering of list items.

    diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index b4ea751e7..c052df9c8 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -100,6 +100,7 @@ Process-linux.cpp SET(MODULE_HEADERS include/modules/Buildings.h +include/modules/Burrows.h include/modules/Constructions.h include/modules/Units.h include/modules/Engravings.h @@ -120,6 +121,7 @@ include/modules/Graphic.h SET( MODULE_SOURCES modules/Buildings.cpp +modules/Burrows.cpp modules/Constructions.cpp modules/Units.cpp modules/Engravings.cpp diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 8c56379a7..507494669 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -46,6 +46,7 @@ distribution. #include "modules/Materials.h" #include "modules/Maps.h" #include "modules/MapCache.h" +#include "modules/Burrows.h" #include "LuaWrapper.h" #include "LuaTools.h" @@ -618,9 +619,6 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, isDead), WRAPM(Units, isAlive), WRAPM(Units, isSane), - WRAPM(Units, clearBurrowMembers), - WRAPM(Units, isInBurrow), - WRAPM(Units, setInBurrow), WRAPM(Units, getAge), WRAPM(Units, getProfessionName), WRAPM(Units, getCasteProfessionName), @@ -704,42 +702,52 @@ static const luaL_Reg dfhack_items_funcs[] = { { NULL, NULL } }; +static const LuaWrapper::FunctionReg dfhack_maps_module[] = { + WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock), + WRAPN(getTileBlock, (df::map_block* (*)(df::coord))Maps::getTileBlock), + WRAPM(Maps, getRegionBiome), + WRAPM(Maps, getGlobalInitFeature), + WRAPM(Maps, getLocalInitFeature), + { NULL, NULL } +}; -static bool maps_isBlockBurrowTile(df::burrow *burrow, df::map_block *block, int x, int y) +static const luaL_Reg dfhack_maps_funcs[] = { + { NULL, NULL } +}; + +static bool burrows_isAssignedBlockTile(df::burrow *burrow, df::map_block *block, int x, int y) { - return Maps::isBlockBurrowTile(burrow, block, df::coord2d(x,y)); + return Burrows::isAssignedBlockTile(burrow, block, df::coord2d(x,y)); } -static bool maps_setBlockBurrowTile(df::burrow *burrow, df::map_block *block, int x, int y, bool enable) +static bool burrows_setAssignedBlockTile(df::burrow *burrow, df::map_block *block, int x, int y, bool enable) { - return Maps::setBlockBurrowTile(burrow, block, df::coord2d(x,y), enable); + return Burrows::setAssignedBlockTile(burrow, block, df::coord2d(x,y), enable); } -static const LuaWrapper::FunctionReg dfhack_maps_module[] = { - WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock), - WRAPN(getTileBlock, (df::map_block* (*)(df::coord))Maps::getTileBlock), - WRAPM(Maps, getRegionBiome), - WRAPM(Maps, getGlobalInitFeature), - WRAPM(Maps, getLocalInitFeature), - WRAPM(Maps, findBurrowByName), - WRAPM(Maps, clearBurrowTiles), - WRAPN(isBlockBurrowTile, maps_isBlockBurrowTile), - WRAPN(setBlockBurrowTile, maps_setBlockBurrowTile), - WRAPM(Maps, isBurrowTile), - WRAPM(Maps, setBurrowTile), +static const LuaWrapper::FunctionReg dfhack_burrows_module[] = { + WRAPM(Burrows, findByName), + WRAPM(Burrows, clearUnits), + WRAPM(Burrows, isAssignedUnit), + WRAPM(Burrows, setAssignedUnit), + WRAPM(Burrows, clearTiles), + WRAPN(isAssignedBlockTile, burrows_isAssignedBlockTile), + WRAPN(setAssignedBlockTile, burrows_setAssignedBlockTile), + WRAPM(Burrows, isAssignedTile), + WRAPM(Burrows, setAssignedTile), { NULL, NULL } }; -static int maps_listBurrowBlocks(lua_State *state) +static int burrows_listBlocks(lua_State *state) { std::vector pvec; - Maps::listBurrowBlocks(&pvec, Lua::CheckDFObject(state,1)); + Burrows::listBlocks(&pvec, Lua::CheckDFObject(state,1)); Lua::PushVector(state, pvec); return 1; } -static const luaL_Reg dfhack_maps_funcs[] = { - { "listBurrowBlocks", maps_listBurrowBlocks }, +static const luaL_Reg dfhack_burrows_funcs[] = { + { "listBlocks", burrows_listBlocks }, { NULL, NULL } }; @@ -759,4 +767,5 @@ void OpenDFHackApi(lua_State *state) OpenModule(state, "units", dfhack_units_module, dfhack_units_funcs); OpenModule(state, "items", dfhack_items_module, dfhack_items_funcs); OpenModule(state, "maps", dfhack_maps_module, dfhack_maps_funcs); + OpenModule(state, "burrows", dfhack_burrows_module, dfhack_burrows_funcs); } diff --git a/library/include/modules/Burrows.h b/library/include/modules/Burrows.h new file mode 100644 index 000000000..e26a2dc20 --- /dev/null +++ b/library/include/modules/Burrows.h @@ -0,0 +1,78 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#pragma once +#include "Export.h" +#include "DataDefs.h" +#include "modules/Maps.h" + +#include + +/** + * \defgroup grp_burrows Burrows module and its types + * @ingroup grp_modules + */ + +namespace df +{ + struct unit; + struct burrow; + struct block_burrow; +} + +namespace DFHack +{ +namespace Burrows +{ + DFHACK_EXPORT df::burrow *findByName(std::string name); + + // Units + DFHACK_EXPORT void clearUnits(df::burrow *burrow); + + DFHACK_EXPORT bool isAssignedUnit(df::burrow *burrow, df::unit *unit); + DFHACK_EXPORT void setAssignedUnit(df::burrow *burrow, df::unit *unit, bool enable); + + // Tiles + DFHACK_EXPORT void clearTiles(df::burrow *burrow); + + DFHACK_EXPORT void listBlocks(std::vector *pvec, df::burrow *burrow); + + DFHACK_EXPORT bool isAssignedBlockTile(df::burrow *burrow, df::map_block *block, df::coord2d tile); + DFHACK_EXPORT bool setAssignedBlockTile(df::burrow *burrow, df::map_block *block, df::coord2d tile, bool enable); + + inline bool isAssignedTile(df::burrow *burrow, df::coord tile) { + return isAssignedBlockTile(burrow, Maps::getTileBlock(tile), tile); + } + inline bool setAssignedTile(df::burrow *burrow, df::coord tile, bool enable) { + return setAssignedBlockTile(burrow, Maps::getTileBlock(tile), tile, enable); + } + + DFHACK_EXPORT df::block_burrow *getBlockMask(df::burrow *burrow, df::map_block *block, bool create = false); + DFHACK_EXPORT bool deleteBlockMask(df::burrow *burrow, df::map_block *block, df::block_burrow *mask); + + inline bool deleteBlockMask(df::burrow *burrow, df::map_block *block) { + return deleteBlockMask(burrow, block, getBlockMask(burrow, block)); + } +} +} diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index b7341f87c..b6077a85e 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -255,35 +255,6 @@ extern DFHACK_EXPORT bool SortBlockEvents(df::map_block *block, /// remove a block event from the block by address extern DFHACK_EXPORT bool RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, df::block_square_event * which ); - -/* - * BURROWS - */ - -DFHACK_EXPORT df::burrow *findBurrowByName(std::string name); - -DFHACK_EXPORT void listBurrowBlocks(std::vector *pvec, df::burrow *burrow); -DFHACK_EXPORT void clearBurrowTiles(df::burrow *burrow); - -DFHACK_EXPORT df::block_burrow *getBlockBurrowMask(df::burrow *burrow, df::map_block *block, bool create = false); -DFHACK_EXPORT bool deleteBlockBurrowMask(df::burrow *burrow, df::map_block *block, df::block_burrow *mask); - -inline bool deleteBlockBurrowMask(df::burrow *burrow, df::map_block *block) -{ - return deleteBlockBurrowMask(burrow, block, getBlockBurrowMask(burrow, block)); -} - - -DFHACK_EXPORT bool isBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coord2d tile); -DFHACK_EXPORT bool setBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coord2d tile, bool enable); - -inline bool isBurrowTile(df::burrow *burrow, df::coord tile) { - return isBlockBurrowTile(burrow, getTileBlock(tile), tile); -} -inline bool setBurrowTile(df::burrow *burrow, df::coord tile, bool enable) { - return setBlockBurrowTile(burrow, getTileBlock(tile), tile, enable); -} - } } #endif diff --git a/library/include/modules/Units.h b/library/include/modules/Units.h index e1f8baa12..7bf4f101b 100644 --- a/library/include/modules/Units.h +++ b/library/include/modules/Units.h @@ -214,11 +214,6 @@ DFHACK_EXPORT bool isSane(df::unit *unit); DFHACK_EXPORT bool isCitizen(df::unit *unit); DFHACK_EXPORT bool isDwarf(df::unit *unit); -DFHACK_EXPORT void clearBurrowMembers(df::burrow *burrow); - -DFHACK_EXPORT bool isInBurrow(df::unit *unit, df::burrow *burrow); -DFHACK_EXPORT void setInBurrow(df::unit *unit, df::burrow *burrow, bool enable); - DFHACK_EXPORT double getAge(df::unit *unit, bool true_age = false); struct NoblePosition { diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 100aea59c..aedd460b1 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -125,6 +125,14 @@ function xyz2pos(x,y,z) end end +function rawset_default(target,source) + for k,v in pairs(source) do + if rawget(target,k) == nil then + rawset(target,k,v) + end + end +end + function safe_index(obj,idx,...) if obj == nil or idx == nil then return nil diff --git a/library/modules/Burrows.cpp b/library/modules/Burrows.cpp new file mode 100644 index 000000000..62f62779a --- /dev/null +++ b/library/modules/Burrows.cpp @@ -0,0 +1,278 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#include "Internal.h" + +#include +#include +using namespace std; + +#include "Error.h" +#include "Core.h" + +#include "modules/Burrows.h" +#include "modules/Maps.h" +#include "modules/Units.h" + +#include "MiscUtils.h" + +#include "DataDefs.h" +#include "df/ui.h" +#include "df/burrow.h" +#include "df/block_burrow.h" +#include "df/block_burrow_link.h" + +using namespace DFHack; +using namespace df::enums; + +using df::global::world; +using df::global::ui; + +df::burrow *Burrows::findByName(std::string name) +{ + auto &vec = df::burrow::get_vector(); + + for (size_t i = 0; i < vec.size(); i++) + if (vec[i]->name == name) + return vec[i]; + + return NULL; +} + +void Burrows::clearUnits(df::burrow *burrow) +{ + CHECK_NULL_POINTER(burrow); + + for (size_t i = 0; i < burrow->units.size(); i++) + { + auto unit = df::unit::find(burrow->units[i]); + + if (unit) + erase_from_vector(unit->burrows, burrow->id); + } + + burrow->units.clear(); + + // Sync ui if active + if (ui && ui->main.mode == ui_sidebar_mode::Burrows && + ui->burrows.in_add_units_mode && ui->burrows.sel_id == burrow->id) + { + auto &sel = ui->burrows.sel_units; + + for (size_t i = 0; i < sel.size(); i++) + sel[i] = false; + } +} + +bool Burrows::isAssignedUnit(df::burrow *burrow, df::unit *unit) +{ + CHECK_NULL_POINTER(unit); + CHECK_NULL_POINTER(burrow); + + return binsearch_index(unit->burrows, burrow->id) >= 0; +} + +void Burrows::setAssignedUnit(df::burrow *burrow, df::unit *unit, bool enable) +{ + using df::global::ui; + + CHECK_NULL_POINTER(unit); + CHECK_NULL_POINTER(burrow); + + if (enable) + { + insert_into_vector(unit->burrows, burrow->id); + insert_into_vector(burrow->units, unit->id); + } + else + { + erase_from_vector(unit->burrows, burrow->id); + erase_from_vector(burrow->units, unit->id); + } + + // Sync ui if active + if (ui && ui->main.mode == ui_sidebar_mode::Burrows && + ui->burrows.in_add_units_mode && ui->burrows.sel_id == burrow->id) + { + int idx = linear_index(ui->burrows.list_units, unit); + if (idx >= 0) + ui->burrows.sel_units[idx] = enable; + } +} + +void Burrows::listBlocks(std::vector *pvec, df::burrow *burrow) +{ + CHECK_NULL_POINTER(burrow); + + pvec->clear(); + pvec->reserve(burrow->block_x.size()); + + df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z); + + for (size_t i = 0; i < burrow->block_x.size(); i++) + { + df::coord pos(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]); + + auto block = Maps::getBlock(pos - base); + if (block) + pvec->push_back(block); + } +} + +static void destroyBurrowMask(df::block_burrow *mask) +{ + if (!mask) return; + + auto link = mask->link; + + link->prev->next = link->next; + if (link->next) + link->next->prev = link->prev; + delete link; + + delete mask; +} + +void Burrows::clearTiles(df::burrow *burrow) +{ + CHECK_NULL_POINTER(burrow); + + df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z); + + for (size_t i = 0; i < burrow->block_x.size(); i++) + { + df::coord pos(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]); + + auto block = Maps::getBlock(pos - base); + if (!block) + continue; + + destroyBurrowMask(getBlockMask(burrow, block)); + } + + burrow->block_x.clear(); + burrow->block_y.clear(); + burrow->block_z.clear(); +} + +df::block_burrow *Burrows::getBlockMask(df::burrow *burrow, df::map_block *block, bool create) +{ + CHECK_NULL_POINTER(burrow); + CHECK_NULL_POINTER(block); + + int32_t id = burrow->id; + df::block_burrow_link *prev = &block->block_burrows; + df::block_burrow_link *link = prev->next; + + for (; link; prev = link, link = link->next) + if (link->item->id == id) + return link->item; + + if (create) + { + link = new df::block_burrow_link; + link->item = new df::block_burrow; + + link->item->id = burrow->id; + link->item->tile_bitmask.clear(); + link->item->link = link; + + link->next = NULL; + link->prev = prev; + prev->next = link; + + df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z); + df::coord pos = base + block->map_pos/16; + + burrow->block_x.push_back(pos.x); + burrow->block_y.push_back(pos.y); + burrow->block_z.push_back(pos.z); + + return link->item; + } + + return NULL; +} + +bool Burrows::deleteBlockMask(df::burrow *burrow, df::map_block *block, df::block_burrow *mask) +{ + CHECK_NULL_POINTER(burrow); + CHECK_NULL_POINTER(block); + + if (!mask) + return false; + + df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z); + df::coord pos = base + block->map_pos/16; + + destroyBurrowMask(mask); + + for (size_t i = 0; i < burrow->block_x.size(); i++) + { + df::coord cur(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]); + + if (cur == pos) + { + vector_erase_at(burrow->block_x, i); + vector_erase_at(burrow->block_y, i); + vector_erase_at(burrow->block_z, i); + + break; + } + } + + return true; +} + +bool Burrows::isAssignedBlockTile(df::burrow *burrow, df::map_block *block, df::coord2d tile) +{ + CHECK_NULL_POINTER(burrow); + + if (!block) return false; + + auto mask = getBlockMask(burrow, block); + + return mask ? mask->getassignment(tile & 15) : false; +} + +bool Burrows::setAssignedBlockTile(df::burrow *burrow, df::map_block *block, df::coord2d tile, bool enable) +{ + CHECK_NULL_POINTER(burrow); + + if (!block) return false; + + auto mask = getBlockMask(burrow, block, enable); + + if (mask) + { + mask->setassignment(tile & 15, enable); + + if (!enable && !mask->has_assignments()) + deleteBlockMask(burrow, block, mask); + } + + return true; +} + diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 3c4933fb7..e7d49deda 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -1056,169 +1056,3 @@ void MapExtras::MapCache::resetTags() it->second->tags = NULL; } } - -df::burrow *Maps::findBurrowByName(std::string name) -{ - auto &vec = df::burrow::get_vector(); - - for (size_t i = 0; i < vec.size(); i++) - if (vec[i]->name == name) - return vec[i]; - - return NULL; -} - -void Maps::listBurrowBlocks(std::vector *pvec, df::burrow *burrow) -{ - CHECK_NULL_POINTER(burrow); - - pvec->clear(); - pvec->reserve(burrow->block_x.size()); - - df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z); - - for (size_t i = 0; i < burrow->block_x.size(); i++) - { - df::coord pos(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]); - - auto block = getBlock(pos - base); - if (block) - pvec->push_back(block); - } -} - -static void destroyBurrowMask(df::block_burrow *mask) -{ - if (!mask) return; - - auto link = mask->link; - - link->prev->next = link->next; - if (link->next) - link->next->prev = link->prev; - delete link; - - delete mask; -} - -void Maps::clearBurrowTiles(df::burrow *burrow) -{ - CHECK_NULL_POINTER(burrow); - - df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z); - - for (size_t i = 0; i < burrow->block_x.size(); i++) - { - df::coord pos(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]); - - auto block = getBlock(pos - base); - if (!block) - continue; - - destroyBurrowMask(getBlockBurrowMask(burrow, block)); - } - - burrow->block_x.clear(); - burrow->block_y.clear(); - burrow->block_z.clear(); -} - -df::block_burrow *Maps::getBlockBurrowMask(df::burrow *burrow, df::map_block *block, bool create) -{ - CHECK_NULL_POINTER(burrow); - CHECK_NULL_POINTER(block); - - int32_t id = burrow->id; - df::block_burrow_link *prev = &block->block_burrows; - df::block_burrow_link *link = prev->next; - - for (; link; prev = link, link = link->next) - if (link->item->id == id) - return link->item; - - if (create) - { - link = new df::block_burrow_link; - link->item = new df::block_burrow; - - link->item->id = burrow->id; - link->item->tile_bitmask.clear(); - link->item->link = link; - - link->next = NULL; - link->prev = prev; - prev->next = link; - - df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z); - df::coord pos = base + block->map_pos/16; - - burrow->block_x.push_back(pos.x); - burrow->block_y.push_back(pos.y); - burrow->block_z.push_back(pos.z); - - return link->item; - } - - return NULL; -} - -bool Maps::deleteBlockBurrowMask(df::burrow *burrow, df::map_block *block, df::block_burrow *mask) -{ - CHECK_NULL_POINTER(burrow); - CHECK_NULL_POINTER(block); - - if (!mask) - return false; - - df::coord base(world->map.region_x*3,world->map.region_y*3,world->map.region_z); - df::coord pos = base + block->map_pos/16; - - destroyBurrowMask(mask); - - for (size_t i = 0; i < burrow->block_x.size(); i++) - { - df::coord cur(burrow->block_x[i], burrow->block_y[i], burrow->block_z[i]); - - if (cur == pos) - { - vector_erase_at(burrow->block_x, i); - vector_erase_at(burrow->block_y, i); - vector_erase_at(burrow->block_z, i); - - break; - } - } - - return true; -} - -bool Maps::isBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coord2d tile) -{ - CHECK_NULL_POINTER(burrow); - - if (!block) return false; - - auto mask = getBlockBurrowMask(burrow, block); - - return mask ? mask->getassignment(tile & 15) : false; -} - -bool Maps::setBlockBurrowTile(df::burrow *burrow, df::map_block *block, df::coord2d tile, bool enable) -{ - CHECK_NULL_POINTER(burrow); - - if (!block) return false; - - auto mask = getBlockBurrowMask(burrow, block, enable); - - if (mask) - { - mask->setassignment(tile & 15, enable); - - if (!enable && !mask->has_assignments()) - deleteBlockBurrowMask(burrow, block, mask); - } - - return true; -} - diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 4ea225ade..77a836c9f 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -677,68 +677,6 @@ bool DFHack::Units::isDwarf(df::unit *unit) return unit->race == ui->race_id; } -void DFHack::Units::clearBurrowMembers(df::burrow *burrow) -{ - CHECK_NULL_POINTER(burrow); - - for (size_t i = 0; i < burrow->units.size(); i++) - { - auto unit = df::unit::find(burrow->units[i]); - - if (unit) - erase_from_vector(unit->burrows, burrow->id); - } - - burrow->units.clear(); - - // Sync ui if active - if (ui && ui->main.mode == ui_sidebar_mode::Burrows && - ui->burrows.in_add_units_mode && ui->burrows.sel_id == burrow->id) - { - auto &sel = ui->burrows.sel_units; - - for (size_t i = 0; i < sel.size(); i++) - sel[i] = false; - } -} - - -bool DFHack::Units::isInBurrow(df::unit *unit, df::burrow *burrow) -{ - CHECK_NULL_POINTER(unit); - CHECK_NULL_POINTER(burrow); - - return binsearch_index(unit->burrows, burrow->id) >= 0; -} - -void DFHack::Units::setInBurrow(df::unit *unit, df::burrow *burrow, bool enable) -{ - using df::global::ui; - - CHECK_NULL_POINTER(unit); - CHECK_NULL_POINTER(burrow); - - if (enable) - { - insert_into_vector(unit->burrows, burrow->id); - insert_into_vector(burrow->units, unit->id); - } - else - { - erase_from_vector(unit->burrows, burrow->id); - erase_from_vector(burrow->units, unit->id); - } - - // Sync ui if active - if (ui && ui->main.mode == ui_sidebar_mode::Burrows && - ui->burrows.in_add_units_mode && ui->burrows.sel_id == burrow->id) - { - int idx = linear_index(ui->burrows.list_units, unit); - if (idx >= 0) - ui->burrows.sel_units[idx] = enable; - } -} - double DFHack::Units::getAge(df::unit *unit, bool true_age) { using df::global::cur_year; diff --git a/plugins/burrows.cpp b/plugins/burrows.cpp index 8080710c3..a6df3524b 100644 --- a/plugins/burrows.cpp +++ b/plugins/burrows.cpp @@ -13,6 +13,7 @@ #include "modules/MapCache.h" #include "modules/World.h" #include "modules/Units.h" +#include "modules/Burrows.h" #include "TileTypes.h" #include "DataDefs.h" @@ -345,7 +346,7 @@ static void handle_burrow_rename(color_ostream &out, df::burrow *burrow) static void add_to_burrows(std::vector &burrows, df::coord pos) { for (size_t i = 0; i < burrows.size(); i++) - Maps::setBurrowTile(burrows[i], pos, true); + Burrows::setAssignedTile(burrows[i], pos, true); } static void add_walls_to_burrows(color_ostream &out, std::vector &burrows, @@ -379,7 +380,7 @@ static void handle_dig_complete(color_ostream &out, df::job_type job, df::coord for (size_t i = 0; i < grow_burrows.size(); i++) { auto b = df::burrow::find(grow_burrows[i]); - if (b && Maps::isBurrowTile(b, pos)) + if (b && Burrows::isAssignedTile(b, pos)) to_grow.push_back(b); } @@ -446,7 +447,7 @@ static void copyUnits(df::burrow *target, df::burrow *source, bool enable) if (source == target) { if (!enable) - Units::clearBurrowMembers(target); + Burrows::clearUnits(target); return; } @@ -456,7 +457,7 @@ static void copyUnits(df::burrow *target, df::burrow *source, bool enable) auto unit = df::unit::find(source->units[i]); if (unit) - Units::setInBurrow(unit, target, enable); + Burrows::setAssignedUnit(target, unit, enable); } } @@ -468,22 +469,22 @@ static void copyTiles(df::burrow *target, df::burrow *source, bool enable) if (source == target) { if (!enable) - Maps::clearBurrowTiles(target); + Burrows::clearTiles(target); return; } std::vector pvec; - Maps::listBurrowBlocks(&pvec, source); + Burrows::listBlocks(&pvec, source); for (size_t i = 0; i < pvec.size(); i++) { auto block = pvec[i]; - auto smask = Maps::getBlockBurrowMask(source, block); + auto smask = Burrows::getBlockMask(source, block); if (!smask) continue; - auto tmask = Maps::getBlockBurrowMask(target, block, enable); + auto tmask = Burrows::getBlockMask(target, block, enable); if (!tmask) continue; @@ -498,7 +499,7 @@ static void copyTiles(df::burrow *target, df::burrow *source, bool enable) tmask->tile_bitmask[j] &= ~smask->tile_bitmask[j]; if (!tmask->has_assignments()) - Maps::deleteBlockBurrowMask(target, block, tmask); + Burrows::deleteBlockMask(target, block, tmask); } } } @@ -523,7 +524,7 @@ static void setTilesByDesignation(df::burrow *target, df::tile_designation d_mas continue; if (!mask) - mask = Maps::getBlockBurrowMask(target, block, enable); + mask = Burrows::getBlockMask(target, block, enable); if (!mask) goto next_block; @@ -532,7 +533,7 @@ static void setTilesByDesignation(df::burrow *target, df::tile_designation d_mas } if (mask && !enable && !mask->has_assignments()) - Maps::deleteBlockBurrowMask(target, block, mask); + Burrows::deleteBlockMask(target, block, mask); next_block:; } @@ -625,7 +626,7 @@ static command_result burrow(color_ostream &out, vector ¶meters) if (!target) return CR_WRONG_USAGE; - Units::clearBurrowMembers(target); + Burrows::clearUnits(target); } } else if (cmd == "set-units" || cmd == "add-units" || cmd == "remove-units") @@ -638,7 +639,7 @@ static command_result burrow(color_ostream &out, vector ¶meters) return CR_WRONG_USAGE; if (cmd == "set-units") - Units::clearBurrowMembers(target); + Burrows::clearUnits(target); bool enable = (cmd != "remove-units"); @@ -662,7 +663,7 @@ static command_result burrow(color_ostream &out, vector ¶meters) if (!target) return CR_WRONG_USAGE; - Maps::clearBurrowTiles(target); + Burrows::clearTiles(target); } } else if (cmd == "set-tiles" || cmd == "add-tiles" || cmd == "remove-tiles") @@ -675,7 +676,7 @@ static command_result burrow(color_ostream &out, vector ¶meters) return CR_WRONG_USAGE; if (cmd == "set-tiles") - Maps::clearBurrowTiles(target); + Burrows::clearTiles(target); bool enable = (cmd != "remove-tiles"); diff --git a/plugins/lua/burrows.lua b/plugins/lua/burrows.lua index b1d51f6a6..871295fdc 100644 --- a/plugins/lua/burrows.lua +++ b/plugins/lua/burrows.lua @@ -2,6 +2,11 @@ local _ENV = mkmodule('plugins.burrows') --[[ + Native events: + + * onBurrowRename(burrow) + * onDigComplete(job_type,pos,old_tiletype,new_tiletype) + Native functions: * findByName(name) -> burrow @@ -13,21 +18,6 @@ local _ENV = mkmodule('plugins.burrows') --]] -clearUnits = dfhack.units.clearBurrowMembers - -function isBurrowUnit(burrow,unit) - return dfhack.units.isInBurrow(unit,burrow) -end -function setBurrowUnit(burrow,unit,enable) - return dfhack.units.setInBurrow(unit,burrow,enable) -end - -clearTiles = dfhack.maps.clearBurrowTiles -listBlocks = dfhack.maps.listBurrowBlocks - -isBurrowTile = dfhack.maps.isBurrowTile -setBurrowTile = dfhack.maps.setBurrowTile -isBlockBurrowTile = dfhack.maps.isBlockBurrowTile -setBlockBurrowTile = dfhack.maps.setBlockBurrowTile +rawset_default(_ENV, dfhack.burrows) return _ENV \ No newline at end of file From 5f5152f4498722d61e7c9c7f207ef3b5dfbda84c Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Thu, 26 Apr 2012 09:38:25 -0500 Subject: [PATCH 30/53] Autolabor: Instead of guessing who the nobles are based on noble skills, actually find out who they are and behave appropriately. Certain responsibilities are assigned various penalties to increase their holders' availability to perform noble functions. Also, holders of the "manage health" responsibility are always assigned the diagnose labor. The responsibility penalties are first-approximation and will likely need tuning. --- plugins/autolabor.cpp | 124 ++++++++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 46 deletions(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index cfd9419db..73f8e7c27 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -23,6 +23,15 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include + +#include using std::string; using std::endl; @@ -446,21 +455,44 @@ static const struct labor_default default_labor_infos[] = { /* WAX_WORKING */ {AUTOMATIC, false, 1, 200, 0}, }; -static const df::job_skill noble_skills[] = { - df::enums::job_skill::APPRAISAL, - df::enums::job_skill::ORGANIZATION, - df::enums::job_skill::RECORD_KEEPING, +static const int responsibility_penalties[] = { + 0, /* LAW_MAKING */ + 0, /* LAW_ENFORCEMENT */ + 3000, /* RECEIVE_DIPLOMATS */ + 0, /* MEET_WORKERS */ + 1000, /* MANAGE_PRODUCTION */ + 3000, /* TRADE */ + 1000, /* ACCOUNTING */ + 0, /* ESTABLISH_COLONY_TRADE_AGREEMENTS */ + 0, /* MAKE_INTRODUCTIONS */ + 0, /* MAKE_PEACE_AGREEMENTS */ + 0, /* MAKE_TOPIC_AGREEMENTS */ + 0, /* COLLECT_TAXES */ + 0, /* ESCORT_TAX_COLLECTOR */ + 0, /* EXECUTIONS */ + 0, /* TAME_EXOTICS */ + 0, /* RELIGION */ + 0, /* ATTACK_ENEMIES */ + 0, /* PATROL_TERRITORY */ + 0, /* MILITARY_GOALS */ + 0, /* MILITARY_STRATEGY */ + 0, /* UPGRADE_SQUAD_EQUIPMENT */ + 0, /* EQUIPMENT_MANIFESTS */ + 0, /* SORT_AMMUNITION */ + 0, /* BUILD_MORALE */ + 5000 /* HEALTH_MANAGEMENT */ }; struct dwarf_info_t { int highest_skill; int total_skill; - bool is_best_noble; int mastery_penalty; int assigned_jobs; dwarf_state state; bool has_exclusive_labor; + int noble_penalty; // penalty for assignment due to noble status + bool medical; // this dwarf has medical responsibility }; static bool isOptionEnabled(unsigned flag) @@ -714,15 +746,45 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) std::vector dwarf_info(n_dwarfs); - std::vector best_noble(ARRAY_COUNT(noble_skills)); - std::vector highest_noble_skill(ARRAY_COUNT(noble_skills)); - std::vector highest_noble_experience(ARRAY_COUNT(noble_skills)); - // Find total skill and highest skill for each dwarf. More skilled dwarves shouldn't be used for minor tasks. for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) { - assert(dwarfs[dwarf]->status.souls.size() > 0); +// assert(dwarfs[dwarf]->status.souls.size() > 0); +// assert fails can cause DF to crash, so don't do that + + if (dwarfs[dwarf]->status.souls.size() <= 0) + continue; + + // compute noble penalty + + int noble_penalty = 0; + + df::historical_figure* hf = df::historical_figure::find(dwarfs[dwarf]->hist_figure_id); + for (int i = 0; i < hf->entity_links.size(); i++) { + df::histfig_entity_link* hfelink = hf->entity_links.at(i); + if (hfelink->getType() == df::histfig_entity_link_type::POSITION) { + df::histfig_entity_link_positionst *epos = + (df::histfig_entity_link_positionst*) hfelink; + df::historical_entity* entity = df::historical_entity::find(epos->entity_id); + if (!entity) + continue; + df::entity_position_assignment* assignment = binsearch_in_vector(entity->positions.assignments, epos->assignment_id); + if (!assignment) + continue; + df::entity_position* position = binsearch_in_vector(entity->positions.own, assignment->position_id); + if (!position) + continue; + + for (int n = 0; n < 25; n++) + if (position->responsibilities[n]) + noble_penalty += responsibility_penalties[n]; + + if (position->responsibilities[df::entity_position_responsibility::HEALTH_MANAGEMENT]) + dwarf_info[dwarf].medical = true; + + } + } for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s != dwarfs[dwarf]->status.souls[0]->skills.end(); s++) { @@ -733,30 +795,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) int skill_level = (*s)->rating; int skill_experience = (*s)->experience; - // Track the dwarfs with the best Appraisal, Organization, and Record Keeping skills. - // They are likely to have appointed noble positions, so should be kept free where possible. - - int noble_skill_id = -1; - for (int i = 0; i < ARRAY_COUNT(noble_skills); i++) - { - if (skill == noble_skills[i]) - noble_skill_id = i; - } - - if (noble_skill_id >= 0) - { - assert(noble_skill_id < ARRAY_COUNT(noble_skills)); - - if (highest_noble_skill[noble_skill_id] < skill_level || - (highest_noble_skill[noble_skill_id] == skill_level && - highest_noble_experience[noble_skill_id] < skill_experience)) - { - highest_noble_skill[noble_skill_id] = skill_level; - highest_noble_experience[noble_skill_id] = skill_experience; - best_noble[noble_skill_id] = dwarf; - } - } - // Track total & highest skill among normal/medical skills. (We don't care about personal or social skills.) if (skill_class != df::enums::job_skill_class::Normal && skill_class != df::enums::job_skill_class::Medical) @@ -768,24 +806,13 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } } - // Mark the best nobles, so we try to keep them non-busy. (It would be better to find the actual assigned nobles.) - - for (int i = 0; i < ARRAY_COUNT(noble_skills); i++) - { - assert(best_noble[i] >= 0); - assert(best_noble[i] < n_dwarfs); - - dwarf_info[best_noble[i]].is_best_noble = true; - } - // Calculate a base penalty for using each dwarf for a task he isn't good at. for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) { dwarf_info[dwarf].mastery_penalty -= 40 * dwarf_info[dwarf].highest_skill; dwarf_info[dwarf].mastery_penalty -= 10 * dwarf_info[dwarf].total_skill; - if (dwarf_info[dwarf].is_best_noble) - dwarf_info[dwarf].mastery_penalty -= 250; + dwarf_info[dwarf].mastery_penalty -= dwarf_info[dwarf].noble_penalty; for (int labor = ENUM_FIRST_ITEM(unit_labor); labor <= ENUM_LAST_ITEM(unit_labor); labor++) { @@ -1032,6 +1059,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) * Note that only idle and busy dwarfs count towards the number of dwarfs. "Other" dwarfs * (sleeping, eating, on break, etc.) will have labors assigned, but will not be counted. * Military and children/nobles will not have labors assigned. + * Dwarfs with the "health management" responsibility are always assigned DIAGNOSIS. */ for (int i = 0; i < candidates.size() && labor_infos[labor].active_dwarfs < max_dwarfs; i++) { @@ -1040,6 +1068,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) assert(dwarf >= 0); assert(dwarf < n_dwarfs); + + bool preferred_dwarf = false; if (want_idle_dwarf && dwarf_info[dwarf].state == IDLE) preferred_dwarf = true; @@ -1047,6 +1077,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) preferred_dwarf = true; if (previously_enabled[dwarf] && labor_infos[labor].is_exclusive) preferred_dwarf = true; + if (dwarf_info[dwarf].medical && labor == df::unit_labor::DIAGNOSE) + preferred_dwarf = true; if (labor_infos[labor].active_dwarfs >= min_dwarfs && !preferred_dwarf) continue; From 82a0e52a3eeb55d62dd51cf92b5b1b26d0cc4c66 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 26 Apr 2012 18:51:39 +0400 Subject: [PATCH 31/53] Add api to check the walkable cache, and update flow_forbid in liquids. --- LUA_API.rst | 10 +++++++++ Lua API.html | 9 ++++++++ library/LuaApi.cpp | 41 +++++++++++++++++++++------------- library/include/LuaTools.h | 5 +++++ library/include/modules/Maps.h | 2 ++ library/modules/Maps.cpp | 14 ++++++++++++ plugins/liquids.cpp | 4 ++++ 7 files changed, 70 insertions(+), 15 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 38b0e2025..d2afacd42 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -795,6 +795,16 @@ Maps module Returns the local feature object with the given region coords and index. +* ``dfhack.maps.canWalkBetween(pos1, pos2)`` + + Checks if a dwarf may be able to walk between the two tiles, + using a pathfinding cache maintained by the game. Note that + this cache is only updated when the game is unpaused, and thus + can get out of date if doors are forbidden or unforbidden, or + tools like liquids or tiletypes are used. It also cannot possibly + take into account anything that depends on the actual units, like + burrows, or the presence of invaders. + Burrows module -------------- diff --git a/Lua API.html b/Lua API.html index 3e08b3bef..fd2004b8d 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1015,6 +1015,15 @@ Returns false in case of error.

  • dfhack.maps.getLocalInitFeature(region_coord2d,index)

    Returns the local feature object with the given region coords and index.

  • +
  • dfhack.maps.canWalkBetween(pos1, pos2)

    +

    Checks if a dwarf may be able to walk between the two tiles, +using a pathfinding cache maintained by the game. Note that +this cache is only updated when the game is unpaused, and thus +can get out of date if doors are forbidden or unforbidden, or +tools like liquids or tiletypes are used. It also cannot possibly +take into account anything that depends on the actual units, like +burrows, or the presence of invaders.

    +
  • diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 507494669..8855a50da 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -80,6 +80,17 @@ distribution. using namespace DFHack; using namespace DFHack::LuaWrapper; +void Lua::Push(lua_State *state, const Units::NoblePosition &pos) +{ + lua_createtable(state, 0, 3); + Lua::PushDFObject(state, pos.entity); + lua_setfield(state, -2, "entity"); + Lua::PushDFObject(state, pos.assignment); + lua_setfield(state, -2, "assignment"); + Lua::PushDFObject(state, pos.position); + lua_setfield(state, -2, "position"); +} + int Lua::PushPosXYZ(lua_State *state, df::coord pos) { if (!pos.isValid()) @@ -558,11 +569,15 @@ static void OpenModule(lua_State *state, const char *mname, #define WRAP(function) { #function, df::wrap_function(function,true) } #define WRAPN(name, function) { #name, df::wrap_function(function,true) } +/***** Translation module *****/ + static const LuaWrapper::FunctionReg dfhack_module[] = { WRAPM(Translation, TranslateName), { NULL, NULL } }; +/***** Gui module *****/ + static const LuaWrapper::FunctionReg dfhack_gui_module[] = { WRAPM(Gui, getSelectedWorkshopJob), WRAPM(Gui, getSelectedJob), @@ -573,6 +588,8 @@ static const LuaWrapper::FunctionReg dfhack_gui_module[] = { { NULL, NULL } }; +/***** Job module *****/ + static bool jobEqual(df::job *job1, df::job *job2) { return *job1 == *job2; } static bool jobItemEqual(df::job_item *job1, df::job_item *job2) { return *job1 == *job2; } @@ -609,6 +626,7 @@ static const luaL_Reg dfhack_job_funcs[] = { { NULL, NULL } }; +/***** Units module *****/ static const LuaWrapper::FunctionReg dfhack_units_module[] = { WRAPM(Units, getContainer), @@ -635,21 +653,7 @@ static int units_getNoblePositions(lua_State *state) std::vector np; if (Units::getNoblePositions(&np, Lua::CheckDFObject(state,1))) - { - lua_createtable(state, np.size(), 0); - - for (size_t i = 0; i < np.size(); i++) - { - lua_createtable(state, 0, 3); - Lua::PushDFObject(state, np[i].entity); - lua_setfield(state, -2, "entity"); - Lua::PushDFObject(state, np[i].assignment); - lua_setfield(state, -2, "assignment"); - Lua::PushDFObject(state, np[i].position); - lua_setfield(state, -2, "position"); - lua_rawseti(state, -2, i+1); - } - } + Lua::PushVector(state, np); else lua_pushnil(state); @@ -662,6 +666,8 @@ static const luaL_Reg dfhack_units_funcs[] = { { NULL, NULL } }; +/***** Items module *****/ + static bool items_moveToGround(df::item *item, df::coord pos) { MapExtras::MapCache mc; @@ -702,12 +708,15 @@ static const luaL_Reg dfhack_items_funcs[] = { { NULL, NULL } }; +/***** Maps module *****/ + static const LuaWrapper::FunctionReg dfhack_maps_module[] = { WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock), WRAPN(getTileBlock, (df::map_block* (*)(df::coord))Maps::getTileBlock), WRAPM(Maps, getRegionBiome), WRAPM(Maps, getGlobalInitFeature), WRAPM(Maps, getLocalInitFeature), + WRAPM(Maps, canWalkBetween), { NULL, NULL } }; @@ -715,6 +724,8 @@ static const luaL_Reg dfhack_maps_funcs[] = { { NULL, NULL } }; +/***** Burrows module *****/ + static bool burrows_isAssignedBlockTile(df::burrow *burrow, df::map_block *block, int x, int y) { return Burrows::isAssignedBlockTile(burrow, block, df::coord2d(x,y)); diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index a41b58d1c..1ca79d331 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -36,6 +36,10 @@ distribution. namespace DFHack { class function_identity_base; + + namespace Units { + struct NoblePosition; + } } namespace DFHack {namespace Lua { @@ -243,6 +247,7 @@ namespace DFHack {namespace Lua { } inline void Push(lua_State *state, df::coord &obj) { PushDFObject(state, &obj); } inline void Push(lua_State *state, df::coord2d &obj) { PushDFObject(state, &obj); } + void Push(lua_State *state, const Units::NoblePosition &pos); template inline void Push(lua_State *state, T *ptr) { PushDFObject(state, ptr); } diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index b6077a85e..332954cda 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -255,6 +255,8 @@ extern DFHACK_EXPORT bool SortBlockEvents(df::map_block *block, /// remove a block event from the block by address extern DFHACK_EXPORT bool RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, df::block_square_event * which ); + +DFHACK_EXPORT bool canWalkBetween(df::coord pos1, df::coord pos2); } } #endif diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index e7d49deda..ddf5d5069 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -387,6 +387,20 @@ bool Maps::ReadGeology(vector > *layer_mats, vector return true; } +bool Maps::canWalkBetween(df::coord pos1, df::coord pos2) +{ + auto block1 = getTileBlock(pos1); + auto block2 = getTileBlock(pos2); + + if (!block1 || !block2) + return false; + + auto tile1 = MapExtras::index_tile(block1->walkable, pos1); + auto tile2 = MapExtras::index_tile(block2->walkable, pos2); + + return tile1 && tile1 == tile2; +} + #define COPY(a,b) memcpy(&a,&b,sizeof(a)) MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) diff --git a/plugins/liquids.cpp b/plugins/liquids.cpp index 8ca66daf5..f644398f5 100644 --- a/plugins/liquids.cpp +++ b/plugins/liquids.cpp @@ -388,6 +388,7 @@ command_result df_liquids_execute(color_ostream &out) mcache.setTemp2At(*iter,10015); df::tile_designation des = mcache.designationAt(*iter); des.bits.flow_size = 0; + des.bits.flow_forbid = false; mcache.setDesignationAt(*iter, des); iter ++; } @@ -494,6 +495,9 @@ command_result df_liquids_execute(color_ostream &out) mcache.setTemp1At(current,10015); mcache.setTemp2At(current,10015); } + // mark the tile passable or impassable like the game does + des.bits.flow_forbid = des.bits.flow_size && + (des.bits.liquid_type == tile_liquid::Magma || des.bits.flow_size > 3); mcache.setDesignationAt(current,des); } seen_blocks.insert(mcache.BlockAt(current / 16)); From b4a8bcd8d02f0277d21e1da2c26201a48ccd559b Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 19 Apr 2012 19:17:07 +0400 Subject: [PATCH 32/53] Refactor MapCache: make it parse everything that is known re tiles & mats. Conflicts: library/xml --- library/include/modules/MapCache.h | 249 +++++++++++++---- library/include/modules/Materials.h | 10 + library/modules/Maps.cpp | 410 ++++++++++++++++++++++++---- plugins/dig.cpp | 8 +- plugins/mapexport/mapexport.cpp | 4 +- plugins/probe.cpp | 58 ++-- plugins/prospector.cpp | 4 +- 7 files changed, 600 insertions(+), 143 deletions(-) diff --git a/library/include/modules/MapCache.h b/library/include/modules/MapCache.h index 8eac514be..4ddf0c984 100644 --- a/library/include/modules/MapCache.h +++ b/library/include/modules/MapCache.h @@ -31,12 +31,17 @@ distribution. #include #include #include "df/map_block.h" +#include "df/tile_bitmask.h" #include "df/block_square_event_mineralst.h" #include "df/construction.h" #include "df/item.h" using namespace DFHack; +namespace df { + struct world_region_details; +} + namespace MapExtras { @@ -50,6 +55,38 @@ inline bool is_valid_tile_coord(df::coord2d p) { return (p.x & ~15) == 0 && (p.y & ~15) == 0; } +class Block; + +class BlockInfo +{ + Block *mblock; + MapCache *parent; + df::map_block *block; + +public: + t_blockmaterials veinmats; + t_blockmaterials basemats; + t_blockmaterials grass; + std::map plants; + + df::feature_init *global_feature; + df::feature_init *local_feature; + + BlockInfo() + : mblock(NULL), parent(NULL), block(NULL), + global_feature(NULL), local_feature(NULL) {} + + void prepare(Block *mblock); + + t_matpair getBaseMaterial(df::tiletype tt, df::coord2d pos); + + static void SquashVeins(df::map_block *mb, t_blockmaterials & materials); + static void SquashFrozenLiquids (df::map_block *mb, tiletypes40d & frozen); + static void SquashRocks (df::map_block *mb, t_blockmaterials & materials, + std::vector< std::vector > * layerassign); + static void SquashGrass(df::map_block *mb, t_blockmaterials &materials); +}; + class DFHACK_EXPORT Block { public: @@ -62,39 +99,81 @@ public: //Arbitrary tag field for flood fills etc. int16_t &tag(df::coord2d p) { + if (!tags) init_tags(); return index_tile(tags, p); } + // Base layer + df::tiletype baseTiletypeAt(df::coord2d p) + { + if (!tiles) init_tiles(); + return index_tile(tiles->base_tiles,p); + } + t_matpair baseMaterialAt(df::coord2d p) + { + if (!basemats) init_tiles(true); + return t_matpair( + index_tile(basemats->mattype,p), + index_tile(basemats->matindex,p) + ); + } + bool isVeinAt(df::coord2d p) + { + using namespace df::enums::tiletype_material; + auto tm = tileMaterial(baseTiletypeAt(p)); + return tm == MINERAL; + } + bool isLayerAt(df::coord2d p) + { + using namespace df::enums::tiletype_material; + auto tm = tileMaterial(baseTiletypeAt(p)); + return tm == STONE || tm == SOIL; + } + int16_t veinMaterialAt(df::coord2d p) { - return index_tile(veinmats,p); + return isVeinAt(p) ? baseMaterialAt(p).mat_index : -1; } - int16_t baseMaterialAt(df::coord2d p) + int16_t layerMaterialAt(df::coord2d p) { - return index_tile(basemats,p); + if (!basemats) init_tiles(true); + return index_tile(basemats->layermat,p); } - df::tiletype BaseTileTypeAt(df::coord2d p) + // Static layer (base + constructions) + df::tiletype staticTiletypeAt(df::coord2d p) { - auto tt = index_tile(contiles,p); - if (tt != tiletype::Void) return tt; - tt = index_tile(icetiles,p); - if (tt != tiletype::Void) return tt; - return index_tile(rawtiles,p); + if (!tiles) init_tiles(); + if (tiles->con_info) + return index_tile(tiles->con_info->tiles,p); + return baseTiletypeAt(p); } - df::tiletype TileTypeAt(df::coord2d p) + t_matpair staticMaterialAt(df::coord2d p) { - return index_tile(rawtiles,p); + if (!basemats) init_tiles(true); + if (tiles->con_info) + return t_matpair( + index_tile(tiles->con_info->mattype,p), + index_tile(tiles->con_info->matindex,p) + ); + return baseMaterialAt(p); } - bool setTiletypeAt(df::coord2d p, df::tiletype tiletype) + bool hasConstructionAt(df::coord2d p) { - if(!valid) return false; - dirty_tiletypes = true; - //printf("setting block %d/%d/%d , %d %d\n",x,y,z, p.x, p.y); - index_tile(rawtiles,p) = tiletype; - return true; + if (!tiles) init_tiles(); + return tiles->con_info && + tiles->con_info->constructed.getassignment(p); } + df::tiletype tiletypeAt(df::coord2d p) + { + if (!block) return tiletype::Void; + if (tiles) + return index_tile(tiles->raw_tiles,p); + return index_tile(block->tiletype,p); + } + bool setTiletypeAt(df::coord2d, df::tiletype tt, bool force = false); + uint16_t temperature1At(df::coord2d p) { return index_tile(temp1,p); @@ -179,29 +258,29 @@ public: bool is_valid() { return valid; } df::map_block *getRaw() { return block; } + MapCache *getParent() { return parent; } + private: friend class MapCache; MapCache *parent; df::map_block *block; - static void SquashVeins(df::map_block *mb, t_blockmaterials & materials); - static void SquashFrozenLiquids (df::map_block *mb, tiletypes40d & frozen); - static void SquashConstructions (df::map_block *mb, tiletypes40d & constructions); - static void SquashRocks (df::map_block *mb, t_blockmaterials & materials, - std::vector< std::vector > * layerassign); - bool valid; bool dirty_designations:1; - bool dirty_tiletypes:1; + bool dirty_tiles:1; bool dirty_temperatures:1; bool dirty_blockflags:1; bool dirty_occupancies:1; DFCoord bcoord; - int16_t tags[16][16]; + // Custom tags for floodfill + typedef int16_t T_tags[16]; + T_tags *tags; + void init_tags(); + // On-ground item count info typedef int T_item_counts[16]; T_item_counts *item_counts; void init_item_counts(); @@ -209,30 +288,53 @@ private: bool addItemOnGround(df::item *item); bool removeItemOnGround(df::item *item); - tiletypes40d rawtiles; + struct ConInfo { + df::tile_bitmask constructed; + df::tiletype tiles[16][16]; + t_blockmaterials mattype; + t_blockmaterials matindex; + }; + struct TileInfo { + df::tile_bitmask frozen; + df::tile_bitmask dirty_raw; + df::tiletype raw_tiles[16][16]; + + ConInfo *con_info; + + df::tile_bitmask dirty_base; + df::tiletype base_tiles[16][16]; + + TileInfo(); + ~TileInfo(); + + void init_coninfo(); + }; + struct BasematInfo { + df::tile_bitmask dirty; + t_blockmaterials mattype; + t_blockmaterials matindex; + t_blockmaterials layermat; + + BasematInfo(); + }; + TileInfo *tiles; + BasematInfo *basemats; + void init_tiles(bool basemat = false); + void ParseTiles(TileInfo *tiles); + void ParseBasemats(TileInfo *tiles, BasematInfo *bmats); + designations40d designation; occupancies40d occupancy; t_blockflags blockflags; - t_blockmaterials veinmats; - t_blockmaterials basemats; t_temperatures temp1; t_temperatures temp2; - tiletypes40d contiles; // what's underneath constructions - tiletypes40d icetiles; // what's underneath ice }; class DFHACK_EXPORT MapCache { public: - MapCache() - { - valid = 0; - Maps::getSize(x_bmax, y_bmax, z_max); - x_tmax = x_bmax*16; y_tmax = y_bmax*16; - validgeo = Maps::ReadGeology(&layer_mats, &geoidx); - valid = true; - }; + MapCache(); ~MapCache() { trash(); @@ -251,19 +353,60 @@ class DFHACK_EXPORT MapCache df::tiletype baseTiletypeAt (DFCoord tilecoord) { - Block * b= BlockAtTile(tilecoord); - return b ? b->BaseTileTypeAt(tilecoord) : tiletype::Void; + Block *b = BlockAtTile(tilecoord); + return b ? b->baseTiletypeAt(tilecoord) : tiletype::Void; + } + t_matpair baseMaterialAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->baseMaterialAt(tilecoord) : t_matpair(); + } + int16_t veinMaterialAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->veinMaterialAt(tilecoord) : -1; + } + int16_t layerMaterialAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->layerMaterialAt(tilecoord) : -1; + } + bool isVeinAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b && b->isVeinAt(tilecoord); } + bool isLayerAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b && b->isLayerAt(tilecoord); + } + + df::tiletype staticTiletypeAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->staticTiletypeAt(tilecoord) : tiletype::Void; + } + t_matpair staticMaterialAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b ? b->staticMaterialAt(tilecoord) : t_matpair(); + } + bool hasConstructionAt (DFCoord tilecoord) + { + Block *b = BlockAtTile(tilecoord); + return b && b->hasConstructionAt(tilecoord); + } + df::tiletype tiletypeAt (DFCoord tilecoord) { - Block * b= BlockAtTile(tilecoord); - return b ? b->TileTypeAt(tilecoord) : tiletype::Void; + Block *b = BlockAtTile(tilecoord); + return b ? b->tiletypeAt(tilecoord) : tiletype::Void; } - bool setTiletypeAt(DFCoord tilecoord, df::tiletype tiletype) + bool setTiletypeAt (DFCoord tilecoord, df::tiletype tt, bool force = false) { - if (Block * b= BlockAtTile(tilecoord)) - return b->setTiletypeAt(tilecoord, tiletype); - return false; + Block *b = BlockAtTile(tilecoord); + return b && b->setTiletypeAt(tilecoord, tt, force); } uint16_t temperature1At (DFCoord tilecoord) @@ -290,17 +433,6 @@ class DFHACK_EXPORT MapCache return false; } - int16_t veinMaterialAt (DFCoord tilecoord) - { - Block * b= BlockAtTile(tilecoord); - return b ? b->veinMaterialAt(tilecoord) : -1; - } - int16_t baseMaterialAt (DFCoord tilecoord) - { - Block * b= BlockAtTile(tilecoord); - return b ? b->baseMaterialAt(tilecoord) : -1; - } - int16_t tagAt(DFCoord tilecoord) { Block * b= BlockAtTile(tilecoord); @@ -311,6 +443,7 @@ class DFHACK_EXPORT MapCache Block * b= BlockAtTile(tilecoord); if (b) b->tag(tilecoord) = val; } + void resetTags(); df::tile_designation designationAt (DFCoord tilecoord) { @@ -378,6 +511,7 @@ class DFHACK_EXPORT MapCache private: friend class Block; + friend class BlockInfo; bool valid; bool validgeo; @@ -388,6 +522,7 @@ private: uint32_t z_max; std::vector geoidx; std::vector< std::vector > layer_mats; + std::map region_details; std::map blocks; }; } diff --git a/library/include/modules/Materials.h b/library/include/modules/Materials.h index 3d70dc9fa..76c89de30 100644 --- a/library/include/modules/Materials.h +++ b/library/include/modules/Materials.h @@ -60,6 +60,14 @@ namespace df namespace DFHack { + struct t_matpair { + int16_t mat_type; + int32_t mat_index; + + t_matpair(int16_t type = -1, int32_t index = -1) + : mat_type(type), mat_index(index) {} + }; + struct DFHACK_EXPORT MaterialInfo { static const int NUM_BUILTIN = 19; static const int GROUP_SIZE = 200; @@ -91,6 +99,7 @@ namespace DFHack public: MaterialInfo(int16_t type = -1, int32_t index = -1) { decode(type, index); } + MaterialInfo(const t_matpair &mp) { decode(mp.mat_type, mp.mat_index); } template MaterialInfo(T *ptr) { decode(ptr); } bool isValid() const { return material != NULL; } @@ -107,6 +116,7 @@ namespace DFHack bool decode(int16_t type, int32_t index = -1); bool decode(df::item *item); bool decode(const df::material_vec_ref &vr, int idx); + bool decode(const t_matpair &mp) { return decode(mp.mat_type, mp.mat_index); } template bool decode(T *ptr) { // Assume and exploit a certain naming convention diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 327e26986..b45ae7191 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -51,6 +51,9 @@ using namespace std; #include "df/burrow.h" #include "df/block_burrow.h" #include "df/block_burrow_link.h" +#include "df/world_region_details.h" +#include "df/builtin_mats.h" +#include "df/block_square_event_grassst.h" using namespace DFHack; using namespace df::enums; @@ -158,7 +161,7 @@ df::world_data::T_region_map *Maps::getRegionBiome(df::coord2d rgn_pos) df::feature_init *Maps::getGlobalInitFeature(int32_t index) { auto data = world->world_data; - if (!data) + if (!data || index < 0) return NULL; auto rgn = vector_get(data->underground_regions, index); @@ -186,7 +189,7 @@ bool Maps::GetGlobalFeature(t_feature &feature, int32_t index) df::feature_init *Maps::getLocalInitFeature(df::coord2d rgn_pos, int32_t index) { auto data = world->world_data; - if (!data) + if (!data || index < 0) return NULL; if (rgn_pos.x < 0 || rgn_pos.x >= data->world_width || @@ -353,7 +356,7 @@ bool Maps::ReadGeology(vector > *layer_mats, vector int bioRX = world->map.region_x / 16 + ((i % 3) - 1); int bioRY = world->map.region_y / 16 + ((i / 3) - 1); - df::coord2d rgn_pos(clip_range(bioRX,0,world_width-1),clip_range(bioRX,0,world_height-1)); + df::coord2d rgn_pos(clip_range(bioRX,0,world_width-1),clip_range(bioRY,0,world_height-1)); (*geoidx)[i] = rgn_pos; @@ -389,7 +392,7 @@ bool Maps::ReadGeology(vector > *layer_mats, vector MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) { dirty_designations = false; - dirty_tiletypes = false; + dirty_tiles = false; dirty_temperatures = false; dirty_blockflags = false; dirty_occupancies = false; @@ -397,12 +400,12 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) bcoord = _bcoord; block = Maps::getBlock(bcoord); item_counts = NULL; - - memset(tags,0,sizeof(tags)); + tags = NULL; + tiles = NULL; + basemats = NULL; if(block) { - COPY(rawtiles, block->tiletype); COPY(designation, block->designation); COPY(occupancy, block->occupancy); blockflags = block->flags; @@ -410,33 +413,189 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) COPY(temp1, block->temperature_1); COPY(temp2, block->temperature_2); - SquashVeins(block,veinmats); - SquashConstructions(block, contiles); - SquashFrozenLiquids(block, icetiles); - if(parent->validgeo) - SquashRocks(block,basemats,&parent->layer_mats); - else - memset(basemats,-1,sizeof(basemats)); valid = true; } else { blockflags.whole = 0; - memset(rawtiles,0,sizeof(rawtiles)); memset(designation,0,sizeof(designation)); memset(occupancy,0,sizeof(occupancy)); memset(temp1,0,sizeof(temp1)); memset(temp2,0,sizeof(temp2)); - memset(veinmats,-1,sizeof(veinmats)); - memset(contiles,0,sizeof(contiles)); - memset(icetiles,0,sizeof(icetiles)); - memset(basemats,-1,sizeof(basemats)); } } MapExtras::Block::~Block() { delete[] item_counts; + delete[] tags; + delete tiles; + delete basemats; +} + +void MapExtras::Block::init_tags() +{ + if (!tags) + tags = new T_tags[16]; + memset(tags,0,sizeof(T_tags)*16); +} + +void MapExtras::Block::init_tiles(bool basemat) +{ + if (!tiles) + { + tiles = new TileInfo(); + + if (block) + ParseTiles(tiles); + } + + if (basemat && !basemats) + { + basemats = new BasematInfo(); + + if (block) + ParseBasemats(tiles, basemats); + } +} + +MapExtras::Block::TileInfo::TileInfo() +{ + frozen.clear(); + dirty_raw.clear(); + memset(raw_tiles,0,sizeof(raw_tiles)); + con_info = NULL; + dirty_base.clear(); + memset(base_tiles,0,sizeof(base_tiles)); +} + +MapExtras::Block::TileInfo::~TileInfo() +{ + delete con_info; +} + +void MapExtras::Block::TileInfo::init_coninfo() +{ + if (con_info) + return; + + con_info = new ConInfo(); + con_info->constructed.clear(); + COPY(con_info->tiles, base_tiles); + memset(con_info->mattype, -1, sizeof(con_info->mattype)); + memset(con_info->matindex, -1, sizeof(con_info->matindex)); +} + +MapExtras::Block::BasematInfo::BasematInfo() +{ + dirty.clear(); + memset(mattype,0,sizeof(mattype)); + memset(matindex,-1,sizeof(matindex)); + memset(layermat,-1,sizeof(layermat)); +} + +bool MapExtras::Block::setTiletypeAt(df::coord2d pos, df::tiletype tt, bool force) +{ + if (!block) + return false; + + if (!basemats) + init_tiles(true); + + pos = pos & 15; + + dirty_tiles = true; + tiles->raw_tiles[pos.x][pos.y] = tt; + tiles->dirty_raw.setassignment(pos, true); + + return true; +} + +void MapExtras::Block::ParseTiles(TileInfo *tiles) +{ + tiletypes40d icetiles; + BlockInfo::SquashFrozenLiquids(block, icetiles); + + COPY(tiles->raw_tiles, block->tiletype); + + for (int x = 0; x < 16; x++) + { + for (int y = 0; y < 16; y++) + { + using namespace df::enums::tiletype_material; + + df::tiletype tt = tiles->raw_tiles[x][y]; + df::coord coord = block->map_pos + df::coord(x,y,0); + + // Frozen liquid comes topmost + if (tileMaterial(tt) == FROZEN_LIQUID) + { + tiles->frozen.setassignment(x,y,true); + if (icetiles[x][y] != tiletype::Void) + { + tt = icetiles[x][y]; + } + } + + // The next layer may be construction + bool is_con = false; + + if (tileMaterial(tt) == CONSTRUCTION) + { + df::construction *con = df::construction::find(coord); + if (con) + { + if (!tiles->con_info) + tiles->init_coninfo(); + + is_con = true; + tiles->con_info->constructed.setassignment(x,y,true); + tiles->con_info->tiles[x][y] = tt; + tiles->con_info->mattype[x][y] = con->mat_type; + tiles->con_info->matindex[x][y] = con->mat_index; + + tt = con->original_tile; + } + } + + // Finally, base material + tiles->base_tiles[x][y] = tt; + + // Copy base info back to construction layer + if (tiles->con_info && !is_con) + tiles->con_info->tiles[x][y] = tt; + } + } +} + +void MapExtras::Block::ParseBasemats(TileInfo *tiles, BasematInfo *bmats) +{ + BlockInfo info; + + info.prepare(this); + + COPY(bmats->layermat, info.basemats); + + for (int x = 0; x < 16; x++) + { + for (int y = 0; y < 16; y++) + { + using namespace df::enums::tiletype_material; + + auto tt = tiles->base_tiles[x][y]; + auto mat = info.getBaseMaterial(tt, df::coord2d(x,y)); + + bmats->mattype[x][y] = mat.mat_type; + bmats->matindex[x][y] = mat.mat_index; + + // Copy base info back to construction layer + if (tiles->con_info && !tiles->con_info->constructed.getassignment(x,y)) + { + tiles->con_info->mattype[x][y] = mat.mat_type; + tiles->con_info->matindex[x][y] = mat.mat_index; + } + } + } } bool MapExtras::Block::Write () @@ -454,10 +613,21 @@ bool MapExtras::Block::Write () block->flags.bits.designated = true; dirty_designations = false; } - if(dirty_tiletypes) + if(dirty_tiles && tiles) { - COPY(block->tiletype, rawtiles); - dirty_tiletypes = false; + dirty_tiles = false; + + for (int x = 0; x < 16; x++) + { + for (int y = 0; y < 16; y++) + { + if (tiles->dirty_raw.getassignment(x,y)) + block->tiletype[x][y] = tiles->raw_tiles[x][y]; + } + } + + delete tiles; tiles = NULL; + delete basemats; basemats = NULL; } if(dirty_temperatures) { @@ -473,65 +643,145 @@ bool MapExtras::Block::Write () return true; } -void MapExtras::Block::SquashVeins(df::map_block *mb, t_blockmaterials & materials) +void MapExtras::BlockInfo::prepare(Block *mblock) { - memset(materials,-1,sizeof(materials)); - std::vector veins; - Maps::SortBlockEvents(mb,&veins); - for (uint32_t x = 0;x<16;x++) for (uint32_t y = 0; y< 16;y++) + this->mblock = mblock; + + block = mblock->getRaw(); + parent = mblock->getParent(); + + SquashVeins(block,veinmats); + SquashGrass(block, grass); + + if (parent->validgeo) + SquashRocks(block,basemats,&parent->layer_mats); + else + memset(basemats,-1,sizeof(basemats)); + + for (size_t i = 0; i < block->plants.size(); i++) { - df::tiletype tt = mb->tiletype[x][y]; - if (tileMaterial(tt) == tiletype_material::MINERAL) + auto pp = block->plants[i]; + plants[pp->pos] = pp; + } + + global_feature = Maps::getGlobalInitFeature(block->global_feature); + local_feature = Maps::getLocalInitFeature(block->region_pos, block->local_feature); +} + +t_matpair MapExtras::BlockInfo::getBaseMaterial(df::tiletype tt, df::coord2d pos) +{ + using namespace df::enums::tiletype_material; + + t_matpair rv(0,-1); + int x = pos.x, y = pos.y; + + switch (tileMaterial(tt)) { + case CONSTRUCTION: // just a fallback + case SOIL: + case STONE: + rv.mat_index = basemats[x][y]; + break; + + case MINERAL: + rv.mat_index = veinmats[x][y]; + break; + + case LAVA_STONE: + if (auto details = parent->region_details[mblock->biomeRegionAt(pos)]) + rv.mat_index = details->lava_stone; + break; + + case PLANT: + rv.mat_type = MaterialInfo::PLANT_BASE; + if (auto plant = plants[block->map_pos + df::coord(x,y,0)]) { - for (size_t i = 0; i < veins.size(); i++) + if (auto raw = df::plant_raw::find(plant->material)) { - if (veins[i]->getassignment(x,y)) - materials[x][y] = veins[i]->inorganic_mat; + rv.mat_type = raw->material_defs.type_basic_mat; + rv.mat_index = raw->material_defs.idx_basic_mat; } } + break; + + case GRASS_LIGHT: + case GRASS_DARK: + case GRASS_DRY: + case GRASS_DEAD: + rv.mat_type = MaterialInfo::PLANT_BASE; + if (auto raw = df::plant_raw::find(grass[x][y])) + { + rv.mat_type = raw->material_defs.type_basic_mat; + rv.mat_index = raw->material_defs.idx_basic_mat; + } + break; + + case FEATURE: + { + auto dsgn = block->designation[x][y]; + + if (dsgn.bits.feature_local && local_feature) + local_feature->getMaterial(&rv.mat_type, &rv.mat_index); + else if (dsgn.bits.feature_global && global_feature) + global_feature->getMaterial(&rv.mat_type, &rv.mat_index); + + break; } + + case FROZEN_LIQUID: + case POOL: + case BROOK: + case RIVER: + rv.mat_type = builtin_mats::WATER; + break; + + case ASHES: + case FIRE: + case CAMPFIRE: + rv.mat_type = builtin_mats::ASH; + break; + + default: + rv.mat_type = -1; + } + + return rv; } -void MapExtras::Block::SquashFrozenLiquids(df::map_block *mb, tiletypes40d & frozen) +void MapExtras::BlockInfo::SquashVeins(df::map_block *mb, t_blockmaterials & materials) { - std::vector ices; - Maps::SortBlockEvents(mb,NULL,&ices); - for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++) + std::vector veins; + Maps::SortBlockEvents(mb,&veins); + memset(materials,-1,sizeof(materials)); + for (uint32_t x = 0;x<16;x++) for (uint32_t y = 0; y< 16;y++) { - df::tiletype tt = mb->tiletype[x][y]; - frozen[x][y] = tiletype::Void; - if (tileMaterial(tt) == tiletype_material::FROZEN_LIQUID) + for (size_t i = 0; i < veins.size(); i++) { - for (size_t i = 0; i < ices.size(); i++) - { - df::tiletype tt2 = ices[i]->tiles[x][y]; - if (tt2 != tiletype::Void) - { - frozen[x][y] = tt2; - break; - } - } + if (veins[i]->getassignment(x,y)) + materials[x][y] = veins[i]->inorganic_mat; } } } -void MapExtras::Block::SquashConstructions (df::map_block *mb, tiletypes40d & constructions) +void MapExtras::BlockInfo::SquashFrozenLiquids(df::map_block *mb, tiletypes40d & frozen) { + std::vector ices; + Maps::SortBlockEvents(mb,NULL,&ices); + memset(frozen,0,sizeof(frozen)); for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++) { - df::tiletype tt = mb->tiletype[x][y]; - constructions[x][y] = tiletype::Void; - if (tileMaterial(tt) == tiletype_material::CONSTRUCTION) + for (size_t i = 0; i < ices.size(); i++) { - DFCoord coord = mb->map_pos + df::coord(x,y,0); - df::construction *con = df::construction::find(coord); - if (con) - constructions[x][y] = con->original_tile; + df::tiletype tt2 = ices[i]->tiles[x][y]; + if (tt2 != tiletype::Void) + { + frozen[x][y] = tt2; + break; + } } } } -void MapExtras::Block::SquashRocks (df::map_block *mb, t_blockmaterials & materials, +void MapExtras::BlockInfo::SquashRocks (df::map_block *mb, t_blockmaterials & materials, std::vector< std::vector > * layerassign) { // get the layer materials @@ -547,6 +797,25 @@ void MapExtras::Block::SquashRocks (df::map_block *mb, t_blockmaterials & materi } } +void MapExtras::BlockInfo::SquashGrass(df::map_block *mb, t_blockmaterials &materials) +{ + std::vector grasses; + Maps::SortBlockEvents(mb, NULL, NULL, NULL, &grasses); + memset(materials,-1,sizeof(materials)); + for (uint32_t x = 0; x < 16; x++) for (uint32_t y = 0; y < 16; y++) + { + int amount = 0; + for (size_t i = 0; i < grasses.size(); i++) + { + if (grasses[i]->amount[x][y] >= amount) + { + amount = grasses[i]->amount[x][y]; + materials[x][y] = grasses[i]->plant_index; + } + } + } +} + df::coord2d MapExtras::Block::biomeRegionAt(df::coord2d p) { if (!block) @@ -662,6 +931,24 @@ bool MapExtras::Block::removeItemOnGround(df::item *item) return true; } +MapExtras::MapCache::MapCache() +{ + valid = 0; + Maps::getSize(x_bmax, y_bmax, z_max); + x_tmax = x_bmax*16; y_tmax = y_bmax*16; + validgeo = Maps::ReadGeology(&layer_mats, &geoidx); + valid = true; + + if (auto data = df::global::world->world_data) + { + for (size_t i = 0; i < data->region_details.size(); i++) + { + auto info = data->region_details[i]; + region_details[info->pos] = info; + } + } +} + MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord) { if(!valid) @@ -685,6 +972,15 @@ MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord) } } +void MapExtras::MapCache::resetTags() +{ + for (auto it = blocks.begin(); it != blocks.end(); ++it) + { + delete[] it->second->tags; + it->second->tags = NULL; + } +} + df::burrow *Maps::findBurrowByName(std::string name) { auto &vec = df::burrow::get_vector(); diff --git a/plugins/dig.cpp b/plugins/dig.cpp index 64161a3aa..21c199102 100644 --- a/plugins/dig.cpp +++ b/plugins/dig.cpp @@ -1196,7 +1196,7 @@ command_result digl (color_ostream &out, vector & parameters) df::tile_designation des = MCache->designationAt(xy); df::tiletype tt = MCache->tiletypeAt(xy); int16_t veinmat = MCache->veinMaterialAt(xy); - int16_t basemat = MCache->baseMaterialAt(xy); + int16_t basemat = MCache->layerMaterialAt(xy); if( veinmat != -1 ) { con.printerr("This is a vein. Use vdig instead!\n"); @@ -1215,7 +1215,7 @@ command_result digl (color_ostream &out, vector & parameters) if (MCache->tagAt(current)) continue; int16_t vmat2 = MCache->veinMaterialAt(current); - int16_t bmat2 = MCache->baseMaterialAt(current); + int16_t bmat2 = MCache->layerMaterialAt(current); tt = MCache->tiletypeAt(current); if(!DFHack::isWallTerrain(tt)) @@ -1282,7 +1282,7 @@ command_result digl (color_ostream &out, vector & parameters) //below = 1; des_minus = MCache->designationAt(current-1); vmat_minus = MCache->veinMaterialAt(current-1); - bmat_minus = MCache->baseMaterialAt(current-1); + bmat_minus = MCache->layerMaterialAt(current-1); tt_minus = MCache->tiletypeAt(current-1); if ( tileMaterial(tt_minus)==tiletype_material::STONE || tileMaterial(tt_minus)==tiletype_material::SOIL) @@ -1293,7 +1293,7 @@ command_result digl (color_ostream &out, vector & parameters) //above = 1; des_plus = MCache->designationAt(current+1); vmat_plus = MCache->veinMaterialAt(current+1); - bmat_plus = MCache->baseMaterialAt(current+1); + bmat_plus = MCache->layerMaterialAt(current+1); tt_plus = MCache->tiletypeAt(current+1); if ( tileMaterial(tt_plus)==tiletype_material::STONE || tileMaterial(tt_plus)==tiletype_material::SOIL) diff --git a/plugins/mapexport/mapexport.cpp b/plugins/mapexport/mapexport.cpp index e0a7e5e69..592c526db 100644 --- a/plugins/mapexport/mapexport.cpp +++ b/plugins/mapexport/mapexport.cpp @@ -193,7 +193,7 @@ command_result mapexport (color_ostream &out, std::vector & parame prototile->set_flow_size(des.bits.flow_size); } - df::tiletype type = b->TileTypeAt(coord); + df::tiletype type = b->tiletypeAt(coord); prototile->set_type((dfproto::Tile::TileType)tileShape(type)); prototile->set_tile_material((dfproto::Tile::TileMaterialType)tileMaterial(type)); @@ -204,7 +204,7 @@ command_result mapexport (color_ostream &out, std::vector & parame case tiletype_material::SOIL: case tiletype_material::STONE: prototile->set_material_type(0); - prototile->set_material_index(b->baseMaterialAt(coord)); + prototile->set_material_index(b->layerMaterialAt(coord)); break; case tiletype_material::MINERAL: prototile->set_material_type(0); diff --git a/plugins/probe.cpp b/plugins/probe.cpp index 06b1f968e..f7430af26 100644 --- a/plugins/probe.cpp +++ b/plugins/probe.cpp @@ -103,6 +103,30 @@ command_result df_cprobe (color_ostream &out, vector & parameters) return CR_OK; } +void describeTile(color_ostream &out, df::tiletype tiletype) +{ + out.print("%d", tiletype); + if(tileName(tiletype)) + out.print(" = %s",tileName(tiletype)); + out.print("\n"); + + df::tiletype_shape shape = tileShape(tiletype); + df::tiletype_material material = tileMaterial(tiletype); + df::tiletype_special special = tileSpecial(tiletype); + df::tiletype_variant variant = tileVariant(tiletype); + out.print("%-10s: %4d %s\n","Class" ,shape, + ENUM_KEY_STR(tiletype_shape, shape).c_str()); + out.print("%-10s: %4d %s\n","Material" , + material, ENUM_KEY_STR(tiletype_material, material).c_str()); + out.print("%-10s: %4d %s\n","Special" , + special, ENUM_KEY_STR(tiletype_special, special).c_str()); + out.print("%-10s: %4d %s\n" ,"Variant" , + variant, ENUM_KEY_STR(tiletype_variant, variant).c_str()); + out.print("%-10s: %s\n" ,"Direction", + tileDirection(tiletype).getStr()); + out.print("\n"); +} + command_result df_probe (color_ostream &out, vector & parameters) { //bool showBlock, showDesig, showOccup, showTile, showMisc; @@ -186,26 +210,12 @@ command_result df_probe (color_ostream &out, vector & parameters) */ // tiletype - out.print("tiletype: %d", tiletype); - if(tileName(tiletype)) - out.print(" = %s",tileName(tiletype)); - out.print("\n"); - - df::tiletype_shape shape = tileShape(tiletype); - df::tiletype_material material = tileMaterial(tiletype); - df::tiletype_special special = tileSpecial(tiletype); - df::tiletype_variant variant = tileVariant(tiletype); - out.print("%-10s: %4d %s\n","Class" ,shape, - ENUM_KEY_STR(tiletype_shape, shape).c_str()); - out.print("%-10s: %4d %s\n","Material" , - material, ENUM_KEY_STR(tiletype_material, material).c_str()); - out.print("%-10s: %4d %s\n","Special" , - special, ENUM_KEY_STR(tiletype_special, special).c_str()); - out.print("%-10s: %4d %s\n" ,"Variant" , - variant, ENUM_KEY_STR(tiletype_variant, variant).c_str()); - out.print("%-10s: %s\n" ,"Direction", - tileDirection(tiletype).getStr()); - out.print("\n"); + out.print("tiletype: "); + describeTile(out, tiletype); + out.print("static: "); + describeTile(out, mc.staticTiletypeAt(cursor)); + out.print("base: "); + describeTile(out, mc.baseTiletypeAt(cursor)); out.print("temperature1: %d U\n",mc.temperature1At(cursor)); out.print("temperature2: %d U\n",mc.temperature2At(cursor)); @@ -232,7 +242,7 @@ command_result df_probe (color_ostream &out, vector & parameters) "evilness " << biome->evilness << ")" << std::endl; out << "geolayer: " << des.bits.geolayer_index << std::endl; - int16_t base_rock = mc.baseMaterialAt(cursor); + int16_t base_rock = mc.layerMaterialAt(cursor); if(base_rock != -1) { out << "Layer material: " << dec << base_rock; @@ -256,6 +266,12 @@ command_result df_probe (color_ostream &out, vector & parameters) else out << endl; } + MaterialInfo minfo(mc.baseMaterialAt(cursor)); + if (minfo.isValid()) + out << "Base material: " << minfo.getToken() << " / " << minfo.toString() << endl; + minfo.decode(mc.staticMaterialAt(cursor)); + if (minfo.isValid()) + out << "Static material: " << minfo.getToken() << " / " << minfo.toString() << endl; // liquids if(des.bits.flow_size) { diff --git a/plugins/prospector.cpp b/plugins/prospector.cpp index c90a66c5a..e2f1e9534 100644 --- a/plugins/prospector.cpp +++ b/plugins/prospector.cpp @@ -474,7 +474,7 @@ command_result prospector (color_ostream &con, vector & parameters) liquidWater.add(global_z); } - df::tiletype type = b->TileTypeAt(coord); + df::tiletype type = b->tiletypeAt(coord); df::tiletype_shape tileshape = tileShape(type); df::tiletype_material tilemat = tileMaterial(type); @@ -506,7 +506,7 @@ command_result prospector (color_ostream &con, vector & parameters) { case tiletype_material::SOIL: case tiletype_material::STONE: - layerMats[b->baseMaterialAt(coord)].add(global_z); + layerMats[b->layerMaterialAt(coord)].add(global_z); break; case tiletype_material::MINERAL: veinMats[b->veinMaterialAt(coord)].add(global_z); From 02e43428ee70874851b9a9775b8cfb579f859f80 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 19 Apr 2012 11:03:29 +0400 Subject: [PATCH 33/53] Sync to changes in the data structure definitions. --- .../df/custom/block_burrow.methods.inc | 17 +++------ .../block_square_event_mineralst.methods.inc | 16 +++----- .../df/custom/tile_bitmask.methods.inc | 38 +++++++++++++++++++ library/modules/Maps.cpp | 2 +- library/xml | 2 +- plugins/devel/buildprobe.cpp | 2 +- 6 files changed, 52 insertions(+), 25 deletions(-) create mode 100644 library/include/df/custom/tile_bitmask.methods.inc diff --git a/library/include/df/custom/block_burrow.methods.inc b/library/include/df/custom/block_burrow.methods.inc index 216a58662..3959fcf35 100644 --- a/library/include/df/custom/block_burrow.methods.inc +++ b/library/include/df/custom/block_burrow.methods.inc @@ -1,26 +1,21 @@ inline bool getassignment( const df::coord2d &xy ) { - return getassignment(xy.x,xy.y); + return tile_bitmask.getassignment(xy); } inline bool getassignment( int x, int y ) { - return (tile_bitmask[y] & (1 << x)); + return tile_bitmask.getassignment(x,y); } inline void setassignment( const df::coord2d &xy, bool bit ) { - return setassignment(xy.x,xy.y, bit); + return tile_bitmask.setassignment(xy, bit); } inline void setassignment( int x, int y, bool bit ) { - if(bit) - tile_bitmask[y] |= (1 << x); - else - tile_bitmask[y] &= ~(1 << x); + return tile_bitmask.setassignment(x, y, bit); } bool has_assignments() { - for (int i = 0; i < 16; i++) - if (tile_bitmask[i]) - return true; - return false; + return tile_bitmask.has_assignments(); } + diff --git a/library/include/df/custom/block_square_event_mineralst.methods.inc b/library/include/df/custom/block_square_event_mineralst.methods.inc index 216a58662..4da4c65be 100644 --- a/library/include/df/custom/block_square_event_mineralst.methods.inc +++ b/library/include/df/custom/block_square_event_mineralst.methods.inc @@ -1,26 +1,20 @@ inline bool getassignment( const df::coord2d &xy ) { - return getassignment(xy.x,xy.y); + return tile_bitmask.getassignment(xy); } inline bool getassignment( int x, int y ) { - return (tile_bitmask[y] & (1 << x)); + return tile_bitmask.getassignment(x,y); } inline void setassignment( const df::coord2d &xy, bool bit ) { - return setassignment(xy.x,xy.y, bit); + return tile_bitmask.setassignment(xy, bit); } inline void setassignment( int x, int y, bool bit ) { - if(bit) - tile_bitmask[y] |= (1 << x); - else - tile_bitmask[y] &= ~(1 << x); + return tile_bitmask.setassignment(x, y, bit); } bool has_assignments() { - for (int i = 0; i < 16; i++) - if (tile_bitmask[i]) - return true; - return false; + return tile_bitmask.has_assignments(); } diff --git a/library/include/df/custom/tile_bitmask.methods.inc b/library/include/df/custom/tile_bitmask.methods.inc new file mode 100644 index 000000000..18b312a08 --- /dev/null +++ b/library/include/df/custom/tile_bitmask.methods.inc @@ -0,0 +1,38 @@ +inline uint16_t &operator[] (int y) +{ + return bits[y]; +} +void clear() +{ + memset(bits,0,sizeof(bits)); +} +void set_all() +{ + memset(bits,0xFF,sizeof(bits)); +} +inline bool getassignment( const df::coord2d &xy ) +{ + return getassignment(xy.x,xy.y); +} +inline bool getassignment( int x, int y ) +{ + return (bits[y] & (1 << x)); +} +inline void setassignment( const df::coord2d &xy, bool bit ) +{ + return setassignment(xy.x,xy.y, bit); +} +inline void setassignment( int x, int y, bool bit ) +{ + if(bit) + bits[y] |= (1 << x); + else + bits[y] &= ~(1 << x); +} +bool has_assignments() +{ + for (int i = 0; i < 16; i++) + if (bits[i]) + return true; + return false; +} diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index b45ae7191..06e525fea 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -1066,7 +1066,7 @@ df::block_burrow *Maps::getBlockBurrowMask(df::burrow *burrow, df::map_block *bl link->item = new df::block_burrow; link->item->id = burrow->id; - memset(link->item->tile_bitmask,0,sizeof(link->item->tile_bitmask)); + link->item->tile_bitmask.clear(); link->item->link = link; link->next = NULL; diff --git a/library/xml b/library/xml index e8036d3f1..a1e342afe 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit e8036d3f13c6be0141899baae90f605ad11d5385 +Subproject commit a1e342afe5a5e1e07672cd8b6553953bc251a05d diff --git a/plugins/devel/buildprobe.cpp b/plugins/devel/buildprobe.cpp index 6c360455a..17e95237f 100644 --- a/plugins/devel/buildprobe.cpp +++ b/plugins/devel/buildprobe.cpp @@ -119,7 +119,7 @@ command_result writeFlag (color_ostream &out, vector & parameters) MapExtras::MapCache * MCache = new MapExtras::MapCache(); t_occupancy oc = MCache->occupancyAt(cursor); - oc.bits.building = value; + oc.bits.building = df::tile_building_occ(value); MCache->setOccupancyAt(cursor, oc); MCache->WriteAll(); From c0c790f58c9cf409492d615ffb5ef6d4ba26fbd0 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 19 Apr 2012 19:17:07 +0400 Subject: [PATCH 34/53] Refactor MapCache: make it parse everything that is known re tiles & mats. --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index a1e342afe..25c2a3dad 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit a1e342afe5a5e1e07672cd8b6553953bc251a05d +Subproject commit 25c2a3dad964abbcceb5abd41558b71fb113e83b From 53e0ba8b8d047a8cd4edd1a8067c218080e6e889 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Thu, 26 Apr 2012 12:54:10 -0500 Subject: [PATCH 35/53] Actually write the noble penalty to the dwarf_info structure.... --- plugins/autolabor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 73f8e7c27..f489c229e 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -784,6 +784,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) dwarf_info[dwarf].medical = true; } + dwarf_info[dwarf].noble_penalty = noble_penalty; } for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s != dwarfs[dwarf]->status.souls[0]->skills.end(); s++) From 7c606da636e77280b6a7281464d7e8cd38f7530a Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Thu, 26 Apr 2012 13:46:38 -0500 Subject: [PATCH 36/53] Autolabor will exclude the trade broker from all labors if the "trader requested" flag is on at the trade depot. --- plugins/autolabor.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index f489c229e..49bd2afc1 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -493,6 +494,7 @@ struct dwarf_info_t bool has_exclusive_labor; int noble_penalty; // penalty for assignment due to noble status bool medical; // this dwarf has medical responsibility + bool trader; // this dwarf has trade responsibility }; static bool isOptionEnabled(unsigned flag) @@ -715,6 +717,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) bool has_butchers = false; bool has_fishery = false; + bool trader_requested = false; for (int i = 0; i < world->buildings.all.size(); ++i) { @@ -727,7 +730,14 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) has_butchers = true; if (df::enums::workshop_type::Fishery == subType) has_fishery = true; - } + } + else if (df::enums::building_type::TradeDepot == type) + { + df::building_tradedepotst* depot = (df::building_tradedepotst*) build; + trader_requested = depot->flags.bits.trader_requested; + if (print_debug) + out.print("Trade depot found and trader requested, trader will be excluded from all labors.\n"); + } } for (int i = 0; i < world->units.all.size(); ++i) @@ -783,6 +793,9 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (position->responsibilities[df::entity_position_responsibility::HEALTH_MANAGEMENT]) dwarf_info[dwarf].medical = true; + if (position->responsibilities[df::entity_position_responsibility::TRADE]) + dwarf_info[dwarf].trader = true; + } dwarf_info[dwarf].noble_penalty = noble_penalty; } @@ -1069,8 +1082,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) assert(dwarf >= 0); assert(dwarf < n_dwarfs); - - bool preferred_dwarf = false; if (want_idle_dwarf && dwarf_info[dwarf].state == IDLE) preferred_dwarf = true; @@ -1080,6 +1091,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) preferred_dwarf = true; if (dwarf_info[dwarf].medical && labor == df::unit_labor::DIAGNOSE) preferred_dwarf = true; + if (dwarf_info[dwarf].trader && trader_requested) + continue; if (labor_infos[labor].active_dwarfs >= min_dwarfs && !preferred_dwarf) continue; From 9c94b7c1e748743309dae75ec95e374f26624d5f Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 28 Apr 2012 11:53:26 +0400 Subject: [PATCH 37/53] Make the hash map implementation error more useful. It instructs to update GCC, but actually the most common reason for the error is missing 32-bit development libs. --- depends/protobuf/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/protobuf/CMakeLists.txt b/depends/protobuf/CMakeLists.txt index 92504f059..dddefb06b 100644 --- a/depends/protobuf/CMakeLists.txt +++ b/depends/protobuf/CMakeLists.txt @@ -57,7 +57,7 @@ IF(CMAKE_COMPILER_IS_GNUCC) ENDIF() IF (HAVE_HASH_MAP EQUAL 0) - MESSAGE(SEND_ERROR "Could not find a working hash map implementation. Please update GCC.") + MESSAGE(SEND_ERROR "Could not find a working hash map implementation. Please install GCC >= 4.4, and all necessary 32-bit C++ development libraries.") ENDIF() FIND_PACKAGE(Threads) From 2303a25bdefab30bea67f32d27d35e5002448e8a Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 29 Apr 2012 21:07:39 +0400 Subject: [PATCH 38/53] Implement unconstructed building instance creation and linking into world. For more flexibility, the base api is split into 3 phases: alloc, setSize, and construct. No support for non-actual buildings like stockpiles and activity zones at the moment. --- library/DataDefs.cpp | 13 +- library/LuaApi.cpp | 36 ++ library/LuaTypes.cpp | 5 + library/MiscUtils.cpp | 6 +- library/include/DataDefs.h | 3 + library/include/Error.h | 12 + library/include/modules/Buildings.h | 45 +++ library/include/modules/Job.h | 7 + library/lua/dfhack.lua | 34 +- library/modules/Buildings.cpp | 579 ++++++++++++++++++++++++++++ library/modules/Job.cpp | 37 ++ library/xml | 2 +- plugins/workflow.cpp | 2 +- 13 files changed, 764 insertions(+), 17 deletions(-) diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 061110ecc..7f0bacc9e 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -232,15 +232,26 @@ void virtual_identity::doInit(Core *core) known[vtable_ptr] = this; } +virtual_identity *virtual_identity::find(const std::string &name) +{ + auto name_it = name_lookup.find(name); + + return (name_it != name_lookup.end()) ? name_it->second : NULL; +} + virtual_identity *virtual_identity::get(virtual_ptr instance_ptr) { if (!instance_ptr) return NULL; + return find(get_vtable(instance_ptr)); +} + +virtual_identity *virtual_identity::find(void *vtable) +{ // Actually, a reader/writer lock would be sufficient, // since the table is only written once per class. tthread::lock_guard lock(*known_mutex); - void *vtable = get_vtable(instance_ptr); std::map::iterator it = known.find(vtable); if (it != known.end()) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 8855a50da..82c8bb84b 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -47,6 +47,7 @@ distribution. #include "modules/Maps.h" #include "modules/MapCache.h" #include "modules/Burrows.h" +#include "modules/Buildings.h" #include "LuaWrapper.h" #include "LuaTools.h" @@ -762,6 +763,40 @@ static const luaL_Reg dfhack_burrows_funcs[] = { { NULL, NULL } }; +/***** Buildings module *****/ + +static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { + WRAPM(Buildings, allocInstance), + WRAPM(Buildings, checkFreeTiles), + WRAPM(Buildings, setSize), + WRAPM(Buildings, constructWithItems), + WRAPM(Buildings, constructWithFilters), + { NULL, NULL } +}; + +static int buildings_getCorrectSize(lua_State *state) +{ + int w = luaL_optint(state, 1, 1); + int h = luaL_optint(state, 2, 1); + int t = luaL_optint(state, 3, -1); + int st = luaL_optint(state, 4, -1); + int cu = luaL_optint(state, 5, -1); + int d = luaL_optint(state, 6, 0); + df::coord2d size(w,h); + df::coord2d center; + bool flexible = Buildings::getCorrectSize(size, center, df::building_type(t), st, cu, d); + lua_pushboolean(state, flexible); + lua_pushinteger(state, size.x); + lua_pushinteger(state, size.y); + lua_pushinteger(state, center.x); + lua_pushinteger(state, center.y); + return 5; +} + +static const luaL_Reg dfhack_buildings_funcs[] = { + { "getCorrectSize", buildings_getCorrectSize }, + { NULL, NULL } +}; /************************ * Main Open function * @@ -779,4 +814,5 @@ void OpenDFHackApi(lua_State *state) OpenModule(state, "items", dfhack_items_module, dfhack_items_funcs); OpenModule(state, "maps", dfhack_maps_module, dfhack_maps_funcs); OpenModule(state, "burrows", dfhack_burrows_module, dfhack_burrows_funcs); + OpenModule(state, "buildings", dfhack_buildings_module, dfhack_buildings_funcs); } diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp index b4a4f5d4f..02a89f9e2 100644 --- a/library/LuaTypes.cpp +++ b/library/LuaTypes.cpp @@ -1029,6 +1029,11 @@ int LuaWrapper::method_wrapper_core(lua_State *state, function_identity_base *id std::string tmp = stl_sprintf("NULL pointer: %s", vn ? vn : "?"); field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke"); } + catch (Error::InvalidArgument &e) { + const char *vn = e.expr(); + std::string tmp = stl_sprintf("Invalid argument; expected: %s", vn ? vn : "?"); + field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke"); + } catch (std::exception &e) { std::string tmp = stl_sprintf("C++ exception: %s", e.what()); field_error(state, UPVAL_METHOD_NAME, tmp.c_str(), "invoke"); diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index b9ff35cfc..8658788bb 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -41,7 +41,11 @@ distribution. #include const char *DFHack::Error::NullPointer::what() const throw() { - return "NULL pointer access"; + return "DFHack::Error::NullPointer"; +} + +const char *DFHack::Error::InvalidArgument::what() const throw() { + return "DFHack::Error::InvalidArgument"; } std::string stl_sprintf(const char *fmt, ...) { diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index a411ae667..57773e898 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -318,6 +318,9 @@ namespace DFHack public: static virtual_identity *get(virtual_ptr instance_ptr); + static virtual_identity *find(void *vtable); + static virtual_identity *find(const std::string &name); + bool is_instance(virtual_ptr instance_ptr) { if (!instance_ptr) return false; if (vtable_ptr) { diff --git a/library/include/Error.h b/library/include/Error.h index 0fbc6ba78..448c1a4f2 100644 --- a/library/include/Error.h +++ b/library/include/Error.h @@ -51,6 +51,18 @@ namespace DFHack #define CHECK_NULL_POINTER(var) \ { if (var == NULL) throw DFHack::Error::NullPointer(#var); } + class DFHACK_EXPORT InvalidArgument : public All { + const char *expr_; + public: + InvalidArgument(const char *expr_ = NULL) : expr_(expr_) {} + const char *expr() const { return expr_; } + virtual const char *what() const throw(); + }; + +#define CHECK_INVALID_ARGUMENT(expr) \ + { if (!(expr)) throw DFHack::Error::InvalidArgument(#expr); } + + class DFHACK_EXPORT AllSymbols : public All{}; // Syntax errors and whatnot, the xml can't be read class DFHACK_EXPORT SymbolsXmlParse : public AllSymbols diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index ec44f7713..5ec04f5e2 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -34,6 +34,13 @@ distribution. #include "df/siegeengine_type.h" #include "df/trap_type.h" +namespace df +{ + struct job_item; + struct item; + struct building_extents; +} + namespace DFHack { namespace Buildings @@ -84,5 +91,43 @@ DFHACK_EXPORT bool Read (const uint32_t index, t_building & building); */ DFHACK_EXPORT bool ReadCustomWorkshopTypes(std::map & btypes); +/** + * Allocates a building object using this type and position. + */ +DFHACK_EXPORT df::building *allocInstance(df::coord pos, df::building_type type, int subtype = -1, int custom = -1); + +/** + * Sets size and center to the correct dimensions of the building. + * If part of the original size value was used, returns true. + */ +DFHACK_EXPORT bool getCorrectSize(df::coord2d &size, df::coord2d ¢er, + df::building_type type, int subtype = -1, int custom = -1, + int direction = 0); + +/** + * Checks if the tiles are free to be built upon. + */ +DFHACK_EXPORT bool checkFreeTiles(df::coord pos, df::coord2d size, + df::building_extents *ext = NULL, bool create_ext = false); + +/** + * Sets the size of the building, using size and direction as guidance. + * Returns true if the building can be created at its position, using that size. + */ +DFHACK_EXPORT bool setSize(df::building *bld, df::coord2d size, int direction = 0); + +/** + * Initiates construction of the building, using specified items as inputs. + * Returns true if success. + */ +DFHACK_EXPORT bool constructWithItems(df::building *bld, std::vector items); + +/** + * Initiates construction of the building, using specified item filters. + * Assumes immediate ownership of the item objects, and deletes them in case of error. + * Returns true if success. + */ +DFHACK_EXPORT bool constructWithFilters(df::building *bld, std::vector items); + } } diff --git a/library/include/modules/Job.h b/library/include/modules/Job.h index d733c542d..a4e173a8d 100644 --- a/library/include/modules/Job.h +++ b/library/include/modules/Job.h @@ -30,6 +30,9 @@ distribution. #include "Module.h" #include +#include "DataDefs.h" +#include "df/job_item_ref.h" + namespace df { struct job; @@ -58,6 +61,10 @@ namespace DFHack // lists jobs with ids >= *id_var, and sets *id_var = *job_next_id; DFHACK_EXPORT bool listNewlyCreated(std::vector *pvec, int *id_var); + + DFHACK_EXPORT bool attachJobItem(df::job *job, df::item *item, + df::job_item_ref::T_role role, + int filter_idx = -1, int insert_idx = -1); } DFHACK_EXPORT bool operator== (const df::job_item &a, const df::job_item &b); diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index aedd460b1..6915cbb8a 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -199,6 +199,25 @@ function dfhack.interpreter(prompt,hfile,env) local t_prompt = nil local vcnt = 1 + local pfix_handlers = { + ['!'] = function(data) + print(table.unpack(data,2,data.n)) + end, + ['~'] = function(data) + print(table.unpack(data,2,data.n)) + printall(data[2]) + end, + ['='] = function(data) + for i=2,data.n do + local varname = '_'..vcnt + prompt_env[varname] = data[i] + dfhack.print(varname..' = ') + safecall(print, data[i]) + vcnt = vcnt + 1 + end + end + } + setmetatable(prompt_env, { __index = env or _G }) while true do @@ -209,7 +228,7 @@ function dfhack.interpreter(prompt,hfile,env) elseif cmdline ~= '' then local pfix = string.sub(cmdline,1,1) - if not t_prompt and (pfix == '!' or pfix == '=') then + if not t_prompt and pfix_handlers[pfix] then cmdline = 'return '..string.sub(cmdline,2) else pfix = nil @@ -236,18 +255,7 @@ function dfhack.interpreter(prompt,hfile,env) if data[1] and data.n > 1 then prompt_env._ = data[2] - - if pfix == '!' then - safecall(print, table.unpack(data,2,data.n)) - else - for i=2,data.n do - local varname = '_'..vcnt - prompt_env[varname] = data[i] - dfhack.print(varname..' = ') - safecall(print, data[i]) - vcnt = vcnt + 1 - end - end + safecall(pfix_handlers[pfix], data) end end end diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 6317f3e10..a4dc1852a 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -35,17 +35,41 @@ using namespace std; #include "Types.h" #include "Error.h" #include "modules/Buildings.h" +#include "modules/Maps.h" +#include "modules/Job.h" #include "ModuleFactory.h" #include "Core.h" +#include "TileTypes.h" +#include "MiscUtils.h" using namespace DFHack; #include "DataDefs.h" #include "df/world.h" +#include "df/ui.h" +#include "df/d_init.h" +#include "df/item.h" +#include "df/job.h" +#include "df/job_item.h" +#include "df/general_ref_building_holderst.h" +#include "df/buildings_other_id.h" +#include "df/building_design.h" #include "df/building_def.h" +#include "df/building_axle_horizontalst.h" +#include "df/building_trapst.h" +#include "df/building_bridgest.h" +#include "df/building_coffinst.h" +#include "df/building_furnacest.h" #include "df/building_workshopst.h" +#include "df/building_screw_pumpst.h" +#include "df/building_water_wheelst.h" +#include "df/building_wellst.h" using namespace df::enums; +using df::global::ui; using df::global::world; +using df::global::d_init; +using df::global::building_next_id; +using df::global::process_jobs; using df::building_def; uint32_t Buildings::getNumBuildings() @@ -86,3 +110,558 @@ bool Buildings::ReadCustomWorkshopTypes(map & btypes) return true; } +df::building *Buildings::allocInstance(df::coord pos, df::building_type type, int subtype, int custom) +{ + if (!building_next_id) + return NULL; + + // Allocate object + const char *classname = ENUM_ATTR(building_type, classname, type); + if (!classname) + return NULL; + + auto id = virtual_identity::find(classname); + if (!id) + return NULL; + + df::building *bld = (df::building*)id->allocate(); + if (!bld) + return NULL; + + // Init base fields + bld->x1 = bld->x2 = bld->centerx = pos.x; + bld->y1 = bld->y2 = bld->centery = pos.y; + bld->z = pos.z; + + bld->race = ui->race_id; + + if (subtype != -1) + bld->setSubtype(subtype); + if (custom != -1) + bld->setCustomType(custom); + + bld->setMaterialAmount(1); + + // Type specific init + switch (type) + { + case building_type::Well: + { + auto obj = (df::building_wellst*)bld; + obj->bucket_z = bld->z; + break; + } + case building_type::Furnace: + { + auto obj = (df::building_furnacest*)bld; + obj->melt_remainder.resize(df::inorganic_raw::get_vector().size(), 0); + break; + } + case building_type::Coffin: + { + auto obj = (df::building_coffinst*)bld; + if (d_init && d_init->flags3.is_set(d_init_flags3::COFFIN_NO_PETS_DEFAULT)) + obj->burial_mode.bits.no_pets = true; + break; + } + case building_type::Trap: + { + auto obj = (df::building_trapst*)bld; + if (obj->trap_type == trap_type::PressurePlate) + obj->unk_cc = 500; + break; + } + default: + break; + } + + return bld; +} + +static void makeOneDim(df::coord2d &size, df::coord2d ¢er, bool vertical) +{ + if (vertical) + size.x = 1; + else + size.y = 1; + center = size/2; +} + +bool Buildings::getCorrectSize(df::coord2d &size, df::coord2d ¢er, + df::building_type type, int subtype, int custom, int direction) +{ + using namespace df::enums::building_type; + + if (size.x <= 0) + size.x = 1; + if (size.y <= 0) + size.y = 1; + + switch (type) + { + case FarmPlot: + case Bridge: + case RoadDirt: + case RoadPaved: + case Stockpile: + case Civzone: + center = size/2; + return true; + + case TradeDepot: + case Shop: + size = df::coord2d(5,5); + center = df::coord2d(2,2); + return false; + + case SiegeEngine: + case Windmill: + case Wagon: + size = df::coord2d(3,3); + center = df::coord2d(1,1); + return false; + + case AxleHorizontal: + makeOneDim(size, center, direction); + return true; + + case WaterWheel: + size = df::coord2d(3,3); + makeOneDim(size, center, direction); + return false; + + case Workshop: + { + using namespace df::enums::workshop_type; + + switch ((df::workshop_type)subtype) + { + case Quern: + case Millstone: + case Tool: + size = df::coord2d(1,1); + center = df::coord2d(0,0); + break; + + case Siege: + case Kennels: + size = df::coord2d(5,5); + center = df::coord2d(2,2); + break; + + case Custom: + if (auto def = df::building_def::find(custom)) + { + size = df::coord2d(def->dim_x, def->dim_y); + center = df::coord2d(def->workloc_x, def->workloc_y); + break; + } + + default: + size = df::coord2d(3,3); + center = df::coord2d(1,1); + } + + return false; + } + + case Furnace: + { + using namespace df::enums::furnace_type; + + switch ((df::furnace_type)subtype) + { + case Custom: + if (auto def = df::building_def::find(custom)) + { + size = df::coord2d(def->dim_x, def->dim_y); + center = df::coord2d(def->workloc_x, def->workloc_y); + break; + } + + default: + size = df::coord2d(3,3); + center = df::coord2d(1,1); + } + + return false; + } + + case ScrewPump: + { + using namespace df::enums::screw_pump_direction; + + switch ((df::screw_pump_direction)direction) + { + case FromEast: + size = df::coord2d(2,1); + center = df::coord2d(1,0); + break; + case FromSouth: + size = df::coord2d(1,2); + center = df::coord2d(0,1); + break; + case FromWest: + size = df::coord2d(2,1); + center = df::coord2d(0,0); + break; + default: + size = df::coord2d(1,2); + center = df::coord2d(0,0); + } + + return false; + } + + default: + size = df::coord2d(1,1); + center = df::coord2d(0,0); + return false; + } +} + +static uint8_t *getExtentTile(df::building_extents &extent, df::coord2d tile) +{ + if (!extent.extents) + return NULL; + int dx = tile.x - extent.x; + int dy = tile.y - extent.y; + if (dx < 0 || dy < 0 || dx >= extent.width || dy >= extent.height) + return NULL; + return &extent.extents[dx + dy*extent.width]; +} + +bool Buildings::checkFreeTiles(df::coord pos, df::coord2d size, + df::building_extents *ext, bool create_ext) +{ + for (int dx = 0; dx < size.x; dx++) + { + for (int dy = 0; dy < size.y; dy++) + { + df::coord tile = pos + df::coord(dx,dy,0); + uint8_t *etile = (ext ? getExtentTile(*ext, tile) : NULL); + + if (etile && !*etile) + continue; + + df::map_block *block = Maps::getTileBlock(tile); + if (!block) + return false; + + df::coord2d btile = df::coord2d(tile) & 15; + + bool allowed = true; + + if (block->occupancy[btile.x][btile.y].bits.building) + allowed = false; + else + { + auto tile = block->tiletype[btile.x][btile.y]; + if (!HighPassable(tile)) + allowed = false; + } + + if (!allowed) + { + if (!ext || !create_ext) + return false; + + if (!ext->extents) + { + ext->extents = new uint8_t[size.x*size.y]; + ext->x = pos.x; + ext->y = pos.y; + ext->width = size.x; + ext->height = size.y; + + memset(ext->extents, 1, size.x*size.y); + etile = getExtentTile(*ext, tile); + } + + if (!etile) + return false; + + *etile = 0; + } + } + } + + return true; +} + +static bool checkBuildingTiles(df::building *bld, bool can_use_extents) +{ + df::coord pos(bld->x1,bld->y1,bld->z); + + return Buildings::checkFreeTiles(pos, df::coord2d(bld->x2+1,bld->y2+1) - pos, + &bld->room, can_use_extents); +} + +bool Buildings::setSize(df::building *bld, df::coord2d size, int direction) +{ + CHECK_NULL_POINTER(bld); + CHECK_INVALID_ARGUMENT(bld->id == -1); + + if (bld->room.extents) + { + delete[] bld->room.extents; + bld->room.extents = NULL; + } + + df::coord2d center; + getCorrectSize(size, center, bld->getType(), bld->getSubtype(), + bld->getCustomType(), direction); + + bld->x2 = bld->x1 + size.x - 1; + bld->y2 = bld->y1 + size.y - 1; + bld->centerx = bld->x1 + center.x; + bld->centery = bld->y1 + center.y; + + auto type = bld->getType(); + bool can_use_extents = false; + + using namespace df::enums::building_type; + + switch (type) + { + case WaterWheel: + { + auto obj = (df::building_water_wheelst*)bld; + obj->is_vertical = !!direction; + break; + } + case AxleHorizontal: + { + auto obj = (df::building_axle_horizontalst*)bld; + obj->is_vertical = !!direction; + break; + } + case ScrewPump: + { + auto obj = (df::building_screw_pumpst*)bld; + obj->direction = (df::screw_pump_direction)direction; + break; + } + case Bridge: + { + auto obj = (df::building_bridgest*)bld; + // TODO: computes some kind of flag from tile data + obj->direction = (df::building_bridgest::T_direction)direction; + break; + } + case FarmPlot: + case RoadDirt: + case RoadPaved: + case Stockpile: + case Civzone: + can_use_extents = true; + break; + default: + break; + } + + bool ok = checkBuildingTiles(bld, can_use_extents); + + if (type != building_type::Construction) + { + int cnt = size.x * size.y; + + if (bld->room.extents) + { + cnt = 0; + for (int i = 0; i < bld->room.width * bld->room.height; i++) + if (bld->room.extents[i] == 1) + cnt++; + } + + bld->setMaterialAmount(cnt/4 + 1); + } + + return ok; +} + +static void markBuildingTiles(df::building *bld, df::tile_building_occ occv, bool stockpile) +{ + for (int tx = bld->x1; tx <= bld->x2; tx++) + { + for (int ty = bld->y1; ty <= bld->y2; ty++) + { + df::coord tile(tx,ty,bld->z); + + uint8_t *etile = getExtentTile(bld->room, tile); + if (etile && !*etile) + continue; + + df::map_block *block = Maps::getTileBlock(tile); + df::coord2d btile = df::coord2d(tile) & 15; + + auto &occ = block->occupancy[btile.x][btile.y]; + occ.bits.building = occv; + + auto &des = block->designation[btile.x][btile.y]; + des.bits.dig = tile_dig_designation::No; + des.bits.pile = stockpile; + } + } +} + +static void linkRooms(df::building *bld) +{ + auto &vec = world->buildings.other[buildings_other_id::ANY_FREE]; + + bool changed = false; + + for (size_t i = 0; i < vec.size(); i++) + { + auto room = vec[i]; + if (!room->is_room || room->z != bld->z) + continue; + + uint8_t *pext = getExtentTile(room->room, df::coord2d(bld->x1, bld->y1)); + if (!pext || !*pext) + continue; + + changed = true; + room->children.push_back(bld); + bld->parents.push_back(room); + + // TODO: the game updates room rent here if economy is enabled + } + + if (changed) + df::global::ui->equipment.update.bits.buildings = true; +} + +static void linkBuilding(df::building *bld) +{ + bld->id = (*building_next_id)++; + + world->buildings.all.push_back(bld); + bld->categorize(true); + + markBuildingTiles(bld, tile_building_occ::Planned, false); + + linkRooms(bld); + + if (process_jobs) + *process_jobs = true; +} + +static void createDesign(df::building *bld, bool rough) +{ + auto job = bld->jobs[0]; + + job->mat_type = bld->mat_type; + job->mat_index = bld->mat_index; + + if (bld->needsDesign()) + { + auto act = (df::building_actual*)bld; + act->design = new df::building_design(); + + act->design->flags.bits.rough = rough; + } +} + +static bool linkForConstruct(df::job* &job, df::building *bld) +{ + if (!checkBuildingTiles(bld, false)) + return false; + + auto ref = df::allocate(); + if (!ref) + { + Core::printerr("Could not allocate general_ref_building_holderst\n"); + return false; + } + + linkBuilding(bld); + + ref->building_id = bld->id; + + job = new df::job(); + job->job_type = df::job_type::ConstructBuilding; + job->pos = df::coord(bld->centerx, bld->centery, bld->z); + job->references.push_back(ref); + + bld->jobs.push_back(job); + + Job::linkIntoWorld(job); + + return true; +} + +bool Buildings::constructWithItems(df::building *bld, std::vector items) +{ + CHECK_NULL_POINTER(bld); + CHECK_INVALID_ARGUMENT(!items.empty()); + CHECK_INVALID_ARGUMENT(bld->id == -1); + CHECK_INVALID_ARGUMENT(bld->isActual()); + + for (size_t i = 0; i < items.size(); i++) + { + CHECK_NULL_POINTER(items[i]); + + if (items[i]->flags.bits.in_job) + return false; + } + + df::job *job = NULL; + if (!linkForConstruct(job, bld)) + return false; + + bool rough = false; + + for (size_t i = 0; i < items.size(); i++) + { + Job::attachJobItem(job, items[i], df::job_item_ref::Hauled); + + if (items[i]->getType() == item_type::BOULDER) + rough = true; + if (bld->mat_type == -1) + bld->mat_type = items[i]->getMaterial(); + if (bld->mat_index == -1) + bld->mat_index = items[i]->getMaterialIndex(); + } + + createDesign(bld, rough); + return true; +} + +bool Buildings::constructWithFilters(df::building *bld, std::vector items) +{ + CHECK_NULL_POINTER(bld); + CHECK_INVALID_ARGUMENT(!items.empty()); + CHECK_INVALID_ARGUMENT(bld->id == -1); + CHECK_INVALID_ARGUMENT(bld->isActual()); + + for (size_t i = 0; i < items.size(); i++) + CHECK_NULL_POINTER(items[i]); + + df::job *job = NULL; + if (!linkForConstruct(job, bld)) + { + for (size_t i = 0; i < items.size(); i++) + delete items[i]; + + return false; + } + + bool rough = false; + + for (size_t i = 0; i < items.size(); i++) + { + job->job_items.push_back(items[i]); + + if (items[i]->item_type == item_type::BOULDER) + rough = true; + if (bld->mat_type == -1) + bld->mat_type = items[i]->mat_type; + if (bld->mat_index == -1) + bld->mat_index = items[i]->mat_index; + } + + createDesign(bld, rough); + return true; +} + diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index 5ce6647d9..8e8e025d4 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -311,3 +311,40 @@ bool DFHack::Job::listNewlyCreated(std::vector *pvec, int *id_var) return true; } + +bool DFHack::Job::attachJobItem(df::job *job, df::item *item, + df::job_item_ref::T_role role, + int filter_idx, int insert_idx) +{ + CHECK_NULL_POINTER(job); + CHECK_NULL_POINTER(item); + + /* + * Functionality 100% reverse-engineered from DF code. + */ + + if (role != df::job_item_ref::TargetContainer) + { + if (item->flags.bits.in_job) + return false; + + item->flags.bits.in_job = true; + } + + auto item_link = new df::item::T_jobs(); + item_link->type = 2; + item_link->job = job; + item->jobs.push_back(item_link); + + auto job_link = new df::job_item_ref(); + job_link->item = item; + job_link->role = role; + job_link->job_item_idx = filter_idx; + + if (size_t(insert_idx) < job->items.size()) + vector_insert_at(job->items, insert_idx, job_link); + else + job->items.push_back(job_link); + + return true; +} diff --git a/library/xml b/library/xml index f649d3100..d75556019 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit f649d31001e6023a9df5fe83c7971c17afe0d87d +Subproject commit d755560192ec318fbb52133e4e52972bee67d1e0 diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 5e5ca84bc..0d6caa472 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -1084,7 +1084,7 @@ static bool itemInRealJob(df::item *item) return false; if (item->jobs.size() != 1 || - item->jobs[0]->unk1 != 2 || + item->jobs[0]->type != 2 || item->jobs[0]->job == NULL) return true; From e7c55ab6e162c1fbc32741d6283780805dea96e7 Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Mon, 30 Apr 2012 09:01:48 -0500 Subject: [PATCH 39/53] Also exclude trader from hauling when requested at depot. --- plugins/autolabor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 49bd2afc1..2eaa27b2b 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -1130,6 +1130,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) std::vector hauler_ids; for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) { + if (dwarf_info[dwarf].trader && trader_requested) + continue; if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) hauler_ids.push_back(dwarf); } From 4cffb6428d5e25b0c3cb09044f65dbf5d43667e9 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 1 May 2012 18:55:30 +0400 Subject: [PATCH 40/53] Update building creation code with new knowledge, and fix zone. Also, document new lua api, and add a more convenient wrapper. --- LUA_API.rst | 64 +++++++++++ Lua API.html | 77 +++++++++++-- library/LuaApi.cpp | 36 +++++- library/include/modules/Buildings.h | 18 ++- library/lua/dfhack.lua | 5 + library/lua/dfhack/buildings.lua | 95 ++++++++++++++++ library/modules/Buildings.cpp | 166 ++++++++++++++++++++-------- library/modules/Units.cpp | 2 +- library/xml | 2 +- plugins/zone.cpp | 4 +- 10 files changed, 404 insertions(+), 65 deletions(-) create mode 100644 library/lua/dfhack/buildings.lua diff --git a/LUA_API.rst b/LUA_API.rst index d2afacd42..9925240dd 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -850,6 +850,70 @@ Burrows module Adds or removes the tile from the burrow. Returns *false* if invalid coords. +Buildings module +---------------- + +* ``dfhack.buildings.getSize(building)`` + + Returns *width, height, centerx, centery*. + +* ``dfhack.buildings.getCorrectSize(width, height, type, subtype, custom, direction)`` + + Computes correct dimensions for the specified building type and orientation, + using width and height for flexible dimensions. + Returns *is_flexible, width, height, center_x, center_y*. + +* ``dfhack.buildings.checkFreeTiles(pos,size[,extents,change_extents,allow_occupied])`` + + Checks if the rectangle defined by ``pos`` and ``size``, and possibly extents, + can be used for placing a building. If ``change_extents`` is true, bad tiles + are removed from extents. If ``allow_occupied``, the occupancy test is skipped. + +* ``dfhack.buildings.countExtentTiles(extents,defval)`` + + Returns the number of tiles included by extents, or defval. + +* ``dfhack.buildings.hasSupport(pos,size)`` + + Checks if a bridge constructed at specified position would have + support from terrain, and thus won't collapse if retracted. + +Low-level building creation functions; + +* ``dfhack.buildings.allocInstance(pos, type, subtype, custom)`` + + Creates a new building instance of given type, subtype and custom type, + at specified position. Returns the object, or *nil* in case of an error. + +* ``dfhack.buildings.setSize(building, width, height, direction)`` + + Configures an object returned by ``allocInstance``, using specified + parameters wherever appropriate. If the building has fixed size along + any dimension, the corresponding input parameter will be ignored. + Returns *nil* if the building cannot be placed, or *true, width, + height, rect_area, true_area*. Returned width and height are the + final values used by the building; true_area is less than rect_area + if any tiles were removed from designation. + +* ``dfhack.buildings.constructWithItems(building, items)`` + + Links a fully configured object created by ``allocInstance`` into the + world for construction, using a list of specific items as material. + Returns *true*, or *false* if impossible. + +* ``dfhack.buildings.constructWithFilters(building, job_items)`` + + Links a fully configured object created by ``allocInstance`` into the + world for construction, using a list of job_item filters as inputs. + Returns *true*, or *false* if impossible. Filter objects are claimed + and possibly destroyed in any case. + Use a negative ``quantity`` field value to auto-compute the amount + from the size of the building. + +More high-level functions are implemented in lua and can be loaded by +``require('dfhack.buildings')``. See ``hack/lua/dfhack/buildings.lua``. + + Core interpreter context ======================== diff --git a/Lua API.html b/Lua API.html index fd2004b8d..69a0ab120 100644 --- a/Lua API.html +++ b/Lua API.html @@ -343,17 +343,18 @@ ul.auto-toc {
  • Items module
  • Maps module
  • Burrows module
  • +
  • Buildings module
  • -
  • Core interpreter context
  • -
  • Plugins @@ -1061,9 +1062,65 @@ burrows, or the presence of invaders.

  • +
    +

    Buildings module

    +
      +
    • dfhack.buildings.getSize(building)

      +

      Returns width, height, centerx, centery.

      +
    • +
    • dfhack.buildings.getCorrectSize(width, height, type, subtype, custom, direction)

      +

      Computes correct dimensions for the specified building type and orientation, +using width and height for flexible dimensions. +Returns is_flexible, width, height, center_x, center_y.

      +
    • +
    • dfhack.buildings.checkFreeTiles(pos,size[,extents,change_extents,allow_occupied])

      +

      Checks if the rectangle defined by pos and size, and possibly extents, +can be used for placing a building. If change_extents is true, bad tiles +are removed from extents. If allow_occupied, the occupancy test is skipped.

      +
    • +
    • dfhack.buildings.countExtentTiles(extents,defval)

      +

      Returns the number of tiles included by extents, or defval.

      +
    • +
    • dfhack.buildings.hasSupport(pos,size)

      +

      Checks if a bridge constructed at specified position would have +support from terrain, and thus won't collapse if retracted.

      +
    • +
    +

    Low-level building creation functions;

    +
      +
    • dfhack.buildings.allocInstance(pos, type, subtype, custom)

      +

      Creates a new building instance of given type, subtype and custom type, +at specified position. Returns the object, or nil in case of an error.

      +
    • +
    • dfhack.buildings.setSize(building, width, height, direction)

      +

      Configures an object returned by allocInstance, using specified +parameters wherever appropriate. If the building has fixed size along +any dimension, the corresponding input parameter will be ignored. +Returns nil if the building cannot be placed, or true, width, +height, rect_area, true_area. Returned width and height are the +final values used by the building; true_area is less than rect_area +if any tiles were removed from designation.

      +
    • +
    • dfhack.buildings.constructWithItems(building, items)

      +

      Links a fully configured object created by allocInstance into the +world for construction, using a list of specific items as material. +Returns true, or false if impossible.

      +
    • +
    • dfhack.buildings.constructWithFilters(building, job_items)

      +

      Links a fully configured object created by allocInstance into the +world for construction, using a list of job_item filters as inputs. +Returns true, or false if impossible. Filter objects are claimed +and possibly destroyed in any case. +Use a negative quantity field value to auto-compute the amount +from the size of the building.

      +
    • +
    +

    More high-level functions are implemented in lua and can be loaded by +require('dfhack.buildings'). See hack/lua/dfhack/buildings.lua.

    +
    -

    Core interpreter context

    +

    Core interpreter context

    While plugins can create any number of interpreter instances, there is one special context managed by dfhack core. It is the only context that can receive events from DF and plugins.

    @@ -1077,7 +1134,7 @@ only context that can receive events from DF and plugins.

    -

    Event type

    +

    Event type

    An event is just a lua table with a predefined metatable that contains a __call metamethod. When it is invoked, it loops through the table with next and calls all contained values. @@ -1103,14 +1160,14 @@ order using dfhack.safecall.

    -

    Plugins

    +

    Plugins

    DFHack plugins may export native functions and events to lua contexts. They are automatically imported by mkmodule('plugins.<name>'); this means that a lua module file is still necessary for require to read.

    The following plugins have lua support.

    -

    burrows

    +

    burrows

    Implements extended burrow manipulations.

    Events:

      @@ -1148,7 +1205,7 @@ set is the same as used by the command line.

      The lua module file also re-exports functions from dfhack.burrows.

    -

    sort

    +

    sort

    Does not export any native functions as of now. Instead, it calls lua code to perform the actual ordering of list items.

    diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 82c8bb84b..11cf73c3f 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -768,7 +768,8 @@ static const luaL_Reg dfhack_burrows_funcs[] = { static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { WRAPM(Buildings, allocInstance), WRAPM(Buildings, checkFreeTiles), - WRAPM(Buildings, setSize), + WRAPM(Buildings, countExtentTiles), + WRAPM(Buildings, hasSupport), WRAPM(Buildings, constructWithItems), WRAPM(Buildings, constructWithFilters), { NULL, NULL } @@ -776,15 +777,16 @@ static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { static int buildings_getCorrectSize(lua_State *state) { - int w = luaL_optint(state, 1, 1); - int h = luaL_optint(state, 2, 1); - int t = luaL_optint(state, 3, -1); + df::coord2d size(luaL_optint(state, 1, 1), luaL_optint(state, 2, 1)); + + auto t = (df::building_type)luaL_optint(state, 3, -1); int st = luaL_optint(state, 4, -1); int cu = luaL_optint(state, 5, -1); int d = luaL_optint(state, 6, 0); - df::coord2d size(w,h); + df::coord2d center; - bool flexible = Buildings::getCorrectSize(size, center, df::building_type(t), st, cu, d); + bool flexible = Buildings::getCorrectSize(size, center, t, st, cu, d); + lua_pushboolean(state, flexible); lua_pushinteger(state, size.x); lua_pushinteger(state, size.y); @@ -793,8 +795,30 @@ static int buildings_getCorrectSize(lua_State *state) return 5; } +static int buildings_setSize(lua_State *state) +{ + auto bld = Lua::CheckDFObject(state, 1); + df::coord2d size(luaL_optint(state, 2, 1), luaL_optint(state, 3, 1)); + int dir = luaL_optint(state, 4, 0); + bool ok = Buildings::setSize(bld, size, dir); + lua_pushboolean(state, ok); + if (ok) + { + auto size = Buildings::getSize(bld).second; + int area = size.x*size.y; + lua_pushinteger(state, size.x); + lua_pushinteger(state, size.y); + lua_pushinteger(state, area); + lua_pushinteger(state, Buildings::countExtentTiles(&bld->room, area)); + return 5; + } + else + return 1; +} + static const luaL_Reg dfhack_buildings_funcs[] = { { "getCorrectSize", buildings_getCorrectSize }, + { "setSize", buildings_setSize }, { NULL, NULL } }; diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 5ec04f5e2..610688187 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -108,7 +108,18 @@ DFHACK_EXPORT bool getCorrectSize(df::coord2d &size, df::coord2d ¢er, * Checks if the tiles are free to be built upon. */ DFHACK_EXPORT bool checkFreeTiles(df::coord pos, df::coord2d size, - df::building_extents *ext = NULL, bool create_ext = false); + df::building_extents *ext = NULL, + bool create_ext = false, bool allow_occupied = false); + +/** + * Returns the number of tiles included by the extent, or defval. + */ +DFHACK_EXPORT int countExtentTiles(df::building_extents *ext, int defval = -1); + +/** + * Checks if the area has support from the terrain. + */ +DFHACK_EXPORT bool hasSupport(df::coord pos, df::coord2d size); /** * Sets the size of the building, using size and direction as guidance. @@ -116,6 +127,11 @@ DFHACK_EXPORT bool checkFreeTiles(df::coord pos, df::coord2d size, */ DFHACK_EXPORT bool setSize(df::building *bld, df::coord2d size, int direction = 0); +/** + * Decodes the size of the building into pos and size. + */ +DFHACK_EXPORT std::pair getSize(df::building *bld); + /** * Initiates construction of the building, using specified items as inputs. * Returns true if success. diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 6915cbb8a..c4c994d2a 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -173,6 +173,11 @@ function dfhack.maps.getTileSize() return map.x_count, map.y_count, map.z_count end +function dfhack.buildings.getSize(bld) + local x, y = bld.x1, bld.y1 + return bld.x2+1-x, bld.y2+1-y, bld.centerx-x, bld.centery-y +end + -- Interactive local print_banner = true diff --git a/library/lua/dfhack/buildings.lua b/library/lua/dfhack/buildings.lua new file mode 100644 index 000000000..57427e9fb --- /dev/null +++ b/library/lua/dfhack/buildings.lua @@ -0,0 +1,95 @@ +local dfhack = dfhack +local _ENV = dfhack.BASE_G +local buildings = dfhack.buildings + +--[[ + Wraps all steps necessary to create a building with + a construct job into one function. + + dfhack.buildings.constructBuilding{ + -- Position: + pos = { x = ..., y = ..., z = ... }, + -- OR + x = ..., y = ..., z = ..., + + -- Type: + type = df.building_type.FOO, subtype = ..., custom = ..., + + -- Field initialization: + fields = { ... }, + + -- Size and orientation: + width = ..., height = ..., direction = ..., + + -- Abort if not all tiles in the rectangle are available: + full_rectangle = true, + + -- Materials: + items = { item, item ... }, + -- OR + filter = { { ... }, { ... }... } + } + + Returns: the created building, or 'nil, error' +--]] + +function buildings.constructBuilding(info) + local btype = info.type + local subtype = info.subtype or -1 + local custom = info.custom or -1 + + if not (info.pos or info.x) then + error('position is required') + end + if not (info.items or info.filters) then + error('either items or filters are required') + elseif info.filters then + for _,v in ipairs(info.filters) do + v.new = true + end + end + if type(btype) ~= 'number' or not df.building_type[btype] then + error('Invalid building type: '..tostring(btype)) + end + + local pos = info.pos or xyz2pos(info.x, info.y, info.z) + + local instance = buildings.allocInstance(pos, btype, subtype, custom) + if not instance then + error('Could not create building of type '..df.building_type[btype]) + end + + local to_delete = instance + return dfhack.with_finalize( + function() + df.delete(to_delete) + end, + function() + if info.fields then + instance:assign(info.fields) + end + local ok,w,h,area,r_area = buildings.setSize( + instance,info.width,info.height,info.direction + ) + if not ok then + return nil, "cannot place at this position" + end + if info.full_rectangle and area ~= r_area then + return nil, "not all tiles can be used" + end + if info.items then + ok = buildings.constructWithItems(instance, info.items) + else + ok = buildings.constructWithFilters(instance, info.filters) + end + if not ok then + return nil, "could not construct the building" + end + -- Success + to_delete = nil + return instance + end + ) +end + +return buildings \ No newline at end of file diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index a4dc1852a..bc062f980 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -160,8 +160,7 @@ df::building *Buildings::allocInstance(df::coord pos, df::building_type type, in case building_type::Coffin: { auto obj = (df::building_coffinst*)bld; - if (d_init && d_init->flags3.is_set(d_init_flags3::COFFIN_NO_PETS_DEFAULT)) - obj->burial_mode.bits.no_pets = true; + obj->initBurialFlags(); // DF has this copy&pasted break; } case building_type::Trap: @@ -332,18 +331,27 @@ static uint8_t *getExtentTile(df::building_extents &extent, df::coord2d tile) } bool Buildings::checkFreeTiles(df::coord pos, df::coord2d size, - df::building_extents *ext, bool create_ext) + df::building_extents *ext, + bool create_ext, bool allow_occupied) { + bool found_any = false; + for (int dx = 0; dx < size.x; dx++) { for (int dy = 0; dy < size.y; dy++) { df::coord tile = pos + df::coord(dx,dy,0); - uint8_t *etile = (ext ? getExtentTile(*ext, tile) : NULL); + uint8_t *etile = NULL; - if (etile && !*etile) - continue; + // Exclude using extents + if (ext && ext->extents) + { + etile = getExtentTile(*ext, tile); + if (!etile || !*etile) + continue; + } + // Look up map block df::map_block *block = Maps::getTileBlock(tile); if (!block) return false; @@ -352,7 +360,9 @@ bool Buildings::checkFreeTiles(df::coord pos, df::coord2d size, bool allowed = true; - if (block->occupancy[btile.x][btile.y].bits.building) + // Check occupancy and tile type + if (!allow_occupied && + block->occupancy[btile.x][btile.y].bits.building) allowed = false; else { @@ -361,7 +371,10 @@ bool Buildings::checkFreeTiles(df::coord pos, df::coord2d size, allowed = false; } - if (!allowed) + // Create extents if requested + if (allowed) + found_any = true; + else { if (!ext || !create_ext) return false; @@ -386,15 +399,72 @@ bool Buildings::checkFreeTiles(df::coord pos, df::coord2d size, } } - return true; + return found_any; } -static bool checkBuildingTiles(df::building *bld, bool can_use_extents) +std::pair Buildings::getSize(df::building *bld) { + CHECK_NULL_POINTER(bld); + df::coord pos(bld->x1,bld->y1,bld->z); - return Buildings::checkFreeTiles(pos, df::coord2d(bld->x2+1,bld->y2+1) - pos, - &bld->room, can_use_extents); + return std::pair(pos, df::coord2d(bld->x2+1,bld->y2+1) - pos); +} + +static bool checkBuildingTiles(df::building *bld, bool can_change) +{ + auto psize = Buildings::getSize(bld); + + return Buildings::checkFreeTiles(psize.first, psize.second, &bld->room, + can_change && bld->isExtentShaped(), + !bld->isSettingOccupancy()); +} + +int Buildings::countExtentTiles(df::building_extents *ext, int defval) +{ + if (!ext || !ext->extents) + return defval; + + int cnt = 0; + for (int i = 0; i < ext->width * ext->height; i++) + if (ext->extents[i]) + cnt++; + return cnt; +} + +bool Buildings::hasSupport(df::coord pos, df::coord2d size) +{ + for (int dx = -1; dx <= size.x; dx++) + { + for (int dy = -1; dy <= size.y; dy++) + { + // skip corners + if ((dx < 0 || dx == size.x) && (dy < 0 || dy == size.y)) + continue; + + df::coord tile = pos + df::coord(dx,dy,0); + df::map_block *block = Maps::getTileBlock(tile); + if (!block) + continue; + + df::coord2d btile = df::coord2d(tile) & 15; + if (!isOpenTerrain(block->tiletype[btile.x][btile.y])) + return true; + } + } + + return false; +} + +static int computeMaterialAmount(df::building *bld) +{ + auto size = Buildings::getSize(bld).second; + int cnt = size.x * size.y; + + if (bld->room.extents && bld->isExtentShaped()) + cnt = Buildings::countExtentTiles(&bld->room, cnt); + + return cnt/4 + 1; } bool Buildings::setSize(df::building *bld, df::coord2d size, int direction) @@ -402,12 +472,14 @@ bool Buildings::setSize(df::building *bld, df::coord2d size, int direction) CHECK_NULL_POINTER(bld); CHECK_INVALID_ARGUMENT(bld->id == -1); + // Delete old extents if (bld->room.extents) { delete[] bld->room.extents; bld->room.extents = NULL; } + // Compute correct size and apply it df::coord2d center; getCorrectSize(size, center, bld->getType(), bld->getSubtype(), bld->getCustomType(), direction); @@ -418,7 +490,6 @@ bool Buildings::setSize(df::building *bld, df::coord2d size, int direction) bld->centery = bld->y1 + center.y; auto type = bld->getType(); - bool can_use_extents = false; using namespace df::enums::building_type; @@ -445,62 +516,65 @@ bool Buildings::setSize(df::building *bld, df::coord2d size, int direction) case Bridge: { auto obj = (df::building_bridgest*)bld; - // TODO: computes some kind of flag from tile data + auto psize = getSize(bld); + obj->gate_flags.bits.has_support = hasSupport(psize.first, psize.second); obj->direction = (df::building_bridgest::T_direction)direction; break; } - case FarmPlot: - case RoadDirt: - case RoadPaved: - case Stockpile: - case Civzone: - can_use_extents = true; - break; default: break; } - bool ok = checkBuildingTiles(bld, can_use_extents); + bool ok = checkBuildingTiles(bld, true); if (type != building_type::Construction) - { - int cnt = size.x * size.y; - - if (bld->room.extents) - { - cnt = 0; - for (int i = 0; i < bld->room.width * bld->room.height; i++) - if (bld->room.extents[i] == 1) - cnt++; - } - - bld->setMaterialAmount(cnt/4 + 1); - } + bld->setMaterialAmount(computeMaterialAmount(bld)); return ok; } -static void markBuildingTiles(df::building *bld, df::tile_building_occ occv, bool stockpile) +static void markBuildingTiles(df::building *bld, bool remove) { + bool use_extents = bld->room.extents && bld->isExtentShaped(); + bool stockpile = (bld->getType() == building_type::Stockpile); + bool complete = (bld->getBuildStage() >= bld->getMaxBuildStage()); + + if (remove) + stockpile = complete = false; + for (int tx = bld->x1; tx <= bld->x2; tx++) { for (int ty = bld->y1; ty <= bld->y2; ty++) { df::coord tile(tx,ty,bld->z); - uint8_t *etile = getExtentTile(bld->room, tile); - if (etile && !*etile) - continue; + if (use_extents) + { + uint8_t *etile = getExtentTile(bld->room, tile); + if (!etile || !*etile) + continue; + } df::map_block *block = Maps::getTileBlock(tile); df::coord2d btile = df::coord2d(tile) & 15; - auto &occ = block->occupancy[btile.x][btile.y]; - occ.bits.building = occv; - auto &des = block->designation[btile.x][btile.y]; - des.bits.dig = tile_dig_designation::No; + des.bits.pile = stockpile; + if (!remove) + des.bits.dig = tile_dig_designation::No; + + if (complete) + bld->updateOccupancy(tx, ty); + else + { + auto &occ = block->occupancy[btile.x][btile.y]; + + if (remove) + occ.bits.building = tile_building_occ::None; + else + occ.bits.building = tile_building_occ::Planned; + } } } } @@ -539,7 +613,8 @@ static void linkBuilding(df::building *bld) world->buildings.all.push_back(bld); bld->categorize(true); - markBuildingTiles(bld, tile_building_occ::Planned, false); + if (bld->isSettingOccupancy()) + markBuildingTiles(bld, false); linkRooms(bld); @@ -651,6 +726,9 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectorquantity < 0) + items[i]->quantity = computeMaterialAmount(bld); + job->job_items.push_back(items[i]); if (items[i]->item_type == item_type::BOULDER) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 77a836c9f..6c417b6e1 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -151,7 +151,7 @@ void Units::CopyCreature(df::unit * source, t_unit & furball) // mood stuff furball.mood = source->mood; - furball.mood_skill = source->job.unk_2f8; // FIXME: really? More like currently used skill anyway. + furball.mood_skill = source->job.mood_skill; // FIXME: really? More like currently used skill anyway. Translation::readName(furball.artifact_name, &source->status.artifact_name); // labors diff --git a/library/xml b/library/xml index d75556019..6ce2b7120 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit d755560192ec318fbb52133e4e52972bee67d1e0 +Subproject commit 6ce2b7120e5a246093116b48cb2ac64c35a8270d diff --git a/plugins/zone.cpp b/plugins/zone.cpp index ee6abf327..4314aa1e0 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -1606,8 +1606,8 @@ void zoneInfo(color_ostream & out, df::building* building, bool verbose) out << ", pen/pasture"; else if (civ->zone_flags.bits.pit_pond) { - out << " (pit flags:" << civ->pit_flags << ")"; - if(civ->pit_flags & 1) + out << " (pit flags:" << civ->pit_flags.whole << ")"; + if(civ->pit_flags.bits.is_pond) out << ", pond"; else out << ", pit"; From eadce959408a7b1811bd158e4d5cb74e57b671e6 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 1 May 2012 19:55:25 +0400 Subject: [PATCH 41/53] Add a convenience function for designating constructions. --- LUA_API.rst | 15 +++++++ Lua API.html | 36 +++++++++++----- library/LuaApi.cpp | 11 +++++ library/include/modules/Buildings.h | 6 +++ library/include/modules/Constructions.h | 5 +++ library/include/modules/Maps.h | 14 +++++++ library/modules/Buildings.cpp | 54 +++++++++++++++++++----- library/modules/Constructions.cpp | 56 +++++++++++++++++++++++++ library/modules/Maps.cpp | 18 ++++++++ 9 files changed, 194 insertions(+), 21 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 9925240dd..e2d336f7e 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -857,6 +857,11 @@ Buildings module Returns *width, height, centerx, centery*. +* ``dfhack.buildings.findAtTile(pos)`` + + Scans the buildings for the one located at the given tile. + Does not work on civzones. Warning: linear scan. + * ``dfhack.buildings.getCorrectSize(width, height, type, subtype, custom, direction)`` Computes correct dimensions for the specified building type and orientation, @@ -913,6 +918,16 @@ Low-level building creation functions; More high-level functions are implemented in lua and can be loaded by ``require('dfhack.buildings')``. See ``hack/lua/dfhack/buildings.lua``. +Constructions module +-------------------- + +* ``dfhack.constructions.designateNew(pos,type,item_type,mat_index)`` + + Designates a new construction at given position. If there already is + a planned but not completed construction there, changes its type. + Returns *true*, or *false* if obstructed. + Note that designated constructions are technically buildings. + Core interpreter context ======================== diff --git a/Lua API.html b/Lua API.html index 69a0ab120..ebfbe80ed 100644 --- a/Lua API.html +++ b/Lua API.html @@ -344,17 +344,18 @@ ul.auto-toc {
  • Maps module
  • Burrows module
  • Buildings module
  • +
  • Constructions module
  • -
  • Core interpreter context
  • -
  • Plugins @@ -1068,6 +1069,10 @@ burrows, or the presence of invaders.

  • dfhack.buildings.getSize(building)

    Returns width, height, centerx, centery.

  • +
  • dfhack.buildings.findAtTile(pos)

    +

    Scans the buildings for the one located at the given tile. +Does not work on civzones. Warning: linear scan.

    +
  • dfhack.buildings.getCorrectSize(width, height, type, subtype, custom, direction)

    Computes correct dimensions for the specified building type and orientation, using width and height for flexible dimensions. @@ -1118,9 +1123,20 @@ from the size of the building.

    More high-level functions are implemented in lua and can be loaded by require('dfhack.buildings'). See hack/lua/dfhack/buildings.lua.

  • +
    +

    Constructions module

    +
      +
    • dfhack.constructions.designateNew(pos,type,item_type,mat_index)

      +

      Designates a new construction at given position. If there already is +a planned but not completed construction there, changes its type. +Returns true or false if obstructed. +Note that designated constructions are technically buildings.

      +
    • +
    +
    -

    Core interpreter context

    +

    Core interpreter context

    While plugins can create any number of interpreter instances, there is one special context managed by dfhack core. It is the only context that can receive events from DF and plugins.

    @@ -1134,7 +1150,7 @@ only context that can receive events from DF and plugins.

    -

    Event type

    +

    Event type

    An event is just a lua table with a predefined metatable that contains a __call metamethod. When it is invoked, it loops through the table with next and calls all contained values. @@ -1160,14 +1176,14 @@ order using dfhack.safecall.

    -

    Plugins

    +

    Plugins

    DFHack plugins may export native functions and events to lua contexts. They are automatically imported by mkmodule('plugins.<name>'); this means that a lua module file is still necessary for require to read.

    The following plugins have lua support.

    -

    burrows

    +

    burrows

    Implements extended burrow manipulations.

    Events:

      @@ -1205,7 +1221,7 @@ set is the same as used by the command line.

      The lua module file also re-exports functions from dfhack.burrows.

    -

    sort

    +

    sort

    Does not export any native functions as of now. Instead, it calls lua code to perform the actual ordering of list items.

    diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 11cf73c3f..0d0647183 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -48,6 +48,7 @@ distribution. #include "modules/MapCache.h" #include "modules/Burrows.h" #include "modules/Buildings.h" +#include "modules/Constructions.h" #include "LuaWrapper.h" #include "LuaTools.h" @@ -766,6 +767,7 @@ static const luaL_Reg dfhack_burrows_funcs[] = { /***** Buildings module *****/ static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { + WRAPM(Buildings, findAtTile), WRAPM(Buildings, allocInstance), WRAPM(Buildings, checkFreeTiles), WRAPM(Buildings, countExtentTiles), @@ -822,6 +824,14 @@ static const luaL_Reg dfhack_buildings_funcs[] = { { NULL, NULL } }; +/***** Constructions module *****/ + +static const LuaWrapper::FunctionReg dfhack_constructions_module[] = { + WRAPM(Constructions, designateNew), + { NULL, NULL } +}; + + /************************ * Main Open function * ************************/ @@ -839,4 +849,5 @@ void OpenDFHackApi(lua_State *state) OpenModule(state, "maps", dfhack_maps_module, dfhack_maps_funcs); OpenModule(state, "burrows", dfhack_burrows_module, dfhack_burrows_funcs); OpenModule(state, "buildings", dfhack_buildings_module, dfhack_buildings_funcs); + OpenModule(state, "constructions", dfhack_constructions_module); } diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 610688187..9e529b6ca 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -91,6 +91,12 @@ DFHACK_EXPORT bool Read (const uint32_t index, t_building & building); */ DFHACK_EXPORT bool ReadCustomWorkshopTypes(std::map & btypes); +/* + * Find the building located at the specified tile. + * Does not work on civzones. + */ +DFHACK_EXPORT df::building *findAtTile(df::coord pos); + /** * Allocates a building object using this type and position. */ diff --git a/library/include/modules/Constructions.h b/library/include/modules/Constructions.h index e76cc7ba1..17da12711 100644 --- a/library/include/modules/Constructions.h +++ b/library/include/modules/Constructions.h @@ -31,6 +31,8 @@ distribution. #include "Export.h" #include "DataDefs.h" #include "df/construction.h" +#include "df/construction_type.h" +#include "df/item_type.h" /** * \defgroup grp_constructions Construction module parts @@ -57,6 +59,9 @@ DFHACK_EXPORT bool isValid(); DFHACK_EXPORT uint32_t getCount(); DFHACK_EXPORT bool copyConstruction (const int32_t index, t_construction &out); DFHACK_EXPORT df::construction * getConstruction (const int32_t index); + +DFHACK_EXPORT bool designateNew(df::coord pos, df::construction_type type, + df::item_type item = df::item_type::NONE, int mat_index = -1); } } #endif diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index 332954cda..fc18ca7e1 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -241,6 +241,20 @@ extern DFHACK_EXPORT df::map_block * getTileBlock (int32_t x, int32_t y, int32_t inline df::map_block * getBlock (df::coord pos) { return getBlock(pos.x, pos.y, pos.z); } inline df::map_block * getTileBlock (df::coord pos) { return getTileBlock(pos.x, pos.y, pos.z); } +extern DFHACK_EXPORT df::tiletype *getTileType(int32_t x, int32_t y, int32_t z); +extern DFHACK_EXPORT df::tile_designation *getTileDesignation(int32_t x, int32_t y, int32_t z); +extern DFHACK_EXPORT df::tile_occupancy *getTileOccupancy(int32_t x, int32_t y, int32_t z); + +inline df::tiletype *getTileType(df::coord pos) { + return getTileType(pos.x, pos.y, pos.z); +} +inline df::tile_designation *getTileDesignation(df::coord pos) { + return getTileDesignation(pos.x, pos.y, pos.z); +} +inline df::tile_occupancy *getTileOccupancy(df::coord pos) { + return getTileOccupancy(pos.x, pos.y, pos.z); +} + DFHACK_EXPORT df::world_data::T_region_map *getRegionBiome(df::coord2d rgn_pos); /// sorts the block event vector into multiple vectors by type diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index bc062f980..e587a4916 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -72,6 +72,17 @@ using df::global::building_next_id; using df::global::process_jobs; using df::building_def; +static uint8_t *getExtentTile(df::building_extents &extent, df::coord2d tile) +{ + if (!extent.extents) + return NULL; + int dx = tile.x - extent.x; + int dy = tile.y - extent.y; + if (dx < 0 || dy < 0 || dx >= extent.width || dy >= extent.height) + return NULL; + return &extent.extents[dx + dy*extent.width]; +} + uint32_t Buildings::getNumBuildings() { return world->buildings.all.size(); @@ -110,6 +121,38 @@ bool Buildings::ReadCustomWorkshopTypes(map & btypes) return true; } +df::building *Buildings::findAtTile(df::coord pos) +{ + auto occ = Maps::getTileOccupancy(pos); + if (!occ || !occ->bits.building) + return NULL; + + auto &vec = df::building::get_vector(); + for (size_t i = 0; i < vec.size(); i++) + { + auto bld = vec[i]; + + if (pos.z != bld->z || + pos.x < bld->x1 || pos.x > bld->x2 || + pos.y < bld->y1 || pos.y > bld->y2) + continue; + + if (!bld->isSettingOccupancy()) + continue; + + if (bld->room.extents && bld->isExtentShaped()) + { + auto etile = getExtentTile(bld->room, pos); + if (!etile || !*etile) + continue; + } + + return bld; + } + + return NULL; +} + df::building *Buildings::allocInstance(df::coord pos, df::building_type type, int subtype, int custom) { if (!building_next_id) @@ -319,17 +362,6 @@ bool Buildings::getCorrectSize(df::coord2d &size, df::coord2d ¢er, } } -static uint8_t *getExtentTile(df::building_extents &extent, df::coord2d tile) -{ - if (!extent.extents) - return NULL; - int dx = tile.x - extent.x; - int dy = tile.y - extent.y; - if (dx < 0 || dy < 0 || dx >= extent.width || dy >= extent.height) - return NULL; - return &extent.extents[dx + dy*extent.width]; -} - bool Buildings::checkFreeTiles(df::coord pos, df::coord2d size, df::building_extents *ext, bool create_ext, bool allow_occupied) diff --git a/library/modules/Constructions.cpp b/library/modules/Constructions.cpp index 4c2bf8ec1..a38048391 100644 --- a/library/modules/Constructions.cpp +++ b/library/modules/Constructions.cpp @@ -35,9 +35,20 @@ using namespace std; #include "MemAccess.h" #include "Types.h" #include "Core.h" + #include "modules/Constructions.h" +#include "modules/Buildings.h" +#include "modules/Maps.h" + +#include "TileTypes.h" + #include "df/world.h" +#include "df/job_item.h" +#include "df/building_type.h" +#include "df/building_constructionst.h" + using namespace DFHack; +using namespace df::enums; using df::global::world; bool Constructions::isValid() @@ -73,3 +84,48 @@ bool Constructions::copyConstruction(const int32_t index, t_construction &out) out.original_tile = out.origin->original_tile; return true; } + +bool Constructions::designateNew(df::coord pos, df::construction_type type, + df::item_type item, int mat_index) +{ + auto ttype = Maps::getTileType(pos); + if (!ttype || tileMaterial(*ttype) == tiletype_material::CONSTRUCTION) + return false; + + auto current = Buildings::findAtTile(pos); + if (current) + { + auto cons = strict_virtual_cast(current); + if (!cons) + return false; + + cons->type = type; + return true; + } + + auto newinst = Buildings::allocInstance(pos, building_type::Construction); + if (!newinst) + return false; + + auto newcons = strict_virtual_cast(newinst); + newcons->type = type; + + df::job_item *filter = new df::job_item(); + filter->item_type = item; + filter->mat_index = mat_index; + filter->flags2.bits.building_material = true; + if (mat_index < 0) + filter->flags2.bits.non_economic = true; + + std::vector filters; + filters.push_back(filter); + + if (!Buildings::constructWithFilters(newinst, filters)) + { + delete newinst; + return false; + } + + return true; +} + diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index ddf5d5069..69f591a0f 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -145,6 +145,24 @@ df::map_block *Maps::getTileBlock (int32_t x, int32_t y, int32_t z) return world->map.block_index[x >> 4][y >> 4][z]; } +df::tiletype *Maps::getTileType(int32_t x, int32_t y, int32_t z) +{ + df::map_block *block = getTileBlock(x,y,z); + return block ? &block->tiletype[x&15][y&15] : NULL; +} + +df::tile_designation *Maps::getTileDesignation(int32_t x, int32_t y, int32_t z) +{ + df::map_block *block = getTileBlock(x,y,z); + return block ? &block->designation[x&15][y&15] : NULL; +} + +df::tile_occupancy *Maps::getTileOccupancy(int32_t x, int32_t y, int32_t z) +{ + df::map_block *block = getTileBlock(x,y,z); + return block ? &block->occupancy[x&15][y&15] : NULL; +} + df::world_data::T_region_map *Maps::getRegionBiome(df::coord2d rgn_pos) { auto data = world->world_data; From 6903f3877fb7f3b8aa1f1062e342f0bc2b6001ae Mon Sep 17 00:00:00 2001 From: Kelly Martin Date: Tue, 1 May 2012 10:58:12 -0500 Subject: [PATCH 42/53] Devel plugin nestboxes scans all nestboxes for unhatchable eggs and optionally dumps them. --- plugins/devel/CMakeLists.txt | 2 +- plugins/devel/nestboxes.cpp | 116 +++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 plugins/devel/nestboxes.cpp diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 70daa7b2c..5d1d585ab 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -16,4 +16,4 @@ DFHACK_PLUGIN(counters counters.cpp) DFHACK_PLUGIN(stockcheck stockcheck.cpp) DFHACK_PLUGIN(stripcaged stripcaged.cpp) DFHACK_PLUGIN(rprobe rprobe.cpp) - +DFHACK_PLUGIN(nestboxes nestboxes.cpp) diff --git a/plugins/devel/nestboxes.cpp b/plugins/devel/nestboxes.cpp new file mode 100644 index 000000000..b3d24cd92 --- /dev/null +++ b/plugins/devel/nestboxes.cpp @@ -0,0 +1,116 @@ +#include "Core.h" +#include "Console.h" +#include "Export.h" +#include "PluginManager.h" + +#include "DataDefs.h" +#include "df/world.h" +#include "df/ui.h" +#include "df/building_nest_boxst.h" +#include "df/building_type.h" +#include "df/global_objects.h" +#include "df/item.h" +#include "df/unit.h" +#include "df/building.h" +#include "df/items_other_id.h" +#include "df/creature_raw.h" +#include "modules/MapCache.h" +#include "modules/Items.h" + + +using std::vector; +using std::string; +using std::endl; +using namespace DFHack; +using namespace df::enums; + +using df::global::world; +using df::global::ui; + +static command_result nestboxes(color_ostream &out, vector & parameters); + +DFHACK_PLUGIN("nestboxes"); + +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) +{ + if (world && ui) { + commands.push_back( + PluginCommand("nestboxes", "Derp.", + nestboxes, false, + "Derp.\n" + ) + ); + } + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +} + + +static command_result nestboxes(color_ostream &out, vector & parameters) +{ + CoreSuspender suspend; + bool clean = false; + int dump_count = 0; + int good_egg = 0; + + if (parameters.size() == 1 && parameters[0] == "clean") + { + clean = true; + } + for (int i = 0; i < world->buildings.all.size(); ++i) + { + df::building *build = world->buildings.all[i]; + auto type = build->getType(); + if (df::enums::building_type::NestBox == type) + { + bool needs_clean = false; + df::building_nest_boxst *nb = virtual_cast(build); + out << "Nestbox at (" << nb->x1 << "," << nb->y1 << ","<< nb->z << "): claimed-by " << nb->claimed_by << ", contained item count " << nb->contained_items.size() << " (" << nb->anon_1 << ")" << endl; + if (nb->contained_items.size() > 1) + needs_clean = true; + if (nb->claimed_by != -1) + { + df::unit* u = df::unit::find(nb->claimed_by); + if (u) + { + out << " Claimed by "; + if (u->name.has_name) + out << u->name.first_name << ", "; + df::creature_raw *raw = df::global::world->raws.creatures.all[u->race]; + out << raw->creature_id + << ", pregnancy timer " << u->relations.pregnancy_timer << endl; + if (u->relations.pregnancy_timer > 0) + needs_clean = false; + } + } + for (int j = 1; j < nb->contained_items.size(); j++) + { + df::item* item = nb->contained_items[j]->item; + if (needs_clean) { + if (clean && !item->flags.bits.dump) + { + item->flags.bits.dump = 1; + dump_count += item->getStackSize(); + + } + } else { + good_egg += item->getStackSize(); + } + } + } + } + + if (clean) + { + out << dump_count << " eggs dumped." << endl; + } + out << good_egg << " fertile eggs found." << endl; + + + return CR_OK; +} + From 28b5068382ebd406b269e6b4aeb073e7cd26503e Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 2 May 2012 12:50:05 +0400 Subject: [PATCH 43/53] Allow both coordinate object and (x,y,z) as arguments to getTileBlock. --- LUA_API.rst | 4 +- Lua API.html | 6 +- library/LuaApi.cpp | 24 ++++++- library/LuaTools.cpp | 128 +++++++++++++++++++++++++++---------- library/include/LuaTools.h | 25 +++++++- 5 files changed, 143 insertions(+), 44 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index e2d336f7e..3b2847a58 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -779,9 +779,9 @@ Maps module Returns a map block object for given x,y,z in local block coordinates. -* ``dfhack.maps.getTileBlock(coords)`` +* ``dfhack.maps.getTileBlock(coords)``, or ``getTileBlock(x,y,z)`` - Returns a map block object for given df::coord in local tile coordinates. + Returns a map block object for given df::coord or x,y,z in local tile coordinates. * ``dfhack.maps.getRegionBiome(region_coord2d)`` diff --git a/Lua API.html b/Lua API.html index ebfbe80ed..c7c2041db 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1005,8 +1005,8 @@ Returns false in case of error.

  • dfhack.maps.getBlock(x,y,z)

    Returns a map block object for given x,y,z in local block coordinates.

  • -
  • dfhack.maps.getTileBlock(coords)

    -

    Returns a map block object for given df::coord in local tile coordinates.

    +
  • dfhack.maps.getTileBlock(coords), or getTileBlock(x,y,z)

    +

    Returns a map block object for given df::coord or x,y,z in local tile coordinates.

  • dfhack.maps.getRegionBiome(region_coord2d)

    Returns the biome info struct for the given global map region.

    @@ -1129,7 +1129,7 @@ from the size of the building.

  • dfhack.constructions.designateNew(pos,type,item_type,mat_index)

    Designates a new construction at given position. If there already is a planned but not completed construction there, changes its type. -Returns true or false if obstructed. +Returns true, or false if obstructed. Note that designated constructions are technically buildings.

  • diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 0d0647183..05a71d610 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -520,8 +520,7 @@ static int dfhack_matinfo_matches(lua_State *state) else if (lua_istable(state, 2)) { df::dfhack_material_category tmp; - if (!Lua::AssignDFObject(*Lua::GetOutput(state), state, &tmp, 2, false)) - lua_error(state); + Lua::CheckDFObject(state, &tmp, 2, false); lua_pushboolean(state, info.matches(tmp)); } else @@ -714,7 +713,6 @@ static const luaL_Reg dfhack_items_funcs[] = { static const LuaWrapper::FunctionReg dfhack_maps_module[] = { WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock), - WRAPN(getTileBlock, (df::map_block* (*)(df::coord))Maps::getTileBlock), WRAPM(Maps, getRegionBiome), WRAPM(Maps, getGlobalInitFeature), WRAPM(Maps, getLocalInitFeature), @@ -722,7 +720,27 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = { { NULL, NULL } }; +static int maps_getTileBlock(lua_State *L) +{ + df::map_block *block; + if (lua_gettop(L) == 1) + { + df::coord pos; + Lua::CheckDFObject(L, &pos, 1); + block = Maps::getTileBlock(pos); + } + else + { + block = Maps::getTileBlock( + luaL_checkint(L, 1), luaL_checkint(L, 2), luaL_checkint(L, 3) + ); + } + Lua::PushDFObject(L, block); + return 1; +} + static const luaL_Reg dfhack_maps_funcs[] = { + { "getTileBlock", maps_getTileBlock }, { NULL, NULL } }; diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 253af099c..85e93fbba 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -84,7 +84,7 @@ void *DFHack::Lua::GetDFObject(lua_State *state, type_identity *type, int val_in return get_object_internal(state, type, val_index, exact_type, false); } -void *DFHack::Lua::CheckDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type) +static void check_valid_ptr_index(lua_State *state, int val_index) { if (lua_type(state, val_index) == LUA_TNONE) { @@ -93,23 +93,47 @@ void *DFHack::Lua::CheckDFObject(lua_State *state, type_identity *type, int val_ else luaL_error(state, "at index %d: pointer expected", val_index); } +} - if (lua_isnil(state, val_index)) - return NULL; +static void dfhack_printerr(lua_State *S, const std::string &str); - void *rv = get_object_internal(state, type, val_index, exact_type, false); +static void signal_typeid_error(color_ostream *out, lua_State *state, + type_identity *type, const char *msg, + int val_index, bool perr, bool signal) +{ + std::string error = stl_sprintf(msg, type->getFullName().c_str()); - if (!rv) + if (signal) { - std::string error = "invalid pointer type"; - if (type) - error += "; expected: " + type->getFullName(); - if (val_index > 0) luaL_argerror(state, val_index, error.c_str()); else luaL_error(state, "at index %d: %s", val_index, error.c_str()); } + else if (perr) + { + if (out) + out->printerr("%s", error.c_str()); + else + dfhack_printerr(state, error); + } + else + lua_pushstring(state, error.c_str()); +} + + +void *DFHack::Lua::CheckDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type) +{ + check_valid_ptr_index(state, val_index); + + if (lua_isnil(state, val_index)) + return NULL; + + void *rv = get_object_internal(state, type, val_index, exact_type, false); + + if (!rv) + signal_typeid_error(NULL, state, type, "invalid pointer type; expected: %s", + val_index, false, true); return rv; } @@ -343,7 +367,7 @@ static void error_tostring(lua_State *L, bool keep_old = false) } } -static void report_error(lua_State *L, color_ostream *out = NULL) +static void report_error(lua_State *L, color_ostream *out = NULL, bool pop = false) { error_tostring(L, true); @@ -355,7 +379,7 @@ static void report_error(lua_State *L, color_ostream *out = NULL) else dfhack_printerr(L, msg); - lua_pop(L, 1); + lua_pop(L, pop?2:1); } static bool convert_to_exception(lua_State *L, int slevel, lua_State *thread = NULL) @@ -544,10 +568,7 @@ bool DFHack::Lua::SafeCall(color_ostream &out, lua_State *L, int nargs, int nres bool ok = lua_pcall(L, nargs, nres, base) == LUA_OK; if (!ok && perr) - { - report_error(L, &out); - lua_pop(L, 1); - } + report_error(L, &out, true); lua_remove(L, base); set_dfhack_output(L, cur_out); @@ -658,10 +679,7 @@ int DFHack::Lua::SafeResume(color_ostream &out, lua_State *from, lua_State *thre int rv = resume_helper(from, thread, nargs, nres); if (!Lua::IsSuccess(rv) && perr) - { - report_error(from, &out); - lua_pop(from, 1); - } + report_error(from, &out, true); set_dfhack_output(from, cur_out); @@ -746,14 +764,64 @@ bool DFHack::Lua::Require(color_ostream &out, lua_State *state, return true; } +static bool doAssignDFObject(color_ostream *out, lua_State *state, + type_identity *type, void *target, int val_index, + bool exact, bool perr, bool signal) +{ + if (signal) + check_valid_ptr_index(state, val_index); + + if (lua_istable(state, val_index)) + { + val_index = lua_absindex(state, val_index); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME); + Lua::PushDFObject(state, type, target); + lua_pushvalue(state, val_index); + + if (signal) + { + lua_call(state, 2, 0); + return true; + } + else + return Lua::SafeCall(*out, state, 2, 0, perr); + } + else if (!lua_isuserdata(state, val_index)) + { + signal_typeid_error(out, state, type, "pointer to %s expected", + val_index, perr, signal); + return false; + } + else + { + void *in_ptr = Lua::GetDFObject(state, type, val_index, exact); + if (!in_ptr) + { + signal_typeid_error(out, state, type, "incompatible pointer type: %s expected", + val_index, perr, signal); + return false; + } + if (!type->copy(target, in_ptr)) + { + signal_typeid_error(out, state, type, "no copy support for %s", + val_index, perr, signal); + return false; + } + return true; + } +} + bool DFHack::Lua::AssignDFObject(color_ostream &out, lua_State *state, - type_identity *type, void *target, int val_index, bool perr) + type_identity *type, void *target, int val_index, + bool exact_type, bool perr) { - val_index = lua_absindex(state, val_index); - lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME); - PushDFObject(state, type, target); - lua_pushvalue(state, val_index); - return Lua::SafeCall(out, state, 2, 0, perr); + return doAssignDFObject(&out, state, type, target, val_index, exact_type, perr, false); +} + +void DFHack::Lua::CheckDFObject(lua_State *state, type_identity *type, + void *target, int val_index, bool exact_type) +{ + doAssignDFObject(NULL, state, type, target, val_index, exact_type, false, true); } bool DFHack::Lua::SafeCallString(color_ostream &out, lua_State *state, const std::string &code, @@ -773,10 +841,7 @@ bool DFHack::Lua::SafeCallString(color_ostream &out, lua_State *state, const std if (luaL_loadbuffer(state, code.data(), code.size(), debug_tag) != LUA_OK) { if (perr) - { - report_error(state, &out); - lua_pop(state, 1); - } + report_error(state, &out, true); return false; } @@ -1113,10 +1178,7 @@ static void do_invoke_event(lua_State *L, int argbase, int num_args, int errorfu lua_pushvalue(L, argbase+i); if (lua_pcall(L, num_args, 0, errorfun) != LUA_OK) - { - report_error(L); - lua_pop(L, 1); - } + report_error(L, NULL, true); } static void dfhack_event_invoke(lua_State *L, int base, bool from_c) diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index 1ca79d331..a6e538d2a 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -109,7 +109,15 @@ namespace DFHack {namespace Lua { * Return behavior is of SafeCall below. */ DFHACK_EXPORT bool AssignDFObject(color_ostream &out, lua_State *state, - type_identity *type, void *target, int val_index, bool perr = true); + type_identity *type, void *target, int val_index, + bool exact_type = false, bool perr = true); + + /** + * Assign the value at val_index to the target of given identity using df.assign(). + * Otherwise throws an error. + */ + DFHACK_EXPORT void CheckDFObject(lua_State *state, type_identity *type, + void *target, int val_index, bool exact_type = false); /** * Push the pointer onto the stack as a wrapped DF object of a specific type. @@ -139,8 +147,19 @@ namespace DFHack {namespace Lua { * Assign the value at val_index to the target using df.assign(). */ template - bool AssignDFObject(color_ostream &out, lua_State *state, T *target, int val_index, bool perr = true) { - return AssignDFObject(out, state, df::identity_traits::get(), target, val_index, perr); + bool AssignDFObject(color_ostream &out, lua_State *state, T *target, + int val_index, bool exact_type = false, bool perr = true) { + return AssignDFObject(out, state, df::identity_traits::get(), + target, val_index, exact_type, perr); + } + + /** + * Assign the value at val_index to the target using df.assign(). + * Throws in case of an error. + */ + template + void CheckDFObject(lua_State *state, T *target, int val_index, bool exact_type = false) { + CheckDFObject(state, df::identity_traits::get(), target, val_index, exact_type); } /** From 5afe2ca0020404d46ad3de840030a7b9e186b059 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 3 May 2012 11:47:04 +0400 Subject: [PATCH 44/53] Update dfhack for specific_ref. --- LUA_API.rst | 8 +++++ Lua API.html | 6 ++++ library/LuaApi.cpp | 2 ++ library/LuaTypes.cpp | 27 ++++++++++++---- library/MiscUtils.cpp | 57 +++++++++++++++++++++++++++++++++ library/include/Types.h | 10 ++++++ library/include/modules/Items.h | 5 +++ library/modules/Items.cpp | 47 ++++++++++----------------- library/modules/Job.cpp | 11 ++++--- library/xml | 2 +- plugins/autolabor.cpp | 2 +- plugins/jobutils.cpp | 2 +- plugins/workflow.cpp | 9 +++--- 13 files changed, 138 insertions(+), 50 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 3b2847a58..d9488d60f 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -738,6 +738,14 @@ Items module Returns true *x,y,z* of the item; may be not equal to item.pos if in inventory. +* ``dfhack.items.getGeneralRef(item, type)`` + + Searches for a general_ref with the given type. + +* ``dfhack.items.getSpecificRef(item, type)`` + + Searches for a specific_ref with the given type. + * ``dfhack.items.getOwner(item)`` Returns the owner unit or *nil*. diff --git a/Lua API.html b/Lua API.html index c7c2041db..40706d14f 100644 --- a/Lua API.html +++ b/Lua API.html @@ -972,6 +972,12 @@ or raws. The ignore_noble boolean disables the
  • dfhack.items.getPosition(item)

    Returns true x,y,z of the item; may be not equal to item.pos if in inventory.

  • +
  • dfhack.items.getGeneralRef(item, type)

    +

    Searches for a general_ref with the given type.

    +
  • +
  • dfhack.items.getSpecificRef(item, type)

    +

    Searches for a specific_ref with the given type.

    +
  • dfhack.items.getOwner(item)

    Returns the owner unit or nil.

  • diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 05a71d610..828e54cbc 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -682,6 +682,8 @@ static bool items_moveToContainer(df::item *item, df::item *container) } static const LuaWrapper::FunctionReg dfhack_items_module[] = { + WRAPM(Items, getGeneralRef), + WRAPM(Items, getSpecificRef), WRAPM(Items, getOwner), WRAPM(Items, setOwner), WRAPM(Items, getContainer), diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp index 02a89f9e2..c58c25d11 100644 --- a/library/LuaTypes.cpp +++ b/library/LuaTypes.cpp @@ -1103,26 +1103,39 @@ static void IndexFields(lua_State *state, int base, struct_identity *pstruct, bo name = pstruct->getName() + ("." + name); lua_pop(state, 1); + bool add_to_enum = true; + // Handle the field switch (fields[i].mode) { case struct_field_info::OBJ_METHOD: AddMethodWrapper(state, base+1, base+2, name.c_str(), (function_identity_base*)fields[i].type); - break; + continue; case struct_field_info::CLASS_METHOD: + continue; + + case struct_field_info::POINTER: + // Skip class-typed pointers within unions + if ((fields[i].count & 2) != 0 && fields[i].type && + fields[i].type->type() == IDTYPE_CLASS) + add_to_enum = false; break; default: - // Do not add invalid globals to the enumeration order - if (!globals || *(void**)fields[i].offset) - AssociateId(state, base+3, ++cnt, name.c_str()); - - lua_pushlightuserdata(state, (void*)&fields[i]); - lua_setfield(state, base+2, name.c_str()); break; } + + // Do not add invalid globals to the enumeration order + if (globals && !*(void**)fields[i].offset) + add_to_enum = false; + + if (add_to_enum) + AssociateId(state, base+3, ++cnt, name.c_str()); + + lua_pushlightuserdata(state, (void*)&fields[i]); + lua_setfield(state, base+2, name.c_str()); } } diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index 8658788bb..c0674686b 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -26,6 +26,7 @@ distribution. #include "Export.h" #include "MiscUtils.h" #include "Error.h" +#include "Types.h" #ifndef LINUX_BUILD #include @@ -124,6 +125,62 @@ std::string toLower(const std::string &str) return rv; } +df::general_ref *DFHack::findRef(std::vector &vec, df::general_ref_type type) +{ + for (int i = vec.size()-1; i >= 0; i--) + { + df::general_ref *ref = vec[i]; + if (ref->getType() == type) + return ref; + } + + return NULL; +} + +bool DFHack::removeRef(std::vector &vec, df::general_ref_type type, int id) +{ + for (int i = vec.size()-1; i >= 0; i--) + { + df::general_ref *ref = vec[i]; + if (ref->getType() != type || ref->getID() != id) + continue; + + vector_erase_at(vec, i); + delete ref; + return true; + } + + return false; +} + +df::specific_ref *DFHack::findRef(std::vector &vec, df::specific_ref_type type) +{ + for (int i = vec.size()-1; i >= 0; i--) + { + df::specific_ref *ref = vec[i]; + if (ref->type == type) + return ref; + } + + return NULL; +} + +bool DFHack::removeRef(std::vector &vec, df::specific_ref_type type, void *ptr) +{ + for (int i = vec.size()-1; i >= 0; i--) + { + df::specific_ref *ref = vec[i]; + if (ref->type != type || ref->object != ptr) + continue; + + vector_erase_at(vec, i); + delete ref; + return true; + } + + return false; +} + #ifdef LINUX_BUILD // Linux uint64_t GetTimeMs64() { diff --git a/library/include/Types.h b/library/include/Types.h index a8b4516cf..018e7ccfd 100644 --- a/library/include/Types.h +++ b/library/include/Types.h @@ -28,6 +28,10 @@ distribution. #include "Pragma.h" #include "Export.h" +#include "DataDefs.h" +#include "df/general_ref.h" +#include "df/specific_ref.h" + namespace DFHack { struct t_matglossPair @@ -69,4 +73,10 @@ namespace DFHack std::string name; uint32_t xpNxtLvl; }; + + DFHACK_EXPORT df::general_ref *findRef(std::vector &vec, df::general_ref_type type); + DFHACK_EXPORT bool removeRef(std::vector &vec, df::general_ref_type type, int id); + + DFHACK_EXPORT df::specific_ref *findRef(std::vector &vec, df::specific_ref_type type); + DFHACK_EXPORT bool removeRef(std::vector &vec, df::specific_ref_type type, void *ptr); }// namespace DFHack \ No newline at end of file diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index e7720fa91..8353d24c3 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -36,6 +36,7 @@ distribution. #include "df/item.h" #include "df/item_type.h" #include "df/general_ref.h" +#include "df/specific_ref.h" namespace df { @@ -126,6 +127,10 @@ DFHACK_EXPORT bool copyItem(df::item * source, dfh_item & target); /// write copied item back to its origin DFHACK_EXPORT bool writeItem(const dfh_item & item); +/// Retrieve refs +DFHACK_EXPORT df::general_ref *getGeneralRef(df::item *item, df::general_ref_type type); +DFHACK_EXPORT df::specific_ref *getSpecificRef(df::item *item, df::specific_ref_type type); + /// Retrieve the owner of the item. DFHACK_EXPORT df::unit *getOwner(df::item *item); /// Set the owner of the item. Pass NULL as unit to remove the owner. diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 96fcc19ce..f1d495bf1 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -421,18 +421,25 @@ bool Items::copyItem(df::item * itembase, DFHack::dfh_item &item) return true; } -df::unit *Items::getOwner(df::item * item) +df::general_ref *Items::getGeneralRef(df::item *item, df::general_ref_type type) { CHECK_NULL_POINTER(item); - for (size_t i = 0; i < item->itemrefs.size(); i++) - { - df::general_ref *ref = item->itemrefs[i]; - if (strict_virtual_cast(ref)) - return ref->getUnit(); - } + return findRef(item->itemrefs, type); +} + +df::specific_ref *Items::getSpecificRef(df::item *item, df::specific_ref_type type) +{ + CHECK_NULL_POINTER(item); + + return findRef(item->specific_refs, type); +} + +df::unit *Items::getOwner(df::item * item) +{ + auto ref = getGeneralRef(item, general_ref_type::UNIT_ITEMOWNER); - return NULL; + return ref ? ref->getUnit() : NULL; } bool Items::setOwner(df::item *item, df::unit *unit) @@ -478,16 +485,9 @@ bool Items::setOwner(df::item *item, df::unit *unit) df::item *Items::getContainer(df::item * item) { - CHECK_NULL_POINTER(item); + auto ref = getGeneralRef(item, general_ref_type::CONTAINED_IN_ITEM); - for (size_t i = 0; i < item->itemrefs.size(); i++) - { - df::general_ref *ref = item->itemrefs[i]; - if (ref->getType() == general_ref_type::CONTAINED_IN_ITEM) - return ref->getItem(); - } - - return NULL; + return ref ? ref->getItem() : NULL; } void Items::getContainedItems(df::item *item, std::vector *items) @@ -546,19 +546,6 @@ df::coord Items::getPosition(df::item *item) return item->pos; } -static void removeRef(std::vector &vec, df::general_ref_type type, int id) -{ - for (int i = vec.size()-1; i >= 0; i--) - { - df::general_ref *ref = vec[i]; - if (ref->getType() != type || ref->getID() != id) - continue; - - vector_erase_at(vec, i); - delete ref; - } -} - static bool detachItem(MapExtras::MapCache &mc, df::item *item) { if (item->flags.bits.on_ground) diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index 8e8e025d4..149707c6e 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -46,6 +46,7 @@ using namespace std; #include "df/job.h" #include "df/job_item.h" #include "df/job_list_link.h" +#include "df/specific_ref.h" #include "df/general_ref.h" #include "df/general_ref_unit_workerst.h" #include "df/general_ref_building_holderst.h" @@ -67,7 +68,7 @@ df::job *DFHack::Job::cloneJobStruct(df::job *job) pnew->list_link = NULL; pnew->completion_timer = -1; pnew->items.clear(); - pnew->misc_links.clear(); + pnew->specific_refs.clear(); // Clone refs for (int i = pnew->references.size()-1; i >= 0; i--) @@ -93,7 +94,7 @@ void DFHack::Job::deleteJobStruct(df::job *job) return; // Only allow free-floating job structs - assert(!job->list_link && job->items.empty() && job->misc_links.empty()); + assert(!job->list_link && job->items.empty() && job->specific_refs.empty()); for (int i = job->references.size()-1; i >= 0; i--) delete job->references[i]; @@ -331,10 +332,10 @@ bool DFHack::Job::attachJobItem(df::job *job, df::item *item, item->flags.bits.in_job = true; } - auto item_link = new df::item::T_jobs(); - item_link->type = 2; + auto item_link = new df::specific_ref(); + item_link->type = specific_ref_type::JOB; item_link->job = job; - item->jobs.push_back(item_link); + item->specific_refs.push_back(item_link); auto job_link = new df::job_item_ref(); job_link->item = item; diff --git a/library/xml b/library/xml index 6ce2b7120..5707a6aa0 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 6ce2b7120e5a246093116b48cb2ac64c35a8270d +Subproject commit 5707a6aa0c035348c8769058ed77b320f9b1436c diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index cfd9419db..797bd7465 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -832,7 +832,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { if (is_on_break) dwarf_info[dwarf].state = OTHER; - else if (dwarfs[dwarf]->meetings.size() > 0) + else if (dwarfs[dwarf]->specific_refs.size() > 0) dwarf_info[dwarf].state = OTHER; else dwarf_info[dwarf].state = IDLE; diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp index de7f81366..24ad4170e 100644 --- a/plugins/jobutils.cpp +++ b/plugins/jobutils.cpp @@ -272,7 +272,7 @@ static command_result job_duplicate(color_ostream &out, vector & parame if (!job) return CR_FAILURE; - if (!job->misc_links.empty() || + if (!job->specific_refs.empty() || (job->job_items.empty() && job->job_type != job_type::CollectSand && job->job_type != job_type::CollectClay)) diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 0d6caa472..1092c86ae 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -364,7 +364,7 @@ static ProtectedJob *get_known(int id) static bool isSupportedJob(df::job *job) { - return job->misc_links.empty() && + return job->specific_refs.empty() && Job::getHolder(job) && (!job->job_items.empty() || job->job_type == job_type::CollectClay || @@ -1083,12 +1083,11 @@ static bool itemInRealJob(df::item *item) if (!item->flags.bits.in_job) return false; - if (item->jobs.size() != 1 || - item->jobs[0]->type != 2 || - item->jobs[0]->job == NULL) + auto ref = Items::getSpecificRef(item, specific_ref_type::JOB); + if (!ref || !ref->job) return true; - return ENUM_ATTR(job_type, type, item->jobs[0]->job->job_type) + return ENUM_ATTR(job_type, type, ref->job->job_type) != job_type_class::Hauling; } From d4d6349f48d01b31f59f94238d6656e3b5d08508 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 4 May 2012 19:47:18 +0400 Subject: [PATCH 45/53] Expose builtin commands to dfhack-run, and add lua script support. Move builtin command implementation to Core methods, and fall back to hack/scripts/*.lua for otherwise unrecognized commands. --- library/CMakeLists.txt | 3 + library/Core.cpp | 218 +++++++++++++++++++++------- library/LuaTools.cpp | 20 ++- library/MiscUtils.cpp | 31 ++++ library/PluginManager.cpp | 37 +---- library/RemoteTools.cpp | 2 +- library/include/Core.h | 10 +- library/include/LuaTools.h | 3 + library/include/MiscUtils.h | 3 + library/lua/dfhack.lua | 18 +++ plugins/Dfusion/src/lua_Console.cpp | 3 +- scripts/lua-example.lua | 6 + 12 files changed, 260 insertions(+), 94 deletions(-) create mode 100644 scripts/lua-example.lua diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index c052df9c8..466175ed4 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -300,6 +300,9 @@ install(DIRECTORY lua/ DESTINATION ${DFHACK_LUA_DESTINATION} FILES_MATCHING PATTERN "*.lua") +install(DIRECTORY ${dfhack_SOURCE_DIR}/scripts + DESTINATION ${DFHACK_DATA_DESTINATION}) + # Unused for so long that it's not even relevant now... if(BUILD_DEVEL) if(WIN32) diff --git a/library/Core.cpp b/library/Core.cpp index 6e1e0986a..60e380598 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -51,6 +51,8 @@ using namespace std; #include "RemoteServer.h" #include "LuaTools.h" +#include "MiscUtils.h" + using namespace DFHack; #include "df/ui.h" @@ -72,8 +74,6 @@ using df::global::init; // FIXME: A lot of code in one file, all doing different things... there's something fishy about it. -static void loadScriptFile(Core *core, PluginManager *plug_mgr, string fname, bool silent); -static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clueless_counter, const string &command); static bool parseKeySpec(std::string keyspec, int *psym, int *pmod); struct Core::Cond @@ -178,21 +178,10 @@ void fHKthread(void * iodata) { color_ostream_proxy out(core->getConsole()); - vector args; - Core::cheap_tokenise(stuff, args); - if (args.empty()) { - out.printerr("Empty hotkey command.\n"); - continue; - } + auto rv = core->runCommand(out, stuff); - string first = args[0]; - args.erase(args.begin()); - command_result cr = plug_mgr->InvokeCommand(out, first, args); - - if(cr == CR_NEEDS_CONSOLE) - { - out.printerr("It isn't possible to run an interactive command outside the console.\n"); - } + if (rv == CR_NOT_IMPLEMENTED) + out.printerr("Invalid hotkey command: '%s'\n", stuff.c_str()); } } } @@ -212,38 +201,122 @@ struct sortable }; }; -static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clueless_counter, const string &command) +static std::string getLuaHelp(std::string path) +{ + ifstream script(path); + + if (script.good()) + { + std::string help; + if (getline(script, help) && + help.substr(0,3) == "-- ") + return help.substr(3); + } + + return "Lua script."; +} + +static std::map listLuaScripts(std::string path) +{ + std::vector files; + getdir(path, files); + + std::map pset; + for (size_t i = 0; i < files.size(); i++) + { + if (hasEnding(files[i], ".lua")) + { + std::string help = getLuaHelp(path + files[i]); + + pset[files[i].substr(0, files[i].size()-4)] = help; + } + } + return pset; +} + +static bool fileExists(std::string path) +{ + ifstream script(path); + return script.good(); +} + +namespace { + struct ScriptArgs { + const string *pcmd; + vector *pargs; + }; +} + +static bool init_run_script(color_ostream &out, lua_State *state, void *info) +{ + auto args = (ScriptArgs*)info; + if (!lua_checkstack(state, args->pargs->size()+10)) + return false; + Lua::PushDFHack(state); + lua_getfield(state, -1, "run_script"); + lua_remove(state, -2); + lua_pushstring(state, args->pcmd->c_str()); + for (size_t i = 0; i < args->pargs->size(); i++) + lua_pushstring(state, (*args->pargs)[i].c_str()); + return true; +} + +static command_result runLuaScript(color_ostream &out, std::string filename, vector &args) +{ + ScriptArgs data; + data.pcmd = &filename; + data.pargs = &args; + +#ifndef LINUX_BUILD + filename = toLower(filename); +#endif + + bool ok = Lua::RunCoreQueryLoop(out, Lua::Core::State, init_run_script, &data); + + return ok ? CR_OK : CR_FAILURE; +} + +command_result Core::runCommand(color_ostream &out, const std::string &command) { - Console & con = core->getConsole(); - if (!command.empty()) { - // cut the input into parts vector parts; Core::cheap_tokenise(command,parts); if(parts.size() == 0) - { - clueless_counter ++; - return; - } + return CR_NOT_IMPLEMENTED; + string first = parts[0]; parts.erase(parts.begin()); - if (first[0] == '#') return; + if (first[0] == '#') + return CR_OK; cerr << "Invoking: " << command << endl; - + + return runCommand(out, first, parts); + } + else + return CR_NOT_IMPLEMENTED; +} + +command_result Core::runCommand(color_ostream &con, const std::string &first, vector &parts) +{ + if (!first.empty()) + { // let's see what we actually got if(first=="help" || first == "?" || first == "man") { if(!parts.size()) { - con.print("This is the DFHack console. You can type commands in and manage DFHack plugins from it.\n" - "Some basic editing capabilities are included (single-line text editing).\n" - "The console also has a command history - you can navigate it with Up and Down keys.\n" - "On Windows, you may have to resize your console window. The appropriate menu is accessible\n" - "by clicking on the program icon in the top bar of the window.\n\n" - "Basic commands:\n" + if (con.is_console()) + { + con.print("This is the DFHack console. You can type commands in and manage DFHack plugins from it.\n" + "Some basic editing capabilities are included (single-line text editing).\n" + "The console also has a command history - you can navigate it with Up and Down keys.\n" + "On Windows, you may have to resize your console window. The appropriate menu is accessible\n" + "by clicking on the program icon in the top bar of the window.\n\n"); + } + con.print("Basic commands:\n" " help|?|man - This text.\n" " help COMMAND - Usage help for the given command.\n" " ls|dir [PLUGIN] - List available commands. Optionally for single plugin.\n" @@ -274,9 +347,15 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue con.reset_color(); if (!pcmd.usage.empty()) con << "Usage:\n" << pcmd.usage << flush; - return; + return CR_OK; } } + auto filename = getHackPath() + "scripts/" + parts[0] + ".lua"; + if (fileExists(filename)) + { + string help = getLuaHelp(filename); + con.print("%s: %s\n", parts[0].c_str(), help.c_str()); + } con.printerr("Unknown command: %s\n", parts[0].c_str()); } else @@ -421,6 +500,13 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue con.print(" %-22s- %s\n",(*iter).name.c_str(), (*iter).description.c_str()); con.reset_color(); } + auto scripts = listLuaScripts(getHackPath() + "scripts/"); + if (!scripts.empty()) + { + con.print("\nscripts:\n"); + for (auto iter = scripts.begin(); iter != scripts.end(); ++iter) + con.print(" %-22s- %s\n", iter->first.c_str(), iter->second.c_str()); + } } } else if(first == "plug") @@ -439,10 +525,10 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue { std::string keystr = parts[1]; if (parts[0] == "set") - core->ClearKeyBindings(keystr); + ClearKeyBindings(keystr); for (int i = parts.size()-1; i >= 2; i--) { - if (!core->AddKeyBinding(keystr, parts[i])) { + if (!AddKeyBinding(keystr, parts[i])) { con.printerr("Invalid key spec: %s\n", keystr.c_str()); break; } @@ -452,7 +538,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue { for (size_t i = 1; i < parts.size(); i++) { - if (!core->ClearKeyBindings(parts[i])) { + if (!ClearKeyBindings(parts[i])) { con.printerr("Invalid key spec: %s\n", parts[i].c_str()); break; } @@ -460,7 +546,7 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue } else if (parts.size() == 2 && parts[0] == "list") { - std::vector list = core->ListKeyBindings(parts[1]); + std::vector list = ListKeyBindings(parts[1]); if (list.empty()) con << "No bindings." << endl; for (size_t i = 0; i < list.size(); i++) @@ -478,13 +564,19 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue } else if(first == "fpause") { - World * w = core->getWorld(); + World * w = getWorld(); w->SetPauseState(true); - con.print("The game was forced to pause!"); + con.print("The game was forced to pause!\n"); } else if(first == "cls") { - con.clear(); + if (con.is_console()) + ((Console&)con).clear(); + else + { + con.printerr("No console to clear.\n"); + return CR_NEEDS_CONSOLE; + } } else if(first == "die") { @@ -494,12 +586,13 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue { if(parts.size() == 1) { - loadScriptFile(core, plug_mgr, parts[0], false); + loadScriptFile(con, parts[0], false); } else { con << "Usage:" << endl << " script " << endl; + return CR_WRONG_USAGE; } } else @@ -507,35 +600,44 @@ static void runInteractiveCommand(Core *core, PluginManager *plug_mgr, int &clue command_result res = plug_mgr->InvokeCommand(con, first, parts); if(res == CR_NOT_IMPLEMENTED) { - con.printerr("%s is not a recognized command.\n", first.c_str()); - clueless_counter ++; + auto filename = getHackPath() + "scripts/" + first + ".lua"; + if (fileExists(filename)) + res = runLuaScript(con, filename, parts); + else + con.printerr("%s is not a recognized command.\n", first.c_str()); } + else if (res == CR_NEEDS_CONSOLE) + con.printerr("%s needs interactive console to work.\n", first.c_str()); + return res; } + + return CR_OK; } + + return CR_NOT_IMPLEMENTED; } -static void loadScriptFile(Core *core, PluginManager *plug_mgr, string fname, bool silent) +bool Core::loadScriptFile(color_ostream &out, string fname, bool silent) { if(!silent) - core->getConsole() << "Loading script at " << fname << std::endl; + out << "Loading script at " << fname << std::endl; ifstream script(fname); if (script.good()) { - int tmp = 0; string command; while (getline(script, command)) { if (!command.empty()) - runInteractiveCommand(core, plug_mgr, tmp, command); + runCommand(out, command); } + return true; } else { if(!silent) - core->getConsole().printerr("Error loading script\n"); + out.printerr("Error loading script\n"); + return false; } - - script.close(); } // A thread function... for the interactive console. @@ -555,7 +657,7 @@ void fIOthread(void * iodata) return; } - loadScriptFile(core, plug_mgr, "dfhack.init", true); + core->loadScriptFile(con, "dfhack.init", true); con.print("DFHack is ready. Have a nice day!\n" "Type in '?' or 'help' for general help, 'ls' to see all commands.\n"); @@ -582,7 +684,10 @@ void fIOthread(void * iodata) main_history.save("dfhack.history"); } - runInteractiveCommand(core, plug_mgr, clueless_counter, command); + auto rv = core->runCommand(con, command); + + if (rv == CR_NOT_IMPLEMENTED) + clueless_counter++; if(clueless_counter == 3) { @@ -636,6 +741,15 @@ void Core::fatal (std::string output, bool deactivate) #endif } +std::string Core::getHackPath() +{ +#ifdef LINUX_BUILD + return p->getPath() + "/hack/"; +#else + return p->getPath() + "\\hack\\"; +#endif +} + bool Core::Init() { if(started) diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 85e93fbba..17ad0abde 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -706,6 +706,16 @@ static int DFHACK_DFHACK_TOKEN = 0; static int DFHACK_BASE_G_TOKEN = 0; static int DFHACK_REQUIRE_TOKEN = 0; +void Lua::PushDFHack(lua_State *state) +{ + lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_DFHACK_TOKEN); +} + +void Lua::PushBaseGlobals(lua_State *state) +{ + lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_BASE_G_TOKEN); +} + bool DFHack::Lua::PushModule(color_ostream &out, lua_State *state, const char *module) { AssertCoreSuspend(state); @@ -884,13 +894,9 @@ bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state, bool (*init)(color_ostream&, lua_State*, void*), void *arg) { - if (!out.is_console()) - return false; if (!lua_checkstack(state, 20)) return false; - Console &con = static_cast(out); - lua_State *thread; int rv; std::string prompt; @@ -910,6 +916,10 @@ bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state, return false; } + // If not interactive, run without coroutine and bail out + if (!out.is_console()) + return SafeCall(out, state, lua_gettop(state)-base-1, 0); + lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_QUERY_COROTABLE_TOKEN); lua_pushvalue(state, base+1); lua_remove(state, base+1); @@ -920,6 +930,8 @@ bool DFHack::Lua::RunCoreQueryLoop(color_ostream &out, lua_State *state, rv = resume_query_loop(out, thread, state, lua_gettop(state)-base, prompt, histfile); } + Console &con = static_cast(out); + while (rv == LUA_YIELD) { if (histfile != histname) diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index c0674686b..65fa21e11 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -30,9 +30,12 @@ distribution. #ifndef LINUX_BUILD #include + #include "wdirent.h" #else #include #include + #include + #include #endif #include @@ -125,6 +128,34 @@ std::string toLower(const std::string &str) return rv; } +int getdir(std::string dir, std::vector &files) +{ + DIR *dp; + struct dirent *dirp; + if((dp = opendir(dir.c_str())) == NULL) + { + return errno; + } + while ((dirp = readdir(dp)) != NULL) { + files.push_back(std::string(dirp->d_name)); + } + closedir(dp); + return 0; +} + +bool hasEnding (std::string const &fullString, std::string const &ending) +{ + if (fullString.length() > ending.length()) + { + return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); + } + else + { + return false; + } +} + + df::general_ref *DFHack::findRef(std::vector &vec, df::general_ref_type type) { for (int i = vec.size()-1; i >= 0; i--) diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 837c3e2d5..69b8bfe0d 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -45,41 +45,8 @@ using namespace std; #include "tinythread.h" using namespace tthread; -#ifdef LINUX_BUILD - #include - #include -#else - #include "wdirent.h" -#endif - #include -static int getdir (string dir, vector &files) -{ - DIR *dp; - struct dirent *dirp; - if((dp = opendir(dir.c_str())) == NULL) - { - return errno; - } - while ((dirp = readdir(dp)) != NULL) { - files.push_back(string(dirp->d_name)); - } - closedir(dp); - return 0; -} - -bool hasEnding (std::string const &fullString, std::string const &ending) -{ - if (fullString.length() > ending.length()) - { - return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); - } - else - { - return false; - } -} struct Plugin::RefLock { RefLock() @@ -563,10 +530,10 @@ void Plugin::push_function(lua_State *state, LuaFunction *fn) PluginManager::PluginManager(Core * core) { #ifdef LINUX_BUILD - string path = core->p->getPath() + "/hack/plugins/"; + string path = core->getHackPath() + "plugins/"; const string searchstr = ".plug.so"; #else - string path = core->p->getPath() + "\\hack\\plugins\\"; + string path = core->getHackPath() + "plugins\\"; const string searchstr = ".plug.dll"; #endif cmdlist_mutex = new mutex(); diff --git a/library/RemoteTools.cpp b/library/RemoteTools.cpp index b72c0c91d..689c783a8 100644 --- a/library/RemoteTools.cpp +++ b/library/RemoteTools.cpp @@ -711,7 +711,7 @@ command_result CoreService::RunCommand(color_ostream &stream, for (int i = 0; i < in->arguments_size(); i++) args.push_back(in->arguments(i)); - return Core::getInstance().plug_mgr->InvokeCommand(stream, cmd, args); + return Core::getInstance().runCommand(stream, cmd, args); } command_result CoreService::CoreSuspend(color_ostream &stream, const EmptyMessage*, IntMessage *cnt) diff --git a/library/include/Core.h b/library/include/Core.h index 4f9b10c84..fe83715cf 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -35,6 +35,8 @@ distribution. #include "modules/Graphic.h" #include "SDL_events.h" +#include "RemoteClient.h" + struct WINDOW; namespace tthread @@ -117,10 +119,16 @@ namespace DFHack /// returns a named pointer. void *GetData(std::string key); + command_result runCommand(color_ostream &out, const std::string &command, std::vector ¶meters); + command_result runCommand(color_ostream &out, const std::string &command); + bool loadScriptFile(color_ostream &out, std::string fname, bool silent = false); + bool ClearKeyBindings(std::string keyspec); bool AddKeyBinding(std::string keyspec, std::string cmdline); std::vector ListKeyBindings(std::string keyspec); + std::string getHackPath(); + bool isWorldLoaded() { return (last_world_data_ptr != NULL); } bool isMapLoaded() { return (last_local_map_ptr != NULL && last_world_data_ptr != NULL); } @@ -177,7 +185,7 @@ namespace DFHack } s_mods; std::vector allModules; DFHack::PluginManager * plug_mgr; - + // hotkey-related stuff struct KeyBinding { int modifiers; diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index a6e538d2a..2ffc5c58a 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -48,6 +48,9 @@ namespace DFHack {namespace Lua { */ DFHACK_EXPORT lua_State *Open(color_ostream &out, lua_State *state = NULL); + DFHACK_EXPORT void PushDFHack(lua_State *state); + DFHACK_EXPORT void PushBaseGlobals(lua_State *state); + /** * Load a module using require(). Leaves the stack as is. */ diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 0cf34c489..08df98d3a 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -274,6 +274,9 @@ DFHACK_EXPORT std::string join_strings(const std::string &separator, const std:: DFHACK_EXPORT std::string toUpper(const std::string &str); DFHACK_EXPORT std::string toLower(const std::string &str); +DFHACK_EXPORT int getdir(std::string dir, std::vector &files); +DFHACK_EXPORT bool hasEnding (std::string const &fullString, std::string const &ending); + inline bool bits_match(unsigned required, unsigned ok, unsigned mask) { return (required & mask) == (required & mask & ok); diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index c4c994d2a..c0ba64375 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -269,5 +269,23 @@ function dfhack.interpreter(prompt,hfile,env) return true end +-- Command scripts + +dfhack.scripts = dfhack.scripts or {} + +function dfhack.run_script(file,...) + local env = dfhack.scripts[file] + if env == nil then + env = {} + setmetatable(env, { __index = base_env }) + dfhack.scripts[file] = env + end + local f,perr = loadfile(file, 't', env) + if f == nil then + error(perr) + end + return f(...) +end + -- Feed the table back to the require() mechanism. return dfhack diff --git a/plugins/Dfusion/src/lua_Console.cpp b/plugins/Dfusion/src/lua_Console.cpp index 881ba5d21..1d52d6158 100644 --- a/plugins/Dfusion/src/lua_Console.cpp +++ b/plugins/Dfusion/src/lua_Console.cpp @@ -1,6 +1,7 @@ -#include "lua_Console.h" #include "LuaTools.h" +#include "lua_Console.h" + #include //TODO error management. Using lua error? or something other? diff --git a/scripts/lua-example.lua b/scripts/lua-example.lua new file mode 100644 index 000000000..044849ed7 --- /dev/null +++ b/scripts/lua-example.lua @@ -0,0 +1,6 @@ +-- Example of a lua script. + +run_count = (run_count or 0) + 1 + +print('Arguments: ',...) +print('Command called '..run_count..' times.') From 7e01b004e9675fec417bee0eead7d3d0702395e6 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 4 May 2012 20:59:06 +0400 Subject: [PATCH 46/53] Implement timeouts in the core lua context, and quicksave script. --- LUA_API.rst | 20 ++++++ Lua API.html | 17 +++++ library/Core.cpp | 4 ++ library/LuaApi.cpp | 7 +- library/LuaTools.cpp | 129 +++++++++++++++++++++++++++++++++++++ library/PluginManager.cpp | 2 +- library/include/LuaTools.h | 2 + scripts/quicksave.lua | 28 ++++++++ 8 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 scripts/quicksave.lua diff --git a/LUA_API.rst b/LUA_API.rst index d9488d60f..938664379 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -598,6 +598,14 @@ One notable difference is that these explicit wrappers allow argument count adjustment according to the usual lua rules, so trailing false/nil arguments can be omitted. +* ``dfhack.isWorldLoaded()`` + + Checks if the world is loaded. + +* ``dfhack.isMapLoaded()`` + + Checks if the world and map are loaded. + * ``dfhack.TranslateName(name[,in_english,only_last_name])`` Convert a language_name or only the last name part to string. @@ -950,6 +958,18 @@ Core context specific functions: Boolean value; *true* in the core context. +* ``dfhack.timeout(time,mode,callback)`` + + Arranges for the callback to be called once the specified + period of time passes. The ``mode`` argument specifies the + unit of time used, and may be one of ``'frames'`` (raw FPS), + ``'ticks'`` (unpaused FPS), ``'days'``, ``'months'``, + ``'years'`` (in-game time). All timers other than + ``'frames'`` are cancelled when the world is unloaded, + and cannot be queued until it is loaded again. + Returns the timer id, or *nil* if unsuccessful due to + world being unloaded. + * ``dfhack.onStateChange.foo = function(code)`` Event. Receives the same codes as plugin_onstatechange in C++. diff --git a/Lua API.html b/Lua API.html index 40706d14f..b96d12156 100644 --- a/Lua API.html +++ b/Lua API.html @@ -855,6 +855,12 @@ One notable difference is that these explicit wrappers allow argument count adjustment according to the usual lua rules, so trailing false/nil arguments can be omitted.

      +
    • dfhack.isWorldLoaded()

      +

      Checks if the world is loaded.

      +
    • +
    • dfhack.isMapLoaded()

      +

      Checks if the world and map are loaded.

      +
    • dfhack.TranslateName(name[,in_english,only_last_name])

      Convert a language_name or only the last name part to string.

    • @@ -1151,6 +1157,17 @@ only context that can receive events from DF and plugins.

    • dfhack.is_core_context

      Boolean value; true in the core context.

    • +
    • dfhack.timeout(time,mode,callback)

      +

      Arranges for the callback to be called once the specified +period of time passes. The mode argument specifies the +unit of time used, and may be one of 'frames' (raw FPS), +'ticks' (unpaused FPS), 'days', 'months', +'years' (in-game time). All timers other than +'frames' are cancelled when the world is unloaded, +and cannot be queued until it is loaded again. +Returns the timer id, or nil if unsuccessful due to +world being unloaded.

      +
    • dfhack.onStateChange.foo = function(code)

      Event. Receives the same codes as plugin_onstatechange in C++.

    • diff --git a/library/Core.cpp b/library/Core.cpp index 60e380598..d7e4435ce 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -355,6 +355,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve { string help = getLuaHelp(filename); con.print("%s: %s\n", parts[0].c_str(), help.c_str()); + return CR_OK; } con.printerr("Unknown command: %s\n", parts[0].c_str()); } @@ -1077,6 +1078,9 @@ int Core::Update() // notify all the plugins that a game tick is finished plug_mgr->OnUpdate(out); + // process timers in lua + Lua::Core::onUpdate(out); + // Release the fake suspend lock { lock_guard lock(d->AccessMutex); diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 828e54cbc..ea723df64 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -570,9 +570,14 @@ static void OpenModule(lua_State *state, const char *mname, #define WRAP(function) { #function, df::wrap_function(function,true) } #define WRAPN(name, function) { #name, df::wrap_function(function,true) } -/***** Translation module *****/ +/***** DFHack module *****/ + +static bool isWorldLoaded() { return Core::getInstance().isWorldLoaded(); } +static bool isMapLoaded() { return Core::getInstance().isMapLoaded(); } static const LuaWrapper::FunctionReg dfhack_module[] = { + WRAP(isWorldLoaded), + WRAP(isMapLoaded), WRAPM(Translation, TranslateName), { NULL, NULL } }; diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 17ad0abde..10ff51ba1 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -53,6 +53,7 @@ distribution. #include "df/building.h" #include "df/unit.h" #include "df/item.h" +#include "df/world.h" #include #include @@ -1412,13 +1413,135 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state) return state; } +static int next_timeout_id = 0; +static int frame_idx = 0; +static std::multimap frame_timers; +static std::multimap tick_timers; + +int DFHACK_TIMEOUTS_TOKEN = 0; + +static const char *const timeout_modes[] = { + "frames", "ticks", "days", "months", "years", NULL +}; + +int dfhack_timeout(lua_State *L) +{ + using df::global::world; + using df::global::enabler; + + lua_Number time = luaL_checknumber(L, 1); + int mode = luaL_checkoption(L, 2, NULL, timeout_modes); + luaL_checktype(L, 3, LUA_TFUNCTION); + lua_settop(L, 3); + + if (mode > 0 && !Core::getInstance().isWorldLoaded()) + { + lua_pushnil(L); + return 1; + } + + switch (mode) + { + case 2: + time *= 1200; + break; + case 3: + time *= 33600; + break; + case 4: + time *= 403200; + break; + default:; + } + + int id = next_timeout_id++; + int delta = time; + + if (delta <= 0) + luaL_error(L, "Invalid timeout: %d", delta); + + if (mode) + tick_timers.insert(std::pair(world->frame_counter+delta, id)); + else + frame_timers.insert(std::pair(frame_idx+delta, id)); + + lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN); + lua_swap(L); + lua_rawseti(L, -2, id); + + lua_pushinteger(L, id); + return 1; +} + +static void cancel_tick_timers() +{ + using Lua::Core::State; + + Lua::StackUnwinder frame(State); + lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN); + + for (auto it = tick_timers.begin(); it != tick_timers.end(); ++it) + { + lua_pushnil(State); + lua_rawseti(State, frame[1], it->second); + } + + tick_timers.clear(); +} + void DFHack::Lua::Core::onStateChange(color_ostream &out, int code) { if (!State) return; + switch (code) + { + case SC_MAP_UNLOADED: + case SC_WORLD_UNLOADED: + cancel_tick_timers(); + break; + + default:; + } + Lua::Push(State, code); Lua::InvokeEvent(out, State, (void*)onStateChange, 1); } +void DFHack::Lua::Core::onUpdate(color_ostream &out) +{ + using df::global::world; + + Lua::StackUnwinder frame(State); + lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN); + + frame_idx++; + + while (!frame_timers.empty() && + frame_timers.begin()->first <= frame_idx) + { + int id = frame_timers.begin()->second; + frame_timers.erase(frame_timers.begin()); + + lua_rawgeti(State, frame[1], id); + lua_pushnil(State); + lua_rawseti(State, frame[1], id); + + Lua::SafeCall(out, State, 0, 0); + } + + while (!tick_timers.empty() && + tick_timers.begin()->first <= world->frame_counter) + { + int id = tick_timers.begin()->second; + tick_timers.erase(tick_timers.begin()); + + lua_rawgeti(State, frame[1], id); + lua_pushnil(State); + lua_rawseti(State, frame[1], id); + + Lua::SafeCall(out, State, 0, 0); + } +} + void DFHack::Lua::Core::Init(color_ostream &out) { if (State) @@ -1428,12 +1551,18 @@ void DFHack::Lua::Core::Init(color_ostream &out) Lua::Open(out, State); + lua_newtable(State); + lua_rawsetp(State, LUA_REGISTRYINDEX, &DFHACK_TIMEOUTS_TOKEN); + // Register events lua_rawgetp(State, LUA_REGISTRYINDEX, &DFHACK_DFHACK_TOKEN); MakeEvent(State, (void*)onStateChange); lua_setfield(State, -2, "onStateChange"); + lua_pushcfunction(State, dfhack_timeout); + lua_setfield(State, -2, "timeout"); + lua_pop(State, 1); } diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 69b8bfe0d..b8619d503 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -591,7 +591,7 @@ command_result PluginManager::InvokeCommand(color_ostream &out, const std::strin bool PluginManager::CanInvokeHotkey(const std::string &command, df::viewscreen *top) { Plugin *plugin = getPluginByCommand(command); - return plugin ? plugin->can_invoke_hotkey(command, top) : false; + return plugin ? plugin->can_invoke_hotkey(command, top) : true; } void PluginManager::OnUpdate(color_ostream &out) diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index 2ffc5c58a..669d4d38b 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -333,6 +333,8 @@ namespace DFHack {namespace Lua { // Events signalled by the core void onStateChange(color_ostream &out, int code); + // Signals timers + void onUpdate(color_ostream &out); template inline void Push(T &arg) { Lua::Push(State, arg); } template inline void Push(const T &arg) { Lua::Push(State, arg); } diff --git a/scripts/quicksave.lua b/scripts/quicksave.lua new file mode 100644 index 000000000..c54cc730b --- /dev/null +++ b/scripts/quicksave.lua @@ -0,0 +1,28 @@ +-- Makes the game immediately save the state. + +if not dfhack.isMapLoaded() then + dfhack.printerr("World and map aren't loaded.") + return +end + +local ui_main = df.global.ui.main +local flags4 = df.global.d_init.flags4 + +local function restore_autobackup() + if ui_main.autosave_request and dfhack.isMapLoaded() then + dfhack.timeout(10, 'frames', restore_autobackup) + else + flags4.AUTOBACKUP = true + end +end + +-- Request auto-save +ui_main.autosave_request = true + +-- And since it will overwrite the backup, disable it temporarily +if flags4.AUTOBACKUP then + flags4.AUTOBACKUP = false + restore_autobackup() +end + +print 'The game should save the state now.' From e3440126a35750338fbc3f239b75d88b7cc2d7de Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 5 May 2012 19:36:22 +0400 Subject: [PATCH 47/53] Fix missing switch case warnings. --- plugins/devel/stockcheck.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/devel/stockcheck.cpp b/plugins/devel/stockcheck.cpp index 8dc44e98a..8ace55cdc 100644 --- a/plugins/devel/stockcheck.cpp +++ b/plugins/devel/stockcheck.cpp @@ -187,7 +187,9 @@ static command_result stockcheck(color_ostream &out, vector & parameter case general_ref_type::BUILDING_HOLDER: building = ref->getBuilding(); break; - + + default: + break; } } @@ -214,7 +216,9 @@ static command_result stockcheck(color_ostream &out, vector & parameter case general_ref_type::BUILDING_HOLDER: building = ref->getBuilding(); break; - + + default: + break; } } } From 81f2240c237253a7a2264c1b9682a291337667b0 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 5 May 2012 20:46:28 +0400 Subject: [PATCH 48/53] Fix some things that confuse MSVC. --- library/LuaApi.cpp | 4 ++-- library/LuaTools.cpp | 2 +- library/include/LuaTools.h | 6 +++--- library/modules/Buildings.cpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index ea723df64..6a8100a34 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -520,7 +520,7 @@ static int dfhack_matinfo_matches(lua_State *state) else if (lua_istable(state, 2)) { df::dfhack_material_category tmp; - Lua::CheckDFObject(state, &tmp, 2, false); + Lua::CheckDFAssign(state, &tmp, 2, false); lua_pushboolean(state, info.matches(tmp)); } else @@ -733,7 +733,7 @@ static int maps_getTileBlock(lua_State *L) if (lua_gettop(L) == 1) { df::coord pos; - Lua::CheckDFObject(L, &pos, 1); + Lua::CheckDFAssign(L, &pos, 1); block = Maps::getTileBlock(pos); } else diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 10ff51ba1..794cb52a6 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -829,7 +829,7 @@ bool DFHack::Lua::AssignDFObject(color_ostream &out, lua_State *state, return doAssignDFObject(&out, state, type, target, val_index, exact_type, perr, false); } -void DFHack::Lua::CheckDFObject(lua_State *state, type_identity *type, +void DFHack::Lua::CheckDFAssign(lua_State *state, type_identity *type, void *target, int val_index, bool exact_type) { doAssignDFObject(NULL, state, type, target, val_index, exact_type, false, true); diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index 669d4d38b..c3a7921c3 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -119,7 +119,7 @@ namespace DFHack {namespace Lua { * Assign the value at val_index to the target of given identity using df.assign(). * Otherwise throws an error. */ - DFHACK_EXPORT void CheckDFObject(lua_State *state, type_identity *type, + DFHACK_EXPORT void CheckDFAssign(lua_State *state, type_identity *type, void *target, int val_index, bool exact_type = false); /** @@ -161,8 +161,8 @@ namespace DFHack {namespace Lua { * Throws in case of an error. */ template - void CheckDFObject(lua_State *state, T *target, int val_index, bool exact_type = false) { - CheckDFObject(state, df::identity_traits::get(), target, val_index, exact_type); + void CheckDFAssign(lua_State *state, T *target, int val_index, bool exact_type = false) { + CheckDFAssign(state, df::identity_traits::get(), target, val_index, exact_type); } /** diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index e587a4916..cdaa016fb 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -559,7 +559,7 @@ bool Buildings::setSize(df::building *bld, df::coord2d size, int direction) bool ok = checkBuildingTiles(bld, true); - if (type != building_type::Construction) + if (type != Construction) bld->setMaterialAmount(computeMaterialAmount(bld)); return ok; From 3bd44e5367e6f51b4fb7af854068c67b6f73bb4a Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 5 May 2012 21:07:18 +0400 Subject: [PATCH 49/53] Move some things that won't work in dfhack-client to a separate file. --- library/CMakeLists.txt | 1 + library/MiscUtils.cpp | 87 ------------------------ library/PluginManager.cpp | 1 + library/Types.cpp | 129 ++++++++++++++++++++++++++++++++++++ library/include/MiscUtils.h | 3 - library/include/Types.h | 3 + 6 files changed, 134 insertions(+), 90 deletions(-) create mode 100644 library/Types.cpp diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 466175ed4..32b7d9dad 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -64,6 +64,7 @@ DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp MiscUtils.cpp +Types.cpp PluginManager.cpp TileTypes.cpp VersionInfoFactory.cpp diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index 65fa21e11..9febe6db6 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -30,12 +30,9 @@ distribution. #ifndef LINUX_BUILD #include - #include "wdirent.h" #else #include #include - #include - #include #endif #include @@ -128,90 +125,6 @@ std::string toLower(const std::string &str) return rv; } -int getdir(std::string dir, std::vector &files) -{ - DIR *dp; - struct dirent *dirp; - if((dp = opendir(dir.c_str())) == NULL) - { - return errno; - } - while ((dirp = readdir(dp)) != NULL) { - files.push_back(std::string(dirp->d_name)); - } - closedir(dp); - return 0; -} - -bool hasEnding (std::string const &fullString, std::string const &ending) -{ - if (fullString.length() > ending.length()) - { - return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); - } - else - { - return false; - } -} - - -df::general_ref *DFHack::findRef(std::vector &vec, df::general_ref_type type) -{ - for (int i = vec.size()-1; i >= 0; i--) - { - df::general_ref *ref = vec[i]; - if (ref->getType() == type) - return ref; - } - - return NULL; -} - -bool DFHack::removeRef(std::vector &vec, df::general_ref_type type, int id) -{ - for (int i = vec.size()-1; i >= 0; i--) - { - df::general_ref *ref = vec[i]; - if (ref->getType() != type || ref->getID() != id) - continue; - - vector_erase_at(vec, i); - delete ref; - return true; - } - - return false; -} - -df::specific_ref *DFHack::findRef(std::vector &vec, df::specific_ref_type type) -{ - for (int i = vec.size()-1; i >= 0; i--) - { - df::specific_ref *ref = vec[i]; - if (ref->type == type) - return ref; - } - - return NULL; -} - -bool DFHack::removeRef(std::vector &vec, df::specific_ref_type type, void *ptr) -{ - for (int i = vec.size()-1; i >= 0; i--) - { - df::specific_ref *ref = vec[i]; - if (ref->type != type || ref->object != ptr) - continue; - - vector_erase_at(vec, i); - delete ref; - return true; - } - - return false; -} - #ifdef LINUX_BUILD // Linux uint64_t GetTimeMs64() { diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index b8619d503..320e8dec8 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -28,6 +28,7 @@ distribution. #include "PluginManager.h" #include "RemoteServer.h" #include "Console.h" +#include "Types.h" #include "DataDefs.h" #include "MiscUtils.h" diff --git a/library/Types.cpp b/library/Types.cpp new file mode 100644 index 000000000..72f8bdb8c --- /dev/null +++ b/library/Types.cpp @@ -0,0 +1,129 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "Internal.h" +#include "Export.h" +#include "MiscUtils.h" +#include "Error.h" +#include "Types.h" + +#ifndef LINUX_BUILD + #include + #include "wdirent.h" +#else + #include + #include + #include + #include +#endif + +#include +#include + +#include +#include + + +int DFHack::getdir(std::string dir, std::vector &files) +{ + DIR *dp; + struct dirent *dirp; + if((dp = opendir(dir.c_str())) == NULL) + { + return errno; + } + while ((dirp = readdir(dp)) != NULL) { + files.push_back(std::string(dirp->d_name)); + } + closedir(dp); + return 0; +} + +bool DFHack::hasEnding (std::string const &fullString, std::string const &ending) +{ + if (fullString.length() > ending.length()) + { + return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); + } + else + { + return false; + } +} + +df::general_ref *DFHack::findRef(std::vector &vec, df::general_ref_type type) +{ + for (int i = vec.size()-1; i >= 0; i--) + { + df::general_ref *ref = vec[i]; + if (ref->getType() == type) + return ref; + } + + return NULL; +} + +bool DFHack::removeRef(std::vector &vec, df::general_ref_type type, int id) +{ + for (int i = vec.size()-1; i >= 0; i--) + { + df::general_ref *ref = vec[i]; + if (ref->getType() != type || ref->getID() != id) + continue; + + vector_erase_at(vec, i); + delete ref; + return true; + } + + return false; +} + +df::specific_ref *DFHack::findRef(std::vector &vec, df::specific_ref_type type) +{ + for (int i = vec.size()-1; i >= 0; i--) + { + df::specific_ref *ref = vec[i]; + if (ref->type == type) + return ref; + } + + return NULL; +} + +bool DFHack::removeRef(std::vector &vec, df::specific_ref_type type, void *ptr) +{ + for (int i = vec.size()-1; i >= 0; i--) + { + df::specific_ref *ref = vec[i]; + if (ref->type != type || ref->object != ptr) + continue; + + vector_erase_at(vec, i); + delete ref; + return true; + } + + return false; +} diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 08df98d3a..0cf34c489 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -274,9 +274,6 @@ DFHACK_EXPORT std::string join_strings(const std::string &separator, const std:: DFHACK_EXPORT std::string toUpper(const std::string &str); DFHACK_EXPORT std::string toLower(const std::string &str); -DFHACK_EXPORT int getdir(std::string dir, std::vector &files); -DFHACK_EXPORT bool hasEnding (std::string const &fullString, std::string const &ending); - inline bool bits_match(unsigned required, unsigned ok, unsigned mask) { return (required & mask) == (required & mask & ok); diff --git a/library/include/Types.h b/library/include/Types.h index 018e7ccfd..1f211649d 100644 --- a/library/include/Types.h +++ b/library/include/Types.h @@ -74,6 +74,9 @@ namespace DFHack uint32_t xpNxtLvl; }; + DFHACK_EXPORT int getdir(std::string dir, std::vector &files); + DFHACK_EXPORT bool hasEnding (std::string const &fullString, std::string const &ending); + DFHACK_EXPORT df::general_ref *findRef(std::vector &vec, df::general_ref_type type); DFHACK_EXPORT bool removeRef(std::vector &vec, df::general_ref_type type, int id); From 05e8083c8443ba604d0402b07824e4a05b86288c Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 6 May 2012 10:09:39 +0400 Subject: [PATCH 50/53] Allow constructing itemless actual buildings. --- library/MiscUtils.cpp | 1 - library/modules/Buildings.cpp | 20 ++++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index 9febe6db6..8658788bb 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -26,7 +26,6 @@ distribution. #include "Export.h" #include "MiscUtils.h" #include "Error.h" -#include "Types.h" #ifndef LINUX_BUILD #include diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index cdaa016fb..0512e9f03 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -698,12 +698,28 @@ static bool linkForConstruct(df::job* &job, df::building *bld) return true; } +static bool needsItems(df::building *bld) +{ + if (!bld->isActual()) + return false; + + switch (bld->getType()) + { + case building_type::FarmPlot: + case building_type::RoadDirt: + return false; + + default: + return true; + } +} + bool Buildings::constructWithItems(df::building *bld, std::vector items) { CHECK_NULL_POINTER(bld); - CHECK_INVALID_ARGUMENT(!items.empty()); CHECK_INVALID_ARGUMENT(bld->id == -1); CHECK_INVALID_ARGUMENT(bld->isActual()); + CHECK_INVALID_ARGUMENT(!items.empty() == needsItems(bld)); for (size_t i = 0; i < items.size(); i++) { @@ -738,9 +754,9 @@ bool Buildings::constructWithItems(df::building *bld, std::vector ite bool Buildings::constructWithFilters(df::building *bld, std::vector items) { CHECK_NULL_POINTER(bld); - CHECK_INVALID_ARGUMENT(!items.empty()); CHECK_INVALID_ARGUMENT(bld->id == -1); CHECK_INVALID_ARGUMENT(bld->isActual()); + CHECK_INVALID_ARGUMENT(!items.empty() == needsItems(bld)); for (size_t i = 0; i < items.size(); i++) CHECK_NULL_POINTER(items[i]); From b70130cf3684d8adbf36023b27aec0f10b213cc7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 6 May 2012 11:22:55 +0400 Subject: [PATCH 51/53] Add a couple more building api functions. --- LUA_API.rst | 16 +++++-- Lua API.html | 14 ++++-- library/LuaApi.cpp | 74 +++++++++++++++++++++++------ library/include/modules/Buildings.h | 13 ++++- library/modules/Buildings.cpp | 45 ++++++++++++++++++ 5 files changed, 140 insertions(+), 22 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 938664379..681caef54 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -873,10 +873,16 @@ Buildings module Returns *width, height, centerx, centery*. -* ``dfhack.buildings.findAtTile(pos)`` +* ``dfhack.buildings.findAtTile(pos)``, or ``findAtTile(x,y,z)`` Scans the buildings for the one located at the given tile. - Does not work on civzones. Warning: linear scan. + Does not work on civzones. Warning: linear scan if the map + tile indicates there are buildings at it. + +* ``dfhack.buildings.findCivzonesAt(pos)``, or ``findCivzonesAt(x,y,z)`` + + Scans civzones, and returns a lua sequence of those that touch + the given tile, or *nil* if none. * ``dfhack.buildings.getCorrectSize(width, height, type, subtype, custom, direction)`` @@ -894,6 +900,10 @@ Buildings module Returns the number of tiles included by extents, or defval. +* ``dfhack.buildings.containsTile(building, x, y[, room])`` + + Checks if the building contains the specified tile, either directly, or as room. + * ``dfhack.buildings.hasSupport(pos,size)`` Checks if a bridge constructed at specified position would have @@ -911,7 +921,7 @@ Low-level building creation functions; Configures an object returned by ``allocInstance``, using specified parameters wherever appropriate. If the building has fixed size along any dimension, the corresponding input parameter will be ignored. - Returns *nil* if the building cannot be placed, or *true, width, + Returns *false* if the building cannot be placed, or *true, width, height, rect_area, true_area*. Returned width and height are the final values used by the building; true_area is less than rect_area if any tiles were removed from designation. diff --git a/Lua API.html b/Lua API.html index b96d12156..094b522c3 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1081,9 +1081,14 @@ burrows, or the presence of invaders.

    • dfhack.buildings.getSize(building)

      Returns width, height, centerx, centery.

    • -
    • dfhack.buildings.findAtTile(pos)

      +
    • dfhack.buildings.findAtTile(pos), or findAtTile(x,y,z)

      Scans the buildings for the one located at the given tile. -Does not work on civzones. Warning: linear scan.

      +Does not work on civzones. Warning: linear scan if the map +tile indicates there are buildings at it.

      +
    • +
    • dfhack.buildings.findCivzonesAt(pos), or findCivzonesAt(x,y,z)

      +

      Scans civzones, and returns a lua sequence of those that touch +the given tile, or nil if none.

    • dfhack.buildings.getCorrectSize(width, height, type, subtype, custom, direction)

      Computes correct dimensions for the specified building type and orientation, @@ -1098,6 +1103,9 @@ are removed from extents. If allow_occupied, t

    • dfhack.buildings.countExtentTiles(extents,defval)

      Returns the number of tiles included by extents, or defval.

    • +
    • dfhack.buildings.containsTile(building, x, y[, room])

      +

      Checks if the building contains the specified tile, either directly, or as room.

      +
    • dfhack.buildings.hasSupport(pos,size)

      Checks if a bridge constructed at specified position would have support from terrain, and thus won't collapse if retracted.

      @@ -1113,7 +1121,7 @@ at specified position. Returns the object, or nil in case of an error.<

      Configures an object returned by allocInstance, using specified parameters wherever appropriate. If the building has fixed size along any dimension, the corresponding input parameter will be ignored. -Returns nil if the building cannot be placed, or true, width, +Returns false if the building cannot be placed, or true, width, height, rect_area, true_area. Returned width and height are the final values used by the building; true_area is less than rect_area if any tiles were removed from designation.

      diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 6a8100a34..bf2ec9364 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -74,6 +74,7 @@ distribution. #include "df/dfhack_material_category.h" #include "df/job_material_category.h" #include "df/burrow.h" +#include "df/building_civzonest.h" #include #include @@ -109,6 +110,37 @@ int Lua::PushPosXYZ(lua_State *state, df::coord pos) } } +static df::coord2d CheckCoordXY(lua_State *state, int base, bool vararg = false) +{ + df::coord2d p; + if (vararg && lua_gettop(state) <= base) + Lua::CheckDFAssign(state, &p, base); + else + { + p = df::coord2d( + luaL_checkint(state, base), + luaL_checkint(state, base+1) + ); + } + return p; +} + +static df::coord CheckCoordXYZ(lua_State *state, int base, bool vararg = false) +{ + df::coord p; + if (vararg && lua_gettop(state) <= base) + Lua::CheckDFAssign(state, &p, base); + else + { + p = df::coord( + luaL_checkint(state, base), + luaL_checkint(state, base+1), + luaL_checkint(state, base+2) + ); + } + return p; +} + /************************************************** * Per-world persistent configuration storage API * **************************************************/ @@ -729,20 +761,8 @@ static const LuaWrapper::FunctionReg dfhack_maps_module[] = { static int maps_getTileBlock(lua_State *L) { - df::map_block *block; - if (lua_gettop(L) == 1) - { - df::coord pos; - Lua::CheckDFAssign(L, &pos, 1); - block = Maps::getTileBlock(pos); - } - else - { - block = Maps::getTileBlock( - luaL_checkint(L, 1), luaL_checkint(L, 2), luaL_checkint(L, 3) - ); - } - Lua::PushDFObject(L, block); + auto pos = CheckCoordXYZ(L, 1, true); + Lua::PushDFObject(L, Maps::getTileBlock(pos)); return 1; } @@ -791,17 +811,39 @@ static const luaL_Reg dfhack_burrows_funcs[] = { /***** Buildings module *****/ +static bool buildings_containsTile(df::building *bld, int x, int y, bool room) { + return Buildings::containsTile(bld, df::coord2d(x,y), room); +} + static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { - WRAPM(Buildings, findAtTile), WRAPM(Buildings, allocInstance), WRAPM(Buildings, checkFreeTiles), WRAPM(Buildings, countExtentTiles), + WRAPN(containsTile, buildings_containsTile), WRAPM(Buildings, hasSupport), WRAPM(Buildings, constructWithItems), WRAPM(Buildings, constructWithFilters), { NULL, NULL } }; +static int buildings_findAtTile(lua_State *L) +{ + auto pos = CheckCoordXYZ(L, 1, true); + Lua::PushDFObject(L, Buildings::findAtTile(pos)); + return 1; +} + +static int buildings_findCivzonesAt(lua_State *L) +{ + auto pos = CheckCoordXYZ(L, 1, true); + std::vector pvec; + if (Buildings::findCivzonesAt(&pvec, pos)) + Lua::PushVector(L, pvec); + else + lua_pushnil(L); + return 1; +} + static int buildings_getCorrectSize(lua_State *state) { df::coord2d size(luaL_optint(state, 1, 1), luaL_optint(state, 2, 1)); @@ -844,6 +886,8 @@ static int buildings_setSize(lua_State *state) } static const luaL_Reg dfhack_buildings_funcs[] = { + { "findAtTile", buildings_findAtTile }, + { "findCivzonesAt", buildings_findCivzonesAt }, { "getCorrectSize", buildings_getCorrectSize }, { "setSize", buildings_setSize }, { NULL, NULL } diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 9e529b6ca..9bb3ba001 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -39,6 +39,7 @@ namespace df struct job_item; struct item; struct building_extents; + struct building_civzonest; } namespace DFHack @@ -91,12 +92,17 @@ DFHACK_EXPORT bool Read (const uint32_t index, t_building & building); */ DFHACK_EXPORT bool ReadCustomWorkshopTypes(std::map & btypes); -/* +/** * Find the building located at the specified tile. * Does not work on civzones. */ DFHACK_EXPORT df::building *findAtTile(df::coord pos); +/** + * Find civzones located at the specified tile. + */ +DFHACK_EXPORT bool findCivzonesAt(std::vector *pvec, df::coord pos); + /** * Allocates a building object using this type and position. */ @@ -122,6 +128,11 @@ DFHACK_EXPORT bool checkFreeTiles(df::coord pos, df::coord2d size, */ DFHACK_EXPORT int countExtentTiles(df::building_extents *ext, int defval = -1); +/** + * Checks if the building contains the specified tile. + */ +DFHACK_EXPORT bool containsTile(df::building *bld, df::coord2d tile, bool room = false); + /** * Checks if the area has support from the terrain. */ diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 0512e9f03..2c5162673 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -58,6 +58,7 @@ using namespace DFHack; #include "df/building_trapst.h" #include "df/building_bridgest.h" #include "df/building_coffinst.h" +#include "df/building_civzonest.h" #include "df/building_furnacest.h" #include "df/building_workshopst.h" #include "df/building_screw_pumpst.h" @@ -153,6 +154,25 @@ df::building *Buildings::findAtTile(df::coord pos) return NULL; } +bool Buildings::findCivzonesAt(std::vector *pvec, df::coord pos) +{ + pvec->clear(); + + auto &vec = world->buildings.other[buildings_other_id::ANY_ZONE]; + + for (size_t i = 0; i < vec.size(); i++) + { + auto bld = strict_virtual_cast(vec[i]); + + if (!bld || bld->z != pos.z || !containsTile(bld, pos)) + continue; + + pvec->push_back(bld); + } + + return !pvec->empty(); +} + df::building *Buildings::allocInstance(df::coord pos, df::building_type type, int subtype, int custom) { if (!building_next_id) @@ -464,6 +484,31 @@ int Buildings::countExtentTiles(df::building_extents *ext, int defval) return cnt; } +bool Buildings::containsTile(df::building *bld, df::coord2d tile, bool room) +{ + CHECK_NULL_POINTER(bld); + + if (room) + { + if (!bld->is_room || !bld->room.extents) + return false; + } + else + { + if (tile.x < bld->x1 || tile.x > bld->x2 || tile.y < bld->y1 || tile.y >= bld->y2) + return false; + } + + if (bld->room.extents && (room || bld->isExtentShaped())) + { + uint8_t *etile = getExtentTile(bld->room, tile); + if (!etile || !*etile) + return false; + } + + return true; +} + bool Buildings::hasSupport(df::coord pos, df::coord2d size) { for (int dx = -1; dx <= size.x; dx++) From 9ad8d767b4fcf0946444469d2c4072d46997b7e6 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 6 May 2012 19:09:11 +0400 Subject: [PATCH 52/53] Add code for adding abstract buildings and removing constructions. --- LUA_API.rst | 17 ++++ Lua API.html | 14 +++ library/LuaApi.cpp | 16 ++++ library/include/modules/Buildings.h | 11 +++ library/include/modules/Constructions.h | 3 + library/lua/dfhack/buildings.lua | 10 +- library/modules/Buildings.cpp | 121 ++++++++++++++++++++++++ library/modules/Constructions.cpp | 40 ++++++++ 8 files changed, 229 insertions(+), 3 deletions(-) diff --git a/LUA_API.rst b/LUA_API.rst index 681caef54..e20b946d8 100644 --- a/LUA_API.rst +++ b/LUA_API.rst @@ -926,6 +926,12 @@ Low-level building creation functions; final values used by the building; true_area is less than rect_area if any tiles were removed from designation. +* ``dfhack.buildings.constructAbstract(building)`` + + Links a fully configured object created by ``allocInstance`` into the + world. The object must be an abstract building, i.e. a stockpile or civzone. + Returns *true*, or *false* if impossible. + * ``dfhack.buildings.constructWithItems(building, items)`` Links a fully configured object created by ``allocInstance`` into the @@ -941,6 +947,11 @@ Low-level building creation functions; Use a negative ``quantity`` field value to auto-compute the amount from the size of the building. +* ``dfhack.buildings.deconstruct(building)`` + + Destroys the building, or queues a deconstruction job. + Returns *true* if the building was destroyed and deallocated immediately. + More high-level functions are implemented in lua and can be loaded by ``require('dfhack.buildings')``. See ``hack/lua/dfhack/buildings.lua``. @@ -954,6 +965,12 @@ Constructions module Returns *true*, or *false* if obstructed. Note that designated constructions are technically buildings. +* ``dfhack.constructions.designateRemove(pos)``, or ``designateRemove(x,y,z)`` + + If there is a construction or a planned construction at the specified + coordinates, designates it for removal, or instantly cancels the planned one. + Returns *true, was_only_planned* if removed; or *false* if none found. + Core interpreter context ======================== diff --git a/Lua API.html b/Lua API.html index 094b522c3..7aa8e651a 100644 --- a/Lua API.html +++ b/Lua API.html @@ -1126,6 +1126,11 @@ height, rect_area, true_area. Returned width and height are the final values used by the building; true_area is less than rect_area if any tiles were removed from designation.

    • +
    • dfhack.buildings.constructAbstract(building)

      +

      Links a fully configured object created by allocInstance into the +world. The object must be an abstract building, i.e. a stockpile or civzone. +Returns true, or false if impossible.

      +
    • dfhack.buildings.constructWithItems(building, items)

      Links a fully configured object created by allocInstance into the world for construction, using a list of specific items as material. @@ -1139,6 +1144,10 @@ and possibly destroyed in any case. Use a negative quantity field value to auto-compute the amount from the size of the building.

    • +
    • dfhack.buildings.deconstruct(building)

      +

      Destroys the building, or queues a deconstruction job. +Returns true if the building was destroyed and deallocated immediately.

      +

    More high-level functions are implemented in lua and can be loaded by require('dfhack.buildings'). See hack/lua/dfhack/buildings.lua.

    @@ -1152,6 +1161,11 @@ a planned but not completed construction there, changes its type. Returns true, or false if obstructed. Note that designated constructions are technically buildings.

    +
  • dfhack.constructions.designateRemove(pos), or designateRemove(x,y,z)

    +

    If there is a construction or a planned construction at the specified +coordinates, designates it for removal, or instantly cancels the planned one. +Returns true, was_only_planned if removed; or false if none found.

    +
  • diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index bf2ec9364..d554754e4 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -821,8 +821,10 @@ static const LuaWrapper::FunctionReg dfhack_buildings_module[] = { WRAPM(Buildings, countExtentTiles), WRAPN(containsTile, buildings_containsTile), WRAPM(Buildings, hasSupport), + WRAPM(Buildings, constructAbstract), WRAPM(Buildings, constructWithItems), WRAPM(Buildings, constructWithFilters), + WRAPM(Buildings, deconstruct), { NULL, NULL } }; @@ -900,6 +902,20 @@ static const LuaWrapper::FunctionReg dfhack_constructions_module[] = { { NULL, NULL } }; +static int constructions_designateRemove(lua_State *L) +{ + auto pos = CheckCoordXYZ(L, 1, true); + bool imm = false; + lua_pushboolean(L, Constructions::designateRemove(pos, &imm)); + lua_pushboolean(L, imm); + return 2; +} + +static const luaL_Reg dfhack_constructions_funcs[] = { + { "designateRemove", constructions_designateRemove }, + { NULL, NULL } +}; + /************************ * Main Open function * diff --git a/library/include/modules/Buildings.h b/library/include/modules/Buildings.h index 9bb3ba001..6e0a22052 100644 --- a/library/include/modules/Buildings.h +++ b/library/include/modules/Buildings.h @@ -149,6 +149,11 @@ DFHACK_EXPORT bool setSize(df::building *bld, df::coord2d size, int direction = */ DFHACK_EXPORT std::pair getSize(df::building *bld); +/** + * Constructs an abstract building, i.e. stockpile or civzone. + */ +DFHACK_EXPORT bool constructAbstract(df::building *bld); + /** * Initiates construction of the building, using specified items as inputs. * Returns true if success. @@ -162,5 +167,11 @@ DFHACK_EXPORT bool constructWithItems(df::building *bld, std::vector */ DFHACK_EXPORT bool constructWithFilters(df::building *bld, std::vector items); +/** + * Deconstructs or queues deconstruction of a building. + * Returns true if the building has been destroyed instantly. + */ +DFHACK_EXPORT bool deconstruct(df::building *bld); + } } diff --git a/library/include/modules/Constructions.h b/library/include/modules/Constructions.h index 17da12711..f8a3e5c61 100644 --- a/library/include/modules/Constructions.h +++ b/library/include/modules/Constructions.h @@ -62,6 +62,9 @@ DFHACK_EXPORT df::construction * getConstruction (const int32_t index); DFHACK_EXPORT bool designateNew(df::coord pos, df::construction_type type, df::item_type item = df::item_type::NONE, int mat_index = -1); + +DFHACK_EXPORT bool designateRemove(df::coord pos, bool *immediate = NULL); + } } #endif diff --git a/library/lua/dfhack/buildings.lua b/library/lua/dfhack/buildings.lua index 57427e9fb..9650dc4cd 100644 --- a/library/lua/dfhack/buildings.lua +++ b/library/lua/dfhack/buildings.lua @@ -28,6 +28,8 @@ local buildings = dfhack.buildings items = { item, item ... }, -- OR filter = { { ... }, { ... }... } + -- OR + abstract = true } Returns: the created building, or 'nil, error' @@ -41,8 +43,8 @@ function buildings.constructBuilding(info) if not (info.pos or info.x) then error('position is required') end - if not (info.items or info.filters) then - error('either items or filters are required') + if not (info.abstract or info.items or info.filters) then + error('one of items, filters or abstract are required') elseif info.filters then for _,v in ipairs(info.filters) do v.new = true @@ -77,7 +79,9 @@ function buildings.constructBuilding(info) if info.full_rectangle and area ~= r_area then return nil, "not all tiles can be used" end - if info.items then + if info.abstract then + ok = buildings.constructAbstract(instance) + elseif info.items then ok = buildings.constructWithItems(instance, info.items) else ok = buildings.constructWithFilters(instance, info.filters) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 2c5162673..557eb1881 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -46,6 +46,7 @@ using namespace DFHack; #include "DataDefs.h" #include "df/world.h" #include "df/ui.h" +#include "df/ui_look_list.h" #include "df/d_init.h" #include "df/item.h" #include "df/job.h" @@ -59,6 +60,7 @@ using namespace DFHack; #include "df/building_bridgest.h" #include "df/building_coffinst.h" #include "df/building_civzonest.h" +#include "df/building_stockpilest.h" #include "df/building_furnacest.h" #include "df/building_workshopst.h" #include "df/building_screw_pumpst.h" @@ -683,6 +685,18 @@ static void linkRooms(df::building *bld) df::global::ui->equipment.update.bits.buildings = true; } +static void unlinkRooms(df::building *bld) +{ + for (size_t i = 0; i < bld->parents.size(); i++) + { + auto parent = bld->parents[i]; + int idx = linear_index(parent->children, bld); + vector_erase_at(parent->children, idx); + } + + bld->parents.clear(); +} + static void linkBuilding(df::building *bld) { bld->id = (*building_next_id)++; @@ -715,6 +729,52 @@ static void createDesign(df::building *bld, bool rough) } } +static int getMaxStockpileId() +{ + auto &vec = world->buildings.other[buildings_other_id::STOCKPILE]; + int max_id = 0; + + for (size_t i = 0; i < vec.size(); i++) + { + auto bld = strict_virtual_cast(vec[i]); + if (bld) + max_id = std::max(max_id, bld->stockpile_number); + } + + return max_id; +} + +bool Buildings::constructAbstract(df::building *bld) +{ + CHECK_NULL_POINTER(bld); + CHECK_INVALID_ARGUMENT(bld->id == -1); + CHECK_INVALID_ARGUMENT(!bld->isActual()); + + if (!checkBuildingTiles(bld, false)) + return false; + + switch (bld->getType()) + { + case building_type::Stockpile: + if (auto stock = strict_virtual_cast(bld)) + stock->stockpile_number = getMaxStockpileId() + 1; + break; + + default: + break; + } + + linkBuilding(bld); + + if (!bld->flags.bits.exists) + { + bld->flags.bits.exists = true; + bld->initFarmSeasons(); + } + + return true; +} + static bool linkForConstruct(df::job* &job, df::building *bld) { if (!checkBuildingTiles(bld, false)) @@ -836,3 +896,64 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectorisActual() && bld->getBuildStage() > 0) + { + bld->queueDestroy(); + return false; + } + + /* 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); + // Assume: not unit destroy target + vector_erase_at(ui->unk8.unk10, linear_index(ui->unk8.unk10, bld)); + // Assume: not used in punishment + // Assume: not used in non-own jobs + // 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]; + if (item->type == df::ui_look_list::T_items::Building && + item->building == bld) + { + vector_erase_at(ui_look_list->items, i); + delete item; + } + } + + if (process_dig) *process_dig = true; + if (process_jobs) *process_jobs = true; + + return true; +} diff --git a/library/modules/Constructions.cpp b/library/modules/Constructions.cpp index a38048391..2d61c447a 100644 --- a/library/modules/Constructions.cpp +++ b/library/modules/Constructions.cpp @@ -129,3 +129,43 @@ bool Constructions::designateNew(df::coord pos, df::construction_type type, return true; } +bool Constructions::designateRemove(df::coord pos, bool *immediate) +{ + using df::global::process_dig; + + if (immediate) + *immediate = false; + + if (auto current = Buildings::findAtTile(pos)) + { + auto cons = strict_virtual_cast(current); + if (!cons) + return false; + + if (Buildings::deconstruct(cons)) + { + if (immediate) + *immediate = true; + } + + return true; + } + + auto block = Maps::getTileBlock(pos); + if (!block) + return false; + + auto ttype = block->tiletype[pos.x&15][pos.y&15]; + + if (tileMaterial(ttype) == tiletype_material::CONSTRUCTION) + { + auto &dsgn = block->designation[pos.x&15][pos.y&15]; + dsgn.bits.dig = tile_dig_designation::Default; + block->flags.bits.designated = true; + if (process_dig) + *process_dig = true; + return true; + } + + return false; +} From fca618ff1baa48ad839317ed6d7479d34f110248 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 6 May 2012 19:11:29 +0400 Subject: [PATCH 53/53] Support default building inputs in dfhack.buildings.constructBuilding. --- library/lua/dfhack/buildings.lua | 348 ++++++++++++++++++++++++++++++- library/lua/utils.lua | 25 +++ 2 files changed, 368 insertions(+), 5 deletions(-) diff --git a/library/lua/dfhack/buildings.lua b/library/lua/dfhack/buildings.lua index 9650dc4cd..eb0a6a5b5 100644 --- a/library/lua/dfhack/buildings.lua +++ b/library/lua/dfhack/buildings.lua @@ -2,6 +2,336 @@ local dfhack = dfhack local _ENV = dfhack.BASE_G local buildings = dfhack.buildings +local utils = require 'utils' + +--[[ Building input material tables. ]] + +local building_inputs = { + [df.building_type.Chair] = { { item_type=df.item_type.CHAIR, vector_id=df.job_item_vector_id.CHAIR } }, + [df.building_type.Bed] = { { item_type=df.item_type.BED, vector_id=df.job_item_vector_id.BED } }, + [df.building_type.Table] = { { item_type=df.item_type.TABLE, vector_id=df.job_item_vector_id.TABLE } }, + [df.building_type.Coffin] = { { item_type=df.item_type.COFFIN, vector_id=df.job_item_vector_id.COFFIN } }, + [df.building_type.FarmPlot] = { }, + [df.building_type.Furnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } }, + [df.building_type.TradeDepot] = { { flags2={ building_material=true, non_economic=true }, quantity=3 } }, + [df.building_type.Door] = { { item_type=df.item_type.DOOR, vector_id=df.job_item_vector_id.DOOR } }, + [df.building_type.Floodgate] = { + { + item_type=df.item_type.FLOODGATE, + vector_id=df.job_item_vector_id.FLOODGATE + } + }, + [df.building_type.Box] = { + { + flags1={ empty=true }, + item_type=df.item_type.BOX, + vector_id=df.job_item_vector_id.BOX + } + }, + [df.building_type.Weaponrack] = { + { + item_type=df.item_type.WEAPONRACK, + vector_id=df.job_item_vector_id.WEAPONRACK + } + }, + [df.building_type.Armorstand] = { + { + item_type=df.item_type.ARMORSTAND, + vector_id=df.job_item_vector_id.ARMORSTAND + } + }, + [df.building_type.Workshop] = { { flags2={ building_material=true, non_economic=true } } }, + [df.building_type.Cabinet] = { + { item_type=df.item_type.CABINET, vector_id=df.job_item_vector_id.CABINET } + }, + [df.building_type.Statue] = { { item_type=df.item_type.STATUE, vector_id=df.job_item_vector_id.STATUE } }, + [df.building_type.WindowGlass] = { { item_type=df.item_type.WINDOW, vector_id=df.job_item_vector_id.WINDOW } }, + [df.building_type.WindowGem] = { + { + item_type=df.item_type.SMALLGEM, + quantity=3, + vector_id=df.job_item_vector_id.ANY_GENERIC35 + } + }, + [df.building_type.Well] = { + { + item_type=df.item_type.BLOCKS, + vector_id=df.job_item_vector_id.ANY_GENERIC35 + }, + { + name='bucket', + flags2={ lye_milk_free=true }, + item_type=df.item_type.BUCKET, + vector_id=df.job_item_vector_id.BUCKET + }, + { + name='chain', + item_type=df.item_type.CHAIN, + vector_id=df.job_item_vector_id.CHAIN + }, + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + }, + [df.building_type.Bridge] = { { flags2={ building_material=true, non_economic=true }, quantity=-1 } }, + [df.building_type.RoadDirt] = { }, + [df.building_type.RoadPaved] = { { flags2={ building_material=true, non_economic=true }, quantity=-1 } }, + [df.building_type.AnimalTrap] = { + { + flags1={ empty=true }, + item_type=df.item_type.ANIMALTRAP, + vector_id=df.job_item_vector_id.ANIMALTRAP + } + }, + [df.building_type.Support] = { { flags2={ building_material=true, non_economic=true } } }, + [df.building_type.ArcheryTarget] = { { flags2={ building_material=true, non_economic=true } } }, + [df.building_type.Chain] = { { item_type=df.item_type.CHAIN, vector_id=df.job_item_vector_id.CHAIN } }, + [df.building_type.Cage] = { { item_type=df.item_type.CAGE, vector_id=df.job_item_vector_id.CAGE } }, + [df.building_type.Weapon] = { { vector_id=df.job_item_vector_id.ANY_SPIKE } }, + [df.building_type.ScrewPump] = { + { + item_type=df.item_type.BLOCKS, + vector_id=df.job_item_vector_id.ANY_GENERIC35 + }, + { + name='screw', + flags2={ screw=true }, + item_type=df.item_type.TRAPCOMP, + vector_id=df.job_item_vector_id.ANY_WEAPON + }, + { + name='pipe', + item_type=df.item_type.PIPE_SECTION, + vector_id=df.job_item_vector_id.PIPE_SECTION + } + }, + [df.building_type.Construction] = { { flags2={ building_material=true, non_economic=true } } }, + [df.building_type.Hatch] = { + { + item_type=df.item_type.HATCH_COVER, + vector_id=df.job_item_vector_id.HATCH_COVER + } + }, + [df.building_type.GrateWall] = { { item_type=df.item_type.GRATE, vector_id=df.job_item_vector_id.GRATE } }, + [df.building_type.GrateFloor] = { { item_type=df.item_type.GRATE, vector_id=df.job_item_vector_id.GRATE } }, + [df.building_type.BarsVertical] = { + { item_type=df.item_type.BAR, vector_id=df.job_item_vector_id.ANY_GENERIC35 } + }, + [df.building_type.BarsFloor] = { + { item_type=df.item_type.BAR, vector_id=df.job_item_vector_id.ANY_GENERIC35 } + }, + [df.building_type.GearAssembly] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + }, + [df.building_type.AxleHorizontal] = { + { item_type=df.item_type.WOOD, vector_id=df.job_item_vector_id.WOOD, quantity=-1 } + }, + [df.building_type.AxleVertical] = { { item_type=df.item_type.WOOD, vector_id=df.job_item_vector_id.WOOD } }, + [df.building_type.WaterWheel] = { + { + item_type=df.item_type.WOOD, + quantity=3, + vector_id=df.job_item_vector_id.WOOD + } + }, + [df.building_type.Windmill] = { + { + item_type=df.item_type.WOOD, + quantity=4, + vector_id=df.job_item_vector_id.WOOD + } + }, + [df.building_type.TractionBench] = { + { + item_type=df.item_type.TRACTION_BENCH, + vector_id=df.job_item_vector_id.TRACTION_BENCH + } + }, + [df.building_type.Slab] = { { item_type=df.item_type.SLAB } }, + [df.building_type.NestBox] = { { has_tool_use=df.tool_uses.NEST_BOX, item_type=df.item_type.TOOL } }, + [df.building_type.Hive] = { { has_tool_use=df.tool_uses.HIVE, item_type=df.item_type.TOOL } } +} + +local furnace_inputs = { + [df.furnace_type.WoodFurnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } }, + [df.furnace_type.Smelter] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } }, + [df.furnace_type.GlassFurnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } }, + [df.furnace_type.Kiln] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } }, + [df.furnace_type.MagmaSmelter] = { { flags2={ building_material=true, magma_safe=true, non_economic=true } } }, + [df.furnace_type.MagmaGlassFurnace] = { { flags2={ building_material=true, magma_safe=true, non_economic=true } } }, + [df.furnace_type.MagmaKiln] = { { flags2={ building_material=true, magma_safe=true, non_economic=true } } } +} + +local workshop_inputs = { + [df.workshop_type.Carpenters] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Farmers] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Masons] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Craftsdwarfs] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Jewelers] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.MetalsmithsForge] = { + { + name='anvil', + flags2={ fire_safe=true }, + item_type=df.item_type.ANVIL, + vector_id=df.job_item_vector_id.ANVIL + }, + { flags2={ building_material=true, fire_safe=true, non_economic=true } } + }, + [df.workshop_type.MagmaForge] = { + { + name='anvil', + flags2={ magma_safe=true }, + item_type=df.item_type.ANVIL, + vector_id=df.job_item_vector_id.ANVIL + }, + { flags2={ building_material=true, magma_safe=true, non_economic=true } } + }, + [df.workshop_type.Bowyers] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Mechanics] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Siege] = { { flags2={ building_material=true, non_economic=true }, quantity=3 } }, + [df.workshop_type.Butchers] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Leatherworks] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Tanners] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Clothiers] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Fishery] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Still] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Loom] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Quern] = { { item_type=df.item_type.QUERN, vector_id=df.job_item_vector_id.QUERN } }, + [df.workshop_type.Kennels] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Kitchen] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Ashery] = { + { + item_type=df.item_type.BLOCKS, + vector_id=df.job_item_vector_id.ANY_GENERIC35 + }, + { + name='barrel', + flags1={ empty=true }, + item_type=df.item_type.BARREL, + vector_id=df.job_item_vector_id.BARREL + }, + { + name='bucket', + flags2={ lye_milk_free=true }, + item_type=df.item_type.BUCKET, + vector_id=df.job_item_vector_id.BUCKET + } + }, + [df.workshop_type.Dyers] = { + { + name='barrel', + flags1={ empty=true }, + item_type=df.item_type.BARREL, + vector_id=df.job_item_vector_id.BARREL + }, + { + name='bucket', + flags2={ lye_milk_free=true }, + item_type=df.item_type.BUCKET, + vector_id=df.job_item_vector_id.BUCKET + } + }, + [df.workshop_type.Millstone] = { + { + item_type=df.item_type.MILLSTONE, + vector_id=df.job_item_vector_id.MILLSTONE + }, + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + } +} + +local trap_inputs = { + [df.trap_type.StoneFallTrap] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + }, + [df.trap_type.WeaponTrap] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + }, + { vector_id=df.job_item_vector_id.ANY_WEAPON } + }, + [df.trap_type.Lever] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + }, + [df.trap_type.PressurePlate] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + }, + [df.trap_type.CageTrap] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + } +} + +local function get_inputs_by_type(type,subtype,custom) + if type == df.building_type.Workshop then + return workshop_inputs[subtype] + elseif type == df.building_type.Furnace then + return furnace_inputs[subtype] + elseif type == df.building_type.Trap then + return trap_inputs[subtype] + else + return building_inputs[type] + end +end + +local function augment_input(input, argtable) + local rv = {} + local arg = argtable[input.name or 'material'] + + if arg then + utils.assign(rv, arg) + end + + utils.assign(rv, input) + + if rv.mat_index and safe_index(rv, 'flags2', 'non_economic') then + rv.flags2.non_economic = false + end + + rv.new = true + rv.name = nil + return rv +end + +function buildings.getFiltersByType(argtable,type,subtype,custom) + local inputs = get_inputs_by_type(type,subtype,custom) + if not inputs then + return nil + end + local rv = {} + for i,v in ipairs(inputs) do + rv[i] = augment_input(v, argtable) + end + return rv +end + --[[ Wraps all steps necessary to create a building with a construct job into one function. @@ -30,6 +360,10 @@ local buildings = dfhack.buildings filter = { { ... }, { ... }... } -- OR abstract = true + -- OR + material = { filter_properties... } + mechanism = { filter_properties... } + barrel, bucket, chain, anvil, screw, pipe } Returns: the created building, or 'nil, error' @@ -39,14 +373,18 @@ function buildings.constructBuilding(info) local btype = info.type local subtype = info.subtype or -1 local custom = info.custom or -1 + local filters = info.filters if not (info.pos or info.x) then error('position is required') end - if not (info.abstract or info.items or info.filters) then - error('one of items, filters or abstract are required') - elseif info.filters then - for _,v in ipairs(info.filters) do + if not (info.abstract or info.items or filters) then + filters = buildings.getFiltersByType(info,btype,subtype,custom) + if not filters then + error('one of items, filters or abstract is required') + end + elseif filters then + for _,v in ipairs(filters) do v.new = true end end @@ -84,7 +422,7 @@ function buildings.constructBuilding(info) elseif info.items then ok = buildings.constructWithItems(instance, info.items) else - ok = buildings.constructWithFilters(instance, info.filters) + ok = buildings.constructWithFilters(instance, filters) end if not ok then return nil, "could not construct the building" diff --git a/library/lua/utils.lua b/library/lua/utils.lua index 00e42f9da..3eeb0cc6f 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -111,4 +111,29 @@ function make_sort_order(data,ordering) return index end +--[[ + Recursively assign data into a table. +--]] +function assign(tgt,src) + if df.isvalid(tgt) == 'ref' then + df.assign(tgt, src) + elseif type(tgt) == 'table' then + for k,v in pairs(src) do + if type(v) == 'table' or df.isvalid(v) == 'ref' then + local cv = tgt[k] + if cv == nil then + cv = {} + tgt[k] = cv + end + assign(cv, v) + else + tgt[k] = v + end + end + else + error('Invalid assign target type: '..tostring(tgt)) + end + return tgt +end + return _ENV \ No newline at end of file