Files
project-hood/addons/TileMapDual/TerrainDual.gd

195 lines
5.4 KiB
GDScript

##[br] Reads a TileSet and dictates which tiles in the display map
##[br] match up with its neighbors in the world map
class_name TerrainDual
extends Resource
# Functions are ordered top to bottom in the transformation pipeline
## Maps a TileSet to a Neighborhood.
static func tileset_neighborhood(tile_set: TileSet) -> Neighborhood:
return GRID_NEIGHBORHOODS[Display.tileset_gridshape(tile_set)]
## Maps a GridShape to a Neighborhood.
const GRID_NEIGHBORHOODS = {
Display.GridShape.SQUARE: Neighborhood.SQUARE,
Display.GridShape.ISO: Neighborhood.ISOMETRIC,
Display.GridShape.HALF_OFF_HORI: Neighborhood.TRIANGLE_HORIZONTAL,
Display.GridShape.HALF_OFF_VERT: Neighborhood.TRIANGLE_VERTICAL,
Display.GridShape.HEX_HORI: Neighborhood.TRIANGLE_HORIZONTAL,
Display.GridShape.HEX_VERT: Neighborhood.TRIANGLE_VERTICAL,
}
## A specific neighborhood that the Display tiles will look at.
enum Neighborhood {
SQUARE,
ISOMETRIC,
TRIANGLE_HORIZONTAL,
TRIANGLE_VERTICAL,
}
## Maps a Neighborhood to a set of atlas terrain neighbors.
const NEIGHBORHOOD_LAYERS := {
Neighborhood.SQUARE: [
{ # []
'terrain_neighborhood': [
TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER,
TileSet.CELL_NEIGHBOR_TOP_RIGHT_CORNER,
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_CORNER,
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER,
],
'display_to_world_neighborhood': [
[TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER],
[TileSet.CELL_NEIGHBOR_TOP_SIDE],
[TileSet.CELL_NEIGHBOR_LEFT_SIDE],
[],
],
},
],
Neighborhood.ISOMETRIC: [
{ # <>
'terrain_neighborhood': [
TileSet.CELL_NEIGHBOR_TOP_CORNER,
TileSet.CELL_NEIGHBOR_RIGHT_CORNER,
TileSet.CELL_NEIGHBOR_LEFT_CORNER,
TileSet.CELL_NEIGHBOR_BOTTOM_CORNER,
],
'display_to_world_neighborhood': [
[TileSet.CELL_NEIGHBOR_TOP_CORNER],
[TileSet.CELL_NEIGHBOR_TOP_RIGHT_SIDE],
[TileSet.CELL_NEIGHBOR_TOP_LEFT_SIDE],
[],
],
},
],
Neighborhood.TRIANGLE_HORIZONTAL: [
{ # v
'terrain_neighborhood': [
TileSet.CELL_NEIGHBOR_BOTTOM_CORNER,
TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER,
TileSet.CELL_NEIGHBOR_TOP_RIGHT_CORNER,
],
'display_to_world_neighborhood': [
[],
[TileSet.CELL_NEIGHBOR_TOP_LEFT_SIDE],
[TileSet.CELL_NEIGHBOR_TOP_RIGHT_SIDE],
],
},
{ # ^
'terrain_neighborhood': [
TileSet.CELL_NEIGHBOR_TOP_CORNER,
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_CORNER,
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER,
],
'display_to_world_neighborhood': [
[TileSet.CELL_NEIGHBOR_TOP_LEFT_SIDE],
[TileSet.CELL_NEIGHBOR_LEFT_SIDE],
[],
],
},
],
# TODO: this is just TRIANGLE_HORIZONTAL but transposed. this can be refactored.
Neighborhood.TRIANGLE_VERTICAL: [
{ # >
'terrain_neighborhood': [
TileSet.CELL_NEIGHBOR_RIGHT_CORNER,
TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER,
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_CORNER,
],
'display_to_world_neighborhood': [
[],
[TileSet.CELL_NEIGHBOR_TOP_LEFT_SIDE],
[TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_SIDE],
],
},
{ # <
'terrain_neighborhood': [
TileSet.CELL_NEIGHBOR_LEFT_CORNER,
TileSet.CELL_NEIGHBOR_TOP_RIGHT_CORNER,
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER,
],
'display_to_world_neighborhood': [
[TileSet.CELL_NEIGHBOR_TOP_LEFT_SIDE],
[TileSet.CELL_NEIGHBOR_TOP_SIDE],
[],
],
},
],
}
## The Neighborhood type of this TerrainDual.
var neighborhood: Neighborhood
## Maps a terrain type to its sprite as registered in the TerrainDual.
var terrains: Dictionary
## The TerrainLayers for this TerrainDual.
var layers: Array
var _tileset_watcher: TileSetWatcher
func _init(tileset_watcher: TileSetWatcher) -> void:
_tileset_watcher = tileset_watcher
_tileset_watcher.terrains_changed.connect(_changed, 1)
_changed()
##[br] Emitted when any of the terrains change.
##[br] NOTE: Prefer connecting to TerrainDual.changed instead of TileSetWatcher.terrains_changed.
func _changed() -> void:
#print('SIGNAL EMITTED: changed(%s)' % {})
read_tileset(_tileset_watcher.tile_set)
emit_changed()
## Create rules for every atlas in a TileSet.
func read_tileset(tile_set: TileSet) -> void:
terrains = {}
layers = []
neighborhood = Neighborhood.SQUARE # default
if tile_set == null:
return
neighborhood = tileset_neighborhood(tile_set)
layers = NEIGHBORHOOD_LAYERS[neighborhood].map(TerrainLayer.new)
for i in tile_set.get_source_count():
var sid := tile_set.get_source_id(i)
var src := tile_set.get_source(sid)
if src is not TileSetAtlasSource:
continue
read_atlas(src, sid)
## Create rules for every tile in an atlas.
func read_atlas(atlas: TileSetAtlasSource, sid: int) -> void:
var size = atlas.get_atlas_grid_size()
for y in size.y:
for x in size.x:
var tile := Vector2i(x, y)
# Take only existing tiles
if not atlas.has_tile(tile):
continue
read_tile(atlas, sid, tile)
## Add a new rule for a specific tile in an atlas.
func read_tile(atlas: TileSetAtlasSource, sid: int, tile: Vector2i) -> void:
var data := atlas.get_tile_data(tile, 0)
var mapping := {'sid': sid, 'tile': tile}
var terrain_set := data.terrain_set
if terrain_set != 0:
push_warning(
"The tile at %s has a terrain set of %d. Only terrain set 0 is supported." % [mapping, terrain_set]
)
return
var terrain := data.terrain
if terrain != -1:
if not terrain in terrains:
terrains[terrain] = mapping
var filters = NEIGHBORHOOD_LAYERS[neighborhood]
for i in layers.size():
var layer: TerrainLayer = layers[i]
layer._register_tile(data, mapping)