Merge pull request #2734 from myk002/myk_tiletypes

Expose tiletype setting from the tiletypes plugin to lua
develop
Myk 2023-01-28 11:55:04 -08:00 committed by GitHub
commit 5f90756677
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 195 additions and 162 deletions

@ -36,7 +36,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
## New Plugins
## Fixes
- Fix issues with clicks "passing through" some DFHack window elements, like scrollbars
-@ Fix issues with clicks "passing through" some DFHack window elements, like scrollbars
## Misc Improvements
- A new cross-compile build script was added for building the Windows files from a Linux Docker builder (see the Compile instructions in the docs)
@ -50,6 +50,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
## Documentation
## API
- ``Buildings::containsTile()``: no longer takes a ``room`` parameter since that's not how rooms work anymore. If the building has extents, the extents will be checked. otherwise, the result just depends on whether the tile is within the building's bounding box.
## Lua
- `helpdb`: new function: ``helpdb.refresh()`` to force a refresh of the database. Call if you are a developer adding new scripts, loading new plugins, or changing help text during play

@ -1933,9 +1933,11 @@ General
Returns the number of tiles included by extents, or defval.
* ``dfhack.buildings.containsTile(building, x, y[, room])``
* ``dfhack.buildings.containsTile(building, x, y)``
Checks if the building contains the specified tile, either directly, or as room.
Checks if the building contains the specified tile. If the building contains extents,
then the extents are checked. Otherwise, returns whether the x and y map coordinates
are within the building's bounding box.
* ``dfhack.buildings.hasSupport(pos,size)``
@ -5603,6 +5605,13 @@ sort
The `sort <sort>` plugin does not export any native functions as of now.
Instead, it calls Lua code to perform the actual ordering of list items.
tiletypes
=========
* ``bool tiletypes_setTile(pos, shape, material, special, variant)`` where
the parameters are enum values from ``df.tiletype_shape``,
``df.tiletype_material``, etc. Returns whether the conversion succeeded.
.. _xlsxreader-api:
xlsxreader

