working keyboard and plugin for better tilemaps
This commit is contained in:
322
addons/TileMapDual/TerrainPreset.gd
Normal file
322
addons/TileMapDual/TerrainPreset.gd
Normal file
@@ -0,0 +1,322 @@
|
||||
## Functions for automatically generating terrains for an atlas.
|
||||
class_name TerrainPreset
|
||||
|
||||
|
||||
## Maps a Neighborhood to a Topology.
|
||||
const NEIGHBORHOOD_TOPOLOGIES := {
|
||||
TerrainDual.Neighborhood.SQUARE: Topology.SQUARE,
|
||||
TerrainDual.Neighborhood.ISOMETRIC: Topology.SQUARE,
|
||||
TerrainDual.Neighborhood.TRIANGLE_HORIZONTAL: Topology.TRIANGLE,
|
||||
TerrainDual.Neighborhood.TRIANGLE_VERTICAL: Topology.TRIANGLE,
|
||||
}
|
||||
|
||||
|
||||
## Determines the available Terrain presets for a certain Atlas.
|
||||
enum Topology {
|
||||
SQUARE,
|
||||
TRIANGLE,
|
||||
}
|
||||
|
||||
|
||||
## Maps a Neighborhood to a preset of the specified name.
|
||||
static func neighborhood_preset(
|
||||
neighborhood: TerrainDual.Neighborhood,
|
||||
preset_name: String = 'Standard'
|
||||
) -> Dictionary:
|
||||
var topology: Topology = NEIGHBORHOOD_TOPOLOGIES[neighborhood]
|
||||
# TODO: test when the preset doesn't exist
|
||||
var available_presets = PRESETS[topology]
|
||||
if preset_name not in available_presets:
|
||||
return {'size': Vector2i.ONE, 'layers': []}
|
||||
var out: Dictionary = available_presets[preset_name].duplicate(true)
|
||||
# All Horizontal neighborhoods can be transposed to Vertical
|
||||
if neighborhood == TerrainDual.Neighborhood.TRIANGLE_VERTICAL:
|
||||
out.size = Util.transpose_vec(out.size)
|
||||
out.fg = Util.transpose_vec(out.fg)
|
||||
out.bg = Util.transpose_vec(out.bg)
|
||||
for seq in out.layers:
|
||||
for i in seq.size():
|
||||
seq[i] = Util.transpose_vec(seq[i])
|
||||
return out
|
||||
|
||||
|
||||
## Contains all of the builtin Terrain presets for each topology
|
||||
const PRESETS := {
|
||||
Topology.SQUARE: {
|
||||
'Standard': {
|
||||
'size': Vector2i(4, 4),
|
||||
'bg': Vector2i(0, 3),
|
||||
'fg': Vector2i(2, 1),
|
||||
'layers': [
|
||||
[ # []
|
||||
Vector2i(0, 3),
|
||||
Vector2i(3, 3),
|
||||
Vector2i(0, 2),
|
||||
Vector2i(1, 2),
|
||||
Vector2i(0, 0),
|
||||
Vector2i(3, 2),
|
||||
Vector2i(2, 3),
|
||||
Vector2i(3, 1),
|
||||
Vector2i(1, 3),
|
||||
Vector2i(0, 1),
|
||||
Vector2i(1, 0),
|
||||
Vector2i(2, 2),
|
||||
Vector2i(3, 0),
|
||||
Vector2i(2, 0),
|
||||
Vector2i(1, 1),
|
||||
Vector2i(2, 1),
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
Topology.TRIANGLE: {
|
||||
'Standard': {
|
||||
'size': Vector2i(4, 4),
|
||||
'bg': Vector2i(0, 0),
|
||||
'fg': Vector2i(0, 2),
|
||||
'layers': [
|
||||
[ # v
|
||||
Vector2i(0, 1),
|
||||
Vector2i(2, 3),
|
||||
Vector2i(3, 1),
|
||||
Vector2i(1, 3),
|
||||
Vector2i(1, 1),
|
||||
Vector2i(3, 3),
|
||||
Vector2i(2, 1),
|
||||
Vector2i(0, 3),
|
||||
],
|
||||
[ # ^
|
||||
Vector2i(0, 0),
|
||||
Vector2i(2, 2),
|
||||
Vector2i(3, 0),
|
||||
Vector2i(1, 2),
|
||||
Vector2i(1, 0),
|
||||
Vector2i(3, 2),
|
||||
Vector2i(2, 0),
|
||||
Vector2i(0, 2),
|
||||
],
|
||||
]
|
||||
},
|
||||
# Old template.
|
||||
# a bit inconvenient to use for Brick (Half-Off Square) tilesets.
|
||||
'Winged': {
|
||||
'size': Vector2i(4, 4),
|
||||
'bg': Vector2i(0, 0),
|
||||
'fg': Vector2i(0, 2),
|
||||
'layers': [
|
||||
[ # v
|
||||
Vector2i(0, 1),
|
||||
Vector2i(2, 1),
|
||||
Vector2i(3, 1),
|
||||
Vector2i(1, 3),
|
||||
Vector2i(1, 1),
|
||||
Vector2i(3, 3),
|
||||
Vector2i(2, 3),
|
||||
Vector2i(0, 3),
|
||||
],
|
||||
[ # ^
|
||||
Vector2i(0, 0),
|
||||
Vector2i(2, 0),
|
||||
Vector2i(3, 0),
|
||||
Vector2i(1, 2),
|
||||
Vector2i(1, 0),
|
||||
Vector2i(3, 2),
|
||||
Vector2i(2, 2),
|
||||
Vector2i(0, 2),
|
||||
],
|
||||
]
|
||||
},
|
||||
# Old template.
|
||||
# The gaps between triangles made them harder to align.
|
||||
'Alternating': {
|
||||
'size': Vector2i(4, 4),
|
||||
'bg': Vector2i(0, 0),
|
||||
'fg': Vector2i(0, 2),
|
||||
'layers': [
|
||||
[ # v
|
||||
Vector2i(0, 0),
|
||||
Vector2i(2, 0),
|
||||
Vector2i(3, 1),
|
||||
Vector2i(1, 3),
|
||||
Vector2i(1, 1),
|
||||
Vector2i(3, 3),
|
||||
Vector2i(2, 2),
|
||||
Vector2i(0, 2),
|
||||
],
|
||||
[ # ^
|
||||
Vector2i(0, 1),
|
||||
Vector2i(2, 1),
|
||||
Vector2i(3, 0),
|
||||
Vector2i(1, 2),
|
||||
Vector2i(1, 0),
|
||||
Vector2i(3, 2),
|
||||
Vector2i(2, 3),
|
||||
Vector2i(0, 3),
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
##[br] Would you like to automatically create tiles in the atlas?
|
||||
##[br]
|
||||
##[br] NOTE: Assumes urm.create_action() was called. Does not actually do anything until urm.commit_action() is called.
|
||||
##[br] NOTE: Assumes atlas only has auto-generated tiles. Does not save peering bit information or anything else for undo/redo.
|
||||
static func write_default_preset(urm: EditorUndoRedoManager, tile_set: TileSet, atlas: TileSetAtlasSource) -> void:
|
||||
#print('writing default')
|
||||
var neighborhood := TerrainDual.tileset_neighborhood(tile_set)
|
||||
var terrain := new_terrain(
|
||||
urm,
|
||||
tile_set,
|
||||
atlas.texture.resource_path.get_file()
|
||||
)
|
||||
write_preset(
|
||||
urm,
|
||||
atlas,
|
||||
neighborhood,
|
||||
terrain,
|
||||
)
|
||||
|
||||
|
||||
##[br] Creates terrain set 0 (the primary terrain set) and terrain 0 (the 'any' terrain)
|
||||
##[br]
|
||||
##[br] NOTE: Assumes urm.create_action() was called. Does not actually do anything until urm.commit_action() is called.
|
||||
static func init_terrains(urm: EditorUndoRedoManager, tile_set: TileSet) -> void:
|
||||
urm.add_do_method(TerrainPreset, "_do_init_terrains", tile_set)
|
||||
urm.add_undo_method(TerrainPreset, "_undo_init_terrains", tile_set)
|
||||
|
||||
static func _do_init_terrains(tile_set: TileSet) -> void:
|
||||
tile_set.add_terrain_set()
|
||||
tile_set.set_terrain_set_mode(0, TileSet.TERRAIN_MODE_MATCH_CORNERS)
|
||||
tile_set.add_terrain(0)
|
||||
tile_set.set_terrain_name(0, 0, "<any>")
|
||||
tile_set.set_terrain_color(0, 0, Color.VIOLET)
|
||||
|
||||
static func _undo_init_terrains(tile_set: TileSet) -> void:
|
||||
tile_set.remove_terrain_set(0)
|
||||
|
||||
|
||||
##[br] Adds a new terrain type to terrain set 0 for the sprites to use.
|
||||
##[br]
|
||||
##[br] NOTE: Assumes urm.create_action() was called. Does not actually do anything until urm.commit_action() is called.
|
||||
static func new_terrain(urm: EditorUndoRedoManager, tile_set: TileSet, terrain_name: String) -> int:
|
||||
var terrain: int
|
||||
if tile_set.get_terrain_sets_count() == 0:
|
||||
init_terrains(urm, tile_set)
|
||||
terrain = 1
|
||||
else:
|
||||
terrain = tile_set.get_terrains_count(0)
|
||||
urm.add_do_method(TerrainPreset, "_do_new_terrain", tile_set, terrain_name)
|
||||
urm.add_undo_method(TerrainPreset, "_undo_new_terrain", tile_set)
|
||||
return terrain
|
||||
|
||||
static func _do_new_terrain(tile_set: TileSet, terrain_name: String) -> void:
|
||||
tile_set.add_terrain(0)
|
||||
var terrain := tile_set.get_terrains_count(0) - 1
|
||||
tile_set.set_terrain_name(0, terrain, "FG -%s" % terrain_name)
|
||||
|
||||
static func _undo_new_terrain(tile_set: TileSet) -> void:
|
||||
var terrain := tile_set.get_terrains_count(0) - 1
|
||||
tile_set.remove_terrain(0, terrain)
|
||||
|
||||
|
||||
##[br] Takes a preset and writes it onto the given atlas, replacing the previous configuration.
|
||||
##[br] ARGUMENTS:
|
||||
##[br] - atlas: the atlas source to apply the preset to.
|
||||
##[br] - filters: the neighborhood filter
|
||||
##[br]
|
||||
##[br] NOTE: Assumes urm.create_action() was called. Does not actually do anything until urm.commit_action() is called.
|
||||
##[br] NOTE: Assumes atlas only has auto-generated tiles. Does not save peering bit information or anything else for undo/redo.
|
||||
static func write_preset(
|
||||
urm: EditorUndoRedoManager,
|
||||
atlas: TileSetAtlasSource,
|
||||
neighborhood: TerrainDual.Neighborhood,
|
||||
terrain_foreground: int,
|
||||
terrain_background: int = 0,
|
||||
preset: Dictionary = neighborhood_preset(neighborhood),
|
||||
) -> void:
|
||||
clear_and_divide_atlas(urm, atlas, preset.size)
|
||||
urm.add_do_method(TerrainPreset, '_do_write_preset', atlas, neighborhood, terrain_foreground, terrain_background, preset)
|
||||
|
||||
static func _do_write_preset(
|
||||
atlas: TileSetAtlasSource,
|
||||
neighborhood: TerrainDual.Neighborhood,
|
||||
terrain_foreground: int,
|
||||
terrain_background: int,
|
||||
preset: Dictionary,
|
||||
) -> void:
|
||||
var layers: Array = TerrainDual.NEIGHBORHOOD_LAYERS[neighborhood]
|
||||
# Set peering bits
|
||||
var sequences: Array = preset.layers
|
||||
for j in layers.size():
|
||||
var terrain_neighborhood = layers[j].terrain_neighborhood
|
||||
var sequence: Array = sequences[j]
|
||||
for i in sequence.size():
|
||||
var tile: Vector2i = sequence[i]
|
||||
atlas.create_tile(tile)
|
||||
var data := atlas.get_tile_data(tile, 0)
|
||||
data.terrain_set = 0
|
||||
for neighbor in terrain_neighborhood:
|
||||
data.set_terrain_peering_bit(
|
||||
neighbor,
|
||||
[terrain_background, terrain_foreground][i & 1]
|
||||
)
|
||||
i >>= 1
|
||||
# Set terrains
|
||||
atlas.get_tile_data(preset.bg, 0).terrain = terrain_background
|
||||
atlas.get_tile_data(preset.fg, 0).terrain = terrain_foreground
|
||||
|
||||
##[br] Unregisters all the tiles in an atlas and changes the size of the individual sprites.
|
||||
##[br]
|
||||
##[br] NOTE: Assumes urm.create_action() was called. Does not actually do anything until urm.commit_action() is called.
|
||||
##[br] NOTE: Assumes atlas only has auto-generated tiles. Does not save peering bit information or anything else for undo/redo.
|
||||
static func clear_and_resize_atlas(urm: EditorUndoRedoManager, atlas: TileSetAtlasSource, size: Vector2) -> void:
|
||||
var atlas_data := _save_atlas_data(atlas)
|
||||
urm.add_do_method(TerrainPreset, '_do_clear_and_resize_atlas', atlas, size)
|
||||
urm.add_undo_method(TerrainPreset, '_undo_clear_and_resize_atlas', atlas, atlas_data)
|
||||
|
||||
static func _do_clear_and_resize_atlas(atlas: TileSetAtlasSource, size: Vector2) -> void:
|
||||
# Clear all tiles
|
||||
atlas.texture_region_size = atlas.texture.get_size() + Vector2.ONE
|
||||
atlas.clear_tiles_outside_texture()
|
||||
# Resize the tiles
|
||||
atlas.texture_region_size = size
|
||||
|
||||
static func _undo_clear_and_resize_atlas(atlas: TileSetAtlasSource, atlas_data: Dictionary) -> void:
|
||||
_load_atlas_data(atlas, atlas_data)
|
||||
|
||||
## NOTE: Assumes atlas only has auto-generated tiles. Does not save peering bit information or anything else.
|
||||
static func _save_atlas_data(atlas: TileSetAtlasSource) -> Dictionary:
|
||||
var size_img := atlas.texture.get_size()
|
||||
var size_sprite := atlas.texture_region_size
|
||||
var size_dims := Vector2i(size_img) / size_sprite
|
||||
var tiles := []
|
||||
for y in size_dims.y:
|
||||
var row := []
|
||||
for x in size_dims.x:
|
||||
var tile := Vector2i(x, y)
|
||||
var exists := atlas.has_tile(tile)
|
||||
row.push_back(exists)
|
||||
tiles.push_back(row)
|
||||
return {
|
||||
'size_sprite': size_sprite,
|
||||
'size_dims': size_dims,
|
||||
'tiles': tiles,
|
||||
}
|
||||
|
||||
static func _load_atlas_data(atlas: TileSetAtlasSource, atlas_data: Dictionary) -> void:
|
||||
_do_clear_and_resize_atlas(atlas, atlas_data.size_sprite)
|
||||
for y in atlas_data.size_dims.y:
|
||||
for x in atlas_data.size_dims.x:
|
||||
if atlas_data.tiles[y][x]:
|
||||
var tile := Vector2i(x, y)
|
||||
atlas.create_tile(tile)
|
||||
|
||||
|
||||
##[br] Unregisters all the tiles in an atlas and changes the size of the
|
||||
## individual sprites to accomodate a divisions.x by divisions.y grid of sprites.
|
||||
##[br]
|
||||
##[br] NOTE: Assumes urm.create_action() was called. Does not actually do anything until urm.commit_action() is called.
|
||||
##[br] NOTE: Assumes atlas only has auto-generated tiles. Does not save peering bit information or anything else for undo/redo.
|
||||
static func clear_and_divide_atlas(urm: EditorUndoRedoManager, atlas: TileSetAtlasSource, divisions: Vector2i) -> void:
|
||||
clear_and_resize_atlas(urm, atlas, atlas.texture.get_size() / Vector2(divisions))
|
||||
Reference in New Issue
Block a user