Merge pull request #61809 from groud/terrain_center_bit
This commit is contained in:
commit
d4f31e201d
@ -79,7 +79,7 @@
|
|||||||
Returns the occluder polygon of the tile for the TileSet occlusion layer with index [code]layer_id[/code].
|
Returns the occluder polygon of the tile for the TileSet occlusion layer with index [code]layer_id[/code].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="get_peering_bit_terrain" qualifiers="const">
|
<method name="get_terrain_peering_bit" qualifiers="const">
|
||||||
<return type="int" />
|
<return type="int" />
|
||||||
<argument index="0" name="peering_bit" type="int" enum="TileSet.CellNeighbor" />
|
<argument index="0" name="peering_bit" type="int" enum="TileSet.CellNeighbor" />
|
||||||
<description>
|
<description>
|
||||||
@ -185,7 +185,7 @@
|
|||||||
Sets the occluder for the TileSet occlusion layer with index [code]layer_id[/code].
|
Sets the occluder for the TileSet occlusion layer with index [code]layer_id[/code].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="set_peering_bit_terrain">
|
<method name="set_terrain_peering_bit">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<argument index="0" name="peering_bit" type="int" enum="TileSet.CellNeighbor" />
|
<argument index="0" name="peering_bit" type="int" enum="TileSet.CellNeighbor" />
|
||||||
<argument index="1" name="terrain" type="int" />
|
<argument index="1" name="terrain" type="int" />
|
||||||
@ -205,6 +205,8 @@
|
|||||||
</member>
|
</member>
|
||||||
<member name="probability" type="float" setter="set_probability" getter="get_probability" default="1.0">
|
<member name="probability" type="float" setter="set_probability" getter="get_probability" default="1.0">
|
||||||
</member>
|
</member>
|
||||||
|
<member name="terrain" type="int" setter="set_terrain" getter="get_terrain" default="-1">
|
||||||
|
</member>
|
||||||
<member name="terrain_set" type="int" setter="set_terrain_set" getter="get_terrain_set" default="-1">
|
<member name="terrain_set" type="int" setter="set_terrain_set" getter="get_terrain_set" default="-1">
|
||||||
</member>
|
</member>
|
||||||
<member name="texture_offset" type="Vector2i" setter="set_texture_offset" getter="get_texture_offset" default="Vector2i(0, 0)">
|
<member name="texture_offset" type="Vector2i" setter="set_texture_offset" getter="get_texture_offset" default="Vector2i(0, 0)">
|
||||||
|
@ -243,15 +243,30 @@
|
|||||||
- The alternative tile identifier [code]alternative_tile[/code] identifies a tile alternative the source is a [TileSetAtlasSource], and the scene for a [TileSetScenesCollectionSource].
|
- The alternative tile identifier [code]alternative_tile[/code] identifies a tile alternative the source is a [TileSetAtlasSource], and the scene for a [TileSetScenesCollectionSource].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="set_cells_from_surrounding_terrains">
|
<method name="set_cells_terrain_connect">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<argument index="0" name="layer" type="int" />
|
<argument index="0" name="layer" type="int" />
|
||||||
<argument index="1" name="cells" type="Vector2i[]" />
|
<argument index="1" name="cells" type="Vector2i[]" />
|
||||||
<argument index="2" name="terrain_set" type="int" />
|
<argument index="2" name="terrain_set" type="int" />
|
||||||
<argument index="3" name="ignore_empty_terrains" type="bool" default="true" />
|
<argument index="3" name="terrain" type="int" />
|
||||||
|
<argument index="4" name="ignore_empty_terrains" type="bool" default="true" />
|
||||||
<description>
|
<description>
|
||||||
Updates all the cells in the [code]cells[/code] coordinates array and replace them by tiles that matches the surrounding cells terrains. Only cells form the given [code]terrain_set[/code] are considered.
|
Update all the cells in the [code]cells[/code] coordinates array so that they use the given [code]terrain[/code] for the given [code]terrain_set[/code]. If an updated cell has the same terrain as one of its neighboring cells, this function tries to join the two. This function might update neighboring tiles if needed to create correct terrain transitions. If [code]ignore_empty_terrains[/code] is true, empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints.
|
||||||
If [code]ignore_empty_terrains[/code] is true, zones with no terrain defined are ignored to select the tiles.
|
If [code]ignore_empty_terrains[/code] is true, empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints.
|
||||||
|
[b]Note:[/b] To work correctly, [code]set_cells_terrain_connect[/code] requires the TileMap's TileSet to have terrains set up with all required terrain combinations. Otherwise, it may produce unexpected results.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
<method name="set_cells_terrain_path">
|
||||||
|
<return type="void" />
|
||||||
|
<argument index="0" name="layer" type="int" />
|
||||||
|
<argument index="1" name="path" type="Vector2i[]" />
|
||||||
|
<argument index="2" name="terrain_set" type="int" />
|
||||||
|
<argument index="3" name="terrain" type="int" />
|
||||||
|
<argument index="4" name="ignore_empty_terrains" type="bool" default="true" />
|
||||||
|
<description>
|
||||||
|
Update all the cells in the [code]cells[/code] coordinates array so that they use the given [code]terrain[/code] for the given [code]terrain_set[/code]. The function will also connect two successive cell in the path with the same terrain. This function might update neighboring tiles if needed to create correct terrain transitions.
|
||||||
|
If [code]ignore_empty_terrains[/code] is true, empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints.
|
||||||
|
[b]Note:[/b] To work correctly, [code]set_cells_terrain_path[/code] requires the TileMap's TileSet to have terrains set up with all required terrain combinations. Otherwise, it may produce unexpected results.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="set_layer_enabled">
|
<method name="set_layer_enabled">
|
||||||
|
1
editor/icons/TerrainConnect.svg
Normal file
1
editor/icons/TerrainConnect.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg height="32" viewBox="0 0 16 16" width="32" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><circle cx="8" cy="8" r="2.000028" stroke-width="1.09711"/><g stroke-width=".5" transform="translate(0 -1036.4)"><path d="m7 1039.9h-1l2-2.5 2 2.5h-1v2h-2z"/><path d="m3.5606601 1041.3748-.7071068.707-.3535533-3.1818 3.1819804.3535-.7071067.7071.7071067.7071-1.4142135 1.4142z"/><path d="m11.0252 1039.9606-.707-.7071 3.1818-.3535-.3535 3.1819-.7071-.7071-.7071.7071-1.4142-1.4142z"/><path d="m12.43934 1047.4252.707107-.707.353553 3.1818-3.18198-.3535.707106-.7071-.707106-.7071 1.414214-1.4142z"/><path d="m4.9748005 1048.8394.707.7071-3.1818.3535.3535-3.1819.7071.7071.7071-.7071 1.4142 1.4142z"/><path d="m12.5 1043.4v-1l2.5 2-2.5 2v-1h-2v-2z"/><path d="m9 1048.9h1l-2 2.5-2-2.5h1v-2h2z"/><path d="m3.5 1045.4v1l-2.5-2 2.5-2v1h2v2z"/></g></g></svg>
|
After Width: | Height: | Size: 854 B |
1
editor/icons/TerrainPath.svg
Normal file
1
editor/icons/TerrainPath.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg height="32" viewBox="0 0 16 16" width="32" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" transform="translate(0 -1036.4)"><circle cx="3" cy="1049.4" r="2.000028" stroke-width="1.09711"/><circle cx="13" cy="1039.4" r="2.000028" stroke-width="1.09711"/><path d="m7 1038.4v10h-4v2h4 2v-2-8h4v-2h-4z" stroke-width=".46291"/></g></svg>
|
After Width: | Height: | Size: 343 B |
@ -1676,10 +1676,15 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
|
|||||||
Vector<Color> color;
|
Vector<Color> color;
|
||||||
color.push_back(Color(1.0, 1.0, 1.0, 0.5));
|
color.push_back(Color(1.0, 1.0, 1.0, 0.5));
|
||||||
|
|
||||||
|
Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
|
||||||
|
if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
|
||||||
|
p_canvas_item->draw_set_transform_matrix(p_transform * xform);
|
||||||
|
p_canvas_item->draw_polygon(polygon, color);
|
||||||
|
}
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
|
||||||
Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
|
polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
|
||||||
if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
|
if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
|
||||||
p_canvas_item->draw_set_transform_matrix(p_transform * xform);
|
p_canvas_item->draw_set_transform_matrix(p_transform * xform);
|
||||||
p_canvas_item->draw_polygon(polygon, color);
|
p_canvas_item->draw_polygon(polygon, color);
|
||||||
@ -1806,10 +1811,19 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
|
|||||||
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
|
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
|
||||||
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
|
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
|
||||||
|
|
||||||
|
Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
|
||||||
|
for (int j = 0; j < polygon.size(); j++) {
|
||||||
|
polygon.write[j] += position;
|
||||||
|
}
|
||||||
|
if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) {
|
||||||
|
// Draw terrain.
|
||||||
|
p_canvas_item->draw_polygon(polygon, color);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
|
||||||
Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
|
polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
|
||||||
for (int j = 0; j < polygon.size(); j++) {
|
for (int j = 0; j < polygon.size(); j++) {
|
||||||
polygon.write[j] += position;
|
polygon.write[j] += position;
|
||||||
}
|
}
|
||||||
@ -1850,10 +1864,16 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
|
|||||||
Vector<Color> color;
|
Vector<Color> color;
|
||||||
color.push_back(Color(1.0, 1.0, 1.0, 0.5));
|
color.push_back(Color(1.0, 1.0, 1.0, 0.5));
|
||||||
|
|
||||||
|
Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
|
||||||
|
if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
|
||||||
|
p_canvas_item->draw_set_transform_matrix(p_transform * xform);
|
||||||
|
p_canvas_item->draw_polygon(polygon, color);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
|
||||||
Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
|
polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
|
||||||
if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
|
if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
|
||||||
p_canvas_item->draw_set_transform_matrix(p_transform * xform);
|
p_canvas_item->draw_set_transform_matrix(p_transform * xform);
|
||||||
p_canvas_item->draw_polygon(polygon, color);
|
p_canvas_item->draw_polygon(polygon, color);
|
||||||
@ -1926,10 +1946,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
|
|||||||
if (!drag_modified.has(cell)) {
|
if (!drag_modified.has(cell)) {
|
||||||
Dictionary dict;
|
Dictionary dict;
|
||||||
dict["terrain_set"] = tile_data->get_terrain_set();
|
dict["terrain_set"] = tile_data->get_terrain_set();
|
||||||
|
dict["terrain"] = tile_data->get_terrain();
|
||||||
Array array;
|
Array array;
|
||||||
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
||||||
array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
|
array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
|
||||||
}
|
}
|
||||||
dict["terrain_peering_bits"] = array;
|
dict["terrain_peering_bits"] = array;
|
||||||
drag_modified[cell] = dict;
|
drag_modified[cell] = dict;
|
||||||
@ -1958,10 +1979,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
|
|||||||
if (!drag_modified.has(cell)) {
|
if (!drag_modified.has(cell)) {
|
||||||
Dictionary dict;
|
Dictionary dict;
|
||||||
dict["terrain_set"] = tile_data->get_terrain_set();
|
dict["terrain_set"] = tile_data->get_terrain_set();
|
||||||
|
dict["terrain"] = tile_data->get_terrain();
|
||||||
Array array;
|
Array array;
|
||||||
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
||||||
array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
|
array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
|
||||||
}
|
}
|
||||||
dict["terrain_peering_bits"] = array;
|
dict["terrain_peering_bits"] = array;
|
||||||
drag_modified[cell] = dict;
|
drag_modified[cell] = dict;
|
||||||
@ -1970,12 +1992,17 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
|
|||||||
// Set the terrains bits.
|
// Set the terrains bits.
|
||||||
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
|
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
|
||||||
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
|
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
|
||||||
|
|
||||||
|
Vector<Vector2> polygon = tile_set->get_terrain_polygon(tile_data->get_terrain_set());
|
||||||
|
if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
|
||||||
|
tile_data->set_terrain(terrain);
|
||||||
|
}
|
||||||
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
||||||
if (tile_data->is_valid_peering_bit_terrain(bit)) {
|
if (tile_data->is_valid_terrain_peering_bit(bit)) {
|
||||||
Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(tile_data->get_terrain_set(), bit);
|
polygon = tile_set->get_terrain_peering_bit_polygon(tile_data->get_terrain_set(), bit);
|
||||||
if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
|
if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
|
||||||
tile_data->set_peering_bit_terrain(bit, terrain);
|
tile_data->set_terrain_peering_bit(bit, terrain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2000,12 +2027,17 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
|
|||||||
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
|
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
|
||||||
dummy_object->set("terrain_set", terrain_set);
|
dummy_object->set("terrain_set", terrain_set);
|
||||||
dummy_object->set("terrain", -1);
|
dummy_object->set("terrain", -1);
|
||||||
|
|
||||||
|
Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
|
||||||
|
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
|
||||||
|
dummy_object->set("terrain", tile_data->get_terrain());
|
||||||
|
}
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
|
||||||
Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
|
polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
|
||||||
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
|
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
|
||||||
dummy_object->set("terrain", tile_data->get_peering_bit_terrain(bit));
|
dummy_object->set("terrain", tile_data->get_terrain_peering_bit(bit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2044,10 +2076,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
|
|||||||
// Save the old terrain_set and terrains bits.
|
// Save the old terrain_set and terrains bits.
|
||||||
Dictionary dict;
|
Dictionary dict;
|
||||||
dict["terrain_set"] = tile_data->get_terrain_set();
|
dict["terrain_set"] = tile_data->get_terrain_set();
|
||||||
|
dict["terrain"] = tile_data->get_terrain();
|
||||||
Array array;
|
Array array;
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
|
array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
|
||||||
}
|
}
|
||||||
dict["terrain_peering_bits"] = array;
|
dict["terrain_peering_bits"] = array;
|
||||||
drag_modified[cell] = dict;
|
drag_modified[cell] = dict;
|
||||||
@ -2085,10 +2118,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
|
|||||||
// Save the old terrain_set and terrains bits.
|
// Save the old terrain_set and terrains bits.
|
||||||
Dictionary dict;
|
Dictionary dict;
|
||||||
dict["terrain_set"] = tile_data->get_terrain_set();
|
dict["terrain_set"] = tile_data->get_terrain_set();
|
||||||
|
dict["terrain"] = tile_data->get_terrain();
|
||||||
Array array;
|
Array array;
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
|
array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
|
||||||
}
|
}
|
||||||
dict["terrain_peering_bits"] = array;
|
dict["terrain_peering_bits"] = array;
|
||||||
drag_modified[cell] = dict;
|
drag_modified[cell] = dict;
|
||||||
@ -2097,12 +2131,16 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
|
|||||||
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
|
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
|
||||||
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
|
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
|
||||||
|
|
||||||
|
Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
|
||||||
|
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
|
||||||
|
tile_data->set_terrain(terrain);
|
||||||
|
}
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
|
||||||
Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
|
polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
|
||||||
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
|
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
|
||||||
tile_data->set_peering_bit_terrain(bit, terrain);
|
tile_data->set_terrain_peering_bit(bit, terrain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2138,10 +2176,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
|
|||||||
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
|
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
|
||||||
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_set());
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_set());
|
||||||
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.alternative_tile), drag_painted_value);
|
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.alternative_tile), drag_painted_value);
|
||||||
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain());
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_data->is_valid_peering_bit_terrain(bit)) {
|
if (tile_data->is_valid_terrain_peering_bit(bit)) {
|
||||||
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_peering_bit_terrain(bit));
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_peering_bit(bit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2154,10 +2193,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
|
|||||||
Vector2i coords = E.key.get_atlas_coords();
|
Vector2i coords = E.key.get_atlas_coords();
|
||||||
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
|
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
|
||||||
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
|
||||||
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
|
||||||
Array array = dict["terrain_peering_bits"];
|
Array array = dict["terrain_peering_bits"];
|
||||||
for (int i = 0; i < array.size(); i++) {
|
for (int i = 0; i < array.size(); i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set"], bit)) {
|
||||||
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2172,13 +2212,15 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
|
|||||||
for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
|
for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
|
||||||
Dictionary dict = E.value;
|
Dictionary dict = E.value;
|
||||||
Vector2i coords = E.key.get_atlas_coords();
|
Vector2i coords = E.key.get_atlas_coords();
|
||||||
|
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), terrain);
|
||||||
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
|
||||||
Array array = dict["terrain_peering_bits"];
|
Array array = dict["terrain_peering_bits"];
|
||||||
for (int i = 0; i < array.size(); i++) {
|
for (int i = 0; i < array.size(); i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
|
||||||
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain);
|
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain);
|
||||||
}
|
}
|
||||||
if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set"], bit)) {
|
||||||
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2224,20 +2266,30 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
|
|||||||
Vector2i coords = E.get_atlas_coords();
|
Vector2i coords = E.get_atlas_coords();
|
||||||
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
|
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
|
||||||
|
|
||||||
|
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
|
||||||
|
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
|
||||||
|
|
||||||
|
Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
|
||||||
|
for (int j = 0; j < polygon.size(); j++) {
|
||||||
|
polygon.write[j] += position;
|
||||||
|
}
|
||||||
|
if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) {
|
||||||
|
// Draw terrain.
|
||||||
|
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), terrain);
|
||||||
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain());
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
|
||||||
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
|
polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
|
||||||
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
|
|
||||||
|
|
||||||
Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
|
|
||||||
for (int j = 0; j < polygon.size(); j++) {
|
for (int j = 0; j < polygon.size(); j++) {
|
||||||
polygon.write[j] += position;
|
polygon.write[j] += position;
|
||||||
}
|
}
|
||||||
if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) {
|
if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) {
|
||||||
// Draw bit.
|
// Draw bit.
|
||||||
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), terrain);
|
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), terrain);
|
||||||
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_peering_bit_terrain(bit));
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_peering_bit(bit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2267,10 +2319,11 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
|
|||||||
if (!drag_modified.has(cell)) {
|
if (!drag_modified.has(cell)) {
|
||||||
Dictionary dict;
|
Dictionary dict;
|
||||||
dict["terrain_set"] = tile_data->get_terrain_set();
|
dict["terrain_set"] = tile_data->get_terrain_set();
|
||||||
|
dict["terrain"] = tile_data->get_terrain();
|
||||||
Array array;
|
Array array;
|
||||||
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
||||||
array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
|
array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
|
||||||
}
|
}
|
||||||
dict["terrain_peering_bits"] = array;
|
dict["terrain_peering_bits"] = array;
|
||||||
drag_modified[cell] = dict;
|
drag_modified[cell] = dict;
|
||||||
@ -2300,10 +2353,11 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
|
|||||||
if (!drag_modified.has(cell)) {
|
if (!drag_modified.has(cell)) {
|
||||||
Dictionary dict;
|
Dictionary dict;
|
||||||
dict["terrain_set"] = tile_data->get_terrain_set();
|
dict["terrain_set"] = tile_data->get_terrain_set();
|
||||||
|
dict["terrain"] = tile_data->get_terrain();
|
||||||
Array array;
|
Array array;
|
||||||
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
||||||
array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
|
array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
|
||||||
}
|
}
|
||||||
dict["terrain_peering_bits"] = array;
|
dict["terrain_peering_bits"] = array;
|
||||||
drag_modified[cell] = dict;
|
drag_modified[cell] = dict;
|
||||||
@ -2312,12 +2366,18 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
|
|||||||
// Set the terrains bits.
|
// Set the terrains bits.
|
||||||
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
|
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
|
||||||
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
|
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
|
||||||
|
|
||||||
|
Vector<Vector2> polygon = tile_set->get_terrain_polygon(tile_data->get_terrain_set());
|
||||||
|
if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
|
||||||
|
tile_data->set_terrain(terrain);
|
||||||
|
}
|
||||||
|
|
||||||
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
||||||
if (tile_data->is_valid_peering_bit_terrain(bit)) {
|
if (tile_data->is_valid_terrain_peering_bit(bit)) {
|
||||||
Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(tile_data->get_terrain_set(), bit);
|
polygon = tile_set->get_terrain_peering_bit_polygon(tile_data->get_terrain_set(), bit);
|
||||||
if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
|
if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
|
||||||
tile_data->set_peering_bit_terrain(bit, terrain);
|
tile_data->set_terrain_peering_bit(bit, terrain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2343,12 +2403,18 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
|
|||||||
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
|
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
|
||||||
dummy_object->set("terrain_set", terrain_set);
|
dummy_object->set("terrain_set", terrain_set);
|
||||||
dummy_object->set("terrain", -1);
|
dummy_object->set("terrain", -1);
|
||||||
|
|
||||||
|
Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
|
||||||
|
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
|
||||||
|
dummy_object->set("terrain", tile_data->get_terrain());
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
|
||||||
Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
|
polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
|
||||||
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
|
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
|
||||||
dummy_object->set("terrain", tile_data->get_peering_bit_terrain(bit));
|
dummy_object->set("terrain", tile_data->get_terrain_peering_bit(bit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2380,7 +2446,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
|
|||||||
Array array;
|
Array array;
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
|
array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
|
||||||
}
|
}
|
||||||
dict["terrain_peering_bits"] = array;
|
dict["terrain_peering_bits"] = array;
|
||||||
drag_modified[cell] = dict;
|
drag_modified[cell] = dict;
|
||||||
@ -2405,10 +2471,11 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
|
|||||||
// Save the old terrain_set and terrains bits.
|
// Save the old terrain_set and terrains bits.
|
||||||
Dictionary dict;
|
Dictionary dict;
|
||||||
dict["terrain_set"] = tile_data->get_terrain_set();
|
dict["terrain_set"] = tile_data->get_terrain_set();
|
||||||
|
dict["terrain"] = tile_data->get_terrain();
|
||||||
Array array;
|
Array array;
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
|
array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
|
||||||
}
|
}
|
||||||
dict["terrain_peering_bits"] = array;
|
dict["terrain_peering_bits"] = array;
|
||||||
drag_modified[cell] = dict;
|
drag_modified[cell] = dict;
|
||||||
@ -2416,12 +2483,17 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
|
|||||||
// Set the terrain bit.
|
// Set the terrain bit.
|
||||||
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
|
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
|
||||||
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
|
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
|
||||||
|
|
||||||
|
Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
|
||||||
|
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
|
||||||
|
tile_data->set_terrain(terrain);
|
||||||
|
}
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
|
||||||
Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
|
polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
|
||||||
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
|
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
|
||||||
tile_data->set_peering_bit_terrain(bit, terrain);
|
tile_data->set_terrain_peering_bit(bit, terrain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2437,6 +2509,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
|
|||||||
Vector2i coords = E.key.get_atlas_coords();
|
Vector2i coords = E.key.get_atlas_coords();
|
||||||
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
|
||||||
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
|
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
|
||||||
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
|
||||||
Array array = dict["terrain_peering_bits"];
|
Array array = dict["terrain_peering_bits"];
|
||||||
for (int i = 0; i < array.size(); i++) {
|
for (int i = 0; i < array.size(); i++) {
|
||||||
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
|
||||||
@ -2452,13 +2525,15 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
|
|||||||
for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
|
for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
|
||||||
Dictionary dict = E.value;
|
Dictionary dict = E.value;
|
||||||
Vector2i coords = E.key.get_atlas_coords();
|
Vector2i coords = E.key.get_atlas_coords();
|
||||||
|
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), terrain);
|
||||||
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
|
||||||
Array array = dict["terrain_peering_bits"];
|
Array array = dict["terrain_peering_bits"];
|
||||||
for (int i = 0; i < array.size(); i++) {
|
for (int i = 0; i < array.size(); i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
|
||||||
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain);
|
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain);
|
||||||
}
|
}
|
||||||
if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set"], bit)) {
|
||||||
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
|
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2321,7 +2321,7 @@ Vector<TileMapEditorPlugin::TabData> TileMapEditorTerrainsPlugin::get_tabs() con
|
|||||||
return tabs;
|
return tabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const HashMap<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const {
|
HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrain_path_or_connect(const Vector<Vector2i> &p_to_paint, int p_terrain_set, int p_terrain, bool p_connect) const {
|
||||||
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
|
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
|
||||||
if (!tile_map) {
|
if (!tile_map) {
|
||||||
return HashMap<Vector2i, TileMapCell>();
|
return HashMap<Vector2i, TileMapCell>();
|
||||||
@ -2332,105 +2332,87 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const
|
|||||||
return HashMap<Vector2i, TileMapCell>();
|
return HashMap<Vector2i, TileMapCell>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output;
|
||||||
|
if (p_connect) {
|
||||||
|
terrain_fill_output = tile_map->terrain_fill_connect(tile_map_layer, p_to_paint, p_terrain_set, p_terrain, false);
|
||||||
|
} else {
|
||||||
|
terrain_fill_output = tile_map->terrain_fill_path(tile_map_layer, p_to_paint, p_terrain_set, p_terrain, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the painted path a set for faster lookups
|
||||||
|
HashSet<Vector2i> painted_set;
|
||||||
|
for (Vector2i coords : p_to_paint) {
|
||||||
|
painted_set.insert(coords);
|
||||||
|
}
|
||||||
|
|
||||||
HashMap<Vector2i, TileMapCell> output;
|
HashMap<Vector2i, TileMapCell> output;
|
||||||
|
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) {
|
||||||
// Add the constraints from the added tiles.
|
if (painted_set.has(E.key)) {
|
||||||
RBSet<TileMap::TerrainConstraint> added_tiles_constraints_set;
|
// Paint a random tile with the correct terrain for the painted path.
|
||||||
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
|
output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
|
||||||
Vector2i coords = E_to_paint.key;
|
} else {
|
||||||
TileSet::TerrainsPattern terrains_pattern = E_to_paint.value;
|
// Avoids updating the painted path from the output if the new pattern is the same as before.
|
||||||
|
bool keep_old = false;
|
||||||
RBSet<TileMap::TerrainConstraint> cell_constraints = tile_map->get_terrain_constraints_from_added_tile(coords, p_terrain_set, terrains_pattern);
|
TileMapCell cell = tile_map->get_cell(tile_map_layer, E.key);
|
||||||
for (const TileMap::TerrainConstraint &E : cell_constraints) {
|
if (cell.source_id != TileSet::INVALID_SOURCE) {
|
||||||
added_tiles_constraints_set.insert(E);
|
TileSetSource *source = *tile_set->get_source(cell.source_id);
|
||||||
}
|
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
|
||||||
}
|
if (atlas_source) {
|
||||||
|
// Get tile data.
|
||||||
// Build the list of potential tiles to replace.
|
TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
|
||||||
RBSet<Vector2i> potential_to_replace;
|
if (tile_data && tile_data->get_terrains_pattern() == E.value) {
|
||||||
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
|
keep_old = true;
|
||||||
Vector2i coords = E_to_paint.key;
|
}
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
}
|
||||||
if (tile_map->is_existing_neighbor(TileSet::CellNeighbor(i))) {
|
}
|
||||||
Vector2i neighbor = tile_map->get_neighbor_cell(coords, TileSet::CellNeighbor(i));
|
if (!keep_old) {
|
||||||
if (!p_to_paint.has(neighbor)) {
|
output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
|
||||||
potential_to_replace.insert(neighbor);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set of tiles to replace
|
HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrain_pattern(const Vector<Vector2i> &p_to_paint, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
|
||||||
RBSet<Vector2i> to_replace;
|
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
|
||||||
|
if (!tile_map) {
|
||||||
// Add the central tiles to the one to replace.
|
return HashMap<Vector2i, TileMapCell>();
|
||||||
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
|
}
|
||||||
to_replace.insert(E_to_paint.key);
|
|
||||||
}
|
Ref<TileSet> tile_set = tile_map->get_tileset();
|
||||||
|
if (!tile_set.is_valid()) {
|
||||||
// Add the constraints from the surroundings of the modified areas.
|
return HashMap<Vector2i, TileMapCell>();
|
||||||
RBSet<TileMap::TerrainConstraint> removed_cells_constraints_set;
|
}
|
||||||
bool to_replace_modified = true;
|
|
||||||
while (to_replace_modified) {
|
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = tile_map->terrain_fill_pattern(tile_map_layer, p_to_paint, p_terrain_set, p_terrains_pattern, false);
|
||||||
// Get the constraints from the removed cells.
|
|
||||||
removed_cells_constraints_set = tile_map->get_terrain_constraints_from_removed_cells_list(tile_map_layer, to_replace, p_terrain_set, false);
|
// Make the painted path a set for faster lookups
|
||||||
|
HashSet<Vector2i> painted_set;
|
||||||
// Filter the sources to make sure they are in the potential_to_replace.
|
for (Vector2i coords : p_to_paint) {
|
||||||
RBMap<TileMap::TerrainConstraint, RBSet<Vector2i>> per_constraint_tiles;
|
painted_set.insert(coords);
|
||||||
for (const TileMap::TerrainConstraint &E : removed_cells_constraints_set) {
|
}
|
||||||
HashMap<Vector2i, TileSet::CellNeighbor> sources_of_constraint = E.get_overlapping_coords_and_peering_bits();
|
|
||||||
for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_source_tile_of_constraint : sources_of_constraint) {
|
HashMap<Vector2i, TileMapCell> output;
|
||||||
if (potential_to_replace.has(E_source_tile_of_constraint.key)) {
|
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) {
|
||||||
per_constraint_tiles[E].insert(E_source_tile_of_constraint.key);
|
if (painted_set.has(E.key)) {
|
||||||
}
|
// Paint a random tile with the correct terrain for the painted path.
|
||||||
}
|
output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
|
||||||
}
|
} else {
|
||||||
|
// Avoids updating the painted path from the output if the new pattern is the same as before.
|
||||||
to_replace_modified = false;
|
TileMapCell cell = tile_map->get_cell(tile_map_layer, E.key);
|
||||||
for (const TileMap::TerrainConstraint &E : added_tiles_constraints_set) {
|
if (cell.source_id != TileSet::INVALID_SOURCE) {
|
||||||
TileMap::TerrainConstraint c = E;
|
TileSetSource *source = *tile_set->get_source(cell.source_id);
|
||||||
// Check if we have a conflict in constraints.
|
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
|
||||||
if (removed_cells_constraints_set.has(c) && removed_cells_constraints_set.find(c)->get().get_terrain() != c.get_terrain()) {
|
if (atlas_source) {
|
||||||
// If we do, we search for a neighbor to remove.
|
// Get tile data.
|
||||||
if (per_constraint_tiles.has(c) && !per_constraint_tiles[c].is_empty()) {
|
TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
|
||||||
// Remove it.
|
if (tile_data && !(tile_data->get_terrains_pattern() == E.value)) {
|
||||||
Vector2i to_add_to_remove = per_constraint_tiles[c].front()->get();
|
output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
|
||||||
potential_to_replace.erase(to_add_to_remove);
|
|
||||||
to_replace.insert(to_add_to_remove);
|
|
||||||
to_replace_modified = true;
|
|
||||||
for (KeyValue<TileMap::TerrainConstraint, RBSet<Vector2i>> &E_source_tiles_of_constraint : per_constraint_tiles) {
|
|
||||||
E_source_tiles_of_constraint.value.erase(to_add_to_remove);
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine all constraints together.
|
|
||||||
RBSet<TileMap::TerrainConstraint> constraints = removed_cells_constraints_set;
|
|
||||||
for (const TileMap::TerrainConstraint &E : added_tiles_constraints_set) {
|
|
||||||
constraints.insert(E);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the central tiles from the ones to replace.
|
|
||||||
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
|
|
||||||
to_replace.erase(E_to_paint.key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run WFC to fill the holes with the constraints.
|
|
||||||
HashMap<Vector2i, TileSet::TerrainsPattern> wfc_output = tile_map->terrain_wave_function_collapse(to_replace, p_terrain_set, constraints);
|
|
||||||
|
|
||||||
// Actually paint the tiles.
|
|
||||||
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
|
|
||||||
output[E_to_paint.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E_to_paint.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the WFC run for the output.
|
|
||||||
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : wfc_output) {
|
|
||||||
output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2445,19 +2427,21 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_line(Vector2i
|
|||||||
return HashMap<Vector2i, TileMapCell>();
|
return HashMap<Vector2i, TileMapCell>();
|
||||||
}
|
}
|
||||||
|
|
||||||
TileSet::TerrainsPattern terrains_pattern;
|
if (selected_type == SELECTED_TYPE_CONNECT) {
|
||||||
if (p_erase) {
|
return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, true);
|
||||||
terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
|
} else if (selected_type == SELECTED_TYPE_PATH) {
|
||||||
} else {
|
return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, false);
|
||||||
terrains_pattern = selected_terrains_pattern;
|
} else { // SELECTED_TYPE_PATTERN
|
||||||
}
|
TileSet::TerrainsPattern terrains_pattern;
|
||||||
|
if (p_erase) {
|
||||||
|
terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
|
||||||
|
} else {
|
||||||
|
terrains_pattern = selected_terrains_pattern;
|
||||||
|
}
|
||||||
|
|
||||||
Vector<Vector2i> line = TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell);
|
Vector<Vector2i> line = TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell);
|
||||||
HashMap<Vector2i, TileSet::TerrainsPattern> to_draw;
|
return _draw_terrain_pattern(line, selected_terrain_set, terrains_pattern);
|
||||||
for (int i = 0; i < line.size(); i++) {
|
|
||||||
to_draw[line[i]] = terrains_pattern;
|
|
||||||
}
|
}
|
||||||
return _draw_terrains(to_draw, selected_terrain_set);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) {
|
HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) {
|
||||||
@ -2471,25 +2455,29 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i
|
|||||||
return HashMap<Vector2i, TileMapCell>();
|
return HashMap<Vector2i, TileMapCell>();
|
||||||
}
|
}
|
||||||
|
|
||||||
TileSet::TerrainsPattern terrains_pattern;
|
|
||||||
if (p_erase) {
|
|
||||||
terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
|
|
||||||
} else {
|
|
||||||
terrains_pattern = selected_terrains_pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
Rect2i rect;
|
Rect2i rect;
|
||||||
rect.set_position(p_start_cell);
|
rect.set_position(p_start_cell);
|
||||||
rect.set_end(p_end_cell);
|
rect.set_end(p_end_cell);
|
||||||
rect = rect.abs();
|
rect = rect.abs();
|
||||||
|
|
||||||
HashMap<Vector2i, TileSet::TerrainsPattern> to_draw;
|
Vector<Vector2i> to_draw;
|
||||||
for (int x = rect.position.x; x <= rect.get_end().x; x++) {
|
for (int x = rect.position.x; x <= rect.get_end().x; x++) {
|
||||||
for (int y = rect.position.y; y <= rect.get_end().y; y++) {
|
for (int y = rect.position.y; y <= rect.get_end().y; y++) {
|
||||||
to_draw[Vector2i(x, y)] = terrains_pattern;
|
to_draw.append(Vector2i(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _draw_terrains(to_draw, selected_terrain_set);
|
|
||||||
|
if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) {
|
||||||
|
return _draw_terrain_path_or_connect(to_draw, selected_terrain_set, selected_terrain, true);
|
||||||
|
} else { // SELECTED_TYPE_PATTERN
|
||||||
|
TileSet::TerrainsPattern terrains_pattern;
|
||||||
|
if (p_erase) {
|
||||||
|
terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
|
||||||
|
} else {
|
||||||
|
terrains_pattern = selected_terrains_pattern;
|
||||||
|
}
|
||||||
|
return _draw_terrain_pattern(to_draw, selected_terrain_set, terrains_pattern);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RBSet<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous) {
|
RBSet<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous) {
|
||||||
@ -2614,20 +2602,23 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_bucket_fill(Ve
|
|||||||
return HashMap<Vector2i, TileMapCell>();
|
return HashMap<Vector2i, TileMapCell>();
|
||||||
}
|
}
|
||||||
|
|
||||||
TileSet::TerrainsPattern terrains_pattern;
|
|
||||||
if (p_erase) {
|
|
||||||
terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
|
|
||||||
} else {
|
|
||||||
terrains_pattern = selected_terrains_pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
RBSet<Vector2i> cells_to_draw = _get_cells_for_bucket_fill(p_coords, p_contiguous);
|
RBSet<Vector2i> cells_to_draw = _get_cells_for_bucket_fill(p_coords, p_contiguous);
|
||||||
HashMap<Vector2i, TileSet::TerrainsPattern> to_draw;
|
Vector<Vector2i> cells_to_draw_as_vector;
|
||||||
for (const Vector2i &coords : cells_to_draw) {
|
for (Vector2i cell : cells_to_draw) {
|
||||||
to_draw[coords] = terrains_pattern;
|
cells_to_draw_as_vector.append(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _draw_terrains(to_draw, selected_terrain_set);
|
if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) {
|
||||||
|
return _draw_terrain_path_or_connect(cells_to_draw_as_vector, selected_terrain_set, selected_terrain, true);
|
||||||
|
} else { // SELECTED_TYPE_PATTERN
|
||||||
|
TileSet::TerrainsPattern terrains_pattern;
|
||||||
|
if (p_erase) {
|
||||||
|
terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
|
||||||
|
} else {
|
||||||
|
terrains_pattern = selected_terrains_pattern;
|
||||||
|
}
|
||||||
|
return _draw_terrain_pattern(cells_to_draw_as_vector, selected_terrain_set, terrains_pattern);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileMapEditorTerrainsPlugin::_stop_dragging() {
|
void TileMapEditorTerrainsPlugin::_stop_dragging() {
|
||||||
@ -2696,11 +2687,13 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
|
|||||||
if (tree_item) {
|
if (tree_item) {
|
||||||
for (int i = 0; i < terrains_tile_list->get_item_count(); i++) {
|
for (int i = 0; i < terrains_tile_list->get_item_count(); i++) {
|
||||||
Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i);
|
Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i);
|
||||||
TileSet::TerrainsPattern in_meta_terrains_pattern(*tile_set, new_terrain_set);
|
if (int(metadata_dict["type"]) == SELECTED_TYPE_PATTERN) {
|
||||||
in_meta_terrains_pattern.set_terrains_from_array(metadata_dict["terrains_pattern"]);
|
TileSet::TerrainsPattern in_meta_terrains_pattern(*tile_set, new_terrain_set);
|
||||||
if (in_meta_terrains_pattern == terrains_pattern) {
|
in_meta_terrains_pattern.from_array(metadata_dict["terrains_pattern"]);
|
||||||
terrains_tile_list->select(i);
|
if (in_meta_terrains_pattern == terrains_pattern) {
|
||||||
break;
|
terrains_tile_list->select(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -2773,22 +2766,33 @@ void TileMapEditorTerrainsPlugin::_update_selection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the selected terrain.
|
// Get the selected terrain.
|
||||||
selected_terrains_pattern = TileSet::TerrainsPattern();
|
|
||||||
selected_terrain_set = -1;
|
selected_terrain_set = -1;
|
||||||
|
selected_terrains_pattern = TileSet::TerrainsPattern();
|
||||||
|
|
||||||
TreeItem *selected_tree_item = terrains_tree->get_selected();
|
TreeItem *selected_tree_item = terrains_tree->get_selected();
|
||||||
if (selected_tree_item && selected_tree_item->get_metadata(0)) {
|
if (selected_tree_item && selected_tree_item->get_metadata(0)) {
|
||||||
Dictionary metadata_dict = selected_tree_item->get_metadata(0);
|
Dictionary metadata_dict = selected_tree_item->get_metadata(0);
|
||||||
// Selected terrain
|
// Selected terrain
|
||||||
selected_terrain_set = metadata_dict["terrain_set"];
|
selected_terrain_set = metadata_dict["terrain_set"];
|
||||||
|
selected_terrain = metadata_dict["terrain_id"];
|
||||||
|
|
||||||
// Selected tile
|
// Selected mode/terrain pattern
|
||||||
if (erase_button->is_pressed()) {
|
if (erase_button->is_pressed()) {
|
||||||
|
selected_type = SELECTED_TYPE_PATTERN;
|
||||||
selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
|
selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
|
||||||
} else if (terrains_tile_list->is_anything_selected()) {
|
} else if (terrains_tile_list->is_anything_selected()) {
|
||||||
metadata_dict = terrains_tile_list->get_item_metadata(terrains_tile_list->get_selected_items()[0]);
|
metadata_dict = terrains_tile_list->get_item_metadata(terrains_tile_list->get_selected_items()[0]);
|
||||||
selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
|
if (int(metadata_dict["type"]) == SELECTED_TYPE_CONNECT) {
|
||||||
selected_terrains_pattern.set_terrains_from_array(metadata_dict["terrains_pattern"]);
|
selected_type = SELECTED_TYPE_CONNECT;
|
||||||
|
} else if (int(metadata_dict["type"]) == SELECTED_TYPE_PATH) {
|
||||||
|
selected_type = SELECTED_TYPE_PATH;
|
||||||
|
} else if (int(metadata_dict["type"]) == SELECTED_TYPE_PATTERN) {
|
||||||
|
selected_type = SELECTED_TYPE_PATTERN;
|
||||||
|
selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
|
||||||
|
selected_terrains_pattern.from_array(metadata_dict["terrains_pattern"]);
|
||||||
|
} else {
|
||||||
|
ERR_FAIL();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2865,7 +2869,7 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
|
|||||||
} else {
|
} else {
|
||||||
// Paint otherwise.
|
// Paint otherwise.
|
||||||
if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
|
if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
|
||||||
if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
|
if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2880,21 +2884,21 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
|
|||||||
tile_map->set_cell(tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
|
tile_map->set_cell(tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
|
||||||
}
|
}
|
||||||
} else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CTRL))) {
|
} else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CTRL))) {
|
||||||
if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
|
if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
drag_type = DRAG_TYPE_LINE;
|
drag_type = DRAG_TYPE_LINE;
|
||||||
drag_start_mouse_pos = mpos;
|
drag_start_mouse_pos = mpos;
|
||||||
drag_modified.clear();
|
drag_modified.clear();
|
||||||
} else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && Input::get_singleton()->is_key_pressed(Key::CTRL))) {
|
} else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && Input::get_singleton()->is_key_pressed(Key::CTRL))) {
|
||||||
if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
|
if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
drag_type = DRAG_TYPE_RECT;
|
drag_type = DRAG_TYPE_RECT;
|
||||||
drag_start_mouse_pos = mpos;
|
drag_start_mouse_pos = mpos;
|
||||||
drag_modified.clear();
|
drag_modified.clear();
|
||||||
} else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
|
} else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
|
||||||
if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
|
if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
drag_type = DRAG_TYPE_BUCKET;
|
drag_type = DRAG_TYPE_BUCKET;
|
||||||
@ -3105,11 +3109,18 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
|
|||||||
cell.alternative_tile = alternative_id;
|
cell.alternative_tile = alternative_id;
|
||||||
|
|
||||||
TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
|
TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
|
||||||
|
|
||||||
|
// Terrain center bit
|
||||||
|
int terrain = terrains_pattern.get_terrain();
|
||||||
|
if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
|
||||||
|
per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
|
||||||
|
}
|
||||||
|
|
||||||
// Terrain bits.
|
// Terrain bits.
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
|
||||||
int terrain = terrains_pattern.get_terrain(bit);
|
terrain = terrains_pattern.get_terrain_peering_bit(bit);
|
||||||
if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
|
if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
|
||||||
per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
|
per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
|
||||||
}
|
}
|
||||||
@ -3191,6 +3202,19 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
|
|||||||
ERR_FAIL_INDEX(selected_terrain_set, tile_set->get_terrain_sets_count());
|
ERR_FAIL_INDEX(selected_terrain_set, tile_set->get_terrain_sets_count());
|
||||||
ERR_FAIL_INDEX(selected_terrain_id, tile_set->get_terrains_count(selected_terrain_set));
|
ERR_FAIL_INDEX(selected_terrain_id, tile_set->get_terrains_count(selected_terrain_set));
|
||||||
|
|
||||||
|
// Add the two first generic modes
|
||||||
|
int item_index = terrains_tile_list->add_icon_item(main_vbox_container->get_theme_icon(SNAME("TerrainConnect"), SNAME("EditorIcons")));
|
||||||
|
terrains_tile_list->set_item_tooltip(item_index, TTR("Connect mode: paints a terrain, then connects it with the surrounding tiles with the same terrain."));
|
||||||
|
Dictionary list_metadata_dict;
|
||||||
|
list_metadata_dict["type"] = SELECTED_TYPE_CONNECT;
|
||||||
|
terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
|
||||||
|
|
||||||
|
item_index = terrains_tile_list->add_icon_item(main_vbox_container->get_theme_icon(SNAME("TerrainPath"), SNAME("EditorIcons")));
|
||||||
|
terrains_tile_list->set_item_tooltip(item_index, TTR("Path mode: paints a terrain, thens connects it to the previous tile painted withing the same stroke."));
|
||||||
|
list_metadata_dict = Dictionary();
|
||||||
|
list_metadata_dict["type"] = SELECTED_TYPE_PATH;
|
||||||
|
terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
|
||||||
|
|
||||||
// Sort the items in a map by the number of corresponding terrains.
|
// Sort the items in a map by the number of corresponding terrains.
|
||||||
RBMap<int, RBSet<TileSet::TerrainsPattern>> sorted;
|
RBMap<int, RBSet<TileSet::TerrainsPattern>> sorted;
|
||||||
|
|
||||||
@ -3200,7 +3224,7 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
|
|||||||
|
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(selected_terrain_set, bit) && E.get_terrain(bit) == selected_terrain_id) {
|
if (tile_set->is_valid_terrain_peering_bit(selected_terrain_set, bit) && E.get_terrain_peering_bit(bit) == selected_terrain_id) {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3241,12 +3265,13 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the ItemList's item.
|
// Create the ItemList's item.
|
||||||
int item_index = terrains_tile_list->add_item("");
|
item_index = terrains_tile_list->add_item("");
|
||||||
terrains_tile_list->set_item_icon(item_index, icon);
|
terrains_tile_list->set_item_icon(item_index, icon);
|
||||||
terrains_tile_list->set_item_icon_region(item_index, region);
|
terrains_tile_list->set_item_icon_region(item_index, region);
|
||||||
terrains_tile_list->set_item_icon_transposed(item_index, transpose);
|
terrains_tile_list->set_item_icon_transposed(item_index, transpose);
|
||||||
Dictionary list_metadata_dict;
|
list_metadata_dict = Dictionary();
|
||||||
list_metadata_dict["terrains_pattern"] = terrains_pattern.get_terrains_as_array();
|
list_metadata_dict["type"] = SELECTED_TYPE_PATTERN;
|
||||||
|
list_metadata_dict["terrains_pattern"] = terrains_pattern.as_array();
|
||||||
terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
|
terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3264,6 +3289,8 @@ void TileMapEditorTerrainsPlugin::_update_theme() {
|
|||||||
|
|
||||||
picker_button->set_icon(main_vbox_container->get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
|
picker_button->set_icon(main_vbox_container->get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
|
||||||
erase_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons")));
|
erase_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons")));
|
||||||
|
|
||||||
|
_update_tiles_list();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) {
|
void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) {
|
||||||
@ -3303,7 +3330,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
|
|||||||
terrains_tile_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
terrains_tile_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||||
terrains_tile_list->set_max_columns(0);
|
terrains_tile_list->set_max_columns(0);
|
||||||
terrains_tile_list->set_same_column_width(true);
|
terrains_tile_list->set_same_column_width(true);
|
||||||
terrains_tile_list->set_fixed_icon_size(Size2(30, 30) * EDSCALE);
|
terrains_tile_list->set_fixed_icon_size(Size2(32, 32) * EDSCALE);
|
||||||
terrains_tile_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
|
terrains_tile_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
|
||||||
tilemap_tab_terrains->add_child(terrains_tile_list);
|
tilemap_tab_terrains->add_child(terrains_tile_list);
|
||||||
|
|
||||||
|
@ -268,14 +268,22 @@ private:
|
|||||||
HashMap<Vector2i, TileMapCell> drag_modified;
|
HashMap<Vector2i, TileMapCell> drag_modified;
|
||||||
|
|
||||||
// Painting
|
// Painting
|
||||||
HashMap<Vector2i, TileMapCell> _draw_terrains(const HashMap<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const;
|
HashMap<Vector2i, TileMapCell> _draw_terrain_path_or_connect(const Vector<Vector2i> &p_to_paint, int p_terrain_set, int p_terrain, bool p_connect) const;
|
||||||
|
HashMap<Vector2i, TileMapCell> _draw_terrain_pattern(const Vector<Vector2i> &p_to_paint, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const;
|
||||||
HashMap<Vector2i, TileMapCell> _draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
|
HashMap<Vector2i, TileMapCell> _draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
|
||||||
HashMap<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
|
HashMap<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
|
||||||
RBSet<Vector2i> _get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous);
|
RBSet<Vector2i> _get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous);
|
||||||
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
|
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
|
||||||
void _stop_dragging();
|
void _stop_dragging();
|
||||||
|
|
||||||
|
enum SelectedType {
|
||||||
|
SELECTED_TYPE_CONNECT = 0,
|
||||||
|
SELECTED_TYPE_PATH,
|
||||||
|
SELECTED_TYPE_PATTERN,
|
||||||
|
};
|
||||||
|
SelectedType selected_type;
|
||||||
int selected_terrain_set = -1;
|
int selected_terrain_set = -1;
|
||||||
|
int selected_terrain = -1;
|
||||||
TileSet::TerrainsPattern selected_terrains_pattern;
|
TileSet::TerrainsPattern selected_terrains_pattern;
|
||||||
void _update_selection();
|
void _update_selection();
|
||||||
|
|
||||||
|
@ -2071,9 +2071,10 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
|
|||||||
}
|
}
|
||||||
} else if (p_property == "terrain_set") {
|
} else if (p_property == "terrain_set") {
|
||||||
int current_terrain_set = tile_data_proxy->get("terrain_set");
|
int current_terrain_set = tile_data_proxy->get("terrain_set");
|
||||||
|
ADD_UNDO(tile_data_proxy, "terrain");
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(current_terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(current_terrain_set, bit)) {
|
||||||
ADD_UNDO(tile_data_proxy, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
|
ADD_UNDO(tile_data_proxy, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -505,7 +505,7 @@ void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_
|
|||||||
for (int terrain_set_index = begin; terrain_set_index < end; terrain_set_index++) {
|
for (int terrain_set_index = begin; terrain_set_index < end; terrain_set_index++) {
|
||||||
for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
|
for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
|
||||||
if (tile_data->is_valid_peering_bit_terrain(bit)) {
|
if (tile_data->is_valid_terrain_peering_bit(bit)) {
|
||||||
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
|
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -513,7 +513,7 @@ void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_
|
|||||||
} else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
|
} else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
|
||||||
for (int terrain_index = 0; terrain_index < TileSet::CELL_NEIGHBOR_MAX; terrain_index++) {
|
for (int terrain_index = 0; terrain_index < TileSet::CELL_NEIGHBOR_MAX; terrain_index++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(terrain_index);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(terrain_index);
|
||||||
if (tile_data->is_valid_peering_bit_terrain(bit)) {
|
if (tile_data->is_valid_terrain_peering_bit(bit)) {
|
||||||
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[terrain_index]));
|
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[terrain_index]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -607,9 +607,10 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p
|
|||||||
|
|
||||||
if (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "mode") {
|
if (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "mode") {
|
||||||
ADD_UNDO(tile_data, "terrain_set");
|
ADD_UNDO(tile_data, "terrain_set");
|
||||||
|
ADD_UNDO(tile_data, "terrain");
|
||||||
for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
|
for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
|
||||||
if (tile_data->is_valid_peering_bit_terrain(bit)) {
|
if (tile_data->is_valid_terrain_peering_bit(bit)) {
|
||||||
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
|
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,27 +36,30 @@
|
|||||||
|
|
||||||
HashMap<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlapping_coords_and_peering_bits() const {
|
HashMap<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlapping_coords_and_peering_bits() const {
|
||||||
HashMap<Vector2i, TileSet::CellNeighbor> output;
|
HashMap<Vector2i, TileSet::CellNeighbor> output;
|
||||||
|
|
||||||
|
ERR_FAIL_COND_V(is_center_bit(), output);
|
||||||
|
|
||||||
Ref<TileSet> tile_set = tile_map->get_tileset();
|
Ref<TileSet> tile_set = tile_map->get_tileset();
|
||||||
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
|
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
|
||||||
|
|
||||||
TileSet::TileShape shape = tile_set->get_tile_shape();
|
TileSet::TileShape shape = tile_set->get_tile_shape();
|
||||||
if (shape == TileSet::TILE_SHAPE_SQUARE) {
|
if (shape == TileSet::TILE_SHAPE_SQUARE) {
|
||||||
switch (bit) {
|
switch (bit) {
|
||||||
case 0:
|
case 1:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 2:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 3:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 4:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
|
||||||
@ -67,23 +70,23 @@ HashMap<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlap
|
|||||||
}
|
}
|
||||||
} else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
|
} else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
|
||||||
switch (bit) {
|
switch (bit) {
|
||||||
case 0:
|
case 1:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 2:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 3:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 4:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
|
||||||
break;
|
break;
|
||||||
@ -95,25 +98,25 @@ HashMap<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlap
|
|||||||
TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
|
TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
|
||||||
if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
|
if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
|
||||||
switch (bit) {
|
switch (bit) {
|
||||||
case 0:
|
case 1:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 2:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 3:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 4:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 5:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
|
||||||
break;
|
break;
|
||||||
@ -122,25 +125,25 @@ HashMap<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlap
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (bit) {
|
switch (bit) {
|
||||||
case 0:
|
case 1:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 2:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 3:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 4:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 5:
|
||||||
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
|
output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
|
||||||
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
|
output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
|
||||||
break;
|
break;
|
||||||
@ -152,6 +155,17 @@ HashMap<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlap
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain) {
|
||||||
|
tile_map = p_tile_map;
|
||||||
|
|
||||||
|
Ref<TileSet> tile_set = tile_map->get_tileset();
|
||||||
|
ERR_FAIL_COND(!tile_set.is_valid());
|
||||||
|
|
||||||
|
bit = 0;
|
||||||
|
base_cell_coords = p_position;
|
||||||
|
terrain = p_terrain;
|
||||||
|
}
|
||||||
|
|
||||||
TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) {
|
TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) {
|
||||||
// The way we build the constraint make it easy to detect conflicting constraints.
|
// The way we build the constraint make it easy to detect conflicting constraints.
|
||||||
tile_map = p_tile_map;
|
tile_map = p_tile_map;
|
||||||
@ -163,35 +177,35 @@ TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const V
|
|||||||
if (shape == TileSet::TILE_SHAPE_SQUARE) {
|
if (shape == TileSet::TILE_SHAPE_SQUARE) {
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
|
case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
|
||||||
bit = 0;
|
bit = 1;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
|
||||||
bit = 1;
|
bit = 2;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
|
||||||
bit = 2;
|
bit = 3;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
|
||||||
bit = 1;
|
bit = 2;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
|
case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
|
||||||
bit = 0;
|
bit = 1;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
|
case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
|
||||||
bit = 1;
|
bit = 2;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_SIDE:
|
case TileSet::CELL_NEIGHBOR_TOP_SIDE:
|
||||||
bit = 2;
|
bit = 3;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
|
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
|
||||||
bit = 1;
|
bit = 2;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -201,35 +215,35 @@ TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const V
|
|||||||
} else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
|
} else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
|
case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
|
||||||
bit = 1;
|
bit = 2;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
|
||||||
bit = 0;
|
bit = 1;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
|
||||||
bit = 1;
|
bit = 2;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
|
||||||
bit = 2;
|
bit = 3;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
|
case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
|
||||||
bit = 1;
|
bit = 2;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
|
case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
|
||||||
bit = 0;
|
bit = 1;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_CORNER:
|
case TileSet::CELL_NEIGHBOR_TOP_CORNER:
|
||||||
bit = 1;
|
bit = 2;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_CORNER);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_CORNER);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
|
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
|
||||||
bit = 2;
|
bit = 3;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -242,51 +256,51 @@ TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const V
|
|||||||
if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
|
if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
|
case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
|
||||||
bit = 0;
|
bit = 1;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
|
||||||
bit = 1;
|
bit = 2;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
|
||||||
bit = 2;
|
bit = 3;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
|
||||||
bit = 3;
|
bit = 4;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
|
||||||
bit = 4;
|
bit = 5;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
|
||||||
bit = 1;
|
bit = 2;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
|
case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
|
||||||
bit = 0;
|
bit = 1;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
|
case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
|
||||||
bit = 3;
|
bit = 4;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
|
case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
|
||||||
bit = 2;
|
bit = 3;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_CORNER:
|
case TileSet::CELL_NEIGHBOR_TOP_CORNER:
|
||||||
bit = 1;
|
bit = 2;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
|
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
|
||||||
bit = 4;
|
bit = 5;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
|
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
|
||||||
bit = 3;
|
bit = 4;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -296,51 +310,51 @@ TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const V
|
|||||||
} else {
|
} else {
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
|
case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
|
||||||
bit = 0;
|
bit = 1;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
|
||||||
bit = 1;
|
bit = 2;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
|
||||||
bit = 2;
|
bit = 3;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
|
||||||
bit = 3;
|
bit = 4;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
|
||||||
bit = 0;
|
bit = 1;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
|
||||||
bit = 4;
|
bit = 5;
|
||||||
base_cell_coords = p_position;
|
base_cell_coords = p_position;
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
|
case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
|
||||||
bit = 2;
|
bit = 3;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
|
case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
|
||||||
bit = 1;
|
bit = 2;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
|
case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
|
||||||
bit = 0;
|
bit = 1;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_SIDE:
|
case TileSet::CELL_NEIGHBOR_TOP_SIDE:
|
||||||
bit = 3;
|
bit = 4;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
|
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
|
||||||
bit = 2;
|
bit = 3;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
|
||||||
break;
|
break;
|
||||||
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
|
case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
|
||||||
bit = 4;
|
bit = 5;
|
||||||
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
|
base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -2149,37 +2163,75 @@ void TileMap::set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RBSet<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints) {
|
TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints) {
|
||||||
if (!tile_set.is_valid()) {
|
if (!tile_set.is_valid()) {
|
||||||
return RBSet<TileSet::TerrainsPattern>();
|
return TileSet::TerrainsPattern();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns all tiles compatible with the given constraints.
|
// Returns all tiles compatible with the given constraints.
|
||||||
RBSet<TileSet::TerrainsPattern> compatible_terrain_tile_patterns;
|
RBMap<TileSet::TerrainsPattern, int> terrain_pattern_score;
|
||||||
for (TileSet::TerrainsPattern &terrain_pattern : tile_set->get_terrains_pattern_set(p_terrain_set)) {
|
RBSet<TileSet::TerrainsPattern> pattern_set = tile_set->get_terrains_pattern_set(p_terrain_set);
|
||||||
int valid = true;
|
ERR_FAIL_COND_V(pattern_set.is_empty(), TileSet::TerrainsPattern());
|
||||||
|
for (TileSet::TerrainsPattern &terrain_pattern : pattern_set) {
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
// Check the center bit constraint
|
||||||
|
TerrainConstraint terrain_constraint = TerrainConstraint(this, p_position, terrain_pattern.get_terrain());
|
||||||
|
RBSet<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_constraint);
|
||||||
|
if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_constraint.get_terrain()) {
|
||||||
|
score += in_set_constraint_element->get().get_priority();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the surrounding bits
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
|
||||||
// Check if the bit is compatible with the constraints.
|
// Check if the bit is compatible with the constraints.
|
||||||
TerrainConstraint terrain_bit_constraint = TerrainConstraint(this, p_position, bit, terrain_pattern.get_terrain(bit));
|
TerrainConstraint terrain_bit_constraint = TerrainConstraint(this, p_position, bit, terrain_pattern.get_terrain_peering_bit(bit));
|
||||||
RBSet<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
|
in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
|
||||||
if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) {
|
if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) {
|
||||||
valid = false;
|
score += in_set_constraint_element->get().get_priority();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid) {
|
terrain_pattern_score[terrain_pattern] = score;
|
||||||
compatible_terrain_tile_patterns.insert(terrain_pattern);
|
}
|
||||||
|
|
||||||
|
// Compute the minimum score
|
||||||
|
TileSet::TerrainsPattern min_score_pattern;
|
||||||
|
int min_score = INT32_MAX;
|
||||||
|
for (KeyValue<TileSet::TerrainsPattern, int> E : terrain_pattern_score) {
|
||||||
|
if (E.value < min_score) {
|
||||||
|
min_score_pattern = E.key;
|
||||||
|
min_score = E.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return compatible_terrain_tile_patterns;
|
return min_score_pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
RBSet<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_cells_list(int p_layer, const RBSet<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains) const {
|
RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_added_pattern(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
|
||||||
|
if (!tile_set.is_valid()) {
|
||||||
|
return RBSet<TerrainConstraint>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the constraints needed from the surrounding tiles.
|
||||||
|
RBSet<TerrainConstraint> output;
|
||||||
|
output.insert(TerrainConstraint(this, p_position, p_terrains_pattern.get_terrain()));
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
|
TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
|
||||||
|
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, side)) {
|
||||||
|
TerrainConstraint c = TerrainConstraint(this, p_position, side, p_terrains_pattern.get_terrain_peering_bit(side));
|
||||||
|
output.insert(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_cells_list(int p_layer, const RBSet<Vector2i> &p_cell_list, int p_terrain_set, bool p_ignore_empty_terrains) const {
|
||||||
if (!tile_set.is_valid()) {
|
if (!tile_set.is_valid()) {
|
||||||
return RBSet<TerrainConstraint>();
|
return RBSet<TerrainConstraint>();
|
||||||
}
|
}
|
||||||
@ -2187,12 +2239,12 @@ RBSet<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_
|
|||||||
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), RBSet<TerrainConstraint>());
|
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), RBSet<TerrainConstraint>());
|
||||||
ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), RBSet<TerrainConstraint>());
|
ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), RBSet<TerrainConstraint>());
|
||||||
|
|
||||||
// Build a set of dummy constraints get the constrained points.
|
// Build a set of dummy constraints to get the constrained points.
|
||||||
RBSet<TerrainConstraint> dummy_constraints;
|
RBSet<TerrainConstraint> dummy_constraints;
|
||||||
for (const Vector2i &E : p_to_replace) {
|
for (const Vector2i &E : p_cell_list) {
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over sides.
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over sides.
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
|
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
|
||||||
dummy_constraints.insert(TerrainConstraint(this, E, bit, -1));
|
dummy_constraints.insert(TerrainConstraint(this, E, bit, -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2200,35 +2252,31 @@ RBSet<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_
|
|||||||
|
|
||||||
// For each constrained point, we get all overlapping tiles, and select the most adequate terrain for it.
|
// For each constrained point, we get all overlapping tiles, and select the most adequate terrain for it.
|
||||||
RBSet<TerrainConstraint> constraints;
|
RBSet<TerrainConstraint> constraints;
|
||||||
for (const TerrainConstraint &E : dummy_constraints) {
|
for (const TerrainConstraint &E_constraint : dummy_constraints) {
|
||||||
TerrainConstraint c = E;
|
|
||||||
|
|
||||||
HashMap<int, int> terrain_count;
|
HashMap<int, int> terrain_count;
|
||||||
|
|
||||||
// Count the number of occurrences per terrain.
|
// Count the number of occurrences per terrain.
|
||||||
HashMap<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits();
|
HashMap<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = E_constraint.get_overlapping_coords_and_peering_bits();
|
||||||
for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) {
|
for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) {
|
||||||
if (!p_to_replace.has(E_overlapping.key)) {
|
TileData *neighbor_tile_data = nullptr;
|
||||||
TileData *neighbor_tile_data = nullptr;
|
TileMapCell neighbor_cell = get_cell(p_layer, E_overlapping.key);
|
||||||
TileMapCell neighbor_cell = get_cell(p_layer, E_overlapping.key);
|
if (neighbor_cell.source_id != TileSet::INVALID_SOURCE) {
|
||||||
if (neighbor_cell.source_id != TileSet::INVALID_SOURCE) {
|
Ref<TileSetSource> source = tile_set->get_source(neighbor_cell.source_id);
|
||||||
Ref<TileSetSource> source = tile_set->get_source(neighbor_cell.source_id);
|
Ref<TileSetAtlasSource> atlas_source = source;
|
||||||
Ref<TileSetAtlasSource> atlas_source = source;
|
if (atlas_source.is_valid()) {
|
||||||
if (atlas_source.is_valid()) {
|
TileData *tile_data = atlas_source->get_tile_data(neighbor_cell.get_atlas_coords(), neighbor_cell.alternative_tile);
|
||||||
TileData *tile_data = atlas_source->get_tile_data(neighbor_cell.get_atlas_coords(), neighbor_cell.alternative_tile);
|
if (tile_data && tile_data->get_terrain_set() == p_terrain_set) {
|
||||||
if (tile_data && tile_data->get_terrain_set() == p_terrain_set) {
|
neighbor_tile_data = tile_data;
|
||||||
neighbor_tile_data = tile_data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int terrain = neighbor_tile_data ? neighbor_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(E_overlapping.value)) : -1;
|
int terrain = neighbor_tile_data ? neighbor_tile_data->get_terrain_peering_bit(TileSet::CellNeighbor(E_overlapping.value)) : -1;
|
||||||
if (!p_ignore_empty_terrains || terrain >= 0) {
|
if (!p_ignore_empty_terrains || terrain >= 0) {
|
||||||
if (!terrain_count.has(terrain)) {
|
if (!terrain_count.has(terrain)) {
|
||||||
terrain_count[terrain] = 0;
|
terrain_count[terrain] = 0;
|
||||||
}
|
|
||||||
terrain_count[terrain] += 1;
|
|
||||||
}
|
}
|
||||||
|
terrain_count[terrain] += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2244,33 +2292,34 @@ RBSet<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_
|
|||||||
|
|
||||||
// Set the adequate terrain.
|
// Set the adequate terrain.
|
||||||
if (max > 0) {
|
if (max > 0) {
|
||||||
|
TerrainConstraint c = E_constraint;
|
||||||
c.set_terrain(max_terrain);
|
c.set_terrain(max_terrain);
|
||||||
constraints.insert(c);
|
constraints.insert(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the centers as constraints
|
||||||
|
for (Vector2i E_coords : p_cell_list) {
|
||||||
|
TileData *tile_data = nullptr;
|
||||||
|
TileMapCell cell = get_cell(p_layer, E_coords);
|
||||||
|
if (cell.source_id != TileSet::INVALID_SOURCE) {
|
||||||
|
Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
|
||||||
|
Ref<TileSetAtlasSource> atlas_source = source;
|
||||||
|
if (atlas_source.is_valid()) {
|
||||||
|
tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int terrain = (tile_data && tile_data->get_terrain_set() == p_terrain_set) ? tile_data->get_terrain() : -1;
|
||||||
|
if (!p_ignore_empty_terrains || terrain >= 0) {
|
||||||
|
constraints.insert(TerrainConstraint(this, E_coords, terrain));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return constraints;
|
return constraints;
|
||||||
}
|
}
|
||||||
|
|
||||||
RBSet<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
|
HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> p_constraints) {
|
||||||
if (!tile_set.is_valid()) {
|
|
||||||
return RBSet<TerrainConstraint>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the constraints needed from the surrounding tiles.
|
|
||||||
RBSet<TerrainConstraint> output;
|
|
||||||
for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
|
||||||
TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
|
|
||||||
if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) {
|
|
||||||
TerrainConstraint c = TerrainConstraint(this, p_position, side, p_terrains_pattern.get_terrain(side));
|
|
||||||
output.insert(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse(const RBSet<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> p_constraints) {
|
|
||||||
if (!tile_set.is_valid()) {
|
if (!tile_set.is_valid()) {
|
||||||
return HashMap<Vector2i, TileSet::TerrainsPattern>();
|
return HashMap<Vector2i, TileSet::TerrainsPattern>();
|
||||||
}
|
}
|
||||||
@ -2278,110 +2327,287 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_colla
|
|||||||
// Copy the constraints set.
|
// Copy the constraints set.
|
||||||
RBSet<TerrainConstraint> constraints = p_constraints;
|
RBSet<TerrainConstraint> constraints = p_constraints;
|
||||||
|
|
||||||
// Compute all acceptable patterns for each cell.
|
|
||||||
HashMap<Vector2i, RBSet<TileSet::TerrainsPattern>> per_cell_acceptable_tiles;
|
|
||||||
for (Vector2i cell : p_to_replace) {
|
|
||||||
per_cell_acceptable_tiles[cell] = _get_valid_terrains_patterns_for_constraints(p_terrain_set, cell, constraints);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output map.
|
// Output map.
|
||||||
HashMap<Vector2i, TileSet::TerrainsPattern> output;
|
HashMap<Vector2i, TileSet::TerrainsPattern> output;
|
||||||
|
|
||||||
// Add all positions to a set.
|
// Add all positions to a set.
|
||||||
RBSet<Vector2i> to_replace = RBSet<Vector2i>(p_to_replace);
|
for (int i = 0; i < p_to_replace.size(); i++) {
|
||||||
while (!to_replace.is_empty()) {
|
const Vector2i &coords = p_to_replace[i];
|
||||||
// Compute the minimum number of tile possibilities for each cell.
|
|
||||||
int min_nb_possibilities = 100000000;
|
|
||||||
for (const KeyValue<Vector2i, RBSet<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) {
|
|
||||||
min_nb_possibilities = MIN(min_nb_possibilities, E.value.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the set of possible cells to fill, out of the most constrained ones.
|
// Select the best pattern for the given constraints
|
||||||
LocalVector<Vector2i> to_choose_from;
|
TileSet::TerrainsPattern pattern = _get_best_terrain_pattern_for_constraints(p_terrain_set, coords, constraints);
|
||||||
for (const KeyValue<Vector2i, RBSet<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) {
|
|
||||||
if (E.value.size() == min_nb_possibilities) {
|
|
||||||
to_choose_from.push_back(E.key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Randomly a cell to fill out of the most constrained.
|
// Update the constraint set with the new ones
|
||||||
Vector2i selected_cell_to_replace = to_choose_from[Math::random(0, to_choose_from.size() - 1)];
|
RBSet<TerrainConstraint> new_constraints = _get_terrain_constraints_from_added_pattern(coords, p_terrain_set, pattern);
|
||||||
|
|
||||||
// Get the list of acceptable patterns for the given cell.
|
|
||||||
RBSet<TileSet::TerrainsPattern> valid_tiles = per_cell_acceptable_tiles[selected_cell_to_replace];
|
|
||||||
if (valid_tiles.is_empty()) {
|
|
||||||
break; // No possibilities :/
|
|
||||||
}
|
|
||||||
|
|
||||||
// Out of the possible patterns, prioritize the one which have the least amount of different terrains.
|
|
||||||
LocalVector<TileSet::TerrainsPattern> valid_tiles_with_least_amount_of_terrains;
|
|
||||||
int min_terrain_count = 10000;
|
|
||||||
LocalVector<int> terrains_counts;
|
|
||||||
int pattern_index = 0;
|
|
||||||
for (const TileSet::TerrainsPattern &pattern : valid_tiles) {
|
|
||||||
RBSet<int> terrains;
|
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
|
||||||
TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
|
|
||||||
if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) {
|
|
||||||
terrains.insert(pattern.get_terrain(side));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
min_terrain_count = MIN(min_terrain_count, terrains.size());
|
|
||||||
terrains_counts.push_back(terrains.size());
|
|
||||||
pattern_index++;
|
|
||||||
}
|
|
||||||
pattern_index = 0;
|
|
||||||
for (const TileSet::TerrainsPattern &pattern : valid_tiles) {
|
|
||||||
if (terrains_counts[pattern_index] == min_terrain_count) {
|
|
||||||
valid_tiles_with_least_amount_of_terrains.push_back(pattern);
|
|
||||||
}
|
|
||||||
pattern_index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Randomly select a pattern out of the remaining ones.
|
|
||||||
TileSet::TerrainsPattern selected_terrain_tile_pattern = valid_tiles_with_least_amount_of_terrains[Math::random(0, valid_tiles_with_least_amount_of_terrains.size() - 1)];
|
|
||||||
|
|
||||||
// Set the selected cell into the output.
|
|
||||||
output[selected_cell_to_replace] = selected_terrain_tile_pattern;
|
|
||||||
to_replace.erase(selected_cell_to_replace);
|
|
||||||
per_cell_acceptable_tiles.erase(selected_cell_to_replace);
|
|
||||||
|
|
||||||
// Add the new constraints from the added tiles.
|
|
||||||
RBSet<TerrainConstraint> new_constraints = get_terrain_constraints_from_added_tile(selected_cell_to_replace, p_terrain_set, selected_terrain_tile_pattern);
|
|
||||||
for (const TerrainConstraint &E_constraint : new_constraints) {
|
for (const TerrainConstraint &E_constraint : new_constraints) {
|
||||||
constraints.insert(E_constraint);
|
if (constraints.has(E_constraint)) {
|
||||||
|
constraints.erase(E_constraint);
|
||||||
|
}
|
||||||
|
TerrainConstraint c = E_constraint;
|
||||||
|
c.set_priority(5);
|
||||||
|
constraints.insert(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute valid tiles again for neighbors.
|
output[coords] = pattern;
|
||||||
for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
|
||||||
TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
|
|
||||||
if (is_existing_neighbor(side)) {
|
|
||||||
Vector2i neighbor = get_neighbor_cell(selected_cell_to_replace, side);
|
|
||||||
if (to_replace.has(neighbor)) {
|
|
||||||
per_cell_acceptable_tiles[neighbor] = _get_valid_terrains_patterns_for_constraints(p_terrain_set, neighbor, constraints);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileMap::set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector2i> p_coords_array, int p_terrain_set, bool p_ignore_empty_terrains) {
|
HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
|
||||||
|
HashMap<Vector2i, TileSet::TerrainsPattern> output;
|
||||||
|
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
|
||||||
|
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output);
|
||||||
|
|
||||||
|
// Build list and set of tiles that can be modified (painted and their surroundings)
|
||||||
|
Vector<Vector2i> can_modify_list;
|
||||||
|
RBSet<Vector2i> can_modify_set;
|
||||||
|
RBSet<Vector2i> painted_set;
|
||||||
|
for (int i = p_coords_array.size() - 1; i >= 0; i--) {
|
||||||
|
const Vector2i &coords = p_coords_array[i];
|
||||||
|
can_modify_list.push_back(coords);
|
||||||
|
can_modify_set.insert(coords);
|
||||||
|
painted_set.insert(coords);
|
||||||
|
}
|
||||||
|
for (Vector2i coords : p_coords_array) {
|
||||||
|
// Find the adequate neighbor
|
||||||
|
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
||||||
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
||||||
|
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
|
||||||
|
Vector2i neighbor = get_neighbor_cell(coords, bit);
|
||||||
|
if (!can_modify_set.has(neighbor)) {
|
||||||
|
can_modify_list.push_back(neighbor);
|
||||||
|
can_modify_set.insert(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a set, out of the possibly modified tiles, of the one with a center bit that is set (or will be) to the painted terrain
|
||||||
|
RBSet<Vector2i> cells_with_terrain_center_bit;
|
||||||
|
for (Vector2i coords : can_modify_set) {
|
||||||
|
bool connect = false;
|
||||||
|
if (painted_set.has(coords)) {
|
||||||
|
connect = true;
|
||||||
|
} else {
|
||||||
|
// Get the center bit of the cell
|
||||||
|
TileData *tile_data = nullptr;
|
||||||
|
TileMapCell cell = get_cell(p_layer, coords);
|
||||||
|
if (cell.source_id != TileSet::INVALID_SOURCE) {
|
||||||
|
Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
|
||||||
|
Ref<TileSetAtlasSource> atlas_source = source;
|
||||||
|
if (atlas_source.is_valid()) {
|
||||||
|
tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tile_data && tile_data->get_terrain_set() == p_terrain_set && tile_data->get_terrain() == p_terrain) {
|
||||||
|
connect = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (connect) {
|
||||||
|
cells_with_terrain_center_bit.insert(coords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RBSet<TerrainConstraint> constraints;
|
||||||
|
|
||||||
|
// Add new constraints from the path drawn.
|
||||||
|
for (Vector2i coords : p_coords_array) {
|
||||||
|
// Constraints on the center bit.
|
||||||
|
TerrainConstraint c = TerrainConstraint(this, coords, p_terrain);
|
||||||
|
c.set_priority(10);
|
||||||
|
constraints.insert(c);
|
||||||
|
|
||||||
|
// Constraints on the connecting bits.
|
||||||
|
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
||||||
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
||||||
|
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
|
||||||
|
c = TerrainConstraint(this, coords, bit, p_terrain);
|
||||||
|
c.set_priority(10);
|
||||||
|
if ((int(bit) % 2) == 0) {
|
||||||
|
// Side peering bits: add the constraint if the center is of the same terrain
|
||||||
|
Vector2i neighbor = get_neighbor_cell(coords, bit);
|
||||||
|
if (cells_with_terrain_center_bit.has(neighbor)) {
|
||||||
|
constraints.insert(c);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Corner peering bits: add the constraint if all tiles on the constraint has the same center bit
|
||||||
|
HashMap<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits();
|
||||||
|
bool valid = true;
|
||||||
|
for (KeyValue<Vector2i, TileSet::CellNeighbor> kv : overlapping_terrain_bits) {
|
||||||
|
if (!cells_with_terrain_center_bit.has(kv.key)) {
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (valid) {
|
||||||
|
constraints.insert(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fills in the constraint list from existing tiles.
|
||||||
|
for (TerrainConstraint c : _get_terrain_constraints_from_cells_list(p_layer, can_modify_set, p_terrain_set, p_ignore_empty_terrains)) {
|
||||||
|
constraints.insert(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the terrains.
|
||||||
|
output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_path(int p_layer, const Vector<Vector2i> &p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
|
||||||
|
HashMap<Vector2i, TileSet::TerrainsPattern> output;
|
||||||
|
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
|
||||||
|
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output);
|
||||||
|
|
||||||
|
// Make sure the path is correct and build the peering bit list while doing it.
|
||||||
|
Vector<TileSet::CellNeighbor> neighbor_list;
|
||||||
|
for (int i = 0; i < p_path.size() - 1; i++) {
|
||||||
|
// Find the adequate neighbor
|
||||||
|
TileSet::CellNeighbor found_bit = TileSet::CELL_NEIGHBOR_MAX;
|
||||||
|
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
||||||
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
||||||
|
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
|
||||||
|
if (get_neighbor_cell(p_path[i], bit) == p_path[i + 1]) {
|
||||||
|
found_bit = bit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ERR_FAIL_COND_V_MSG(found_bit == TileSet::CELL_NEIGHBOR_MAX, output, vformat("Invalid terrain path, %s is not a neighbouring tile of %s", p_path[i + 1], p_path[i]));
|
||||||
|
neighbor_list.push_back(found_bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build list and set of tiles that can be modified (painted and their surroundings)
|
||||||
|
Vector<Vector2i> can_modify_list;
|
||||||
|
RBSet<Vector2i> can_modify_set;
|
||||||
|
for (int i = p_path.size() - 1; i >= 0; i--) {
|
||||||
|
const Vector2i &coords = p_path[i];
|
||||||
|
can_modify_list.push_back(coords);
|
||||||
|
can_modify_set.insert(coords);
|
||||||
|
}
|
||||||
|
for (Vector2i coords : p_path) {
|
||||||
|
// Find the adequate neighbor
|
||||||
|
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
||||||
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
||||||
|
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
|
||||||
|
Vector2i neighbor = get_neighbor_cell(coords, bit);
|
||||||
|
if (!can_modify_set.has(neighbor)) {
|
||||||
|
can_modify_list.push_back(neighbor);
|
||||||
|
can_modify_set.insert(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RBSet<TerrainConstraint> constraints;
|
||||||
|
|
||||||
|
// Add new constraints from the path drawn.
|
||||||
|
for (Vector2i coords : p_path) {
|
||||||
|
// Constraints on the center bit
|
||||||
|
TerrainConstraint c = TerrainConstraint(this, coords, p_terrain);
|
||||||
|
c.set_priority(10);
|
||||||
|
constraints.insert(c);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < p_path.size() - 1; i++) {
|
||||||
|
// Constraints on the peering bits.
|
||||||
|
TerrainConstraint c = TerrainConstraint(this, p_path[i], neighbor_list[i], p_terrain);
|
||||||
|
c.set_priority(10);
|
||||||
|
constraints.insert(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fills in the constraint list from existing tiles.
|
||||||
|
for (TerrainConstraint c : _get_terrain_constraints_from_cells_list(p_layer, can_modify_set, p_terrain_set, p_ignore_empty_terrains)) {
|
||||||
|
constraints.insert(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the terrains.
|
||||||
|
output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains) {
|
||||||
|
HashMap<Vector2i, TileSet::TerrainsPattern> output;
|
||||||
|
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
|
||||||
|
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output);
|
||||||
|
|
||||||
|
// Build list and set of tiles that can be modified (painted and their surroundings).
|
||||||
|
Vector<Vector2i> can_modify_list;
|
||||||
|
RBSet<Vector2i> can_modify_set;
|
||||||
|
for (int i = p_coords_array.size() - 1; i >= 0; i--) {
|
||||||
|
const Vector2i &coords = p_coords_array[i];
|
||||||
|
can_modify_list.push_back(coords);
|
||||||
|
can_modify_set.insert(coords);
|
||||||
|
}
|
||||||
|
for (Vector2i coords : p_coords_array) {
|
||||||
|
// Find the adequate neighbor
|
||||||
|
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
|
||||||
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
|
||||||
|
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
|
||||||
|
Vector2i neighbor = get_neighbor_cell(coords, bit);
|
||||||
|
if (!can_modify_set.has(neighbor)) {
|
||||||
|
can_modify_list.push_back(neighbor);
|
||||||
|
can_modify_set.insert(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add constraint by the new ones.
|
||||||
|
RBSet<TerrainConstraint> constraints;
|
||||||
|
|
||||||
|
// Add new constraints from the path drawn.
|
||||||
|
for (Vector2i coords : p_coords_array) {
|
||||||
|
// Constraints on the center bit
|
||||||
|
RBSet<TerrainConstraint> added_constraints = _get_terrain_constraints_from_added_pattern(coords, p_terrain_set, p_terrains_pattern);
|
||||||
|
for (TerrainConstraint c : added_constraints) {
|
||||||
|
c.set_priority(10);
|
||||||
|
constraints.insert(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fills in the constraint list from modified tiles border.
|
||||||
|
for (TerrainConstraint c : _get_terrain_constraints_from_cells_list(p_layer, can_modify_set, p_terrain_set, p_ignore_empty_terrains)) {
|
||||||
|
constraints.insert(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the terrains.
|
||||||
|
output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileMap::set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
|
||||||
ERR_FAIL_COND(!tile_set.is_valid());
|
ERR_FAIL_COND(!tile_set.is_valid());
|
||||||
ERR_FAIL_INDEX(p_layer, (int)layers.size());
|
ERR_FAIL_INDEX(p_layer, (int)layers.size());
|
||||||
ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count());
|
ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count());
|
||||||
|
|
||||||
RBSet<Vector2i> coords_set;
|
Vector<Vector2i> vector_cells;
|
||||||
for (int i = 0; i < p_coords_array.size(); i++) {
|
for (int i = 0; i < p_cells.size(); i++) {
|
||||||
coords_set.insert(p_coords_array[i]);
|
vector_cells.push_back(p_cells[i]);
|
||||||
}
|
}
|
||||||
|
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_connect(p_layer, vector_cells, p_terrain_set, p_terrain, p_ignore_empty_terrains);
|
||||||
|
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) {
|
||||||
|
TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
|
||||||
|
set_cell(p_layer, E.key, c.source_id, c.get_atlas_coords(), c.alternative_tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RBSet<TileMap::TerrainConstraint> constraints = get_terrain_constraints_from_removed_cells_list(p_layer, coords_set, p_terrain_set, p_ignore_empty_terrains);
|
void TileMap::set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
|
||||||
|
ERR_FAIL_COND(!tile_set.is_valid());
|
||||||
|
ERR_FAIL_INDEX(p_layer, (int)layers.size());
|
||||||
|
ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count());
|
||||||
|
|
||||||
HashMap<Vector2i, TileSet::TerrainsPattern> wfc_output = terrain_wave_function_collapse(coords_set, p_terrain_set, constraints);
|
Vector<Vector2i> vector_path;
|
||||||
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : wfc_output) {
|
for (int i = 0; i < p_path.size(); i++) {
|
||||||
TileMapCell cell = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value);
|
vector_path.push_back(p_path[i]);
|
||||||
set_cell(p_layer, kv.key, cell.source_id, cell.get_atlas_coords(), cell.alternative_tile);
|
}
|
||||||
|
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_path(p_layer, vector_path, p_terrain_set, p_terrain, p_ignore_empty_terrains);
|
||||||
|
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) {
|
||||||
|
TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
|
||||||
|
set_cell(p_layer, E.key, c.source_id, c.get_atlas_coords(), c.alternative_tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3640,7 +3866,8 @@ void TileMap::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("map_pattern", "position_in_tilemap", "coords_in_pattern", "pattern"), &TileMap::map_pattern);
|
ClassDB::bind_method(D_METHOD("map_pattern", "position_in_tilemap", "coords_in_pattern", "pattern"), &TileMap::map_pattern);
|
||||||
ClassDB::bind_method(D_METHOD("set_pattern", "layer", "position", "pattern"), &TileMap::set_pattern);
|
ClassDB::bind_method(D_METHOD("set_pattern", "layer", "position", "pattern"), &TileMap::set_pattern);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("set_cells_from_surrounding_terrains", "layer", "cells", "terrain_set", "ignore_empty_terrains"), &TileMap::set_cells_from_surrounding_terrains, DEFVAL(true));
|
ClassDB::bind_method(D_METHOD("set_cells_terrain_connect", "layer", "cells", "terrain_set", "terrain", "ignore_empty_terrains"), &TileMap::set_cells_terrain_connect, DEFVAL(true));
|
||||||
|
ClassDB::bind_method(D_METHOD("set_cells_terrain_path", "layer", "path", "terrain_set", "terrain", "ignore_empty_terrains"), &TileMap::set_cells_terrain_path, DEFVAL(true));
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles);
|
ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles);
|
||||||
ClassDB::bind_method(D_METHOD("clear_layer", "layer"), &TileMap::clear_layer);
|
ClassDB::bind_method(D_METHOD("clear_layer", "layer"), &TileMap::clear_layer);
|
||||||
|
@ -119,6 +119,8 @@ public:
|
|||||||
int bit = -1;
|
int bit = -1;
|
||||||
int terrain = -1;
|
int terrain = -1;
|
||||||
|
|
||||||
|
int priority = 1;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool operator<(const TerrainConstraint &p_other) const {
|
bool operator<(const TerrainConstraint &p_other) const {
|
||||||
if (base_cell_coords == p_other.base_cell_coords) {
|
if (base_cell_coords == p_other.base_cell_coords) {
|
||||||
@ -128,13 +130,17 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
String to_string() const {
|
String to_string() const {
|
||||||
return vformat("Constraint {pos:%s, bit:%d, terrain:%d}", base_cell_coords, bit, terrain);
|
return vformat("Constraint {pos:%s, bit:%d, terrain:%d, priotity:%d}", base_cell_coords, bit, terrain, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2i get_base_cell_coords() const {
|
Vector2i get_base_cell_coords() const {
|
||||||
return base_cell_coords;
|
return base_cell_coords;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_center_bit() const {
|
||||||
|
return bit == 0;
|
||||||
|
}
|
||||||
|
|
||||||
HashMap<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
|
HashMap<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
|
||||||
|
|
||||||
void set_terrain(int p_terrain) {
|
void set_terrain(int p_terrain) {
|
||||||
@ -145,8 +151,17 @@ public:
|
|||||||
return terrain;
|
return terrain;
|
||||||
}
|
}
|
||||||
|
|
||||||
TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain);
|
void set_priority(int p_priority) {
|
||||||
TerrainConstraint() {}
|
priority = p_priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_priority() {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain); // For the center terrain bit
|
||||||
|
TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain); // For peering bits
|
||||||
|
TerrainConstraint(){};
|
||||||
};
|
};
|
||||||
|
|
||||||
enum VisibilityMode {
|
enum VisibilityMode {
|
||||||
@ -251,7 +266,9 @@ private:
|
|||||||
void _scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
|
void _scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
|
||||||
|
|
||||||
// Terrains.
|
// Terrains.
|
||||||
RBSet<TileSet::TerrainsPattern> _get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints);
|
TileSet::TerrainsPattern _get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints);
|
||||||
|
RBSet<TerrainConstraint> _get_terrain_constraints_from_added_pattern(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const;
|
||||||
|
RBSet<TerrainConstraint> _get_terrain_constraints_from_cells_list(int p_layer, const RBSet<Vector2i> &p_on_map, int p_terrain_set, bool p_ignore_empty_terrains) const;
|
||||||
|
|
||||||
// Set and get tiles from data arrays.
|
// Set and get tiles from data arrays.
|
||||||
void _set_tile_data(int p_layer, const Vector<int> &p_data);
|
void _set_tile_data(int p_layer, const Vector<int> &p_data);
|
||||||
@ -333,10 +350,13 @@ public:
|
|||||||
void set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPattern> p_pattern);
|
void set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPattern> p_pattern);
|
||||||
|
|
||||||
// Terrains.
|
// Terrains.
|
||||||
RBSet<TerrainConstraint> get_terrain_constraints_from_removed_cells_list(int p_layer, const RBSet<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains = true) const; // Not exposed.
|
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> p_constraints); // Not exposed.
|
||||||
RBSet<TerrainConstraint> get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const; // Not exposed.
|
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed.
|
||||||
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_wave_function_collapse(const RBSet<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> p_constraints); // Not exposed.
|
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed.
|
||||||
void set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector2i> p_coords_array, int p_terrain_set, bool p_ignore_empty_terrains = true);
|
HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true); // Not exposed.
|
||||||
|
|
||||||
|
void set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
|
||||||
|
void set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
|
||||||
|
|
||||||
// Not exposed to users
|
// Not exposed to users
|
||||||
TileMapCell get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
|
TileMapCell get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||||
@ -365,7 +385,7 @@ public:
|
|||||||
// For finding tiles from collision.
|
// For finding tiles from collision.
|
||||||
Vector2i get_coords_for_body_rid(RID p_physics_body);
|
Vector2i get_coords_for_body_rid(RID p_physics_body);
|
||||||
|
|
||||||
// Fixing a nclearing methods.
|
// Fixing and clearing methods.
|
||||||
void fix_invalid_tiles();
|
void fix_invalid_tiles();
|
||||||
|
|
||||||
// Clears tiles from a given layer
|
// Clears tiles from a given layer
|
||||||
|
@ -236,6 +236,9 @@ bool TileSet::TerrainsPattern::operator<(const TerrainsPattern &p_terrains_patte
|
|||||||
return is_valid_bit[i] < p_terrains_pattern.is_valid_bit[i];
|
return is_valid_bit[i] < p_terrains_pattern.is_valid_bit[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (terrain != p_terrains_pattern.terrain) {
|
||||||
|
return terrain < p_terrains_pattern.terrain;
|
||||||
|
}
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
if (is_valid_bit[i] && bits[i] != p_terrains_pattern.bits[i]) {
|
if (is_valid_bit[i] && bits[i] != p_terrains_pattern.bits[i]) {
|
||||||
return bits[i] < p_terrains_pattern.bits[i];
|
return bits[i] < p_terrains_pattern.bits[i];
|
||||||
@ -253,10 +256,23 @@ bool TileSet::TerrainsPattern::operator==(const TerrainsPattern &p_terrains_patt
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (terrain != p_terrains_pattern.terrain) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileSet::TerrainsPattern::set_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain) {
|
void TileSet::TerrainsPattern::set_terrain(int p_terrain) {
|
||||||
|
ERR_FAIL_COND(p_terrain < -1);
|
||||||
|
|
||||||
|
terrain = p_terrain;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TileSet::TerrainsPattern::get_terrain() const {
|
||||||
|
return terrain;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileSet::TerrainsPattern::set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain) {
|
||||||
ERR_FAIL_COND(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX);
|
ERR_FAIL_COND(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX);
|
||||||
ERR_FAIL_COND(!is_valid_bit[p_peering_bit]);
|
ERR_FAIL_COND(!is_valid_bit[p_peering_bit]);
|
||||||
ERR_FAIL_COND(p_terrain < -1);
|
ERR_FAIL_COND(p_terrain < -1);
|
||||||
@ -271,25 +287,27 @@ void TileSet::TerrainsPattern::set_terrain(TileSet::CellNeighbor p_peering_bit,
|
|||||||
bits[p_peering_bit] = p_terrain;
|
bits[p_peering_bit] = p_terrain;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TileSet::TerrainsPattern::get_terrain(TileSet::CellNeighbor p_peering_bit) const {
|
int TileSet::TerrainsPattern::get_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const {
|
||||||
ERR_FAIL_COND_V(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX, -1);
|
ERR_FAIL_COND_V(p_peering_bit == TileSet::CELL_NEIGHBOR_MAX, -1);
|
||||||
ERR_FAIL_COND_V(!is_valid_bit[p_peering_bit], -1);
|
ERR_FAIL_COND_V(!is_valid_bit[p_peering_bit], -1);
|
||||||
return bits[p_peering_bit];
|
return bits[p_peering_bit];
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileSet::TerrainsPattern::set_terrains_from_array(Array p_terrains) {
|
void TileSet::TerrainsPattern::from_array(Array p_terrains) {
|
||||||
int in_array_index = 0;
|
set_terrain(p_terrains[0]);
|
||||||
|
int in_array_index = 1;
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
if (is_valid_bit[i]) {
|
if (is_valid_bit[i]) {
|
||||||
ERR_FAIL_INDEX(in_array_index, p_terrains.size());
|
ERR_FAIL_INDEX(in_array_index, p_terrains.size());
|
||||||
set_terrain(TileSet::CellNeighbor(i), p_terrains[in_array_index]);
|
set_terrain_peering_bit(TileSet::CellNeighbor(i), p_terrains[in_array_index]);
|
||||||
in_array_index++;
|
in_array_index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Array TileSet::TerrainsPattern::get_terrains_as_array() const {
|
Array TileSet::TerrainsPattern::as_array() const {
|
||||||
Array output;
|
Array output;
|
||||||
|
output.push_back(get_terrain());
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
if (is_valid_bit[i]) {
|
if (is_valid_bit[i]) {
|
||||||
output.push_back(bits[i]);
|
output.push_back(bits[i]);
|
||||||
@ -297,10 +315,11 @@ Array TileSet::TerrainsPattern::get_terrains_as_array() const {
|
|||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
TileSet::TerrainsPattern::TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set) {
|
TileSet::TerrainsPattern::TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set) {
|
||||||
ERR_FAIL_COND(p_terrain_set < 0);
|
ERR_FAIL_COND(p_terrain_set < 0);
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
is_valid_bit[i] = (p_tile_set->is_valid_peering_bit_terrain(p_terrain_set, TileSet::CellNeighbor(i)));
|
is_valid_bit[i] = (p_tile_set->is_valid_terrain_peering_bit(p_terrain_set, TileSet::CellNeighbor(i)));
|
||||||
bits[i] = -1;
|
bits[i] = -1;
|
||||||
}
|
}
|
||||||
valid = true;
|
valid = true;
|
||||||
@ -410,11 +429,16 @@ void TileSet::_update_terrains_cache() {
|
|||||||
|
|
||||||
TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
|
TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
|
||||||
|
|
||||||
|
// Main terrain.
|
||||||
|
if (terrains_pattern.get_terrain() >= 0) {
|
||||||
|
per_terrain_pattern_tiles[terrain_set][terrains_pattern].insert(cell);
|
||||||
|
}
|
||||||
|
|
||||||
// Terrain bits.
|
// Terrain bits.
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
CellNeighbor bit = CellNeighbor(i);
|
CellNeighbor bit = CellNeighbor(i);
|
||||||
if (is_valid_peering_bit_terrain(terrain_set, bit)) {
|
if (is_valid_terrain_peering_bit(terrain_set, bit)) {
|
||||||
int terrain = terrains_pattern.get_terrain(bit);
|
int terrain = terrains_pattern.get_terrain_peering_bit(bit);
|
||||||
if (terrain >= 0) {
|
if (terrain >= 0) {
|
||||||
per_terrain_pattern_tiles[terrain_set][terrains_pattern].insert(cell);
|
per_terrain_pattern_tiles[terrain_set][terrains_pattern].insert(cell);
|
||||||
}
|
}
|
||||||
@ -822,7 +846,7 @@ Color TileSet::get_terrain_color(int p_terrain_set, int p_terrain_index) const {
|
|||||||
return terrain_sets[p_terrain_set].terrains[p_terrain_index].color;
|
return terrain_sets[p_terrain_set].terrains[p_terrain_index].color;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TileSet::is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const {
|
bool TileSet::is_valid_terrain_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const {
|
||||||
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
|
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
|
||||||
if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) {
|
if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) {
|
||||||
if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE ||
|
if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE ||
|
||||||
@ -905,13 +929,13 @@ bool TileSet::is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const {
|
bool TileSet::is_valid_terrain_peering_bit(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const {
|
||||||
if (p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count()) {
|
if (p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set);
|
TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set);
|
||||||
return is_valid_peering_bit_for_mode(terrain_mode, p_peering_bit);
|
return is_valid_terrain_peering_bit_for_mode(terrain_mode, p_peering_bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
@ -1494,26 +1518,48 @@ void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Point2> TileSet::get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit) {
|
Vector<Point2> TileSet::get_terrain_polygon(int p_terrain_set) {
|
||||||
|
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
|
||||||
|
return _get_square_terrain_polygon(tile_size);
|
||||||
|
} else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
|
||||||
|
return _get_isometric_terrain_polygon(tile_size);
|
||||||
|
} else {
|
||||||
|
float overlap = 0.0;
|
||||||
|
switch (tile_shape) {
|
||||||
|
case TileSet::TILE_SHAPE_HEXAGON:
|
||||||
|
overlap = 0.25;
|
||||||
|
break;
|
||||||
|
case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE:
|
||||||
|
overlap = 0.0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return _get_half_offset_terrain_polygon(tile_size, overlap, tile_offset_axis);
|
||||||
|
}
|
||||||
|
return Vector<Point2>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<Point2> TileSet::get_terrain_peering_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit) {
|
||||||
ERR_FAIL_COND_V(p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count(), Vector<Point2>());
|
ERR_FAIL_COND_V(p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count(), Vector<Point2>());
|
||||||
|
|
||||||
TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set);
|
TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set);
|
||||||
|
|
||||||
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
|
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
|
||||||
if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
|
if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
|
||||||
return _get_square_corner_or_side_terrain_bit_polygon(tile_size, p_bit);
|
return _get_square_corner_or_side_terrain_peering_bit_polygon(tile_size, p_bit);
|
||||||
} else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
|
} else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
|
||||||
return _get_square_corner_terrain_bit_polygon(tile_size, p_bit);
|
return _get_square_corner_terrain_peering_bit_polygon(tile_size, p_bit);
|
||||||
} else { // TileData::TERRAIN_MODE_MATCH_SIDES
|
} else { // TileData::TERRAIN_MODE_MATCH_SIDES
|
||||||
return _get_square_side_terrain_bit_polygon(tile_size, p_bit);
|
return _get_square_side_terrain_peering_bit_polygon(tile_size, p_bit);
|
||||||
}
|
}
|
||||||
} else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
|
} else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
|
||||||
if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
|
if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
|
||||||
return _get_isometric_corner_or_side_terrain_bit_polygon(tile_size, p_bit);
|
return _get_isometric_corner_or_side_terrain_peering_bit_polygon(tile_size, p_bit);
|
||||||
} else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
|
} else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
|
||||||
return _get_isometric_corner_terrain_bit_polygon(tile_size, p_bit);
|
return _get_isometric_corner_terrain_peering_bit_polygon(tile_size, p_bit);
|
||||||
} else { // TileData::TERRAIN_MODE_MATCH_SIDES
|
} else { // TileData::TERRAIN_MODE_MATCH_SIDES
|
||||||
return _get_isometric_side_terrain_bit_polygon(tile_size, p_bit);
|
return _get_isometric_side_terrain_peering_bit_polygon(tile_size, p_bit);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
float overlap = 0.0;
|
float overlap = 0.0;
|
||||||
@ -1528,11 +1574,11 @@ Vector<Point2> TileSet::get_terrain_bit_polygon(int p_terrain_set, TileSet::Cell
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
|
if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
|
||||||
return _get_half_offset_corner_or_side_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis);
|
return _get_half_offset_corner_or_side_terrain_peering_bit_polygon(tile_size, overlap, tile_offset_axis, p_bit);
|
||||||
} else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
|
} else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
|
||||||
return _get_half_offset_corner_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis);
|
return _get_half_offset_corner_terrain_peering_bit_polygon(tile_size, overlap, tile_offset_axis, p_bit);
|
||||||
} else { // TileData::TERRAIN_MODE_MATCH_SIDES
|
} else { // TileData::TERRAIN_MODE_MATCH_SIDES
|
||||||
return _get_half_offset_side_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis);
|
return _get_half_offset_side_terrain_peering_bit_polygon(tile_size, overlap, tile_offset_axis, p_bit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1544,30 +1590,68 @@ void TileSet::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform,
|
|||||||
|
|
||||||
if (terrain_bits_meshes_dirty) {
|
if (terrain_bits_meshes_dirty) {
|
||||||
// Recompute the meshes.
|
// Recompute the meshes.
|
||||||
terrain_bits_meshes.clear();
|
terrain_peering_bits_meshes.clear();
|
||||||
|
|
||||||
for (int terrain_mode_index = 0; terrain_mode_index < 3; terrain_mode_index++) {
|
for (int terrain_mode_index = 0; terrain_mode_index < 3; terrain_mode_index++) {
|
||||||
TerrainMode terrain_mode = TerrainMode(terrain_mode_index);
|
TerrainMode terrain_mode = TerrainMode(terrain_mode_index);
|
||||||
|
|
||||||
|
// Center terrain
|
||||||
|
Vector<Vector2> polygon;
|
||||||
|
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
|
||||||
|
polygon = _get_square_terrain_polygon(tile_size);
|
||||||
|
} else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
|
||||||
|
polygon = _get_isometric_terrain_polygon(tile_size);
|
||||||
|
} else {
|
||||||
|
float overlap = 0.0;
|
||||||
|
switch (tile_shape) {
|
||||||
|
case TileSet::TILE_SHAPE_HEXAGON:
|
||||||
|
overlap = 0.25;
|
||||||
|
break;
|
||||||
|
case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE:
|
||||||
|
overlap = 0.0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
polygon = _get_half_offset_terrain_polygon(tile_size, overlap, tile_offset_axis);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Ref<ArrayMesh> mesh;
|
||||||
|
mesh.instantiate();
|
||||||
|
Vector<Vector2> uvs;
|
||||||
|
uvs.resize(polygon.size());
|
||||||
|
Vector<Color> colors;
|
||||||
|
colors.resize(polygon.size());
|
||||||
|
colors.fill(Color(1.0, 1.0, 1.0, 1.0));
|
||||||
|
Array a;
|
||||||
|
a.resize(Mesh::ARRAY_MAX);
|
||||||
|
a[Mesh::ARRAY_VERTEX] = polygon;
|
||||||
|
a[Mesh::ARRAY_TEX_UV] = uvs;
|
||||||
|
a[Mesh::ARRAY_COLOR] = colors;
|
||||||
|
a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(polygon);
|
||||||
|
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES);
|
||||||
|
terrain_meshes[terrain_mode] = mesh;
|
||||||
|
}
|
||||||
|
// Peering bits
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
CellNeighbor bit = CellNeighbor(i);
|
CellNeighbor bit = CellNeighbor(i);
|
||||||
|
|
||||||
if (is_valid_peering_bit_for_mode(terrain_mode, bit)) {
|
if (is_valid_terrain_peering_bit_for_mode(terrain_mode, bit)) {
|
||||||
Vector<Vector2> polygon;
|
|
||||||
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
|
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
|
||||||
if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
|
if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
|
||||||
polygon = _get_square_corner_or_side_terrain_bit_polygon(tile_size, bit);
|
polygon = _get_square_corner_or_side_terrain_peering_bit_polygon(tile_size, bit);
|
||||||
} else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
|
} else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
|
||||||
polygon = _get_square_corner_terrain_bit_polygon(tile_size, bit);
|
polygon = _get_square_corner_terrain_peering_bit_polygon(tile_size, bit);
|
||||||
} else { // TileData::TERRAIN_MODE_MATCH_SIDES
|
} else { // TileData::TERRAIN_MODE_MATCH_SIDES
|
||||||
polygon = _get_square_side_terrain_bit_polygon(tile_size, bit);
|
polygon = _get_square_side_terrain_peering_bit_polygon(tile_size, bit);
|
||||||
}
|
}
|
||||||
} else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
|
} else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
|
||||||
if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
|
if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
|
||||||
polygon = _get_isometric_corner_or_side_terrain_bit_polygon(tile_size, bit);
|
polygon = _get_isometric_corner_or_side_terrain_peering_bit_polygon(tile_size, bit);
|
||||||
} else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
|
} else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
|
||||||
polygon = _get_isometric_corner_terrain_bit_polygon(tile_size, bit);
|
polygon = _get_isometric_corner_terrain_peering_bit_polygon(tile_size, bit);
|
||||||
} else { // TileData::TERRAIN_MODE_MATCH_SIDES
|
} else { // TileData::TERRAIN_MODE_MATCH_SIDES
|
||||||
polygon = _get_isometric_side_terrain_bit_polygon(tile_size, bit);
|
polygon = _get_isometric_side_terrain_peering_bit_polygon(tile_size, bit);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
float overlap = 0.0;
|
float overlap = 0.0;
|
||||||
@ -1582,29 +1666,30 @@ void TileSet::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
|
if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
|
||||||
polygon = _get_half_offset_corner_or_side_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis);
|
polygon = _get_half_offset_corner_or_side_terrain_peering_bit_polygon(tile_size, overlap, tile_offset_axis, bit);
|
||||||
} else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
|
} else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
|
||||||
polygon = _get_half_offset_corner_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis);
|
polygon = _get_half_offset_corner_terrain_peering_bit_polygon(tile_size, overlap, tile_offset_axis, bit);
|
||||||
} else { // TileData::TERRAIN_MODE_MATCH_SIDES
|
} else { // TileData::TERRAIN_MODE_MATCH_SIDES
|
||||||
polygon = _get_half_offset_side_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis);
|
polygon = _get_half_offset_side_terrain_peering_bit_polygon(tile_size, overlap, tile_offset_axis, bit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
Ref<ArrayMesh> mesh;
|
Ref<ArrayMesh> mesh;
|
||||||
mesh.instantiate();
|
mesh.instantiate();
|
||||||
Vector<Vector2> uvs;
|
Vector<Vector2> uvs;
|
||||||
uvs.resize(polygon.size());
|
uvs.resize(polygon.size());
|
||||||
Vector<Color> colors;
|
Vector<Color> colors;
|
||||||
colors.resize(polygon.size());
|
colors.resize(polygon.size());
|
||||||
colors.fill(Color(1.0, 1.0, 1.0, 1.0));
|
colors.fill(Color(1.0, 1.0, 1.0, 1.0));
|
||||||
Array a;
|
Array a;
|
||||||
a.resize(Mesh::ARRAY_MAX);
|
a.resize(Mesh::ARRAY_MAX);
|
||||||
a[Mesh::ARRAY_VERTEX] = polygon;
|
a[Mesh::ARRAY_VERTEX] = polygon;
|
||||||
a[Mesh::ARRAY_TEX_UV] = uvs;
|
a[Mesh::ARRAY_TEX_UV] = uvs;
|
||||||
a[Mesh::ARRAY_COLOR] = colors;
|
a[Mesh::ARRAY_COLOR] = colors;
|
||||||
a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(polygon);
|
a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(polygon);
|
||||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES);
|
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES);
|
||||||
terrain_bits_meshes[terrain_mode][bit] = mesh;
|
terrain_peering_bits_meshes[terrain_mode][bit] = mesh;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1618,14 +1703,21 @@ void TileSet::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform,
|
|||||||
TileSet::TerrainMode terrain_mode = get_terrain_set_mode(terrain_set);
|
TileSet::TerrainMode terrain_mode = get_terrain_set_mode(terrain_set);
|
||||||
|
|
||||||
RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform);
|
RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform);
|
||||||
|
int terrain_id = p_tile_data->get_terrain();
|
||||||
|
if (terrain_id >= 0) {
|
||||||
|
Color color = get_terrain_color(terrain_set, terrain_id);
|
||||||
|
color.a = TERRAIN_ALPHA;
|
||||||
|
p_canvas_item->draw_mesh(terrain_meshes[terrain_mode], Ref<Texture2D>(), Transform2D(), color);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
CellNeighbor bit = CellNeighbor(i);
|
CellNeighbor bit = CellNeighbor(i);
|
||||||
if (is_valid_peering_bit_terrain(terrain_set, bit)) {
|
if (is_valid_terrain_peering_bit(terrain_set, bit)) {
|
||||||
int terrain_id = p_tile_data->get_peering_bit_terrain(bit);
|
terrain_id = p_tile_data->get_terrain_peering_bit(bit);
|
||||||
if (terrain_id >= 0) {
|
if (terrain_id >= 0) {
|
||||||
Color color = get_terrain_color(terrain_set, terrain_id);
|
Color color = get_terrain_color(terrain_set, terrain_id);
|
||||||
color.a = TERRAIN_ALPHA;
|
color.a = TERRAIN_ALPHA;
|
||||||
p_canvas_item->draw_mesh(terrain_bits_meshes[terrain_mode][bit], Ref<Texture2D>(), Transform2D(), color);
|
p_canvas_item->draw_mesh(terrain_peering_bits_meshes[terrain_mode][bit], Ref<Texture2D>(), Transform2D(), color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1670,13 +1762,16 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) {
|
|||||||
for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) {
|
for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) {
|
||||||
bit_counts[terrain] = 0;
|
bit_counts[terrain] = 0;
|
||||||
}
|
}
|
||||||
|
if (tile_data->get_terrain() >= 0) {
|
||||||
|
bit_counts[tile_data->get_terrain()] += 10;
|
||||||
|
}
|
||||||
for (int terrain_bit = 0; terrain_bit < TileSet::CELL_NEIGHBOR_MAX; terrain_bit++) {
|
for (int terrain_bit = 0; terrain_bit < TileSet::CELL_NEIGHBOR_MAX; terrain_bit++) {
|
||||||
TileSet::CellNeighbor cell_neighbor = TileSet::CellNeighbor(terrain_bit);
|
TileSet::CellNeighbor cell_neighbor = TileSet::CellNeighbor(terrain_bit);
|
||||||
if (is_valid_peering_bit_terrain(terrain_set, cell_neighbor)) {
|
if (is_valid_terrain_peering_bit(terrain_set, cell_neighbor)) {
|
||||||
int terrain = tile_data->get_peering_bit_terrain(cell_neighbor);
|
int terrain = tile_data->get_terrain_peering_bit(cell_neighbor);
|
||||||
if (terrain >= 0) {
|
if (terrain >= 0) {
|
||||||
if (terrain >= (int)bit_counts.size()) {
|
if (terrain >= (int)bit_counts.size()) {
|
||||||
WARN_PRINT(vformat("Invalid peering bit terrain: %d", terrain));
|
WARN_PRINT(vformat("Invalid terrain peering bit: %d", terrain));
|
||||||
} else {
|
} else {
|
||||||
bit_counts[terrain] += 1;
|
bit_counts[terrain] += 1;
|
||||||
}
|
}
|
||||||
@ -1730,7 +1825,17 @@ void TileSet::_source_changed() {
|
|||||||
emit_changed();
|
emit_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Point2> TileSet::_get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
|
Vector<Point2> TileSet::_get_square_terrain_polygon(Vector2i p_size) {
|
||||||
|
Rect2 rect(-Vector2(p_size) / 6.0, Vector2(p_size) / 3.0);
|
||||||
|
return {
|
||||||
|
rect.position,
|
||||||
|
Vector2(rect.get_end().x, rect.position.y),
|
||||||
|
rect.get_end(),
|
||||||
|
Vector2(rect.position.x, rect.get_end().y)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<Point2> TileSet::_get_square_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
|
||||||
Rect2 bit_rect;
|
Rect2 bit_rect;
|
||||||
bit_rect.size = Vector2(p_size) / 3;
|
bit_rect.size = Vector2(p_size) / 3;
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
@ -1773,7 +1878,7 @@ Vector<Point2> TileSet::_get_square_corner_or_side_terrain_bit_polygon(Vector2i
|
|||||||
return polygon;
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Point2> TileSet::_get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
|
Vector<Point2> TileSet::_get_square_corner_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
|
||||||
Vector2 unit = Vector2(p_size) / 6.0;
|
Vector2 unit = Vector2(p_size) / 6.0;
|
||||||
Vector<Vector2> polygon;
|
Vector<Vector2> polygon;
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
@ -1815,7 +1920,7 @@ Vector<Point2> TileSet::_get_square_corner_terrain_bit_polygon(Vector2i p_size,
|
|||||||
return polygon;
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Point2> TileSet::_get_square_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
|
Vector<Point2> TileSet::_get_square_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
|
||||||
Vector2 unit = Vector2(p_size) / 6.0;
|
Vector2 unit = Vector2(p_size) / 6.0;
|
||||||
Vector<Vector2> polygon;
|
Vector<Vector2> polygon;
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
@ -1849,7 +1954,17 @@ Vector<Point2> TileSet::_get_square_side_terrain_bit_polygon(Vector2i p_size, Ti
|
|||||||
return polygon;
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Point2> TileSet::_get_isometric_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
|
Vector<Point2> TileSet::_get_isometric_terrain_polygon(Vector2i p_size) {
|
||||||
|
Vector2 unit = Vector2(p_size) / 6.0;
|
||||||
|
return {
|
||||||
|
Vector2(1, 0) * unit,
|
||||||
|
Vector2(0, 1) * unit,
|
||||||
|
Vector2(-1, 0) * unit,
|
||||||
|
Vector2(0, -1) * unit,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<Point2> TileSet::_get_isometric_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
|
||||||
Vector2 unit = Vector2(p_size) / 6.0;
|
Vector2 unit = Vector2(p_size) / 6.0;
|
||||||
Vector<Vector2> polygon;
|
Vector<Vector2> polygon;
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
@ -1907,7 +2022,7 @@ Vector<Point2> TileSet::_get_isometric_corner_or_side_terrain_bit_polygon(Vector
|
|||||||
return polygon;
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Point2> TileSet::_get_isometric_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
|
Vector<Point2> TileSet::_get_isometric_corner_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
|
||||||
Vector2 unit = Vector2(p_size) / 6.0;
|
Vector2 unit = Vector2(p_size) / 6.0;
|
||||||
Vector<Vector2> polygon;
|
Vector<Vector2> polygon;
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
@ -1949,7 +2064,7 @@ Vector<Point2> TileSet::_get_isometric_corner_terrain_bit_polygon(Vector2i p_siz
|
|||||||
return polygon;
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Point2> TileSet::_get_isometric_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
|
Vector<Point2> TileSet::_get_isometric_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) {
|
||||||
Vector2 unit = Vector2(p_size) / 6.0;
|
Vector2 unit = Vector2(p_size) / 6.0;
|
||||||
Vector<Vector2> polygon;
|
Vector<Vector2> polygon;
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
@ -1983,7 +2098,30 @@ Vector<Point2> TileSet::_get_isometric_side_terrain_bit_polygon(Vector2i p_size,
|
|||||||
return polygon;
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
|
Vector<Point2> TileSet::_get_half_offset_terrain_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
|
||||||
|
Vector2 unit = Vector2(p_size) / 6.0;
|
||||||
|
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
|
||||||
|
return {
|
||||||
|
Vector2(1, 1.0 - p_overlap * 2.0) * unit,
|
||||||
|
Vector2(0, 1) * unit,
|
||||||
|
Vector2(-1, 1.0 - p_overlap * 2.0) * unit,
|
||||||
|
Vector2(-1, -1.0 + p_overlap * 2.0) * unit,
|
||||||
|
Vector2(0, -1) * unit,
|
||||||
|
Vector2(1, -1.0 + p_overlap * 2.0) * unit,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
Vector2(1, 0) * unit,
|
||||||
|
Vector2(1.0 - p_overlap * 2.0, -1) * unit,
|
||||||
|
Vector2(-1.0 + p_overlap * 2.0, -1) * unit,
|
||||||
|
Vector2(-1, 0) * unit,
|
||||||
|
Vector2(-1.0 + p_overlap * 2.0, 1) * unit,
|
||||||
|
Vector2(1.0 - p_overlap * 2.0, 1) * unit,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit) {
|
||||||
Vector<Vector2> point_list = {
|
Vector<Vector2> point_list = {
|
||||||
Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0),
|
Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0),
|
||||||
Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)),
|
Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)),
|
||||||
@ -2006,12 +2144,11 @@ Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vect
|
|||||||
};
|
};
|
||||||
|
|
||||||
Vector2 unit = Vector2(p_size) / 6.0;
|
Vector2 unit = Vector2(p_size) / 6.0;
|
||||||
for (int i = 0; i < point_list.size(); i++) {
|
|
||||||
point_list.write[i] = point_list[i] * unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector<Vector2> polygon;
|
Vector<Vector2> polygon;
|
||||||
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
|
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
|
||||||
|
for (int i = 0; i < point_list.size(); i++) {
|
||||||
|
point_list.write[i] = point_list[i] * unit;
|
||||||
|
}
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
|
case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
|
||||||
polygon.push_back(point_list[17]);
|
polygon.push_back(point_list[17]);
|
||||||
@ -2071,10 +2208,8 @@ Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vect
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
|
for (int i = 0; i < point_list.size(); i++) {
|
||||||
for (int i = 0; i < point_list.size(); i++) {
|
point_list.write[i] = Vector2(point_list[i].y, point_list[i].x) * unit;
|
||||||
point_list.write[i] = Vector2(point_list[i].y, point_list[i].x);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
|
case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
|
||||||
@ -2144,7 +2279,7 @@ Vector<Point2> TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vect
|
|||||||
return polygon;
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Point2> TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
|
Vector<Point2> TileSet::_get_half_offset_corner_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit) {
|
||||||
Vector<Vector2> point_list = {
|
Vector<Vector2> point_list = {
|
||||||
Vector2(3, 0),
|
Vector2(3, 0),
|
||||||
Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)),
|
Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)),
|
||||||
@ -2161,12 +2296,11 @@ Vector<Point2> TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_s
|
|||||||
};
|
};
|
||||||
|
|
||||||
Vector2 unit = Vector2(p_size) / 6.0;
|
Vector2 unit = Vector2(p_size) / 6.0;
|
||||||
for (int i = 0; i < point_list.size(); i++) {
|
|
||||||
point_list.write[i] = point_list[i] * unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector<Vector2> polygon;
|
Vector<Vector2> polygon;
|
||||||
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
|
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
|
||||||
|
for (int i = 0; i < point_list.size(); i++) {
|
||||||
|
point_list.write[i] = point_list[i] * unit;
|
||||||
|
}
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
|
||||||
polygon.push_back(point_list[0]);
|
polygon.push_back(point_list[0]);
|
||||||
@ -2202,10 +2336,8 @@ Vector<Point2> TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_s
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
|
for (int i = 0; i < point_list.size(); i++) {
|
||||||
for (int i = 0; i < point_list.size(); i++) {
|
point_list.write[i] = Vector2(point_list[i].y, point_list[i].x) * unit;
|
||||||
point_list.write[i] = Vector2(point_list[i].y, point_list[i].x);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
|
case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
|
||||||
@ -2251,7 +2383,7 @@ Vector<Point2> TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_s
|
|||||||
return polygon;
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Point2> TileSet::_get_half_offset_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) {
|
Vector<Point2> TileSet::_get_half_offset_side_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit) {
|
||||||
Vector<Vector2> point_list = {
|
Vector<Vector2> point_list = {
|
||||||
Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)),
|
Vector2(3, 3.0 * (1.0 - p_overlap * 2.0)),
|
||||||
Vector2(0, 3),
|
Vector2(0, 3),
|
||||||
@ -2262,12 +2394,11 @@ Vector<Point2> TileSet::_get_half_offset_side_terrain_bit_polygon(Vector2i p_siz
|
|||||||
};
|
};
|
||||||
|
|
||||||
Vector2 unit = Vector2(p_size) / 6.0;
|
Vector2 unit = Vector2(p_size) / 6.0;
|
||||||
for (int i = 0; i < point_list.size(); i++) {
|
|
||||||
point_list.write[i] = point_list[i] * unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector<Vector2> polygon;
|
Vector<Vector2> polygon;
|
||||||
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
|
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
|
||||||
|
for (int i = 0; i < point_list.size(); i++) {
|
||||||
|
point_list.write[i] = point_list[i] * unit;
|
||||||
|
}
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
|
case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
|
||||||
polygon.push_back(point_list[5]);
|
polygon.push_back(point_list[5]);
|
||||||
@ -2297,10 +2428,8 @@ Vector<Point2> TileSet::_get_half_offset_side_terrain_bit_polygon(Vector2i p_siz
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
|
for (int i = 0; i < point_list.size(); i++) {
|
||||||
for (int i = 0; i < point_list.size(); i++) {
|
point_list.write[i] = Vector2(point_list[i].y, point_list[i].x) * unit;
|
||||||
point_list.write[i] = Vector2(point_list[i].y, point_list[i].x);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
switch (p_bit) {
|
switch (p_bit) {
|
||||||
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
|
case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
|
||||||
@ -5114,36 +5243,51 @@ int TileData::get_terrain_set() const {
|
|||||||
return terrain_set;
|
return terrain_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) {
|
void TileData::set_terrain(int p_terrain) {
|
||||||
|
ERR_FAIL_COND(terrain_set < 0);
|
||||||
|
ERR_FAIL_COND(p_terrain < -1);
|
||||||
|
if (tile_set) {
|
||||||
|
ERR_FAIL_COND(p_terrain >= tile_set->get_terrains_count(terrain_set));
|
||||||
|
}
|
||||||
|
terrain = p_terrain;
|
||||||
|
emit_signal(SNAME("changed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
int TileData::get_terrain() const {
|
||||||
|
return terrain;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileData::set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) {
|
||||||
ERR_FAIL_INDEX(p_peering_bit, TileSet::CellNeighbor::CELL_NEIGHBOR_MAX);
|
ERR_FAIL_INDEX(p_peering_bit, TileSet::CellNeighbor::CELL_NEIGHBOR_MAX);
|
||||||
ERR_FAIL_COND(terrain_set < 0);
|
ERR_FAIL_COND(terrain_set < 0);
|
||||||
ERR_FAIL_COND(p_terrain_index < -1);
|
ERR_FAIL_COND(p_terrain_index < -1);
|
||||||
if (tile_set) {
|
if (tile_set) {
|
||||||
ERR_FAIL_COND(p_terrain_index >= tile_set->get_terrains_count(terrain_set));
|
ERR_FAIL_COND(p_terrain_index >= tile_set->get_terrains_count(terrain_set));
|
||||||
ERR_FAIL_COND(!is_valid_peering_bit_terrain(p_peering_bit));
|
ERR_FAIL_COND(!is_valid_terrain_peering_bit(p_peering_bit));
|
||||||
}
|
}
|
||||||
terrain_peering_bits[p_peering_bit] = p_terrain_index;
|
terrain_peering_bits[p_peering_bit] = p_terrain_index;
|
||||||
emit_signal(SNAME("changed"));
|
emit_signal(SNAME("changed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
int TileData::get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const {
|
int TileData::get_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const {
|
||||||
ERR_FAIL_COND_V(!is_valid_peering_bit_terrain(p_peering_bit), -1);
|
ERR_FAIL_COND_V(!is_valid_terrain_peering_bit(p_peering_bit), -1);
|
||||||
return terrain_peering_bits[p_peering_bit];
|
return terrain_peering_bits[p_peering_bit];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const {
|
bool TileData::is_valid_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const {
|
||||||
ERR_FAIL_COND_V(!tile_set, false);
|
ERR_FAIL_COND_V(!tile_set, false);
|
||||||
|
|
||||||
return tile_set->is_valid_peering_bit_terrain(terrain_set, p_peering_bit);
|
return tile_set->is_valid_terrain_peering_bit(terrain_set, p_peering_bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
TileSet::TerrainsPattern TileData::get_terrains_pattern() const {
|
TileSet::TerrainsPattern TileData::get_terrains_pattern() const {
|
||||||
ERR_FAIL_COND_V(!tile_set, TileSet::TerrainsPattern());
|
ERR_FAIL_COND_V(!tile_set, TileSet::TerrainsPattern());
|
||||||
|
|
||||||
TileSet::TerrainsPattern output(tile_set, terrain_set);
|
TileSet::TerrainsPattern output(tile_set, terrain_set);
|
||||||
|
output.set_terrain(terrain);
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
if (tile_set->is_valid_peering_bit_terrain(terrain_set, TileSet::CellNeighbor(i))) {
|
if (tile_set->is_valid_terrain_peering_bit(terrain_set, TileSet::CellNeighbor(i))) {
|
||||||
output.set_terrain(TileSet::CellNeighbor(i), get_peering_bit_terrain(TileSet::CellNeighbor(i)));
|
output.set_terrain_peering_bit(TileSet::CellNeighbor(i), get_terrain_peering_bit(TileSet::CellNeighbor(i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
@ -5293,7 +5437,7 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
|
|||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (components[1] == TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]) {
|
if (components[1] == TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]) {
|
||||||
set_peering_bit_terrain(bit, p_value);
|
set_terrain_peering_bit(bit, p_value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5455,9 +5599,9 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const {
|
|||||||
p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
|
p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
|
||||||
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
|
||||||
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
|
||||||
if (is_valid_peering_bit_terrain(bit)) {
|
if (is_valid_terrain_peering_bit(bit)) {
|
||||||
property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
|
property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
|
||||||
if (get_peering_bit_terrain(bit) == -1) {
|
if (get_terrain_peering_bit(bit) == -1) {
|
||||||
property_info.usage ^= PROPERTY_USAGE_STORAGE;
|
property_info.usage ^= PROPERTY_USAGE_STORAGE;
|
||||||
}
|
}
|
||||||
p_list->push_back(property_info);
|
p_list->push_back(property_info);
|
||||||
@ -5531,8 +5675,10 @@ void TileData::_bind_methods() {
|
|||||||
// Terrain
|
// Terrain
|
||||||
ClassDB::bind_method(D_METHOD("set_terrain_set", "terrain_set"), &TileData::set_terrain_set);
|
ClassDB::bind_method(D_METHOD("set_terrain_set", "terrain_set"), &TileData::set_terrain_set);
|
||||||
ClassDB::bind_method(D_METHOD("get_terrain_set"), &TileData::get_terrain_set);
|
ClassDB::bind_method(D_METHOD("get_terrain_set"), &TileData::get_terrain_set);
|
||||||
ClassDB::bind_method(D_METHOD("set_peering_bit_terrain", "peering_bit", "terrain"), &TileData::set_peering_bit_terrain);
|
ClassDB::bind_method(D_METHOD("set_terrain", "terrain"), &TileData::set_terrain);
|
||||||
ClassDB::bind_method(D_METHOD("get_peering_bit_terrain", "peering_bit"), &TileData::get_peering_bit_terrain);
|
ClassDB::bind_method(D_METHOD("get_terrain"), &TileData::get_terrain);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_terrain_peering_bit", "peering_bit", "terrain"), &TileData::set_terrain_peering_bit);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_terrain_peering_bit", "peering_bit"), &TileData::get_terrain_peering_bit);
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
ClassDB::bind_method(D_METHOD("set_navigation_polygon", "layer_id", "navigation_polygon"), &TileData::set_navigation_polygon);
|
ClassDB::bind_method(D_METHOD("set_navigation_polygon", "layer_id", "navigation_polygon"), &TileData::set_navigation_polygon);
|
||||||
@ -5560,6 +5706,7 @@ void TileData::_bind_methods() {
|
|||||||
|
|
||||||
ADD_GROUP("Terrains", "");
|
ADD_GROUP("Terrains", "");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "terrain_set"), "set_terrain_set", "get_terrain_set");
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "terrain_set"), "set_terrain_set", "get_terrain_set");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "terrain"), "set_terrain", "get_terrain");
|
||||||
|
|
||||||
ADD_GROUP("Miscellaneous", "");
|
ADD_GROUP("Miscellaneous", "");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "probability"), "set_probability", "get_probability");
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "probability"), "set_probability", "get_probability");
|
||||||
|
@ -265,6 +265,7 @@ public:
|
|||||||
|
|
||||||
class TerrainsPattern {
|
class TerrainsPattern {
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
|
int terrain = -1;
|
||||||
int bits[TileSet::CELL_NEIGHBOR_MAX];
|
int bits[TileSet::CELL_NEIGHBOR_MAX];
|
||||||
bool is_valid_bit[TileSet::CELL_NEIGHBOR_MAX];
|
bool is_valid_bit[TileSet::CELL_NEIGHBOR_MAX];
|
||||||
|
|
||||||
@ -277,11 +278,14 @@ public:
|
|||||||
bool operator<(const TerrainsPattern &p_terrains_pattern) const;
|
bool operator<(const TerrainsPattern &p_terrains_pattern) const;
|
||||||
bool operator==(const TerrainsPattern &p_terrains_pattern) const;
|
bool operator==(const TerrainsPattern &p_terrains_pattern) const;
|
||||||
|
|
||||||
void set_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain);
|
void set_terrain(int p_terrain);
|
||||||
int get_terrain(TileSet::CellNeighbor p_peering_bit) const;
|
int get_terrain() const;
|
||||||
|
|
||||||
void set_terrains_from_array(Array p_terrains);
|
void set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain);
|
||||||
Array get_terrains_as_array() const;
|
int get_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const;
|
||||||
|
|
||||||
|
void from_array(Array p_terrains);
|
||||||
|
Array as_array() const;
|
||||||
|
|
||||||
TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set);
|
TerrainsPattern(const TileSet *p_tile_set, int p_terrain_set);
|
||||||
TerrainsPattern() {}
|
TerrainsPattern() {}
|
||||||
@ -333,7 +337,8 @@ private:
|
|||||||
};
|
};
|
||||||
Vector<TerrainSet> terrain_sets;
|
Vector<TerrainSet> terrain_sets;
|
||||||
|
|
||||||
HashMap<TerrainMode, HashMap<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes;
|
HashMap<TerrainMode, Ref<ArrayMesh>> terrain_meshes;
|
||||||
|
HashMap<TerrainMode, HashMap<CellNeighbor, Ref<ArrayMesh>>> terrain_peering_bits_meshes;
|
||||||
bool terrain_bits_meshes_dirty = true;
|
bool terrain_bits_meshes_dirty = true;
|
||||||
|
|
||||||
LocalVector<RBMap<TileSet::TerrainsPattern, RBSet<TileMapCell>>> per_terrain_pattern_tiles; // Cached data.
|
LocalVector<RBMap<TileSet::TerrainsPattern, RBSet<TileMapCell>>> per_terrain_pattern_tiles; // Cached data.
|
||||||
@ -371,17 +376,20 @@ private:
|
|||||||
RBMap<Array, Array> alternative_level_proxies;
|
RBMap<Array, Array> alternative_level_proxies;
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
Vector<Point2> _get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
Vector<Point2> _get_square_terrain_polygon(Vector2i p_size);
|
||||||
Vector<Point2> _get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
Vector<Point2> _get_square_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
||||||
Vector<Point2> _get_square_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
Vector<Point2> _get_square_corner_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
||||||
|
Vector<Point2> _get_square_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
||||||
|
|
||||||
Vector<Point2> _get_isometric_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
Vector<Point2> _get_isometric_terrain_polygon(Vector2i p_size);
|
||||||
Vector<Point2> _get_isometric_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
Vector<Point2> _get_isometric_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
||||||
Vector<Point2> _get_isometric_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
Vector<Point2> _get_isometric_corner_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
||||||
|
Vector<Point2> _get_isometric_side_terrain_peering_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit);
|
||||||
|
|
||||||
Vector<Point2> _get_half_offset_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
|
Vector<Point2> _get_half_offset_terrain_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
|
||||||
Vector<Point2> _get_half_offset_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
|
Vector<Point2> _get_half_offset_corner_or_side_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit);
|
||||||
Vector<Point2> _get_half_offset_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis);
|
Vector<Point2> _get_half_offset_corner_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit);
|
||||||
|
Vector<Point2> _get_half_offset_side_terrain_peering_bit_polygon(Vector2i p_size, float p_overlap, TileSet::TileOffsetAxis p_offset_axis, TileSet::CellNeighbor p_bit);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
@ -454,8 +462,8 @@ public:
|
|||||||
String get_terrain_name(int p_terrain_set, int p_terrain_index) const;
|
String get_terrain_name(int p_terrain_set, int p_terrain_index) const;
|
||||||
void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color);
|
void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color);
|
||||||
Color get_terrain_color(int p_terrain_set, int p_terrain_index) const;
|
Color get_terrain_color(int p_terrain_set, int p_terrain_index) const;
|
||||||
bool is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const;
|
bool is_valid_terrain_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const;
|
||||||
bool is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const;
|
bool is_valid_terrain_peering_bit(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const;
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
int get_navigation_layers_count() const;
|
int get_navigation_layers_count() const;
|
||||||
@ -516,7 +524,8 @@ public:
|
|||||||
Vector<Vector2> get_tile_shape_polygon();
|
Vector<Vector2> get_tile_shape_polygon();
|
||||||
void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>());
|
void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>());
|
||||||
|
|
||||||
Vector<Point2> get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit);
|
Vector<Point2> get_terrain_polygon(int p_terrain_set);
|
||||||
|
Vector<Point2> get_terrain_peering_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit);
|
||||||
void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data);
|
void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data);
|
||||||
Vector<Vector<Ref<Texture2D>>> generate_terrains_icons(Size2i p_size);
|
Vector<Vector<Ref<Texture2D>>> generate_terrains_icons(Size2i p_size);
|
||||||
|
|
||||||
@ -798,6 +807,7 @@ private:
|
|||||||
|
|
||||||
// Terrain
|
// Terrain
|
||||||
int terrain_set = -1;
|
int terrain_set = -1;
|
||||||
|
int terrain = -1;
|
||||||
int terrain_peering_bits[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
|
int terrain_peering_bits[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
@ -887,9 +897,11 @@ public:
|
|||||||
// Terrain
|
// Terrain
|
||||||
void set_terrain_set(int p_terrain_id);
|
void set_terrain_set(int p_terrain_id);
|
||||||
int get_terrain_set() const;
|
int get_terrain_set() const;
|
||||||
void set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_id);
|
void set_terrain(int p_terrain_id);
|
||||||
int get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
|
int get_terrain() const;
|
||||||
bool is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
|
void set_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit, int p_terrain_id);
|
||||||
|
int get_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const;
|
||||||
|
bool is_valid_terrain_peering_bit(TileSet::CellNeighbor p_peering_bit) const;
|
||||||
|
|
||||||
TileSet::TerrainsPattern get_terrains_pattern() const; // Not exposed.
|
TileSet::TerrainsPattern get_terrains_pattern() const; // Not exposed.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user