@ -2234,8 +2234,8 @@ 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 bool buildings_containsTile(df::building *bld, int x, int y) {
return Buildings::containsTile(bld, df::coord2d(x,y));
}
static const LuaWrapper::FunctionReg dfhack_buildings_module[] = {

@ -151,9 +151,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.
* Checks if the building contains the specified tile. If the building has
* extents, returns whether tile is included within the extents. The x and y in
* tile are the map coordinates without the z component.
*/
DFHACK_EXPORT bool containsTile(df::building *bld, df::coord2d tile, bool room = false);
DFHACK_EXPORT bool containsTile(df::building *bld, df::coord2d tile);
/**
* Checks if the area has support from the terrain.

@ -407,7 +407,7 @@ df::building *Buildings::findAtTile(df::coord pos)
if (building && building->z == pos.z &&
building->isSettingOccupancy() &&
containsTile(building, pos, false))
containsTile(building, pos))
{
return building;
}
@ -442,24 +442,19 @@ df::building *Buildings::findAtTile(df::coord pos)
static unordered_map<int32_t, df::coord> corner1;
static unordered_map<int32_t, df::coord> corner2;
static void cacheBuilding(df::building *building, bool is_civzone) {
static void cacheBuilding(df::building *building) {
int32_t id = building->id;
df::coord p1(min(building->x1, building->x2), min(building->y1,building->y2), building->z);
df::coord p2(max(building->x1, building->x2), max(building->y1,building->y2), building->z);
if (!is_civzone) {
// civzones can be dynamically shrunk so we don't bother to cache
// their boundaries. findCivzonesAt() will trim the cache as needed.
corner1[id] = p1;
corner2[id] = p2;
}
corner1[id] = p1;
corner2[id] = p2;
for (int32_t x = p1.x; x <= p2.x; x++) {
for (int32_t y = p1.y; y <= p2.y; y++) {
df::coord pt(x, y, building->z);
if (Buildings::containsTile(building, pt, is_civzone)) {
if (!is_civzone)
locationToBuilding[pt] = id;
if (Buildings::containsTile(building, pt)) {
locationToBuilding[pt] = id;
}
}
}
@ -868,31 +863,16 @@ int Buildings::countExtentTiles(df::building_extents *ext, int defval)
return cnt;
}
bool Buildings::containsTile(df::building *bld, df::coord2d tile, bool room)
{
bool Buildings::containsTile(df::building *bld, df::coord2d tile) {
CHECK_NULL_POINTER(bld);
if (room)
{
/* TODO: understand how this changes for v50
if (!bld->is_room || !bld->room.extents)
return false;
*/
}
else
{
if (!bld->isExtentShaped() || !bld->room.extents) {
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()))
{
df::building_extents_type *etile = getExtentTile(bld->room, tile);
if (!etile || !*etile)
return false;
}
return true;
df::building_extents_type *etile = getExtentTile(bld->room, tile);
return etile && *etile;
}
bool Buildings::hasSupport(df::coord pos, df::coord2d size)
@ -1491,7 +1471,7 @@ void Buildings::updateBuildings(color_ostream&, void* ptr)
{
bool is_civzone = !building->isSettingOccupancy();
if (!corner1.count(id) && !is_civzone)
cacheBuilding(building, false);
cacheBuilding(building);
}
else if (corner1.count(id))
{
@ -1697,7 +1677,7 @@ StockpileIterator& StockpileIterator::operator++() {
continue;
}
if (!Buildings::containsTile(stockpile, item->pos, false)) {
if (!Buildings::containsTile(stockpile, item->pos)) {
continue;
}

@ -259,7 +259,7 @@ struct TileType
inline bool matches(const df::tiletype source,
const df::tile_designation des,
const t_matpair mat)
const t_matpair mat) const
{
bool rv = true;
rv &= (shape == -1 || shape == tileShape(source));
@ -735,6 +735,111 @@ bool processTileType(color_ostream & out, TileType &paint, std::vector<std::stri
return found;
}
static bool paintTile(MapExtras::MapCache &map, const df::coord &pos,
const TileType &target, const TileType &match = TileType()) {
MapExtras::Block *blk = map.BlockAtTile(pos);
if (!blk)
return false;
df::tiletype source = map.tiletypeAt(pos);
df::tile_designation des = map.designationAt(pos);
// Stone painting operates on the base layer
if (target.stone_material >= 0)
source = blk->baseTiletypeAt(pos);
t_matpair basemat = blk->baseMaterialAt(pos);
if (!match.matches(source, des, basemat))
return true;
df::tiletype_shape shape = target.shape;
if (shape == tiletype_shape::NONE)
shape = tileShape(source);
df::tiletype_material material = target.material;
if (material == tiletype_material::NONE)
material = tileMaterial(source);
df::tiletype_special special = target.special;
if (special == tiletype_special::NONE)
special = tileSpecial(source);
df::tiletype_variant variant = target.variant;
/*
* FIXME: variant should be:
* 1. If user variant:
* 2. If user variant \belongs target variants
* 3. use user variant
* 4. Else
* 5. use variant 0
* 6. If the source variant \belongs target variants
* 7 use source variant
* 8 ElseIf num target shape/material variants > 1
* 9. pick one randomly
* 10.Else
* 11. use variant 0
*
* The following variant check has been disabled because it's severely limiting
* the usefullness of the tool.
*/
/*
if (variant == tiletype_variant::NONE)
{
variant = tileVariant(source);
}
*/
// Remove direction from directionless tiles
DFHack::TileDirection direction = tileDirection(source);
if (!(material == tiletype_material::RIVER || shape == tiletype_shape::BROOK_BED || special == tiletype_special::TRACK || (shape == tiletype_shape::WALL && (material == tiletype_material::CONSTRUCTION || special == tiletype_special::SMOOTH))))
direction.whole = 0;
df::tiletype type = DFHack::findTileType(shape, material, variant, special, direction);
// hack for empty space
if (shape == tiletype_shape::EMPTY && material == tiletype_material::AIR && variant == tiletype_variant::VAR_1 && special == tiletype_special::NORMAL && direction.whole == 0)
type = tiletype::OpenSpace;
// make sure it's not invalid
if (type != tiletype::Void) {
if (target.stone_material >= 0) {
if (!blk->setStoneAt(pos, type, target.stone_material, target.vein_type, true, true))
return false;
}
else
map.setTiletypeAt(pos, type);
}
if (target.hidden > -1)
des.bits.hidden = target.hidden;
if (target.light > -1)
des.bits.light = target.light;
if (target.subterranean > -1)
des.bits.subterranean = target.subterranean;
if (target.skyview > -1)
des.bits.outside = target.skyview;
if (target.aquifer > -1)
des.bits.water_table = target.aquifer;
// Remove liquid from walls, etc
if (type != (df::tiletype)-1 && !DFHack::FlowPassable(type))
{
des.bits.flow_size = 0;
//des.bits.liquid_type = DFHack::liquid_water;
//des.bits.water_table = 0;
des.bits.flow_forbid = 0;
//des.bits.liquid_static = 0;
//des.bits.water_stagnant = 0;
//des.bits.water_salt = 0;
}
map.setDesignationAt(pos, des);
return true;
}
command_result executePaintJob(color_ostream &out,
const tiletypes_options &opts)
{
@ -786,128 +891,8 @@ command_result executePaintJob(color_ostream &out,
for (coord_vec::iterator iter = all_tiles.begin(); iter != all_tiles.end(); ++iter)
{
MapExtras::Block *blk = map.BlockAtTile(*iter);
if (!blk)
continue;
df::tiletype source = map.tiletypeAt(*iter);
df::tile_designation des = map.designationAt(*iter);
// Stone painting operates on the base layer
if (paint.stone_material >= 0)
source = blk->baseTiletypeAt(*iter);
t_matpair basemat = blk->baseMaterialAt(*iter);
if (!filter.matches(source, des, basemat))
{
continue;
}
df::tiletype_shape shape = paint.shape;
if (shape == tiletype_shape::NONE)
{
shape = tileShape(source);
}
df::tiletype_material material = paint.material;
if (material == tiletype_material::NONE)
{
material = tileMaterial(source);
}
df::tiletype_special special = paint.special;
if (special == tiletype_special::NONE)
{
special = tileSpecial(source);
}
df::tiletype_variant variant = paint.variant;
/*
* FIXME: variant should be:
* 1. If user variant:
* 2. If user variant \belongs target variants
* 3. use user variant
* 4. Else
* 5. use variant 0
* 6. If the source variant \belongs target variants
* 7 use source variant
* 8 ElseIf num target shape/material variants > 1
* 9. pick one randomly
* 10.Else
* 11. use variant 0
*
* The following variant check has been disabled because it's severely limiting
* the usefullness of the tool.
*/
/*
if (variant == tiletype_variant::NONE)
{
variant = tileVariant(source);
}
*/
// Remove direction from directionless tiles
DFHack::TileDirection direction = tileDirection(source);
if (!(material == tiletype_material::RIVER || shape == tiletype_shape::BROOK_BED || special == tiletype_special::TRACK || (shape == tiletype_shape::WALL && (material == tiletype_material::CONSTRUCTION || special == tiletype_special::SMOOTH))))
{
direction.whole = 0;
}
df::tiletype type = DFHack::findTileType(shape, material, variant, special, direction);
// hack for empty space
if (shape == tiletype_shape::EMPTY && material == tiletype_material::AIR && variant == tiletype_variant::VAR_1 && special == tiletype_special::NORMAL && direction.whole == 0)
{
type = tiletype::OpenSpace;
}
// make sure it's not invalid
if(type != tiletype::Void)
{
if (paint.stone_material >= 0)
{
if (!blk->setStoneAt(*iter, type, paint.stone_material, paint.vein_type, true, true))
failures++;
}
else
map.setTiletypeAt(*iter, type);
}
if (paint.hidden > -1)
{
des.bits.hidden = paint.hidden;
}
if (paint.light > -1)
{
des.bits.light = paint.light;
}
if (paint.subterranean > -1)
{
des.bits.subterranean = paint.subterranean;
}
if (paint.skyview > -1)
{
des.bits.outside = paint.skyview;
}
if (paint.aquifer > -1)
{
des.bits.water_table = paint.aquifer;
}
// Remove liquid from walls, etc
if (type != (df::tiletype)-1 && !DFHack::FlowPassable(type))
{
des.bits.flow_size = 0;
//des.bits.liquid_type = DFHack::liquid_water;
//des.bits.water_table = 0;
des.bits.flow_forbid = 0;
//des.bits.liquid_static = 0;
//des.bits.water_stagnant = 0;
//des.bits.water_salt = 0;
}
map.setDesignationAt(*iter, des);
if (!paintTile(map, *iter, paint, filter))
++failures;
}
if (failures > 0)
@ -1135,3 +1120,59 @@ command_result df_tiletypes_here_point (color_ostream &out, vector <string> & pa
brush = old;
return rv;
}
static bool setTile(color_ostream &out, df::coord pos, df::tiletype_shape shape,
df::tiletype_material material, df::tiletype_special special,
df::tiletype_variant variant) {
if (!Maps::isValidTilePos(pos)) {
out.printerr("Invalid map position: %d, %d, %d\n", pos.x, pos.y, pos.z);
return false;
}
if (!is_valid_enum_item(shape)) {
out.printerr("Invalid shape type: %d\n", shape);
return false;
}
if (!is_valid_enum_item(material)) {
out.printerr("Invalid material type: %d\n", material);
return false;
}
if (!is_valid_enum_item(special)) {
out.printerr("Invalid special type: %d\n", special);
return false;
}
if (!is_valid_enum_item(variant)) {
out.printerr("Invalid variant type: %d\n", variant);
return false;
}
TileType target;
target.shape = shape;
target.material = material;
target.special = special;
target.variant = variant;
MapExtras::MapCache map;
return paintTile(map, pos, target) && map.WriteAll();
}
static int tiletypes_setTile(lua_State *L) {
color_ostream *out = Lua::GetOutput(L);
if (!out)
out = &Core::getInstance().getConsole();
df::coord pos;
Lua::CheckDFAssign(L, &pos, 1);
df::tiletype_shape shape = (df::tiletype_shape)lua_tointeger(L, 2);
df::tiletype_material material = (df::tiletype_material)lua_tointeger(L, 3);
df::tiletype_special special = (df::tiletype_special)lua_tointeger(L, 4);
df::tiletype_variant variant = (df::tiletype_variant)lua_tointeger(L, 5);
Lua::Push(L, setTile(*out, pos, shape, material, special, variant));
return 1;
}
DFHACK_PLUGIN_LUA_COMMANDS {
DFHACK_LUA_COMMAND(tiletypes_setTile),
DFHACK_LUA_END
};