Merge pull request #61809 from groud/terrain_center_bit

This commit is contained in:
Rémi Verschelde 2022-06-13 17:06:34 +02:00 committed by GitHub
commit d4f31e201d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1070 additions and 533 deletions

View File

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

View File

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

View 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

View 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

View File

@ -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]);
} }
} }

View File

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

View File

@ -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();

View File

@ -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]));
} }
} }

View File

@ -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]));
} }
} }

View File

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

View File

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

View File

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

View File

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