From 59ddbfacb70a8f56e3fa2088ada1df71d8d158de Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 10 Apr 2012 20:19:41 +0400 Subject: [PATCH] Implement item occupancy tracking in MapCache. --- library/include/modules/MapCache.h | 28 +++++++++ library/modules/Maps.cpp | 77 +++++++++++++++++++++++++ plugins/autodump.cpp | 91 +++++------------------------- 3 files changed, 119 insertions(+), 77 deletions(-) diff --git a/library/include/modules/MapCache.h b/library/include/modules/MapCache.h index b0ee31aa5..f2aebc23d 100644 --- a/library/include/modules/MapCache.h +++ b/library/include/modules/MapCache.h @@ -33,6 +33,7 @@ distribution. #include "df/map_block.h" #include "df/block_square_event_mineralst.h" #include "df/construction.h" +#include "df/item.h" using namespace DFHack; @@ -45,10 +46,15 @@ template inline R index_tile(T &v, df::coord2d p) { return v[p.x&15][p.y&15]; } +inline bool is_valid_tile_coord(df::coord2d p) { + return (p.x & ~15) == 0 && (p.y & ~15) == 0; +} + class DFHACK_EXPORT Block { public: Block(MapCache *parent, DFCoord _bcoord); + ~Block(); /* * All coordinates are taken mod 16. @@ -143,6 +149,12 @@ public: return true; } + int itemCountAt(df::coord2d p) + { + if (!item_counts) init_item_counts(); + return index_tile(item_counts,p); + } + t_blockflags BlockFlags() { return blockflags; @@ -189,6 +201,13 @@ private: int16_t tags[16][16]; + typedef int T_item_counts[16]; + T_item_counts *item_counts; + void init_item_counts(); + + bool addItemOnGround(df::item *item); + bool removeItemOnGround(df::item *item); + tiletypes40d rawtiles; designations40d designation; occupancies40d occupancy; @@ -322,6 +341,15 @@ class DFHACK_EXPORT MapCache return (b && b->valid); } + bool addItemOnGround(df::item *item) { + Block * b= BlockAtTile(item->pos); + return b ? b->addItemOnGround(item) : false; + } + bool removeItemOnGround(df::item *item) { + Block * b= BlockAtTile(item->pos); + return b ? b->removeItemOnGround(item) : false; + } + bool WriteAll() { std::map::iterator p; diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 4d312fa8d..848454468 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -385,6 +385,7 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) valid = false; bcoord = _bcoord; block = Maps::getBlock(bcoord); + item_counts = NULL; memset(tags,0,sizeof(tags)); @@ -422,6 +423,11 @@ MapExtras::Block::Block(MapCache *parent, DFCoord _bcoord) : parent(parent) } } +MapExtras::Block::~Block() +{ + delete[] item_counts; +} + bool MapExtras::Block::Write () { if(!valid) return false; @@ -558,6 +564,77 @@ bool MapExtras::Block::GetLocalFeature(t_feature *out) return Maps::GetLocalFeature(*out, block->map_pos/16, block->local_feature); } +void MapExtras::Block::init_item_counts() +{ + if (item_counts) return; + + item_counts = new T_item_counts[16]; + memset(item_counts, 0, sizeof(T_item_counts)); + + if (!block) return; + + for (size_t i = 0; i < block->items.size(); i++) + { + auto it = df::item::find(block->items[i]); + if (!it || !it->flags.bits.on_ground) + continue; + + df::coord tidx = it->pos - block->map_pos; + if (!is_valid_tile_coord(tidx) || tidx.z != 0) + continue; + + item_counts[tidx.x][tidx.y]++; + } +} + +bool MapExtras::Block::addItemOnGround(df::item *item) +{ + if (!block) + return false; + + init_item_counts(); + + bool inserted; + insert_into_vector(block->items, item->id, &inserted); + + if (inserted) + { + int &count = index_tile(item_counts,item->pos); + + if (count++ == 0) + { + index_tile(occupancy,item->pos).bits.item = true; + index_tile(block->occupancy,item->pos).bits.item = true; + } + } + + return inserted; +} + +bool MapExtras::Block::removeItemOnGround(df::item *item) +{ + if (!block) + return false; + + init_item_counts(); + + int idx = binsearch_index(block->items, item->id); + if (idx < 0) + return false; + + vector_erase_at(block->items, idx); + + int &count = index_tile(item_counts,item->pos); + + if (--count == 0) + { + index_tile(occupancy,item->pos).bits.item = false; + index_tile(block->occupancy,item->pos).bits.item = false; + } + + return true; +} + MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord) { if(!valid) diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index 3f1157f41..a71555b7a 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -147,26 +147,13 @@ static command_result autodump_main(color_ostream &out, vector & parame } } } - coordmap counts; + // proceed with the dumpification operation for(size_t i=0; i< numItems; i++) { df::item * itm = world->items.all[i]; DFCoord pos_item(itm->pos.x, itm->pos.y, itm->pos.z); - // keep track how many items are at places. all items. - coordmap::iterator it = counts.find(pos_item); - if(it == counts.end()) - { - pair< coordmap::iterator, bool > inserted = counts.insert(make_pair(pos_item,1)); - it = inserted.first; - } - else - { - it->second ++; - } - // iterator is valid here, we use it later to decrement the pile counter if the item is moved - // only dump the stuff marked for dumping and laying on the ground if ( !itm->flags.bits.dump || !itm->flags.bits.on_ground @@ -194,38 +181,16 @@ static command_result autodump_main(color_ostream &out, vector & parame itm->flags.bits.forbid = true; // Don't move items if they're already at the cursor - if (pos_cursor == pos_item) - continue; - - // Do we need to fix block-local item ID vector? - if(pos_item/16 != pos_cursor/16) + if (pos_cursor != pos_item) { - // yes... - cerr << "Moving from block to block!" << endl; - df::map_block * bl_src = Maps::getBlockAbs(itm->pos.x, itm->pos.y, itm->pos.z); - df::map_block * bl_tgt = Maps::getBlockAbs(cx, cy, cz); - if(bl_src) - { - remove(bl_src->items.begin(), bl_src->items.end(),itm->id); - } - else - { - cerr << "No source block" << endl; - } - if(bl_tgt) - { - bl_tgt->items.push_back(itm->id); - } - else - { - cerr << "No target block" << endl; - } - } + if (!MC.removeItemOnGround(itm)) + out.printerr("Item %d wasn't in the source block.\n", itm->id); - // Move the item - itm->pos.x = pos_cursor.x; - itm->pos.y = pos_cursor.y; - itm->pos.z = pos_cursor.z; + itm->pos = pos_cursor; + + if (!MC.addItemOnGround(itm)) + out.printerr("Could not add item %d to destination block.\n", itm->id); + } } else // destroy { @@ -238,42 +203,14 @@ static command_result autodump_main(color_ostream &out, vector & parame itm->flags.bits.forbid = true; itm->flags.bits.hidden = true; } - // keeping track of item pile sizes ;) - it->second --; + dumped_total++; } - if(!destroy) // TODO: do we have to do any of this when destroying items? - { - // for each item pile, see if it reached zero. if so, unset item flag on the tile it's on - coordmap::iterator it = counts.begin(); - coordmap::iterator end = counts.end(); - while(it != end) - { - if(it->second == 0) - { - df::tile_occupancy occ = MC.occupancyAt(it->first); - occ.bits.item = false; - MC.setOccupancyAt(it->first, occ); - } - it++; - } - // Set "item here" flag on target tile, if we moved any items to the target tile. - if (dumped_total > 0) - { - // assume there is a possibility the cursor points at some weird location with missing block data - Block * b = MC.BlockAt(pos_cursor / 16); - if(b) - { - df::tile_occupancy occ = MC.occupancyAt(pos_cursor); - occ.bits.item = 1; - MC.setOccupancyAt(pos_cursor,occ); - } - } - // write map changes back to DF. + + // write map changes back to DF. + if(!destroy) MC.WriteAll(); - // Is this necessary? Is "forbid" a dirtyable attribute like "dig" is? - //Maps::WriteDirtyBit(cx/16, cy/16, cz, true); - } + out.print("Done. %d items %s.\n", dumped_total, destroy ? "marked for destruction" : "quickdumped"); return CR_OK; }