Allow updating TileMap cells using surrounding terrains

This commit is contained in:
Gilles Roudière 2021-10-21 16:42:06 +02:00
parent ef93e1381b
commit d1aef45072
7 changed files with 871 additions and 756 deletions

View file

@ -301,6 +301,66 @@ int TileSet::get_next_source_id() const {
return next_source_id;
}
void TileSet::_update_terrains_cache() {
if (terrains_cache_dirty) {
// Organizes tiles into structures.
per_terrain_pattern_tiles.resize(terrain_sets.size());
for (int i = 0; i < (int)per_terrain_pattern_tiles.size(); i++) {
per_terrain_pattern_tiles[i].clear();
}
for (const KeyValue<int, Ref<TileSetSource>> &kv : sources) {
Ref<TileSetSource> source = kv.value;
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
for (int tile_index = 0; tile_index < source->get_tiles_count(); tile_index++) {
Vector2i tile_id = source->get_tile_id(tile_index);
for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) {
int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index);
// Executed for each tile_data.
TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
int terrain_set = tile_data->get_terrain_set();
if (terrain_set >= 0) {
TileMapCell cell;
cell.source_id = kv.key;
cell.set_atlas_coords(tile_id);
cell.alternative_tile = alternative_id;
TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
// Terrain bits.
for (int i = 0; i < terrains_pattern.size(); i++) {
int terrain = terrains_pattern[i];
if (terrain >= 0) {
per_terrain_pattern_tiles[terrain_set][terrains_pattern].insert(cell);
}
}
}
}
}
}
}
// Add the empty cell in the possible patterns and cells.
for (int i = 0; i < terrain_sets.size(); i++) {
TileSet::TerrainsPattern empty_pattern;
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
if (is_valid_peering_bit_terrain(i, TileSet::CellNeighbor(j))) {
empty_pattern.push_back(-1);
}
}
TileMapCell empty_cell;
empty_cell.source_id = TileSet::INVALID_SOURCE;
empty_cell.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
empty_cell.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
per_terrain_pattern_tiles[i][empty_pattern].insert(empty_cell);
}
terrains_cache_dirty = false;
}
}
void TileSet::_compute_next_source_id() {
while (sources.has(next_source_id)) {
next_source_id = (next_source_id + 1) % 1073741824; // 2 ** 30
@ -321,6 +381,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source
sources[new_source_id]->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed));
terrains_cache_dirty = true;
emit_changed();
return new_source_id;
@ -336,6 +397,7 @@ void TileSet::remove_source(int p_source_id) {
source_ids.erase(p_source_id);
source_ids.sort();
terrains_cache_dirty = true;
emit_changed();
}
@ -357,6 +419,7 @@ void TileSet::set_source_id(int p_source_id, int p_new_source_id) {
_compute_next_source_id();
terrains_cache_dirty = true;
emit_changed();
}
@ -545,6 +608,7 @@ void TileSet::add_terrain_set(int p_index) {
}
notify_property_list_changed();
terrains_cache_dirty = true;
emit_changed();
}
@ -557,6 +621,7 @@ void TileSet::move_terrain_set(int p_from_index, int p_to_pos) {
source.value->move_terrain_set(p_from_index, p_to_pos);
}
notify_property_list_changed();
terrains_cache_dirty = true;
emit_changed();
}
@ -567,6 +632,7 @@ void TileSet::remove_terrain_set(int p_index) {
source.value->remove_terrain_set(p_index);
}
notify_property_list_changed();
terrains_cache_dirty = true;
emit_changed();
}
@ -578,6 +644,7 @@ void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode
}
notify_property_list_changed();
terrains_cache_dirty = true;
emit_changed();
}
@ -612,6 +679,7 @@ void TileSet::add_terrain(int p_terrain_set, int p_index) {
}
notify_property_list_changed();
terrains_cache_dirty = true;
emit_changed();
}
@ -627,6 +695,7 @@ void TileSet::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
source.value->move_terrain(p_terrain_set, p_from_index, p_to_pos);
}
notify_property_list_changed();
terrains_cache_dirty = true;
emit_changed();
}
@ -640,6 +709,7 @@ void TileSet::remove_terrain(int p_terrain_set, int p_index) {
source.value->remove_terrain(p_terrain_set, p_index);
}
notify_property_list_changed();
terrains_cache_dirty = true;
emit_changed();
}
@ -1196,6 +1266,73 @@ int TileSet::get_patterns_count() {
return patterns.size();
}
Set<TileSet::TerrainsPattern> TileSet::get_terrains_pattern_set(int p_terrain_set) {
ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileSet::TerrainsPattern>());
_update_terrains_cache();
Set<TileSet::TerrainsPattern> output;
for (KeyValue<TileSet::TerrainsPattern, Set<TileMapCell>> kv : per_terrain_pattern_tiles[p_terrain_set]) {
output.insert(kv.key);
}
return output;
}
Set<TileMapCell> TileSet::get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) {
ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileMapCell>());
_update_terrains_cache();
return per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern];
}
TileMapCell TileSet::get_random_tile_from_pattern(int p_terrain_set, TileSet::TerrainsPattern p_terrain_tile_pattern) {
ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), TileMapCell());
_update_terrains_cache();
// Count the sum of probabilities.
double sum = 0.0;
Set<TileMapCell> set = per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern];
for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
if (E->get().source_id >= 0) {
Ref<TileSetSource> source = sources[E->get().source_id];
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
sum += tile_data->get_probability();
} else {
sum += 1.0;
}
} else {
sum += 1.0;
}
}
// Generate a random number.
double count = 0.0;
double picked = Math::random(0.0, sum);
// Pick the tile.
for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
if (E->get().source_id >= 0) {
Ref<TileSetSource> source = sources[E->get().source_id];
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
count += tile_data->get_probability();
} else {
count += 1.0;
}
} else {
count += 1.0;
}
if (count >= picked) {
return E->get();
}
}
ERR_FAIL_V(TileMapCell());
}
Vector<Vector2> TileSet::get_tile_shape_polygon() {
Vector<Vector2> points;
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
@ -1510,6 +1647,7 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) {
}
void TileSet::_source_changed() {
terrains_cache_dirty = true;
emit_changed();
}
@ -4743,6 +4881,18 @@ bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit)
return tile_set->is_valid_peering_bit_terrain(terrain_set, p_peering_bit);
}
TileSet::TerrainsPattern TileData::get_terrains_pattern() const {
ERR_FAIL_COND_V(!tile_set, TileSet::TerrainsPattern());
TileSet::TerrainsPattern output;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
if (tile_set->is_valid_peering_bit_terrain(terrain_set, TileSet::CellNeighbor(i))) {
output.push_back(get_peering_bit_terrain(TileSet::CellNeighbor(i)));
}
}
return output;
}
// Navigation
void TileData::set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon) {
ERR_FAIL_INDEX(p_layer_id, navigation.size());