Implement item occupancy tracking in MapCache.

develop
Alexander Gavrilov 2012-04-10 20:19:41 +04:00
parent b15d2da819
commit 59ddbfacb7
3 changed files with 119 additions and 77 deletions

@ -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<class R, class T> 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<int>(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<DFCoord, Block *>::iterator p;

@ -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<int&>(item_counts,item->pos);
if (count++ == 0)
{
index_tile<df::tile_occupancy&>(occupancy,item->pos).bits.item = true;
index_tile<df::tile_occupancy&>(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<int&>(item_counts,item->pos);
if (--count == 0)
{
index_tile<df::tile_occupancy&>(occupancy,item->pos).bits.item = false;
index_tile<df::tile_occupancy&>(block->occupancy,item->pos).bits.item = false;
}
return true;
}
MapExtras::Block *MapExtras::MapCache::BlockAt(DFCoord blockcoord)
{
if(!valid)

@ -147,26 +147,13 @@ static command_result autodump_main(color_ostream &out, vector <string> & 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 <string> & 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 <string> & 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;
}