only reset extents if they are unusable

this allows callers of Buildings::setSize() to "pre-initialize" the
extents to declare non-rectangular structures. this allows quickfort to
create non-rectangular stockpiles, farm plots, zones, etc. the extents
are still reset as before if the size of the building doesn't match the
caller's expectations.

this commit also fixes a memory leak when setSize() allocates memory for
extents, but the memory is not deallocated if the building is ultimately
invalid for some reason.
develop
myk002 2020-12-16 11:10:47 -08:00
parent de21e0cdb8
commit 3ba984c22c
No known key found for this signature in database
GPG Key ID: 8A39CA0FA0C16E78
4 changed files with 23 additions and 9 deletions

@ -1679,7 +1679,11 @@ Low-level building creation functions:
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.
if any tiles were removed from designation. You can specify a non-rectangular
designation for building types that support extents by setting the
``room.extents`` bitmap before calling this function. The extents will be
reset, however, if the size returned by this function doesn't match the
input size parameter.
* ``dfhack.buildings.constructAbstract(building)``
@ -1768,7 +1772,9 @@ Among them are:
- ``fields = { ... }``
Initializes fields of the building object after creation with ``df.assign``.
Initializes fields of the building object after creation with
``df.assign``. If ``room.extents`` is assigned this way and this function
returns with error, the memory allocated for the extents is freed.
- ``width = ..., height = ..., direction = ...``

@ -162,6 +162,8 @@ DFHACK_EXPORT bool hasSupport(df::coord pos, df::coord2d size);
/**
* Sets the size of the building, using size and direction as guidance.
* Any custom extents set for the building will be cleared if the size passed in
* as guidance cannot be applied to the building.
* 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);

@ -485,6 +485,11 @@ function buildings.constructBuilding(info)
local to_delete = instance
return dfhack.with_finalize(
function()
-- ensure we don't leak extents created by Buildings::checkFreeTiles
if to_delete and to_delete.room.extents then
df.delete(to_delete.room.extents)
to_delete.room.extents = nil
end
df.delete(to_delete)
end,
function()

@ -758,18 +758,19 @@ 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 old_size = size;
df::coord2d center;
getCorrectSize(size, center, bld->getType(), bld->getSubtype(),
bld->getCustomType(), direction);
// Delete old extents if size has changed.
if (old_size != size && bld->room.extents)
{
delete[] bld->room.extents;
bld->room.extents = NULL;
}
bld->x2 = bld->x1 + size.x - 1;
bld->y2 = bld->y1 + size.y - 1;
bld->centerx = bld->x1 + center.x;