195 lines
5.4 KiB
GDScript
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)
|