Update building creation code with new knowledge, and fix zone.

Also, document new lua api, and add a more convenient wrapper.
develop
Alexander Gavrilov 2012-05-01 18:55:30 +04:00
parent 2303a25bde
commit 4cffb6428d
10 changed files with 404 additions and 65 deletions

@ -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
========================

@ -343,17 +343,18 @@ ul.auto-toc {
<li><a class="reference internal" href="#items-module" id="id17">Items module</a></li>
<li><a class="reference internal" href="#maps-module" id="id18">Maps module</a></li>
<li><a class="reference internal" href="#burrows-module" id="id19">Burrows module</a></li>
<li><a class="reference internal" href="#buildings-module" id="id20">Buildings module</a></li>
</ul>
</li>
<li><a class="reference internal" href="#core-interpreter-context" id="id20">Core interpreter context</a><ul>
<li><a class="reference internal" href="#event-type" id="id21">Event type</a></li>
<li><a class="reference internal" href="#core-interpreter-context" id="id21">Core interpreter context</a><ul>
<li><a class="reference internal" href="#event-type" id="id22">Event type</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#plugins" id="id22">Plugins</a><ul>
<li><a class="reference internal" href="#burrows" id="id23">burrows</a></li>
<li><a class="reference internal" href="#sort" id="id24">sort</a></li>
<li><a class="reference internal" href="#plugins" id="id23">Plugins</a><ul>
<li><a class="reference internal" href="#burrows" id="id24">burrows</a></li>
<li><a class="reference internal" href="#sort" id="id25">sort</a></li>
</ul>
</li>
</ul>
@ -1061,9 +1062,65 @@ burrows, or the presence of invaders.</p>
</li>
</ul>
</div>
<div class="section" id="buildings-module">
<h3><a class="toc-backref" href="#id20">Buildings module</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.getSize(building)</tt></p>
<p>Returns <em>width, height, centerx, centery</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.getCorrectSize(width, height, type, subtype, custom, direction)</tt></p>
<p>Computes correct dimensions for the specified building type and orientation,
using width and height for flexible dimensions.
Returns <em>is_flexible, width, height, center_x, center_y</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.buildings.checkFreeTiles(pos,size[,extents,change_extents,allow_occupied])</span></tt></p>
<p>Checks if the rectangle defined by <tt class="docutils literal">pos</tt> and <tt class="docutils literal">size</tt>, and possibly extents,
can be used for placing a building. If <tt class="docutils literal">change_extents</tt> is true, bad tiles
are removed from extents. If <tt class="docutils literal">allow_occupied</tt>, the occupancy test is skipped.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.countExtentTiles(extents,defval)</tt></p>
<p>Returns the number of tiles included by extents, or defval.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.hasSupport(pos,size)</tt></p>
<p>Checks if a bridge constructed at specified position would have
support from terrain, and thus won't collapse if retracted.</p>
</li>
</ul>
<p>Low-level building creation functions;</p>
<ul>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.allocInstance(pos, type, subtype, custom)</tt></p>
<p>Creates a new building instance of given type, subtype and custom type,
at specified position. Returns the object, or <em>nil</em> in case of an error.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.setSize(building, width, height, direction)</tt></p>
<p>Configures an object returned by <tt class="docutils literal">allocInstance</tt>, using specified
parameters wherever appropriate. If the building has fixed size along
any dimension, the corresponding input parameter will be ignored.
Returns <em>nil</em> if the building cannot be placed, or <em>true, width,
height, rect_area, true_area</em>. 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.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.constructWithItems(building, items)</tt></p>
<p>Links a fully configured object created by <tt class="docutils literal">allocInstance</tt> into the
world for construction, using a list of specific items as material.
Returns <em>true</em>, or <em>false</em> if impossible.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.constructWithFilters(building, job_items)</tt></p>
<p>Links a fully configured object created by <tt class="docutils literal">allocInstance</tt> into the
world for construction, using a list of job_item filters as inputs.
Returns <em>true</em>, or <em>false</em> if impossible. Filter objects are claimed
and possibly destroyed in any case.
Use a negative <tt class="docutils literal">quantity</tt> field value to auto-compute the amount
from the size of the building.</p>
</li>
</ul>
<p>More high-level functions are implemented in lua and can be loaded by
<tt class="docutils literal"><span class="pre">require('dfhack.buildings')</span></tt>. See <tt class="docutils literal">hack/lua/dfhack/buildings.lua</tt>.</p>
</div>
</div>
<div class="section" id="core-interpreter-context">
<h2><a class="toc-backref" href="#id20">Core interpreter context</a></h2>
<h2><a class="toc-backref" href="#id21">Core interpreter context</a></h2>
<p>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.</p>
@ -1077,7 +1134,7 @@ only context that can receive events from DF and plugins.</p>
</li>
</ul>
<div class="section" id="event-type">
<h3><a class="toc-backref" href="#id21">Event type</a></h3>
<h3><a class="toc-backref" href="#id22">Event type</a></h3>
<p>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 <tt class="docutils literal">dfhack.safecall</tt>.</p>
</div>
</div>
<div class="section" id="plugins">
<h1><a class="toc-backref" href="#id22">Plugins</a></h1>
<h1><a class="toc-backref" href="#id23">Plugins</a></h1>
<p>DFHack plugins may export native functions and events
to lua contexts. They are automatically imported by
<tt class="docutils literal"><span class="pre">mkmodule('plugins.&lt;name&gt;')</span></tt>; this means that a lua
module file is still necessary for <tt class="docutils literal">require</tt> to read.</p>
<p>The following plugins have lua support.</p>
<div class="section" id="burrows">
<h2><a class="toc-backref" href="#id23">burrows</a></h2>
<h2><a class="toc-backref" href="#id24">burrows</a></h2>
<p>Implements extended burrow manipulations.</p>
<p>Events:</p>
<ul>
@ -1148,7 +1205,7 @@ set is the same as used by the command line.</p>
<p>The lua module file also re-exports functions from <tt class="docutils literal">dfhack.burrows</tt>.</p>
</div>
<div class="section" id="sort">
<h2><a class="toc-backref" href="#id24">sort</a></h2>
<h2><a class="toc-backref" href="#id25">sort</a></h2>
<p>Does not export any native functions as of now. Instead, it
calls lua code to perform the actual ordering of list items.</p>
</div>

@ -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<df::building>(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 }
};

@ -108,7 +108,18 @@ DFHACK_EXPORT bool getCorrectSize(df::coord2d &size, df::coord2d &center,
* 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<df::coord,df::coord2d> getSize(df::building *bld);
/**
* Initiates construction of the building, using specified items as inputs.
* Returns true if success.

@ -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

@ -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

@ -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<df::coord,df::coord2d> 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<df::coord,df::coord2d>(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::vector<df::job_item
for (size_t i = 0; i < items.size(); i++)
{
if (items[i]->quantity < 0)
items[i]->quantity = computeMaterialAmount(bld);
job->job_items.push_back(items[i]);
if (items[i]->item_type == item_type::BOULDER)

@ -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

@ -1 +1 @@
Subproject commit d755560192ec318fbb52133e4e52972bee67d1e0
Subproject commit 6ce2b7120e5a246093116b48cb2ac64c35a8270d

@ -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";