added the game

This commit is contained in:
JHDev2006
2025-09-13 16:30:32 +01:00
parent 5ef689109b
commit 3773bdaf64
3616 changed files with 263702 additions and 0 deletions

View File

@@ -0,0 +1,258 @@
using Godot;
using Godot.Collections;
#nullable disable
/*
This is a lightweight wrapper for Better Terrain in C#.
It is not a C# implementation, it merely provides a type safe interface to access
the BetterTerrain autoload from C#. If you are not using Godot in C#, you can ignore
this file.
The interface is created for a specific tilemap node, which it uses to locate the
autoload, and to fill in as a parameter to simplify all the subsequent calls.
Very simple example:
```
BetterTerrain betterTerrain;
public override void _Ready()
{
TileMapLayer tileMapLayer = GetNode<TileMapLayer>("TileMapLayer");
betterTerrain = new BetterTerrain(tm);
var coordinates = new Vector2I(0, 0);
betterTerrain.SetCell(coordinates, 1);
betterTerrain.UpdateTerrainCell(coordinates);
}
```
The functions available are the same as BetterTerrain's, though the TileMapLayer or
TileSet parameters are automatically filled in. The help is not duplicated here,
refer to the GDScript version for specifics.
*/
public class BetterTerrain
{
public enum TerrainType
{
MatchTiles = 0,
MatchVertices = 1,
Category = 2,
Decoration = 3
}
public enum SymmetryType
{
None = 0,
Mirror = 1, // Horizontally mirror
Flip = 2, // Vertically flip
Reflect = 3, // All four reflections
RotateClockwise = 4,
RotateCounterClockwise = 5,
Rotate180 = 6,
RotateAll = 7, // All four rotated forms
All = 8 // All rotated and reflected forms
}
private static readonly NodePath nodePath = new("/root/BetterTerrain");
private readonly Node betterTerrain;
private readonly TileMapLayer tileMapLayer;
public BetterTerrain(TileMapLayer tileMapLayer)
{
this.tileMapLayer = tileMapLayer;
betterTerrain = tileMapLayer.GetNode(nodePath);
}
public Array<Godot.Collections.Dictionary<string, Variant>> GetTerrainCategories()
{
return (Array<Godot.Collections.Dictionary<string, Variant>>)betterTerrain.Call(MethodName.GetTerrainCategories, tileMapLayer.TileSet);
}
public bool AddTerrain(string name, Color color, TerrainType type, Array<int> categories = null, Godot.Collections.Dictionary<Variant, Variant> icon = null)
{
categories ??= new Array<int>();
icon ??= new Godot.Collections.Dictionary<Variant, Variant>();
return (bool)betterTerrain.Call(MethodName.AddTerrain, tileMapLayer.TileSet, name, color, (int)type, categories, icon);
}
public bool RemoveTerrain(int index)
{
return (bool)betterTerrain.Call(MethodName.RemoveTerrain, tileMapLayer.TileSet, index);
}
public int TerrainCount()
{
return (int)betterTerrain.Call(MethodName.TerrainCount, tileMapLayer.TileSet);
}
public Godot.Collections.Dictionary<string, Variant> GetTerrain(int index)
{
return (Godot.Collections.Dictionary<string, Variant>)betterTerrain.Call(MethodName.GetTerrain, tileMapLayer.TileSet, index);
}
public bool SetTerrain(int index, string name, Color color, TerrainType type, Array<int> categories = null, Godot.Collections.Dictionary<Variant, Variant> icon = null)
{
categories ??= new Array<int>();
icon ??= new Godot.Collections.Dictionary<Variant, Variant>();
return (bool)betterTerrain.Call(MethodName.SetTerrain, tileMapLayer.TileSet, index, name, color, (int)type, categories, icon);
}
public bool SwapTerrains(int index1, int index2)
{
return (bool)betterTerrain.Call(MethodName.SwapTerrains, tileMapLayer.TileSet, index1, index2);
}
public bool SetTileTerrainType(TileData tileData, int type)
{
return (bool)betterTerrain.Call(MethodName.SetTileTerrainType, tileMapLayer.TileSet, tileData, type);
}
public int GetTileTerrainType(TileData tileData)
{
return (int)betterTerrain.Call(MethodName.GetTileTerrainType, tileData);
}
public bool SetTileSymmetryType(TileData tileData, SymmetryType type)
{
return (bool)betterTerrain.Call(MethodName.SetTileSymmetryType, tileMapLayer.TileSet, tileData, (int)type);
}
public SymmetryType GetTileSymmetryType(TileData tileData)
{
return (SymmetryType)(int)betterTerrain.Call(MethodName.GetTileSymmetryType, tileData);
}
public Array<TileData> GetTilesInTerrain(int type)
{
return (Array<TileData>)betterTerrain.Call(MethodName.GetTilesInTerrain, tileMapLayer.TileSet, type);
}
public Array<Godot.Collections.Dictionary<string, Variant>> GetTileSourcesInTerrain(int type)
{
return (Array<Godot.Collections.Dictionary<string, Variant>>)betterTerrain.Call(MethodName.GetTileSourcesInTerrain, tileMapLayer.TileSet, type);
}
public bool AddTilePeeringType(TileData tileData, TileSet.CellNeighbor peering, int type)
{
return (bool)betterTerrain.Call(MethodName.AddTilePeeringType, tileMapLayer.TileSet, tileData, (int)peering, type);
}
public bool RemoveTilePeeringType(TileData tileData, TileSet.CellNeighbor peering, int type)
{
return (bool)betterTerrain.Call(MethodName.RemoveTilePeeringType, tileMapLayer.TileSet, tileData, (int)peering, type);
}
public Array<TileSet.CellNeighbor> TilePeeringKeys(TileData tileData)
{
return (Array<TileSet.CellNeighbor>)betterTerrain.Call(MethodName.TilePeeringKeys, tileData);
}
public Array<int> TilePeeringTypes(TileData tileData, TileSet.CellNeighbor peering)
{
return (Array<int>)betterTerrain.Call(MethodName.TilePeeringTypes, tileData, (int)peering);
}
public Array<TileSet.CellNeighbor> TilePeeringForType(TileData tileData, int type)
{
return (Array<TileSet.CellNeighbor>)betterTerrain.Call(MethodName.TilePeeringForType, tileData, type);
}
public bool SetCell(Vector2I coordinate, int type)
{
return (bool)betterTerrain.Call(MethodName.SetCell, tileMapLayer, coordinate, type);
}
public bool SetCells(Array<Vector2I> coordinates, int type)
{
return (bool)betterTerrain.Call(MethodName.SetCells, tileMapLayer, coordinates, type);
}
public bool ReplaceCell(Vector2I coordinate, int type)
{
return (bool)betterTerrain.Call(MethodName.ReplaceCell, tileMapLayer, coordinate, type);
}
public bool ReplaceCells(Array<Vector2I> coordinates, int type)
{
return (bool)betterTerrain.Call(MethodName.ReplaceCells, tileMapLayer, coordinates, type);
}
public int GetCell(Vector2I coordinate)
{
return (int)betterTerrain.Call(MethodName.GetCell, tileMapLayer, coordinate);
}
public void UpdateTerrainCells(Array<Vector2I> cells, bool updateSurroundingCells = true)
{
betterTerrain.Call(MethodName.UpdateTerrainCells, tileMapLayer, cells, updateSurroundingCells);
}
public void UpdateTerrainCell(Vector2I cell, bool updateSurroundingCells = true)
{
betterTerrain.Call(MethodName.UpdateTerrainCell, tileMapLayer, cell, updateSurroundingCells);
}
public void UpdateTerrainArea(Rect2I area, bool updateSurroundingCells = true)
{
betterTerrain.Call(MethodName.UpdateTerrainArea, tileMapLayer, area, updateSurroundingCells);
}
public Godot.Collections.Dictionary<Variant, Variant> CreateTerrainChangeset(Godot.Collections.Dictionary<Vector2I, int> paint)
{
return (Godot.Collections.Dictionary<Variant, Variant>)betterTerrain.Call(MethodName.CreateTerrainChangeset, tileMapLayer, paint);
}
public bool IsTerrainChangesetReady(Godot.Collections.Dictionary<Variant, Variant> changeset)
{
return (bool)betterTerrain.Call(MethodName.IsTerrainChangesetReady, changeset);
}
public void WaitForTerrainChangeset(Godot.Collections.Dictionary<Variant, Variant> changeset)
{
betterTerrain.Call(MethodName.WaitForTerrainChangeset, changeset);
}
public void ApplyTerrainChangeset(Godot.Collections.Dictionary<Variant, Variant> changeset)
{
betterTerrain.Call(MethodName.ApplyTerrainChangeset, changeset);
}
private static class MethodName
{
public static readonly StringName GetTerrainCategories = "get_terrain_categories";
public static readonly StringName AddTerrain = "add_terrain";
public static readonly StringName RemoveTerrain = "remove_terrain";
public static readonly StringName TerrainCount = "terrain_count";
public static readonly StringName GetTerrain = "get_terrain";
public static readonly StringName SetTerrain = "set_terrain";
public static readonly StringName SwapTerrains = "swap_terrains";
public static readonly StringName SetTileTerrainType = "set_tile_terrain_type";
public static readonly StringName GetTileTerrainType = "get_tile_terrain_type";
public static readonly StringName SetTileSymmetryType = "set_tile_symmetry_type";
public static readonly StringName GetTileSymmetryType = "get_tile_symmetry_type";
public static readonly StringName GetTilesInTerrain = "get_tiles_in_terrain";
public static readonly StringName GetTileSourcesInTerrain = "get_tile_sources_in_terrain";
public static readonly StringName AddTilePeeringType = "add_tile_peering_type";
public static readonly StringName RemoveTilePeeringType = "remove_tile_peering_type";
public static readonly StringName TilePeeringKeys = "tile_peering_keys";
public static readonly StringName TilePeeringTypes = "tile_peering_types";
public static readonly StringName TilePeeringForType = "tile_peering_for_type";
public static readonly StringName SetCell = "set_cell";
public static readonly StringName SetCells = "set_cells";
public static readonly StringName ReplaceCell = "replace_cell";
public static readonly StringName ReplaceCells = "replace_cells";
public static readonly StringName GetCell = "get_cell";
public static readonly StringName UpdateTerrainCells = "update_terrain_cells";
public static readonly StringName UpdateTerrainCell = "update_terrain_cell";
public static readonly StringName UpdateTerrainArea = "update_terrain_area";
public static readonly StringName CreateTerrainChangeset = "create_terrain_changeset";
public static readonly StringName IsTerrainChangesetReady = "is_terrain_changeset_ready";
public static readonly StringName WaitForTerrainChangeset = "wait_for_terrain_changeset";
public static readonly StringName ApplyTerrainChangeset = "apply_terrain_changeset";
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
uid://b5wvl76qnagcj

View File

@@ -0,0 +1,598 @@
@tool
## Data functions for [TileSet] properties.
##
## This data class has functions for retrieving data regarding the mathematical
## properties of a tile set.
const _terrain_peering_square_tiles : Array[int] = [0, 3, 4, 7, 8, 11, 12, 15]
const _terrain_peering_square_vertices : Array[int] = [3, 7, 11, 15]
const _terrain_peering_isometric_tiles : Array[int] = [1, 2, 5, 6, 9, 10, 13, 14]
const _terrain_peering_isometric_vertices : Array[int] = [1, 5, 9, 13]
const _terrain_peering_horiztonal_tiles : Array[int] = [0, 2, 6, 8, 10, 14]
const _terrain_peering_horiztonal_vertices : Array[int] = [3, 5, 7, 11, 13, 15]
const _terrain_peering_vertical_tiles : Array[int] = [2, 4, 6, 10, 12, 14]
const _terrain_peering_vertical_vertices : Array[int] = [1, 3, 7, 9, 11, 15]
const _terrain_peering_non_modifying : Array[int] = []
const _terrain_peering_hflip : Array[int] = [8, 9, 6, 7, 4, 5, 2, 3, 0, 1, 14, 15, 12, 13, 10, 11]
const _terrain_peering_vflip : Array[int] = [0, 1, 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3]
const _terrain_peering_transpose : Array[int] = [4, 5, 2, 3, 0, 1, 14, 15, 12, 13, 10, 11, 8, 9, 6, 7]
const symmetry_mapping := {
BetterTerrain.SymmetryType.NONE: [0],
BetterTerrain.SymmetryType.MIRROR: [0, TileSetAtlasSource.TRANSFORM_FLIP_H],
BetterTerrain.SymmetryType.FLIP: [0, TileSetAtlasSource.TRANSFORM_FLIP_V],
BetterTerrain.SymmetryType.REFLECT: [
0,
TileSetAtlasSource.TRANSFORM_FLIP_H,
TileSetAtlasSource.TRANSFORM_FLIP_V,
TileSetAtlasSource.TRANSFORM_FLIP_H | TileSetAtlasSource.TRANSFORM_FLIP_V
],
BetterTerrain.SymmetryType.ROTATE_CLOCKWISE: [0, TileSetAtlasSource.TRANSFORM_FLIP_H | TileSetAtlasSource.TRANSFORM_TRANSPOSE],
BetterTerrain.SymmetryType.ROTATE_COUNTER_CLOCKWISE: [0, TileSetAtlasSource.TRANSFORM_FLIP_V | TileSetAtlasSource.TRANSFORM_TRANSPOSE],
BetterTerrain.SymmetryType.ROTATE_180: [0, TileSetAtlasSource.TRANSFORM_FLIP_H | TileSetAtlasSource.TRANSFORM_FLIP_V],
BetterTerrain.SymmetryType.ROTATE_ALL: [
0,
TileSetAtlasSource.TRANSFORM_FLIP_H | TileSetAtlasSource.TRANSFORM_TRANSPOSE,
TileSetAtlasSource.TRANSFORM_FLIP_H | TileSetAtlasSource.TRANSFORM_FLIP_V,
TileSetAtlasSource.TRANSFORM_FLIP_V | TileSetAtlasSource.TRANSFORM_TRANSPOSE
],
BetterTerrain.SymmetryType.ALL: [
0,
TileSetAtlasSource.TRANSFORM_FLIP_H,
TileSetAtlasSource.TRANSFORM_FLIP_V,
TileSetAtlasSource.TRANSFORM_FLIP_H | TileSetAtlasSource.TRANSFORM_FLIP_V,
TileSetAtlasSource.TRANSFORM_TRANSPOSE,
TileSetAtlasSource.TRANSFORM_FLIP_H | TileSetAtlasSource.TRANSFORM_TRANSPOSE,
TileSetAtlasSource.TRANSFORM_FLIP_V | TileSetAtlasSource.TRANSFORM_TRANSPOSE,
TileSetAtlasSource.TRANSFORM_FLIP_H | TileSetAtlasSource.TRANSFORM_FLIP_V | TileSetAtlasSource.TRANSFORM_TRANSPOSE
]
}
## Returns an [Array] of ints of type [enum TileSet.CellNeighbor] which represent
## the valid neighboring tiles for a terrain of [code]type[/code] in TileSet
static func get_terrain_peering_cells(ts: TileSet, type: int) -> Array[int]:
if !ts or type < 0 or type >= BetterTerrain.TerrainType.MAX:
return []
if type == BetterTerrain.TerrainType.CATEGORY:
return _terrain_peering_non_modifying
if type == BetterTerrain.TerrainType.DECORATION:
type = BetterTerrain.TerrainType.MATCH_TILES
match [ts.tile_shape, type]:
[TileSet.TILE_SHAPE_SQUARE, BetterTerrain.TerrainType.MATCH_TILES]:
return _terrain_peering_square_tiles
[TileSet.TILE_SHAPE_SQUARE, BetterTerrain.TerrainType.MATCH_VERTICES]:
return _terrain_peering_square_vertices
[TileSet.TILE_SHAPE_ISOMETRIC, BetterTerrain.TerrainType.MATCH_TILES]:
return _terrain_peering_isometric_tiles
[TileSet.TILE_SHAPE_ISOMETRIC, BetterTerrain.TerrainType.MATCH_VERTICES]:
return _terrain_peering_isometric_vertices
match [ts.tile_offset_axis, type]:
[TileSet.TILE_OFFSET_AXIS_VERTICAL, BetterTerrain.TerrainType.MATCH_TILES]:
return _terrain_peering_vertical_tiles
[TileSet.TILE_OFFSET_AXIS_VERTICAL, BetterTerrain.TerrainType.MATCH_VERTICES]:
return _terrain_peering_vertical_vertices
[TileSet.TILE_OFFSET_AXIS_HORIZONTAL, BetterTerrain.TerrainType.MATCH_TILES]:
return _terrain_peering_horiztonal_tiles
[TileSet.TILE_OFFSET_AXIS_HORIZONTAL, BetterTerrain.TerrainType.MATCH_VERTICES]:
return _terrain_peering_horiztonal_vertices
return []
## Returns true if [code]peering[/code] is a valid neighboring cell for a terrain of
## [code]type[/code] in [TileSet]
static func is_terrain_peering_cell(ts: TileSet, type: int, peering: int) -> bool:
return peering in get_terrain_peering_cells(ts, type)
static func _peering_polygon_square_tiles(peering: int) -> PackedVector2Array:
const t := 1.0 / 3.0
var result : PackedVector2Array
match peering:
TileSet.CELL_NEIGHBOR_RIGHT_SIDE: result.append(Vector2(2*t, t))
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: result.append(Vector2(2*t, 2*t))
TileSet.CELL_NEIGHBOR_BOTTOM_SIDE: result.append(Vector2(t, 2*t))
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: result.append(Vector2(0, 2*t))
TileSet.CELL_NEIGHBOR_LEFT_SIDE: result.append(Vector2(0, t))
TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER: result.append(Vector2(0, 0))
TileSet.CELL_NEIGHBOR_TOP_SIDE: result.append(Vector2(t, 0))
TileSet.CELL_NEIGHBOR_TOP_RIGHT_CORNER: result.append(Vector2(2*t, 0))
-1: result.append(Vector2(t, t))
result.append(result[0] + Vector2(t, 0))
result.append(result[0] + Vector2(t, t))
result.append(result[0] + Vector2(0, t))
return result
static func _peering_polygon_square_vertices(peering: int) -> PackedVector2Array:
const t := 1.0 / 2.0
var result : PackedVector2Array
match peering:
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
result.append(Vector2(1, t))
result.append(Vector2(1, 1))
result.append(Vector2(t, 1))
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
result.append(Vector2(0, t))
result.append(Vector2(t, 1))
result.append(Vector2(0, 1))
TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER:
result.append(Vector2(0, 0))
result.append(Vector2(t, 0))
result.append(Vector2(0, t))
TileSet.CELL_NEIGHBOR_TOP_RIGHT_CORNER:
result.append(Vector2(t, 0))
result.append(Vector2(1, 0))
result.append(Vector2(1, t))
-1:
result.append(Vector2(t, 0))
result.append(Vector2(1, t))
result.append(Vector2(t, 1))
result.append(Vector2(0, t))
return result
static func _peering_polygon_isometric_tiles(peering: int) -> PackedVector2Array:
const t := 1.0 / 4.0
match peering:
-1: return PackedVector2Array([Vector2(2 * t, t), Vector2(3 * t, 2 * t), Vector2(2 * t, 3 * t), Vector2(t, 2 * t)])
TileSet.CELL_NEIGHBOR_RIGHT_CORNER:
return PackedVector2Array([Vector2(3 * t, 2 * t), Vector2(1, t), Vector2(1, 3 * t)])
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
return PackedVector2Array([Vector2(3 * t, 2 * t), Vector2(1, 3 * t), Vector2(3 * t, 1), Vector2(2 * t, 3 * t)])
TileSet.CELL_NEIGHBOR_BOTTOM_CORNER:
return PackedVector2Array([Vector2(2 * t, 3 * t), Vector2(3 * t, 1), Vector2(t, 1)])
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
return PackedVector2Array([Vector2(t, 2 * t), Vector2(2 * t, 3 * t), Vector2(t, 1), Vector2(0, 3 * t)])
TileSet.CELL_NEIGHBOR_LEFT_CORNER:
return PackedVector2Array([Vector2(0, t), Vector2(t, 2 * t), Vector2(0, 3 * t)])
TileSet.CELL_NEIGHBOR_TOP_LEFT_SIDE:
return PackedVector2Array([Vector2(t, 0), Vector2(2 * t, t), Vector2(t, 2 * t), Vector2(0, t)])
TileSet.CELL_NEIGHBOR_TOP_CORNER:
return PackedVector2Array([Vector2(t, 0), Vector2(3 * t, 0), Vector2(2 * t, t)])
TileSet.CELL_NEIGHBOR_TOP_RIGHT_SIDE:
return PackedVector2Array([Vector2(3 * t, 0), Vector2(1, t), Vector2(3 * t, 2 * t), Vector2(2 * t, t)])
return PackedVector2Array()
static func _peering_polygon_isometric_vertices(peering: int) -> PackedVector2Array:
const t := 1.0 / 4.0
const ttt := 3.0 * t
match peering:
-1: return PackedVector2Array([Vector2(t, t), Vector2(ttt, t), Vector2(ttt, ttt), Vector2(t, ttt)])
TileSet.CELL_NEIGHBOR_RIGHT_CORNER:
return PackedVector2Array([Vector2(ttt, t), Vector2(1, 0), Vector2(1, 1), Vector2(ttt, ttt)])
TileSet.CELL_NEIGHBOR_BOTTOM_CORNER:
return PackedVector2Array([Vector2(t, ttt), Vector2(ttt, ttt), Vector2(1, 1), Vector2(0, 1)])
TileSet.CELL_NEIGHBOR_LEFT_CORNER:
return PackedVector2Array([Vector2(0, 0), Vector2(t, t), Vector2(t, ttt), Vector2(0, 1)])
TileSet.CELL_NEIGHBOR_TOP_CORNER:
return PackedVector2Array([Vector2(0, 0), Vector2(1, 0), Vector2(ttt, t), Vector2(t, t)])
return PackedVector2Array()
static func _peering_polygon_horizontal_tiles(peering: int) -> PackedVector2Array:
const e := 1.0 / (2.0 * sqrt(3.0))
const w := sqrt(3.0) / 8.0
const t := 1.0 / 2.0
const s := 1.0 / 8.0
match peering:
-1:
return PackedVector2Array([
Vector2(t, 2 * s),
Vector2(t + w, t - s),
Vector2(t + w, t + s),
Vector2(t, 6 * s),
Vector2(t - w, t + s),
Vector2(t - w, t - s)
])
TileSet.CELL_NEIGHBOR_RIGHT_SIDE:
return PackedVector2Array([
Vector2(t + w, t - s),
Vector2(1, t - e),
Vector2(1, t + e),
Vector2(t + w, t + s)
])
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
return PackedVector2Array([
Vector2(t + w, t + s),
Vector2(1, t + e),
Vector2(t, 1),
Vector2(t, 6 * s)
])
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
return PackedVector2Array([
Vector2(t, 6 * s),
Vector2(t, 1),
Vector2(0, t + e),
Vector2(t - w, t + s)
])
TileSet.CELL_NEIGHBOR_LEFT_SIDE:
return PackedVector2Array([
Vector2(t - w, t + s),
Vector2(0, t + e),
Vector2(0, t - e),
Vector2(t - w, t - s)
])
TileSet.CELL_NEIGHBOR_TOP_LEFT_SIDE:
return PackedVector2Array([
Vector2(t - w, t - s),
Vector2(0, t - e),
Vector2(t, 0),
Vector2(t, 2 * s)
])
TileSet.CELL_NEIGHBOR_TOP_RIGHT_SIDE:
return PackedVector2Array([
Vector2(t, 2 * s),
Vector2(t, 0),
Vector2(1, t - e),
Vector2(t + w, t - s)
])
return PackedVector2Array()
static func _peering_polygon_horizontal_vertices(peering: int) -> PackedVector2Array:
const e := 1.0 / (2.0 * sqrt(3.0))
const w := sqrt(3.0) / 8.0
const t := 1.0 / 2.0
const s := 1.0 / 8.0
match peering:
-1:
return PackedVector2Array([
Vector2(t - s, t - w),
Vector2(t + s, t - w),
Vector2(6 * s, t),
Vector2(t + s, t + w),
Vector2(t - s, t + w),
Vector2(2 * s, t)
])
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
return PackedVector2Array([
Vector2(6 * s, t),
Vector2(1, t),
Vector2(1, t + e),
Vector2(t + e, 1 - s),
Vector2(t + s, t + w)
])
TileSet.CELL_NEIGHBOR_BOTTOM_CORNER:
return PackedVector2Array([
Vector2(t - s, t + w),
Vector2(t + s, t + w),
Vector2(t + e, 1 - s),
Vector2(t, 1),
Vector2(t - e, 1 - s)
])
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
return PackedVector2Array([
Vector2(0, t),
Vector2(2 * s, t),
Vector2(t - s, t + w),
Vector2(t - e, 1 - s),
Vector2(0, t + e)
])
TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER:
return PackedVector2Array([
Vector2(t - e, s),
Vector2(t - s, t - w),
Vector2(2 * s, t),
Vector2(0, t),
Vector2(0, t - e)
])
TileSet.CELL_NEIGHBOR_TOP_CORNER:
return PackedVector2Array([
Vector2(t, 0),
Vector2(t + e, s),
Vector2(t + s, t - w),
Vector2(t - s, t - w),
Vector2(t - e, s)
])
TileSet.CELL_NEIGHBOR_TOP_RIGHT_CORNER:
return PackedVector2Array([
Vector2(t + e, s),
Vector2(1, t - e),
Vector2(1, t),
Vector2(6 * s, t),
Vector2(t + s, t - w)
])
return PackedVector2Array()
static func _peering_polygon_vertical_tiles(peering: int) -> PackedVector2Array:
const e := 1.0 / (2.0 * sqrt(3.0))
const w := sqrt(3.0) / 8.0
const t := 1.0 / 2.0
const s := 1.0 / 8.0
match peering:
-1:
return PackedVector2Array([
Vector2(t - s, t - w),
Vector2(t + s, t - w),
Vector2(6 * s, t),
Vector2(t + s, t + w),
Vector2(t - s, t + w),
Vector2(2 * s, t)
])
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
return PackedVector2Array([
Vector2(6 * s, t),
Vector2(1, t),
Vector2(t + e, 1),
Vector2(t + s, t + w)
])
TileSet.CELL_NEIGHBOR_BOTTOM_SIDE:
return PackedVector2Array([
Vector2(t - s, t + w),
Vector2(t + s, t + w),
Vector2(t + e, 1),
Vector2(t - e, 1)
])
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
return PackedVector2Array([
Vector2(0, t),
Vector2(2 * s, t),
Vector2(t - s, t + w),
Vector2(t - e, 1)
])
TileSet.CELL_NEIGHBOR_TOP_LEFT_SIDE:
return PackedVector2Array([
Vector2(t - e, 0),
Vector2(t - s, t - w),
Vector2(2 * s, t),
Vector2(0, t)
])
TileSet.CELL_NEIGHBOR_TOP_SIDE:
return PackedVector2Array([
Vector2(t - e, 0),
Vector2(t + e, 0),
Vector2(t + s, t - w),
Vector2(t - s, t - w)
])
TileSet.CELL_NEIGHBOR_TOP_RIGHT_SIDE:
return PackedVector2Array([
Vector2(t + e, 0),
Vector2(1, t),
Vector2(6 * s, t),
Vector2(t + s, t - w)
])
return PackedVector2Array()
static func _peering_polygon_vertical_vertices(peering: int) -> PackedVector2Array:
const e := 1.0 / (2.0 * sqrt(3.0))
const w := sqrt(3.0) / 8.0
const t := 1.0 / 2.0
const s := 1.0 / 8.0
match peering:
-1:
return PackedVector2Array([
Vector2(t, 2 * s),
Vector2(t + w, t - s),
Vector2(t + w, t + s),
Vector2(t, 6 * s),
Vector2(t - w, t + s),
Vector2(t - w, t - s)
])
TileSet.CELL_NEIGHBOR_RIGHT_CORNER:
return PackedVector2Array([
Vector2(1 - s, t - e),
Vector2(1, t),
Vector2(1 - s, t + e),
Vector2(t + w, t + s),
Vector2(t + w, t - s)
])
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
return PackedVector2Array([
Vector2(t + w, t + s),
Vector2(1 - s, t + e),
Vector2(t + e, 1),
Vector2(t, 1),
Vector2(t, 6 * s)
])
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
return PackedVector2Array([
Vector2(t - w, t + s),
Vector2(t, 6 * s),
Vector2(t, 1),
Vector2(t - e, 1),
Vector2(s, t + e)
])
TileSet.CELL_NEIGHBOR_LEFT_CORNER:
return PackedVector2Array([
Vector2(s, t - e),
Vector2(t - w, t - s),
Vector2(t - w, t + s),
Vector2(s, t + e),
Vector2(0, t)
])
TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER:
return PackedVector2Array([
Vector2(t - e, 0),
Vector2(t, 0),
Vector2(t, 2 * s),
Vector2(t - w, t - s),
Vector2(s, t - e)
])
TileSet.CELL_NEIGHBOR_TOP_RIGHT_CORNER:
return PackedVector2Array([
Vector2(t, 0),
Vector2(t + e, 0),
Vector2(1 - s, t - e),
Vector2(t + w, t - s),
Vector2(t, 2 * s)
])
return PackedVector2Array()
static func _peering_non_modifying() -> PackedVector2Array:
const t := 1.0 / 3.0
return PackedVector2Array([
Vector2(t, 0),
Vector2(2 * t, 0),
Vector2(1, t),
Vector2(1, 2 * t),
Vector2(2 * t, 1),
Vector2(t, 1),
Vector2(0, 2 * t),
Vector2(0, t)
])
## Returns a parameterized polygon (coordinated are between 0 and 1) for [code]peering[/code]
## direction for a terrain of [code]type[/code] in [TileSet]
static func peering_polygon(ts: TileSet, type: int, peering: int) -> PackedVector2Array:
if type == BetterTerrain.TerrainType.CATEGORY:
return _peering_non_modifying()
if type == BetterTerrain.TerrainType.DECORATION:
type = BetterTerrain.TerrainType.MATCH_TILES
match [ts.tile_shape, type]:
[TileSet.TILE_SHAPE_SQUARE, BetterTerrain.TerrainType.MATCH_TILES]:
return _peering_polygon_square_tiles(peering)
[TileSet.TILE_SHAPE_SQUARE, BetterTerrain.TerrainType.MATCH_VERTICES]:
return _peering_polygon_square_vertices(peering)
[TileSet.TILE_SHAPE_ISOMETRIC, BetterTerrain.TerrainType.MATCH_TILES]:
return _peering_polygon_isometric_tiles(peering)
[TileSet.TILE_SHAPE_ISOMETRIC, BetterTerrain.TerrainType.MATCH_VERTICES]:
return _peering_polygon_isometric_vertices(peering)
match [ts.tile_offset_axis, type]:
[TileSet.TILE_OFFSET_AXIS_VERTICAL, BetterTerrain.TerrainType.MATCH_TILES]:
return _peering_polygon_vertical_tiles(peering)
[TileSet.TILE_OFFSET_AXIS_VERTICAL, BetterTerrain.TerrainType.MATCH_VERTICES]:
return _peering_polygon_vertical_vertices(peering)
[TileSet.TILE_OFFSET_AXIS_HORIZONTAL, BetterTerrain.TerrainType.MATCH_TILES]:
return _peering_polygon_horizontal_tiles(peering)
[TileSet.TILE_OFFSET_AXIS_HORIZONTAL, BetterTerrain.TerrainType.MATCH_VERTICES]:
return _peering_polygon_horizontal_vertices(peering)
return PackedVector2Array()
## Returns as polygon centered on 0, 0 which represents the shape of the cell of
## a tile from [TileSet].
static func cell_polygon(ts: TileSet) -> PackedVector2Array:
const t := 1.0 / 2.0
if ts.tile_shape in [TileSet.TILE_SHAPE_SQUARE, TileSet.TILE_SHAPE_HALF_OFFSET_SQUARE]:
return PackedVector2Array([Vector2(-t, -t), Vector2(t, -t), Vector2(t, t), Vector2(-t, t)])
if ts.tile_shape == TileSet.TILE_SHAPE_ISOMETRIC:
return PackedVector2Array([Vector2(0, -t), Vector2(t, 0), Vector2(0, t), Vector2(-t, 0)])
const e := t - 1.0 / (2.0 * sqrt(3.0))
if ts.tile_offset_axis == TileSet.TILE_OFFSET_AXIS_HORIZONTAL:
return PackedVector2Array([
Vector2(0, -t),
Vector2(t, -e),
Vector2(t, e),
Vector2(0, t),
Vector2(-t, e),
Vector2(-t, -e),
])
return PackedVector2Array([
Vector2(-t, 0),
Vector2(-e, -t),
Vector2(e, -t),
Vector2(t, 0),
Vector2(e, t),
Vector2(-e, t),
])
## Returns an [Array] of coordinated that neighbor [code]coord[/code] based on [code]peering[/code]
## [Array] of [enum TileSet.CellNeighbor] for a [TileSet].
static func neighboring_coords(tm: TileMapLayer, coord: Vector2i, peerings: Array) -> Array:
return peerings.map(func(p): return tm.get_neighbor_cell(coord, p))
## Returns an [Array] of coordinates which neighbor the vertex describe by [code]corner[/code]
## (which is of type [enum TileSet.CellNeighbor]) from [code]coord[/code] in [TileSet].
static func associated_vertex_cells(tm: TileMapLayer, coord: Vector2i, corner: int) -> Array:
# get array of associated peering bits
if tm.tile_set.tile_shape in [TileSet.TILE_SHAPE_SQUARE, TileSet.TILE_SHAPE_ISOMETRIC]:
match corner:
# Square
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
return neighboring_coords(tm, coord, [0, 3, 4])
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
return neighboring_coords(tm, coord, [4, 7, 8])
TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER:
return neighboring_coords(tm, coord, [8, 11, 12])
TileSet.CELL_NEIGHBOR_TOP_RIGHT_CORNER:
return neighboring_coords(tm, coord, [12, 15, 0])
# Isometric
TileSet.CELL_NEIGHBOR_RIGHT_CORNER:
return neighboring_coords(tm, coord, [14, 1, 2])
TileSet.CELL_NEIGHBOR_BOTTOM_CORNER:
return neighboring_coords(tm, coord, [2, 5, 6])
TileSet.CELL_NEIGHBOR_LEFT_CORNER:
return neighboring_coords(tm, coord, [6, 9, 10])
TileSet.CELL_NEIGHBOR_TOP_CORNER:
return neighboring_coords(tm, coord, [10, 13, 14])
if tm.tile_set.tile_offset_axis == TileSet.TILE_OFFSET_AXIS_HORIZONTAL:
match corner:
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
return neighboring_coords(tm, coord, [0, 2])
TileSet.CELL_NEIGHBOR_BOTTOM_CORNER:
return neighboring_coords(tm, coord, [2, 6])
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
return neighboring_coords(tm, coord, [6, 8])
TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER:
return neighboring_coords(tm, coord, [8, 10])
TileSet.CELL_NEIGHBOR_TOP_CORNER:
return neighboring_coords(tm, coord, [10, 14])
TileSet.CELL_NEIGHBOR_TOP_RIGHT_CORNER:
return neighboring_coords(tm, coord, [14, 0])
# TileSet.TILE_OFFSET_AXIS_VERTICAL
match corner:
TileSet.CELL_NEIGHBOR_RIGHT_CORNER:
return neighboring_coords(tm, coord, [14, 2])
TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
return neighboring_coords(tm, coord, [2, 4])
TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
return neighboring_coords(tm, coord, [4, 6])
TileSet.CELL_NEIGHBOR_LEFT_CORNER:
return neighboring_coords(tm, coord, [6, 10])
TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER:
return neighboring_coords(tm, coord, [10, 12])
TileSet.CELL_NEIGHBOR_TOP_RIGHT_CORNER:
return neighboring_coords(tm, coord, [12, 14])
return []
## Returns an [Array] of [enum TileSet.CellNeighbor] suitable for flood filling
## an area in [TileSet].
static func cells_adjacent_for_fill(ts: TileSet) -> Array[int]:
if ts.tile_shape == TileSet.TILE_SHAPE_SQUARE:
return [0, 4, 8, 12]
if ts.tile_shape == TileSet.TILE_SHAPE_ISOMETRIC:
return [2, 6, 10, 14]
if ts.tile_offset_axis == TileSet.TILE_OFFSET_AXIS_HORIZONTAL:
return _terrain_peering_horiztonal_tiles
return _terrain_peering_vertical_tiles
static func peering_bit_after_symmetry(bit: int, altflags: int) -> int:
if altflags & TileSetAtlasSource.TRANSFORM_TRANSPOSE:
bit = _terrain_peering_transpose[bit]
if altflags & TileSetAtlasSource.TRANSFORM_FLIP_H:
bit = _terrain_peering_hflip[bit]
if altflags & TileSetAtlasSource.TRANSFORM_FLIP_V:
bit = _terrain_peering_vflip[bit]
return bit
static func peering_bits_after_symmetry(dict: Dictionary, altflags: int) -> Dictionary:
# rearrange dictionary keys based on altflags
var result := {}
for k in dict:
result[peering_bit_after_symmetry(k, altflags)] = dict[k]
return result

View File

@@ -0,0 +1 @@
uid://dns30obk1hpvd

View File

@@ -0,0 +1,72 @@
@tool
extends EditorPlugin
const AUTOLOAD_NAME = "BetterTerrain"
var dock : Control
var button : Button
func _enter_tree() -> void:
# Wait for autoloads to register
await get_tree().process_frame
if !get_tree().root.get_node_or_null(^"BetterTerrain"):
# Autoload wasn't present on plugin init, which means plugin won't have loaded correctly
add_autoload_singleton(AUTOLOAD_NAME, "res://addons/better-terrain/BetterTerrain.gd")
ProjectSettings.save()
var confirm = ConfirmationDialog.new()
confirm.dialog_text = "The editor needs to be restarted for Better Terrain to load correctly. Restart now? Note: Unsaved changes will be lost."
confirm.confirmed.connect(func():
OS.set_restart_on_exit(true, ["-e"])
get_tree().quit()
)
get_editor_interface().popup_dialog_centered(confirm)
dock = load("res://addons/better-terrain/editor/Dock.tscn").instantiate()
dock.update_overlay.connect(self.update_overlays)
get_editor_interface().get_editor_main_screen().mouse_exited.connect(dock.canvas_mouse_exit)
dock.undo_manager = get_undo_redo()
button = add_control_to_bottom_panel(dock, "Terrain")
button.toggled.connect(dock.about_to_be_visible)
dock.force_show_terrains.connect(button.toggled.emit.bind(true))
button.visible = false
func _exit_tree() -> void:
remove_control_from_bottom_panel(dock)
dock.queue_free()
func _handles(object) -> bool:
return object is TileMapLayer or object is TileSet
func _make_visible(visible) -> void:
button.visible = visible
func _edit(object) -> void:
var new_tileset : TileSet = null
if object is TileMapLayer:
dock.tilemap = object
new_tileset = object.tile_set
if object is TileSet:
new_tileset = object
if dock.tileset != new_tileset:
dock.tiles_about_to_change()
dock.tileset = new_tileset
dock.tiles_changed()
func _forward_canvas_draw_over_viewport(overlay: Control) -> void:
if dock.visible:
dock.canvas_draw(overlay)
func _forward_canvas_gui_input(event: InputEvent) -> bool:
if !dock.visible:
return false
return dock.canvas_input(event)

View File

@@ -0,0 +1 @@
uid://bj2cm2q4tdgno

View File

@@ -0,0 +1,20 @@
@tool
extends Node
signal trigger
var complete := false
var tileset : TileSet
func tidy() -> bool:
if complete:
return false
complete = true
queue_free()
return true
func activate():
if tidy():
trigger.emit()

View File

@@ -0,0 +1 @@
uid://d3rqd2eagpoe0

View File

@@ -0,0 +1,939 @@
@tool
extends Control
signal update_overlay
signal force_show_terrains
# The maximum individual tiles the overlay will draw before shortcutting the display
# To prevent editor lag when drawing large rectangles or filling large areas
const MAX_CANVAS_RENDER_TILES = 1500
const TERRAIN_PROPERTIES_SCENE := preload("res://addons/better-terrain/editor/TerrainProperties.tscn")
const TERRAIN_ENTRY_SCENE := preload("res://addons/better-terrain/editor/TerrainEntry.tscn")
const MIN_ZOOM_SETTING := "editor/better_terrain/min_zoom_amount"
const MAX_ZOOM_SETTING := "editor/better_terrain/max_zoom_amount"
# Buttons
@onready var draw_button: Button = $VBox/Toolbar/Draw
@onready var line_button: Button = $VBox/Toolbar/Line
@onready var rectangle_button: Button = $VBox/Toolbar/Rectangle
@onready var fill_button: Button = $VBox/Toolbar/Fill
@onready var replace_button: Button = $VBox/Toolbar/Replace
@onready var paint_type: Button = $VBox/Toolbar/PaintType
@onready var paint_terrain: Button = $VBox/Toolbar/PaintTerrain
@onready var select_tiles: Button = $VBox/Toolbar/SelectTiles
@onready var paint_symmetry: Button = $VBox/Toolbar/PaintSymmetry
@onready var symmetry_options: OptionButton = $VBox/Toolbar/SymmetryOptions
@onready var shuffle_random: Button = $VBox/Toolbar/ShuffleRandom
@onready var zoom_slider_container: VBoxContainer = $VBox/Toolbar/ZoomContainer
@onready var source_selector: MenuBar = $VBox/Toolbar/Sources
@onready var source_selector_popup: PopupMenu = $VBox/Toolbar/Sources/Sources
@onready var clean_button: Button = $VBox/Toolbar/Clean
@onready var layer_up: Button = $VBox/Toolbar/LayerUp
@onready var layer_down: Button = $VBox/Toolbar/LayerDown
@onready var layer_highlight: Button = $VBox/Toolbar/LayerHighlight
@onready var layer_grid: Button = $VBox/Toolbar/LayerGrid
@onready var grid_mode_button: Button = $VBox/HSplit/Terrains/LowerToolbar/GridMode
@onready var quick_mode_button: Button = $VBox/HSplit/Terrains/LowerToolbar/QuickMode
@onready var edit_tool_buttons: HBoxContainer = $VBox/HSplit/Terrains/LowerToolbar/EditTools
@onready var add_terrain_button: Button = $VBox/HSplit/Terrains/LowerToolbar/EditTools/AddTerrain
@onready var edit_terrain_button: Button = $VBox/HSplit/Terrains/LowerToolbar/EditTools/EditTerrain
@onready var pick_icon_button: Button = $VBox/HSplit/Terrains/LowerToolbar/EditTools/PickIcon
@onready var move_up_button: Button = $VBox/HSplit/Terrains/LowerToolbar/EditTools/MoveUp
@onready var move_down_button: Button = $VBox/HSplit/Terrains/LowerToolbar/EditTools/MoveDown
@onready var remove_terrain_button: Button = $VBox/HSplit/Terrains/LowerToolbar/EditTools/RemoveTerrain
@onready var scroll_container: ScrollContainer = $VBox/HSplit/Terrains/Panel/ScrollContainer
@onready var terrain_list: HFlowContainer = $VBox/HSplit/Terrains/Panel/ScrollContainer/TerrainList
@onready var tile_view: Control = $VBox/HSplit/Panel/ScrollArea/TileView
var selected_entry := -2
var tilemap : TileMapLayer
var tileset : TileSet
var undo_manager : EditorUndoRedoManager
var terrain_undo
var draw_overlay := false
var initial_click : Vector2i
var prev_position : Vector2i
var current_position : Vector2i
var tileset_dirty := false
var zoom_slider : HSlider
enum PaintMode {
NO_PAINT,
PAINT,
ERASE
}
enum PaintAction {
NO_ACTION,
LINE,
RECT
}
enum SourceSelectors {
ALL = 1000000,
NONE = 1000001,
}
var paint_mode := PaintMode.NO_PAINT
var paint_action := PaintAction.NO_ACTION
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
draw_button.icon = get_theme_icon("Edit", "EditorIcons")
line_button.icon = get_theme_icon("Line", "EditorIcons")
rectangle_button.icon = get_theme_icon("Rectangle", "EditorIcons")
fill_button.icon = get_theme_icon("Bucket", "EditorIcons")
select_tiles.icon = get_theme_icon("ToolSelect", "EditorIcons")
add_terrain_button.icon = get_theme_icon("Add", "EditorIcons")
edit_terrain_button.icon = get_theme_icon("Tools", "EditorIcons")
pick_icon_button.icon = get_theme_icon("ColorPick", "EditorIcons")
move_up_button.icon = get_theme_icon("ArrowUp", "EditorIcons")
move_down_button.icon = get_theme_icon("ArrowDown", "EditorIcons")
remove_terrain_button.icon = get_theme_icon("Remove", "EditorIcons")
grid_mode_button.icon = get_theme_icon("FileThumbnail", "EditorIcons")
quick_mode_button.icon = get_theme_icon("GuiVisibilityVisible", "EditorIcons")
layer_up.icon = get_theme_icon("MoveUp", "EditorIcons")
layer_down.icon = get_theme_icon("MoveDown", "EditorIcons")
layer_highlight.icon = get_theme_icon("TileMapHighlightSelected", "EditorIcons")
layer_grid.icon = get_theme_icon("Grid", "EditorIcons")
select_tiles.button_group.pressed.connect(_on_bit_button_pressed)
terrain_undo = load("res://addons/better-terrain/editor/TerrainUndo.gd").new()
add_child(terrain_undo)
tile_view.undo_manager = undo_manager
tile_view.terrain_undo = terrain_undo
tile_view.paste_occurred.connect(_on_paste_occurred)
tile_view.change_zoom_level.connect(_on_change_zoom_level)
tile_view.terrain_updated.connect(_on_terrain_updated)
# Zoom slider is manipulated by settings, make it at runtime
zoom_slider = HSlider.new()
zoom_slider.custom_minimum_size = Vector2(100, 0)
zoom_slider.value_changed.connect(tile_view._on_zoom_value_changed)
zoom_slider_container.add_child(zoom_slider)
# Init settings if needed
if !ProjectSettings.has_setting(MIN_ZOOM_SETTING):
ProjectSettings.set(MIN_ZOOM_SETTING, 1.0)
ProjectSettings.add_property_info({
"name": MIN_ZOOM_SETTING,
"type": TYPE_FLOAT,
"hint": PROPERTY_HINT_RANGE,
"hint_string": "0.1,1.0,0.1"
})
ProjectSettings.set_initial_value(MIN_ZOOM_SETTING, 1.0)
ProjectSettings.set_as_basic(MIN_ZOOM_SETTING, true)
if !ProjectSettings.has_setting(MAX_ZOOM_SETTING):
ProjectSettings.set(MAX_ZOOM_SETTING, 8.0)
ProjectSettings.add_property_info({
"name": MAX_ZOOM_SETTING,
"type": TYPE_FLOAT,
"hint": PROPERTY_HINT_RANGE,
"hint_string": "2.0,32.0,1.0"
})
ProjectSettings.set_initial_value(MAX_ZOOM_SETTING, 8.0)
ProjectSettings.set_as_basic(MAX_ZOOM_SETTING, true)
ProjectSettings.set_order(MAX_ZOOM_SETTING, ProjectSettings.get_order(MIN_ZOOM_SETTING) + 1)
ProjectSettings.settings_changed.connect(_on_adjust_settings)
_on_adjust_settings()
zoom_slider.value = 1.0
func _process(delta):
scroll_container.scroll_horizontal = 0
func _on_adjust_settings():
zoom_slider.min_value = ProjectSettings.get_setting(MIN_ZOOM_SETTING, 1.0)
zoom_slider.max_value = ProjectSettings.get_setting(MAX_ZOOM_SETTING, 8.0)
zoom_slider.step = (zoom_slider.max_value - zoom_slider.min_value) / 100.0
func _get_fill_cells(target: Vector2i) -> Array:
var pick := BetterTerrain.get_cell(tilemap, target)
var bounds := tilemap.get_used_rect()
var neighbors = BetterTerrain.data.cells_adjacent_for_fill(tileset)
# No sets yet, so use a dictionary
var checked := {}
var pending := [target]
var goal := []
while !pending.is_empty():
var p = pending.pop_front()
if checked.has(p):
continue
checked[p] = true
if !bounds.has_point(p) or BetterTerrain.get_cell(tilemap, p) != pick:
continue
goal.append(p)
pending.append_array(BetterTerrain.data.neighboring_coords(tilemap, p, neighbors))
return goal
func tiles_about_to_change() -> void:
if tileset and tileset.changed.is_connected(queue_tiles_changed):
tileset.changed.disconnect(queue_tiles_changed)
func tiles_changed() -> void:
# ensure up to date
BetterTerrain._update_terrain_data(tileset)
# clear terrains
for c in terrain_list.get_children():
terrain_list.remove_child(c)
c.queue_free()
# load terrains from tileset
var terrain_count := BetterTerrain.terrain_count(tileset)
var item_count = terrain_count + 1
for i in terrain_count:
var terrain := BetterTerrain.get_terrain(tileset, i)
if i >= terrain_list.get_child_count():
add_terrain_entry(terrain, i)
if item_count > terrain_list.get_child_count():
var terrain := BetterTerrain.get_terrain(tileset, BetterTerrain.TileCategory.EMPTY)
if terrain.valid:
add_terrain_entry(terrain, item_count - 1)
while item_count < terrain_list.get_child_count():
var child = terrain_list.get_child(terrain_list.get_child_count() - 1)
terrain_list.remove_child(child)
child.free()
source_selector_popup.clear()
source_selector_popup.add_item("All", SourceSelectors.ALL)
source_selector_popup.add_item("None", SourceSelectors.NONE)
var source_count = tileset.get_source_count() if tileset else 0
for s in source_count:
var source_id = tileset.get_source_id(s)
var source := tileset.get_source(source_id)
if !(source is TileSetAtlasSource):
continue
var name := source.resource_name
if name.is_empty():
var texture := (source as TileSetAtlasSource).texture
var texture_name := texture.resource_name if texture else ""
if !texture_name.is_empty():
name = texture_name
else:
var texture_path := texture.resource_path if texture else ""
if !texture_path.is_empty():
name = texture_path.get_file()
if !name.is_empty():
name += " "
name += " (ID: %d)" % source_id
source_selector_popup.add_check_item(name, source_id)
source_selector_popup.set_item_checked(source_selector_popup.get_item_index(source_id), true)
source_selector.visible = source_selector_popup.item_count > 3 # All, None and more than one source
update_tile_view_paint()
tile_view.refresh_tileset(tileset)
if tileset and !tileset.changed.is_connected(queue_tiles_changed):
tileset.changed.connect(queue_tiles_changed)
clean_button.visible = BetterTerrain._has_invalid_peering_types(tileset)
tileset_dirty = false
_on_grid_mode_pressed()
_on_quick_mode_pressed()
func about_to_be_visible(visible: bool) -> void:
if !visible:
return
if tileset != tilemap.tile_set:
tiles_about_to_change()
tileset = tilemap.tile_set
tiles_changed()
var settings := EditorInterface.get_editor_settings()
layer_highlight.set_pressed_no_signal(settings.get_setting("editors/tiles_editor/highlight_selected_layer"))
layer_grid.set_pressed_no_signal(settings.get_setting("editors/tiles_editor/display_grid"))
func queue_tiles_changed() -> void:
# Bring terrain data up to date with complex tileset changes
if !tileset or tileset_dirty:
return
tileset_dirty = true
tiles_changed.call_deferred()
func _on_entry_select(index:int):
selected_entry = index
if selected_entry >= BetterTerrain.terrain_count(tileset):
selected_entry = BetterTerrain.TileCategory.EMPTY
for i in range(terrain_list.get_child_count()):
if i != index:
terrain_list.get_child(i).set_selected(false)
update_tile_view_paint()
func _on_clean_pressed() -> void:
var confirmed := [false]
var popup := ConfirmationDialog.new()
popup.dialog_text = tr("Tile set changes have caused terrain to become invalid. Remove invalid terrain data?")
popup.dialog_hide_on_ok = false
popup.confirmed.connect(func():
confirmed[0] = true
popup.hide()
)
EditorInterface.popup_dialog_centered(popup)
await popup.visibility_changed
popup.queue_free()
if confirmed[0]:
undo_manager.create_action("Clean invalid terrain peering data", UndoRedo.MERGE_DISABLE, tileset)
undo_manager.add_do_method(BetterTerrain, &"_clear_invalid_peering_types", tileset)
undo_manager.add_do_method(self, &"tiles_changed")
terrain_undo.create_peering_restore_point(undo_manager, tileset)
undo_manager.add_undo_method(self, &"tiles_changed")
undo_manager.commit_action()
func _on_grid_mode_pressed() -> void:
for c in terrain_list.get_children():
c.grid_mode = grid_mode_button.button_pressed
c.update_style()
func _on_quick_mode_pressed() -> void:
edit_tool_buttons.visible = !quick_mode_button.button_pressed
for c in terrain_list.get_children():
c.visible = !quick_mode_button.button_pressed or c.terrain.type in [BetterTerrain.TerrainType.MATCH_TILES, BetterTerrain.TerrainType.MATCH_VERTICES]
func update_tile_view_paint() -> void:
tile_view.paint = selected_entry
tile_view.queue_redraw()
var editable = tile_view.paint != BetterTerrain.TileCategory.EMPTY
edit_terrain_button.disabled = !editable
move_up_button.disabled = !editable or tile_view.paint == 0
move_down_button.disabled = !editable or tile_view.paint == BetterTerrain.terrain_count(tileset) - 1
remove_terrain_button.disabled = !editable
pick_icon_button.disabled = !editable
func _on_add_terrain_pressed() -> void:
if !tileset:
return
var popup := TERRAIN_PROPERTIES_SCENE.instantiate()
popup.set_category_data(BetterTerrain.get_terrain_categories(tileset))
popup.terrain_name = "New terrain"
popup.terrain_color = Color.from_hsv(randf(), 0.3 + 0.7 * randf(), 0.6 + 0.4 * randf())
popup.terrain_icon = ""
popup.terrain_type = 0
EditorInterface.popup_dialog_centered(popup)
await popup.visibility_changed
if popup.accepted:
undo_manager.create_action("Add terrain type", UndoRedo.MERGE_DISABLE, tileset)
undo_manager.add_do_method(self, &"perform_add_terrain", popup.terrain_name, popup.terrain_color, popup.terrain_type, popup.terrain_categories, {path = popup.terrain_icon})
undo_manager.add_undo_method(self, &"perform_remove_terrain", terrain_list.get_child_count() - 1)
undo_manager.commit_action()
popup.queue_free()
func _on_edit_terrain_pressed() -> void:
if !tileset:
return
if selected_entry < 0:
return
var t := BetterTerrain.get_terrain(tileset, selected_entry)
var categories = BetterTerrain.get_terrain_categories(tileset)
categories = categories.filter(func(x): return x.id != selected_entry)
var popup := TERRAIN_PROPERTIES_SCENE.instantiate()
popup.set_category_data(categories)
t.icon = t.icon.duplicate()
popup.terrain_name = t.name
popup.terrain_type = t.type
popup.terrain_color = t.color
if t.has("icon") and t.icon.has("path"):
popup.terrain_icon = t.icon.path
popup.terrain_categories = t.categories
EditorInterface.popup_dialog_centered(popup)
await popup.visibility_changed
if popup.accepted:
undo_manager.create_action("Edit terrain details", UndoRedo.MERGE_DISABLE, tileset)
undo_manager.add_do_method(self, &"perform_edit_terrain", selected_entry, popup.terrain_name, popup.terrain_color, popup.terrain_type, popup.terrain_categories, {path = popup.terrain_icon})
undo_manager.add_undo_method(self, &"perform_edit_terrain", selected_entry, t.name, t.color, t.type, t.categories, t.icon)
if t.type != popup.terrain_type:
terrain_undo.create_terrain_type_restore_point(undo_manager, tileset)
terrain_undo.create_peering_restore_point_specific(undo_manager, tileset, selected_entry)
undo_manager.commit_action()
popup.queue_free()
func _on_pick_icon_pressed():
if selected_entry < 0:
return
tile_view.pick_icon_terrain = selected_entry
func _on_pick_icon_focus_exited():
tile_view.pick_icon_terrain_cancel = true
pick_icon_button.button_pressed = false
func _on_move_pressed(down: bool) -> void:
if !tileset:
return
if selected_entry < 0:
return
var index1 = selected_entry
var index2 = index1 + (1 if down else -1)
if index2 < 0 or index2 >= terrain_list.get_child_count():
return
undo_manager.create_action("Reorder terrains", UndoRedo.MERGE_DISABLE, tileset)
undo_manager.add_do_method(self, &"perform_swap_terrain", index1, index2)
undo_manager.add_undo_method(self, &"perform_swap_terrain", index1, index2)
undo_manager.commit_action()
func _on_remove_terrain_pressed() -> void:
if !tileset:
return
if selected_entry < 0:
return
# store confirmation in array to pass by ref
var t := BetterTerrain.get_terrain(tileset, selected_entry)
var confirmed := [false]
var popup := ConfirmationDialog.new()
popup.dialog_text = tr("Are you sure you want to remove {0}?").format([t.name])
popup.dialog_hide_on_ok = false
popup.confirmed.connect(func():
confirmed[0] = true
popup.hide()
)
EditorInterface.popup_dialog_centered(popup)
await popup.visibility_changed
popup.queue_free()
if confirmed[0]:
undo_manager.create_action("Remove terrain type", UndoRedo.MERGE_DISABLE, tileset)
undo_manager.add_do_method(self, &"perform_remove_terrain", selected_entry)
undo_manager.add_undo_method(self, &"perform_add_terrain", t.name, t.color, t.type, t.categories, t.icon)
for n in range(terrain_list.get_child_count() - 2, selected_entry, -1):
undo_manager.add_undo_method(self, &"perform_swap_terrain", n, n - 1)
if t.type == BetterTerrain.TerrainType.CATEGORY:
terrain_undo.create_terrain_type_restore_point(undo_manager, tileset)
terrain_undo.create_peering_restore_point_specific(undo_manager, tileset, selected_entry)
undo_manager.commit_action()
func add_terrain_entry(terrain:Dictionary, index:int = -1):
if index < 0:
index = terrain_list.get_child_count()
var entry = TERRAIN_ENTRY_SCENE.instantiate()
entry.tileset = tileset
entry.terrain = terrain
entry.grid_mode = grid_mode_button.button_pressed
entry.select.connect(_on_entry_select)
terrain_list.add_child(entry)
terrain_list.move_child(entry, index)
func remove_terrain_entry(index: int):
terrain_list.get_child(index).free()
for i in range(index, terrain_list.get_child_count()):
var child = terrain_list.get_child(i)
child.terrain = BetterTerrain.get_terrain(tileset, i)
child.update()
func perform_add_terrain(name: String, color: Color, type: int, categories: Array, icon:Dictionary = {}) -> void:
if BetterTerrain.add_terrain(tileset, name, color, type, categories, icon):
var index = BetterTerrain.terrain_count(tileset) - 1
var terrain = BetterTerrain.get_terrain(tileset, index)
add_terrain_entry(terrain, index)
func perform_remove_terrain(index: int) -> void:
if index >= BetterTerrain.terrain_count(tileset):
return
if BetterTerrain.remove_terrain(tileset, index):
remove_terrain_entry(index)
update_tile_view_paint()
func perform_swap_terrain(index1: int, index2: int) -> void:
var lower := min(index1, index2)
var higher := max(index1, index2)
if lower >= terrain_list.get_child_count() or higher >= terrain_list.get_child_count():
return
var item1 = terrain_list.get_child(lower)
var item2 = terrain_list.get_child(higher)
if BetterTerrain.swap_terrains(tileset, lower, higher):
terrain_list.move_child(item1, higher)
item1.terrain = BetterTerrain.get_terrain(tileset, higher)
item1.update()
item2.terrain = BetterTerrain.get_terrain(tileset, lower)
item2.update()
selected_entry = index2
terrain_list.get_child(index2).set_selected(true)
update_tile_view_paint()
func perform_edit_terrain(index: int, name: String, color: Color, type: int, categories: Array, icon: Dictionary = {}) -> void:
if index >= terrain_list.get_child_count():
return
var entry = terrain_list.get_child(index)
# don't overwrite empty icon
var valid_icon = icon
if icon.has("path") and icon.path.is_empty():
var terrain = BetterTerrain.get_terrain(tileset, index)
valid_icon = terrain.icon
if BetterTerrain.set_terrain(tileset, index, name, color, type, categories, valid_icon):
entry.terrain = BetterTerrain.get_terrain(tileset, index)
entry.update()
tile_view.queue_redraw()
func _on_shuffle_random_pressed():
BetterTerrain.use_seed = !shuffle_random.button_pressed
func _on_bit_button_pressed(button: BaseButton) -> void:
match select_tiles.button_group.get_pressed_button():
select_tiles: tile_view.paint_mode = tile_view.PaintMode.SELECT
paint_type: tile_view.paint_mode = tile_view.PaintMode.PAINT_TYPE
paint_terrain: tile_view.paint_mode = tile_view.PaintMode.PAINT_PEERING
paint_symmetry: tile_view.paint_mode = tile_view.PaintMode.PAINT_SYMMETRY
_: tile_view.paint_mode = tile_view.PaintMode.NO_PAINT
tile_view.queue_redraw()
symmetry_options.visible = paint_symmetry.button_pressed
func _on_symmetry_selected(index):
tile_view.paint_symmetry = index
func _on_paste_occurred():
select_tiles.button_pressed = true
func _on_change_zoom_level(value):
zoom_slider.value = value
func _on_terrain_updated(index):
var entry = terrain_list.get_child(index)
entry.terrain = BetterTerrain.get_terrain(tileset, index)
entry.update()
func canvas_tilemap_transform() -> Transform2D:
var transform := tilemap.get_viewport_transform() * tilemap.global_transform
# Handle subviewport
var editor_viewport := EditorInterface.get_editor_viewport_2d()
if tilemap.get_viewport() != editor_viewport:
var container = tilemap.get_viewport().get_parent() as SubViewportContainer
if container:
transform = editor_viewport.global_canvas_transform * container.get_transform() * transform
return transform
func canvas_draw(overlay: Control) -> void:
if !draw_overlay:
return
if selected_entry < 0:
return
var type = selected_entry
var terrain := BetterTerrain.get_terrain(tileset, type)
if !terrain.valid:
return
var tiles := []
var transform := canvas_tilemap_transform()
if paint_action == PaintAction.RECT and paint_mode != PaintMode.NO_PAINT:
var area := Rect2i(initial_click, current_position - initial_click).abs()
# Shortcut fill for large areas
if area.size.x > 1 and area.size.y > 1 and area.size.x * area.size.y > MAX_CANVAS_RENDER_TILES:
var shortcut := PackedVector2Array([
tilemap.map_to_local(area.position),
tilemap.map_to_local(Vector2i(area.end.x, area.position.y)),
tilemap.map_to_local(area.end),
tilemap.map_to_local(Vector2i(area.position.x, area.end.y))
])
overlay.draw_colored_polygon(transform * shortcut, Color(terrain.color, 0.5))
return
for y in range(area.position.y, area.end.y + 1):
for x in range(area.position.x, area.end.x + 1):
tiles.append(Vector2i(x, y))
elif paint_action == PaintAction.LINE and paint_mode != PaintMode.NO_PAINT:
var cells := _get_tileset_line(initial_click, current_position, tileset)
var shape = BetterTerrain.data.cell_polygon(tileset)
for c in cells:
var tile_transform := Transform2D(0.0, tilemap.tile_set.tile_size, 0.0, tilemap.map_to_local(c))
overlay.draw_colored_polygon(transform * tile_transform * shape, Color(terrain.color, 0.5))
elif fill_button.button_pressed:
tiles = _get_fill_cells(current_position)
if tiles.size() > MAX_CANVAS_RENDER_TILES:
tiles.resize(MAX_CANVAS_RENDER_TILES)
else:
tiles.append(current_position)
var shape = BetterTerrain.data.cell_polygon(tileset)
for t in tiles:
var tile_transform := Transform2D(0.0, tilemap.tile_set.tile_size, 0.0, tilemap.map_to_local(t))
overlay.draw_colored_polygon(transform * tile_transform * shape, Color(terrain.color, 0.5))
func canvas_input(event: InputEvent) -> bool:
if selected_entry < 0:
return false
draw_overlay = true
if event is InputEventMouseMotion:
var tr := canvas_tilemap_transform()
var pos := tr.affine_inverse() * Vector2(event.position)
var event_position := tilemap.local_to_map(pos)
prev_position = current_position
if event_position == current_position:
return false
current_position = event_position
update_overlay.emit()
var replace_mode = replace_button.button_pressed
var released : bool = event is InputEventMouseButton and !event.pressed
if released:
terrain_undo.finish_action()
var type = selected_entry
if paint_action == PaintAction.RECT and paint_mode != PaintMode.NO_PAINT:
var area := Rect2i(initial_click, current_position - initial_click).abs()
# Fill from initial_target to target
undo_manager.create_action(tr("Draw terrain rectangle"), UndoRedo.MERGE_DISABLE, tilemap)
for y in range(area.position.y, area.end.y + 1):
for x in range(area.position.x, area.end.x + 1):
var coord := Vector2i(x, y)
if paint_mode == PaintMode.PAINT:
if replace_mode:
undo_manager.add_do_method(BetterTerrain, &"replace_cell", tilemap, coord, type)
else:
undo_manager.add_do_method(BetterTerrain, &"set_cell", tilemap, coord, type)
else:
undo_manager.add_do_method(tilemap, &"erase_cell", coord)
undo_manager.add_do_method(BetterTerrain, &"update_terrain_area", tilemap, area)
terrain_undo.create_tile_restore_point_area(undo_manager, tilemap, area)
undo_manager.commit_action()
update_overlay.emit()
elif paint_action == PaintAction.LINE and paint_mode != PaintMode.NO_PAINT:
undo_manager.create_action(tr("Draw terrain line"), UndoRedo.MERGE_DISABLE, tilemap)
var cells := _get_tileset_line(initial_click, current_position, tileset)
if paint_mode == PaintMode.PAINT:
if replace_mode:
undo_manager.add_do_method(BetterTerrain, &"replace_cells", tilemap, cells, type)
else:
undo_manager.add_do_method(BetterTerrain, &"set_cells", tilemap, cells, type)
elif paint_mode == PaintMode.ERASE:
for c in cells:
undo_manager.add_do_method(tilemap, &"erase_cell", c)
undo_manager.add_do_method(BetterTerrain, &"update_terrain_cells", tilemap, cells)
terrain_undo.create_tile_restore_point(undo_manager, tilemap, cells)
undo_manager.commit_action()
update_overlay.emit()
paint_mode = PaintMode.NO_PAINT
return true
var clicked : bool = event is InputEventMouseButton and event.pressed
if clicked:
paint_mode = PaintMode.NO_PAINT
if (event.is_command_or_control_pressed() and !event.shift_pressed):
var pick = BetterTerrain.get_cell(tilemap, current_position)
if pick >= 0:
terrain_list.get_children()[pick]._on_focus_entered()
#_on_entry_select(pick)
return true
paint_action = PaintAction.NO_ACTION
if rectangle_button.button_pressed:
paint_action = PaintAction.RECT
elif line_button.button_pressed:
paint_action = PaintAction.LINE
elif draw_button.button_pressed:
if event.shift_pressed:
paint_action = PaintAction.LINE
if event.is_command_or_control_pressed():
paint_action = PaintAction.RECT
if event.button_index == MOUSE_BUTTON_LEFT:
paint_mode = PaintMode.PAINT
elif event.button_index == MOUSE_BUTTON_RIGHT:
paint_mode = PaintMode.ERASE
else:
return false
if (clicked or event is InputEventMouseMotion) and paint_mode != PaintMode.NO_PAINT:
if clicked:
initial_click = current_position
terrain_undo.action_index += 1
terrain_undo.action_count = 0
var type = selected_entry
if paint_action == PaintAction.LINE or paint_action == PaintAction.RECT:
# if painting as line, execution happens on release.
# prevent other painting actions from running.
pass
elif draw_button.button_pressed:
undo_manager.create_action(tr("Draw terrain") + str(terrain_undo.action_index), UndoRedo.MERGE_ALL, tilemap, true)
var cells := _get_tileset_line(prev_position, current_position, tileset)
if paint_mode == PaintMode.PAINT:
if replace_mode:
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"replace_cells", [tilemap, cells, type])
else:
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"set_cells", [tilemap, cells, type])
elif paint_mode == PaintMode.ERASE:
for c in cells:
terrain_undo.add_do_method(undo_manager, tilemap, &"erase_cell", [c])
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"update_terrain_cells", [tilemap, cells])
terrain_undo.create_tile_restore_point(undo_manager, tilemap, cells)
undo_manager.commit_action()
terrain_undo.action_count += 1
elif fill_button.button_pressed:
var cells := _get_fill_cells(current_position)
undo_manager.create_action(tr("Fill terrain"), UndoRedo.MERGE_DISABLE, tilemap)
if paint_mode == PaintMode.PAINT:
if replace_mode:
undo_manager.add_do_method(BetterTerrain, &"replace_cells", tilemap, cells, type)
else:
undo_manager.add_do_method(BetterTerrain, &"set_cells", tilemap, cells, type)
elif paint_mode == PaintMode.ERASE:
for c in cells:
undo_manager.add_do_method(tilemap, &"erase_cell", c)
undo_manager.add_do_method(BetterTerrain, &"update_terrain_cells", tilemap, cells)
terrain_undo.create_tile_restore_point(undo_manager, tilemap, cells)
undo_manager.commit_action()
update_overlay.emit()
return true
return false
func canvas_mouse_exit() -> void:
draw_overlay = false
update_overlay.emit()
func _shortcut_input(event) -> void:
if event is InputEventKey:
if event.keycode == KEY_C and (event.is_command_or_control_pressed() and not event.echo):
get_viewport().set_input_as_handled()
tile_view.copy_selection()
if event.keycode == KEY_V and (event.is_command_or_control_pressed() and not event.echo):
get_viewport().set_input_as_handled()
tile_view.paste_selection()
## bresenham alg ported from Geometry2D::bresenham_line()
func _get_line(from:Vector2i, to:Vector2i) -> Array[Vector2i]:
if from == to:
return [to]
var points:Array[Vector2i] = []
var delta := (to - from).abs() * 2
var step := (to - from).sign()
var current := from
if delta.x > delta.y:
var err:int = delta.x / 2
while current.x != to.x:
points.push_back(current);
err -= delta.y
if err < 0:
current.y += step.y
err += delta.x
current.x += step.x
else:
var err:int = delta.y / 2
while current.y != to.y:
points.push_back(current)
err -= delta.x
if err < 0:
current.x += step.x
err += delta.y
current.y += step.y
points.push_back(current);
return points;
## half-offset bresenham alg ported from TileMapEditor::get_line
func _get_tileset_line(from:Vector2i, to:Vector2i, tileset:TileSet) -> Array[Vector2i]:
if tileset.tile_shape == TileSet.TILE_SHAPE_SQUARE:
return _get_line(from, to)
var points:Array[Vector2i] = []
var transposed := tileset.get_tile_offset_axis() == TileSet.TILE_OFFSET_AXIS_VERTICAL
if transposed:
from = Vector2i(from.y, from.x)
to = Vector2i(to.y, to.x)
var delta:Vector2i = to - from
delta = Vector2i(2 * delta.x + abs(posmod(to.y, 2)) - abs(posmod(from.y, 2)), delta.y)
var sign:Vector2i = delta.sign()
var current := from;
points.push_back(Vector2i(current.y, current.x) if transposed else current)
var err := 0
if abs(delta.y) < abs(delta.x):
var err_step:Vector2i = 3 * delta.abs()
while current != to:
err += err_step.y
if err > abs(delta.x):
if sign.x == 0:
current += Vector2i(sign.y, 0)
else:
current += Vector2i(sign.x if bool(current.y % 2) != (sign.x < 0) else 0, sign.y)
err -= err_step.x
else:
current += Vector2i(sign.x, 0)
err += err_step.y
points.push_back(Vector2i(current.y, current.x) if transposed else current)
else:
var err_step:Vector2i = delta.abs()
while current != to:
err += err_step.x
if err > 0:
if sign.x == 0:
current += Vector2i(0, sign.y)
else:
current += Vector2i(sign.x if bool(current.y % 2) != (sign.x < 0) else 0, sign.y)
err -= err_step.y;
else:
if sign.x == 0:
current += Vector2i(0, sign.y)
else:
current += Vector2i(-sign.x if bool(current.y % 2) != (sign.x > 0) else 0, sign.y)
err += err_step.y
points.push_back(Vector2i(current.y, current.x) if transposed else current)
return points
func _on_terrain_enable_id_pressed(id):
if id in [SourceSelectors.ALL, SourceSelectors.NONE]:
for i in source_selector_popup.item_count:
if source_selector_popup.is_item_checkable(i):
source_selector_popup.set_item_checked(i, id == SourceSelectors.ALL)
else:
var index = source_selector_popup.get_item_index(id)
var checked = source_selector_popup.is_item_checked(index)
source_selector_popup.set_item_checked(index, !checked)
var disabled_sources : Array[int]
for i in source_selector_popup.item_count:
if source_selector_popup.is_item_checkable(i) and !source_selector_popup.is_item_checked(i):
disabled_sources.append(source_selector_popup.get_item_id(i))
tile_view.disabled_sources = disabled_sources
func corresponding_tilemap_editor_button(similar: Button) -> Button:
var editors = EditorInterface.get_base_control().find_children("*", "TileMapLayerEditor", true, false)
var tile_map_layer_editor = editors[0]
var buttons = tile_map_layer_editor.find_children("*", "Button", true, false)
for button: Button in buttons:
if button.icon == similar.icon:
return button
return null
func _on_layer_up_or_down_pressed(button: Button) -> void:
var matching_button = corresponding_tilemap_editor_button(button)
if !matching_button:
return
# Major hack, to reduce flicker hide the tileset editor briefly
var editors = EditorInterface.get_base_control().find_children("*", "TileSetEditor", true, false)
var tile_set_editor = editors[0]
matching_button.pressed.emit()
tile_set_editor.modulate = Color.TRANSPARENT
await get_tree().process_frame
await get_tree().process_frame
force_show_terrains.emit()
tile_set_editor.modulate = Color.WHITE
func _on_layer_up_pressed() -> void:
_on_layer_up_or_down_pressed(layer_up)
func _on_layer_down_pressed() -> void:
_on_layer_up_or_down_pressed(layer_down)
func _on_layer_highlight_toggled(toggled: bool) -> void:
var settings = EditorInterface.get_editor_settings()
settings.set_setting("editors/tiles_editor/highlight_selected_layer", toggled)
var highlight = corresponding_tilemap_editor_button(layer_highlight)
if highlight:
highlight.toggled.emit(toggled)
func _on_layer_grid_toggled(toggled: bool) -> void:
var settings = EditorInterface.get_editor_settings()
settings.set_setting("editors/tiles_editor/display_grid", toggled)
var grid = corresponding_tilemap_editor_button(layer_grid)
if grid:
grid.toggled.emit(toggled)

View File

@@ -0,0 +1 @@
uid://ynajlxcomlkc

View File

@@ -0,0 +1,399 @@
[gd_scene load_steps=32 format=3 uid="uid://de8b6h6ieal7r"]
[ext_resource type="Script" uid="uid://ynajlxcomlkc" path="res://addons/better-terrain/editor/Dock.gd" id="1_raoha"]
[ext_resource type="Texture2D" uid="uid://c6lxq2y7mpb18" path="res://addons/better-terrain/icons/EditType.svg" id="2_cpm2t"]
[ext_resource type="Texture2D" uid="uid://y3xy6qdckht6" path="res://addons/better-terrain/icons/Replace.svg" id="2_fvmt6"]
[ext_resource type="Texture2D" uid="uid://bo2cjv08jkvf8" path="res://addons/better-terrain/icons/EditTerrain.svg" id="3_pqb1p"]
[ext_resource type="Texture2D" uid="uid://b0es228gfcykd" path="res://addons/better-terrain/icons/Warning.svg" id="4_6ahwe"]
[ext_resource type="Script" uid="uid://cpm7dq6r0n0sn" path="res://addons/better-terrain/editor/TileView.gd" id="4_nqppq"]
[ext_resource type="Texture2D" uid="uid://co6gwwmog0pjy" path="res://addons/better-terrain/icons/EditSymmetry.svg" id="5_kfjwu"]
[ext_resource type="Texture2D" uid="uid://cs4mdmluiydj6" path="res://addons/better-terrain/icons/ShuffleRandom.svg" id="5_n3owo"]
[ext_resource type="Texture2D" uid="uid://5hm3bfj3dvej" path="res://addons/better-terrain/icons/SymmetryMirror.svg" id="6_mofuh"]
[ext_resource type="Texture2D" uid="uid://dqmc1jp56or8m" path="res://addons/better-terrain/icons/SymmetryFlip.svg" id="7_ojxs0"]
[ext_resource type="Texture2D" uid="uid://cxoewno1cefua" path="res://addons/better-terrain/icons/SymmetryReflect.svg" id="8_8dhyg"]
[ext_resource type="Texture2D" uid="uid://baxhjy28r1iqj" path="res://addons/better-terrain/icons/SymmetryRotateClockwise.svg" id="9_tq76a"]
[ext_resource type="Texture2D" uid="uid://csbwdkr6bc2db" path="res://addons/better-terrain/icons/SymmetryRotateCounterClockwise.svg" id="10_o5h1f"]
[ext_resource type="Texture2D" uid="uid://8mcycyl3e66r" path="res://addons/better-terrain/icons/SymmetryRotate180.svg" id="11_m6syp"]
[ext_resource type="Texture2D" uid="uid://b7fx4mk18lmls" path="res://addons/better-terrain/icons/SymmetryRotateAll.svg" id="12_11vru"]
[ext_resource type="Texture2D" uid="uid://cyjra4g05dwh" path="res://addons/better-terrain/icons/SymmetryAll.svg" id="13_lp5m2"]
[sub_resource type="ButtonGroup" id="ButtonGroup_aon7c"]
[sub_resource type="InputEventKey" id="InputEventKey_saph6"]
device = -1
keycode = 68
unicode = 100
[sub_resource type="Shortcut" id="Shortcut_3k2al"]
events = [SubResource("InputEventKey_saph6")]
[sub_resource type="SVGTexture" id="SVGTexture_nkf6h"]
_source = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\"><path fill=\"#ff5d5d\" d=\"M2 1v8.586l1.293-1.293a1 1 0 0 1 1.414 0L7 10.587l2.293-2.293a1 1 0 0 1 1.414 0L13 10.586l1-1V6H9V1H2zm8 0v4h4zm-6 9.414-2 2V15h12v-2.586l-.293.293a1 1 0 0 1-1.414 0L10 10.414l-2.293 2.293a1 1 0 0 1-1.414 0L4 10.414z\"/></svg>
"
[sub_resource type="InputEventKey" id="InputEventKey_q1v0d"]
device = -1
keycode = 76
unicode = 108
[sub_resource type="Shortcut" id="Shortcut_wc6bu"]
events = [SubResource("InputEventKey_q1v0d")]
[sub_resource type="InputEventKey" id="InputEventKey_68n3h"]
device = -1
keycode = 82
unicode = 114
[sub_resource type="InputEventKey" id="InputEventKey_qcu1e"]
device = -1
keycode = 67
unicode = 99
[sub_resource type="Shortcut" id="Shortcut_tcjet"]
events = [SubResource("InputEventKey_68n3h"), SubResource("InputEventKey_qcu1e")]
[sub_resource type="InputEventKey" id="InputEventKey_grxy4"]
device = -1
keycode = 66
unicode = 98
[sub_resource type="Shortcut" id="Shortcut_46fac"]
events = [SubResource("InputEventKey_grxy4")]
[sub_resource type="InputEventKey" id="InputEventKey_xd61m"]
device = -1
keycode = 80
unicode = 112
[sub_resource type="Shortcut" id="Shortcut_uwwa1"]
events = [SubResource("InputEventKey_xd61m")]
[sub_resource type="ButtonGroup" id="ButtonGroup_3wrxn"]
allow_unpress = true
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mpeb7"]
bg_color = Color(0, 0, 0, 0.4)
[node name="Dock" type="Control" node_paths=PackedStringArray("shortcut_context")]
custom_minimum_size = Vector2(0, 100)
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
focus_mode = 2
shortcut_context = NodePath(".")
script = ExtResource("1_raoha")
[node name="VBox" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Toolbar" type="HBoxContainer" parent="VBox"]
layout_mode = 2
[node name="Draw" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Draw terrain
Shift: Draw line.
Ctrl/Cmd+Shift: Draw rectangle."
toggle_mode = true
button_pressed = true
button_group = SubResource("ButtonGroup_aon7c")
shortcut = SubResource("Shortcut_3k2al")
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="Line" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Draw line"
toggle_mode = true
button_group = SubResource("ButtonGroup_aon7c")
shortcut = SubResource("Shortcut_wc6bu")
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="Rectangle" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Fill a rectangle of terrain"
toggle_mode = true
button_group = SubResource("ButtonGroup_aon7c")
shortcut = SubResource("Shortcut_tcjet")
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="Fill" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Bucket fill terrain"
toggle_mode = true
button_group = SubResource("ButtonGroup_aon7c")
shortcut = SubResource("Shortcut_46fac")
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="Replace" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Toggle replace mode"
toggle_mode = true
shortcut = SubResource("Shortcut_uwwa1")
icon = ExtResource("2_fvmt6")
[node name="VSeparator" type="VSeparator" parent="VBox/Toolbar"]
layout_mode = 2
[node name="SelectTiles" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Select"
toggle_mode = true
button_group = SubResource("ButtonGroup_3wrxn")
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="PaintType" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Paint terrain types"
toggle_mode = true
button_group = SubResource("ButtonGroup_3wrxn")
icon = ExtResource("2_cpm2t")
flat = true
[node name="PaintTerrain" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Paint terrain connecting types"
toggle_mode = true
button_group = SubResource("ButtonGroup_3wrxn")
icon = ExtResource("3_pqb1p")
flat = true
[node name="PaintSymmetry" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Paint tile symmetry"
toggle_mode = true
button_group = SubResource("ButtonGroup_3wrxn")
icon = ExtResource("5_kfjwu")
flat = true
[node name="SymmetryOptions" type="OptionButton" parent="VBox/Toolbar"]
visible = false
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
selected = 0
item_count = 9
popup/item_0/text = "No symmetry"
popup/item_0/id = 8
popup/item_1/text = "Mirror"
popup/item_1/icon = ExtResource("6_mofuh")
popup/item_1/id = 1
popup/item_2/text = "Flip"
popup/item_2/icon = ExtResource("7_ojxs0")
popup/item_2/id = 1
popup/item_3/text = "Reflect"
popup/item_3/icon = ExtResource("8_8dhyg")
popup/item_3/id = 2
popup/item_4/text = "Rotate clockwise"
popup/item_4/icon = ExtResource("9_tq76a")
popup/item_4/id = 3
popup/item_5/text = "Rotate counter-clockwise"
popup/item_5/icon = ExtResource("10_o5h1f")
popup/item_5/id = 4
popup/item_6/text = "Rotate 180"
popup/item_6/icon = ExtResource("11_m6syp")
popup/item_6/id = 5
popup/item_7/text = "All rotations"
popup/item_7/icon = ExtResource("12_11vru")
popup/item_7/id = 6
popup/item_8/text = "All reflections & rotations"
popup/item_8/icon = ExtResource("13_lp5m2")
popup/item_8/id = 7
[node name="VSeparator3" type="VSeparator" parent="VBox/Toolbar"]
layout_mode = 2
[node name="ZoomContainer" type="VBoxContainer" parent="VBox/Toolbar"]
layout_mode = 2
alignment = 1
[node name="Sources" type="MenuBar" parent="VBox/Toolbar"]
layout_mode = 2
[node name="Sources" type="PopupMenu" parent="VBox/Toolbar/Sources"]
auto_translate_mode = 2
auto_translate = false
hide_on_item_selection = false
hide_on_checkable_item_selection = false
[node name="Spacer" type="Control" parent="VBox/Toolbar"]
layout_mode = 2
size_flags_horizontal = 3
[node name="ShuffleRandom" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Shuffle random tiles each update"
toggle_mode = true
icon = ExtResource("5_n3owo")
flat = true
[node name="Clean" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
text = "Clean tile data"
icon = ExtResource("4_6ahwe")
[node name="VSeparator2" type="VSeparator" parent="VBox/Toolbar"]
layout_mode = 2
[node name="LayerUp" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Select previous layer"
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="LayerDown" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Select next layer"
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="LayerHighlight" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Highlight selected layer"
toggle_mode = true
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="LayerGrid" type="Button" parent="VBox/Toolbar"]
layout_mode = 2
tooltip_text = "Toggle grid visibility"
toggle_mode = true
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="HSplit" type="HSplitContainer" parent="VBox"]
layout_mode = 2
size_flags_vertical = 3
split_offset = 325
[node name="Terrains" type="VBoxContainer" parent="VBox/HSplit"]
layout_mode = 2
[node name="Panel" type="PanelContainer" parent="VBox/HSplit/Terrains"]
layout_mode = 2
size_flags_vertical = 3
theme_override_styles/panel = SubResource("StyleBoxFlat_mpeb7")
[node name="ScrollContainer" type="ScrollContainer" parent="VBox/HSplit/Terrains/Panel"]
layout_mode = 2
horizontal_scroll_mode = 3
[node name="TerrainList" type="HFlowContainer" parent="VBox/HSplit/Terrains/Panel/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="LowerToolbar" type="HBoxContainer" parent="VBox/HSplit/Terrains"]
layout_mode = 2
[node name="GridMode" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar"]
layout_mode = 2
tooltip_text = "Toggle grid view"
toggle_mode = true
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="QuickMode" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar"]
auto_translate_mode = 1
layout_mode = 2
tooltip_text = "Toggle quick mode. Only shows paintable terrain types."
toggle_mode = true
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="VSeparator" type="VSeparator" parent="VBox/HSplit/Terrains/LowerToolbar"]
layout_mode = 2
[node name="EditTools" type="HBoxContainer" parent="VBox/HSplit/Terrains/LowerToolbar"]
layout_mode = 2
size_flags_horizontal = 3
alignment = 2
[node name="AddTerrain" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar/EditTools"]
layout_mode = 2
tooltip_text = "Add terrain type"
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="EditTerrain" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar/EditTools"]
layout_mode = 2
tooltip_text = "Edit terrain type"
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="PickIcon" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar/EditTools"]
layout_mode = 2
tooltip_text = "Pick terrain icon from tileset"
toggle_mode = true
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="MoveUp" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar/EditTools"]
layout_mode = 2
tooltip_text = "Move selected terrain up"
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="MoveDown" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar/EditTools"]
layout_mode = 2
tooltip_text = "Move selected terrain down"
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="RemoveTerrain" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar/EditTools"]
layout_mode = 2
tooltip_text = "Remove selected terrain type(s)"
icon = SubResource("SVGTexture_nkf6h")
flat = true
[node name="Panel" type="Panel" parent="VBox/HSplit"]
custom_minimum_size = Vector2(0, 80)
layout_mode = 2
[node name="ScrollArea" type="ScrollContainer" parent="VBox/HSplit/Panel"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
[node name="TileView" type="Control" parent="VBox/HSplit/Panel/ScrollArea"]
texture_filter = 1
texture_repeat = 1
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
focus_mode = 2
script = ExtResource("4_nqppq")
[connection signal="item_selected" from="VBox/Toolbar/SymmetryOptions" to="." method="_on_symmetry_selected"]
[connection signal="id_pressed" from="VBox/Toolbar/Sources/Sources" to="." method="_on_terrain_enable_id_pressed"]
[connection signal="pressed" from="VBox/Toolbar/ShuffleRandom" to="." method="_on_shuffle_random_pressed"]
[connection signal="pressed" from="VBox/Toolbar/Clean" to="." method="_on_clean_pressed"]
[connection signal="pressed" from="VBox/Toolbar/LayerUp" to="." method="_on_layer_up_pressed"]
[connection signal="pressed" from="VBox/Toolbar/LayerDown" to="." method="_on_layer_down_pressed"]
[connection signal="toggled" from="VBox/Toolbar/LayerHighlight" to="." method="_on_layer_highlight_toggled"]
[connection signal="toggled" from="VBox/Toolbar/LayerGrid" to="." method="_on_layer_grid_toggled"]
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/GridMode" to="." method="_on_grid_mode_pressed"]
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/QuickMode" to="." method="_on_quick_mode_pressed"]
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/AddTerrain" to="." method="_on_add_terrain_pressed"]
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/EditTerrain" to="." method="_on_edit_terrain_pressed"]
[connection signal="focus_exited" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/PickIcon" to="." method="_on_pick_icon_focus_exited"]
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/PickIcon" to="." method="_on_pick_icon_pressed"]
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/MoveUp" to="." method="_on_move_pressed" binds= [false]]
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/MoveDown" to="." method="_on_move_pressed" binds= [true]]
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/RemoveTerrain" to="." method="_on_remove_terrain_pressed"]
[connection signal="mouse_exited" from="VBox/HSplit/Panel/ScrollArea/TileView" to="VBox/HSplit/Panel/ScrollArea/TileView" method="clear_highlighted_tile"]

View File

@@ -0,0 +1,185 @@
@tool
extends PanelContainer
signal select(index)
@onready var color_panel := %Color
@onready var terrain_icon_slot := %TerrainIcon
@onready var type_icon_slot := %TypeIcon
@onready var type_icon_panel := %TerrainIconPanel
@onready var name_label := %Name
@onready var layout_container := %Layout
@onready var icon_layout_container := %IconLayout
var selected := false
var tileset:TileSet
var terrain:Dictionary
var grid_mode := false
var color_style_list:StyleBoxFlat
var color_style_grid:StyleBoxFlat
var color_style_decoration:StyleBoxFlat
var _terrain_texture:Texture2D
var _terrain_texture_rect:Rect2i
var _icon_draw_connected := false
func _ready():
update()
func update():
if !terrain or !terrain.valid:
return
if !tileset:
return
name_label.text = terrain.name
tooltip_text = "%s (%d)" % [terrain.name, terrain.id]
color_style_list = color_panel.get_theme_stylebox("panel").duplicate()
color_style_grid = color_panel.get_theme_stylebox("panel").duplicate()
color_style_decoration = color_panel.get_theme_stylebox("panel").duplicate()
color_style_list.bg_color = terrain.color
color_style_list.corner_radius_top_left = 8
color_style_list.corner_radius_bottom_left = 8
color_style_list.corner_radius_top_right = 0
color_style_list.corner_radius_bottom_right = 0
color_style_list.content_margin_left = -1
color_style_list.content_margin_right = -1
color_style_list.border_width_left = 0
color_style_list.border_width_right = 0
color_style_list.border_width_top = 0
color_style_list.border_width_bottom = 0
color_style_grid.bg_color = terrain.color
color_style_grid.corner_radius_top_left = 6
color_style_grid.corner_radius_bottom_left = 6
color_style_grid.corner_radius_top_right = 6
color_style_grid.corner_radius_bottom_right = 6
color_style_grid.content_margin_left = -1
color_style_grid.content_margin_right = -1
color_style_grid.border_width_left = 0
color_style_grid.border_width_right = 0
color_style_grid.border_width_top = 0
color_style_grid.border_width_bottom = 0
color_style_decoration.bg_color = terrain.color
color_style_decoration.corner_radius_top_left = 8
color_style_decoration.corner_radius_bottom_left = 8
color_style_decoration.corner_radius_top_right = 8
color_style_decoration.corner_radius_bottom_right = 8
color_style_decoration.content_margin_left = -1
color_style_decoration.content_margin_right = -1
color_style_decoration.border_width_left = 4
color_style_decoration.border_width_right = 4
color_style_decoration.border_width_top = 4
color_style_decoration.border_width_bottom = 4
match terrain.type:
BetterTerrain.TerrainType.MATCH_TILES:
type_icon_slot.texture = load("res://addons/better-terrain/icons/MatchTiles.svg")
BetterTerrain.TerrainType.MATCH_VERTICES:
type_icon_slot.texture = load("res://addons/better-terrain/icons/MatchVertices.svg")
BetterTerrain.TerrainType.CATEGORY:
type_icon_slot.texture = load("res://addons/better-terrain/icons/NonModifying.svg")
BetterTerrain.TerrainType.DECORATION:
type_icon_slot.texture = load("res://addons/better-terrain/icons/Decoration.svg")
var has_icon = false
if terrain.has("icon"):
if terrain.icon.has("path") and not terrain.icon.path.is_empty():
terrain_icon_slot.texture = load(terrain.icon.path)
_terrain_texture = null
terrain_icon_slot.queue_redraw()
has_icon = true
elif terrain.icon.has("source_id") and tileset.has_source(terrain.icon.source_id):
var source := tileset.get_source(terrain.icon.source_id) as TileSetAtlasSource
var coord := terrain.icon.coord as Vector2i
var rect := source.get_tile_texture_region(coord, 0)
_terrain_texture = source.texture
_terrain_texture_rect = rect
terrain_icon_slot.queue_redraw()
has_icon = true
if not has_icon:
var tiles = BetterTerrain.get_tile_sources_in_terrain(tileset, get_index())
if tiles.size() > 0:
var source := tiles[0].source as TileSetAtlasSource
var coord := tiles[0].coord as Vector2i
var rect := source.get_tile_texture_region(coord, 0)
_terrain_texture = source.texture
_terrain_texture_rect = rect
terrain_icon_slot.queue_redraw()
if _terrain_texture:
terrain_icon_slot.texture = null
if not _icon_draw_connected:
terrain_icon_slot.connect("draw", func():
if _terrain_texture:
terrain_icon_slot.draw_texture_rect_region(_terrain_texture, Rect2i(0,0, 44, 44), _terrain_texture_rect)
)
_icon_draw_connected = true
update_style()
func update_style():
if terrain.type == BetterTerrain.TerrainType.DECORATION:
type_icon_panel.visible = false
color_panel.custom_minimum_size = Vector2i(52,52)
else:
type_icon_panel.visible = true
color_panel.custom_minimum_size = Vector2i(24,24)
if grid_mode:
if terrain.type == BetterTerrain.TerrainType.DECORATION:
color_panel.add_theme_stylebox_override("panel", color_style_decoration)
color_panel.size_flags_vertical = Control.SIZE_FILL
icon_layout_container.size_flags_vertical = Control.SIZE_EXPAND_FILL
else:
color_panel.add_theme_stylebox_override("panel", color_style_grid)
color_panel.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
icon_layout_container.size_flags_vertical = Control.SIZE_FILL
custom_minimum_size = Vector2(0, 60)
size_flags_horizontal = Control.SIZE_FILL
layout_container.vertical = true
name_label.visible = false
icon_layout_container.add_theme_constant_override("separation", -24)
else:
if terrain.type == BetterTerrain.TerrainType.DECORATION:
color_panel.add_theme_stylebox_override("panel", color_style_decoration)
else:
color_panel.add_theme_stylebox_override("panel", color_style_list)
icon_layout_container.size_flags_vertical = Control.SIZE_FILL
custom_minimum_size = Vector2(2000, 60)
size_flags_horizontal = Control.SIZE_EXPAND_FILL
layout_container.vertical = false
name_label.visible = true
color_panel.size_flags_vertical = Control.SIZE_FILL
icon_layout_container.add_theme_constant_override("separation", 4)
func set_selected(value:bool = true):
selected = value
if value:
select.emit(get_index())
queue_redraw()
func _draw():
if selected:
draw_rect(Rect2(Vector2.ZERO, get_rect().size), Color(0.15, 0.70, 1, 0.3))
func _on_focus_entered():
queue_redraw()
selected = true
select.emit(get_index())
func _on_focus_exited():
queue_redraw()

View File

@@ -0,0 +1 @@
uid://c2qfovpuj58b7

View File

@@ -0,0 +1,114 @@
[gd_scene load_steps=8 format=3 uid="uid://u2y444hj182c"]
[ext_resource type="Script" uid="uid://c2qfovpuj58b7" path="res://addons/better-terrain/editor/TerrainEntry.gd" id="1_o2na3"]
[ext_resource type="Texture2D" uid="uid://kmypxsqhynyv" path="res://addons/better-terrain/icons/Decoration.svg" id="2_ossyj"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3pdcc"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
draw_center = false
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dqhir"]
bg_color = Color(0.243, 0.816, 0.518, 1)
border_color = Color(0, 0, 0, 0.439216)
corner_radius_top_left = 8
corner_radius_bottom_left = 8
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rohyw"]
content_margin_left = 2.0
content_margin_top = 2.0
content_margin_right = 2.0
content_margin_bottom = 2.0
bg_color = Color(0, 0, 0, 0.439216)
corner_radius_top_left = 4
corner_radius_top_right = 4
corner_radius_bottom_right = 4
corner_radius_bottom_left = 4
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xa0fl"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(0, 0, 0, 0.439216)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_b4rkm"]
content_margin_left = 3.0
bg_color = Color(0, 0, 0, 0.439216)
draw_center = false
[node name="TerrainEntry" type="PanelContainer"]
custom_minimum_size = Vector2(60, 60)
offset_right = 200.0
offset_bottom = 60.0
size_flags_vertical = 3
focus_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_3pdcc")
script = ExtResource("1_o2na3")
[node name="Layout" type="BoxContainer" parent="."]
unique_name_in_owner = true
layout_mode = 2
theme_override_constants/separation = 4
[node name="IconLayout" type="HBoxContainer" parent="Layout"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 4
[node name="Color" type="PanelContainer" parent="Layout/IconLayout"]
unique_name_in_owner = true
z_index = 1
custom_minimum_size = Vector2(24, 24)
layout_mode = 2
size_flags_horizontal = 0
mouse_filter = 1
theme_override_styles/panel = SubResource("StyleBoxFlat_dqhir")
[node name="PanelContainer" type="PanelContainer" parent="Layout/IconLayout/Color"]
layout_mode = 2
size_flags_horizontal = 4
size_flags_vertical = 4
mouse_filter = 1
theme_override_styles/panel = SubResource("StyleBoxFlat_rohyw")
[node name="TypeIcon" type="TextureRect" parent="Layout/IconLayout/Color/PanelContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 4
size_flags_vertical = 4
texture = ExtResource("2_ossyj")
[node name="TerrainIconPanel" type="PanelContainer" parent="Layout/IconLayout"]
unique_name_in_owner = true
custom_minimum_size = Vector2(52, 52)
layout_mode = 2
size_flags_horizontal = 4
size_flags_vertical = 4
mouse_filter = 1
theme_override_styles/panel = SubResource("StyleBoxFlat_xa0fl")
[node name="TerrainIcon" type="TextureRect" parent="Layout/IconLayout/TerrainIconPanel"]
unique_name_in_owner = true
texture_filter = 1
custom_minimum_size = Vector2(40, 40)
layout_mode = 2
expand_mode = 4
stretch_mode = 5
[node name="Name" type="Label" parent="Layout"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 0
theme_override_styles/normal = SubResource("StyleBoxFlat_b4rkm")
text = "New Terrain"
vertical_alignment = 1
text_overrun_behavior = 3
[connection signal="focus_entered" from="." to="." method="_on_focus_entered"]
[connection signal="focus_exited" from="." to="." method="_on_focus_exited"]

View File

@@ -0,0 +1,85 @@
@tool
extends ConfirmationDialog
var category_icon := load("res://addons/better-terrain/icons/NonModifying.svg")
const CATEGORY_CHECK_ID = &"category_check_id"
var accepted := false
var terrain_name : String:
set(value): %NameEdit.text = value
get: return %NameEdit.text
var terrain_color : Color:
set(value): %ColorPicker.color = value
get: return %ColorPicker.color
var terrain_icon : String:
set(value): %IconEdit.text = value
get: return %IconEdit.text
var terrain_type : int:
set(value):
%TypeOption.selected = value
_on_type_option_item_selected(value)
get: return %TypeOption.selected
var terrain_categories : Array: set = set_categories, get = get_categories
# category is name, color, id
func set_category_data(options: Array) -> void:
if !options.is_empty():
%CategoryLabel.show()
%CategoryContainer.show()
for o in options:
var c = CheckBox.new()
c.text = o.name
c.icon = category_icon
c.add_theme_color_override(&"icon_normal_color", o.color)
c.add_theme_color_override(&"icon_disabled_color", Color(o.color, 0.4))
c.add_theme_color_override(&"icon_focus_color", o.color)
c.add_theme_color_override(&"icon_hover_color", o.color)
c.add_theme_color_override(&"icon_hover_pressed_color", o.color)
c.add_theme_color_override(&"icon_normal_color", o.color)
c.add_theme_color_override(&"icon_pressed_color", o.color)
c.set_meta(CATEGORY_CHECK_ID, o.id)
%CategoryLayout.add_child(c)
func set_categories(ids : Array):
for c in %CategoryLayout.get_children():
c.button_pressed = c.get_meta(CATEGORY_CHECK_ID) in ids
func get_categories() -> Array:
var result := []
if terrain_type == BetterTerrain.TerrainType.CATEGORY:
return result
for c in %CategoryLayout.get_children():
if c.button_pressed:
result.push_back(c.get_meta(CATEGORY_CHECK_ID))
return result
func _on_confirmed() -> void:
# confirm valid name
if terrain_name.is_empty():
var dialog := AcceptDialog.new()
dialog.dialog_text = "Name cannot be empty"
EditorInterface.popup_dialog_centered(dialog)
await dialog.visibility_changed
dialog.queue_free()
return
accepted = true
hide()
func _on_type_option_item_selected(index: int) -> void:
var categories_available = (index != BetterTerrain.TerrainType.CATEGORY)
for c in %CategoryLayout.get_children():
c.disabled = !categories_available

View File

@@ -0,0 +1 @@
uid://j81f0xo4p36y

View File

@@ -0,0 +1,96 @@
[gd_scene load_steps=5 format=3 uid="uid://fdjybw6e7whr"]
[ext_resource type="Script" uid="uid://j81f0xo4p36y" path="res://addons/better-terrain/editor/TerrainProperties.gd" id="1_52nx8"]
[ext_resource type="Texture2D" uid="uid://d1h1p7pcwdnjk" path="res://addons/better-terrain/icons/MatchTiles.svg" id="2_ncc5p"]
[ext_resource type="Texture2D" uid="uid://dfemy1g6okwlv" path="res://addons/better-terrain/icons/MatchVertices.svg" id="3_0nvmi"]
[ext_resource type="Texture2D" uid="uid://1yr6yruwl63u" path="res://addons/better-terrain/icons/NonModifying.svg" id="5_awp83"]
[node name="TerrainProperties" type="ConfirmationDialog"]
auto_translate_mode = 2
oversampling_override = 1.0
title = "Edit terrain properties"
initial_position = 2
size = Vector2i(317, 257)
visible = true
dialog_hide_on_ok = false
script = ExtResource("1_52nx8")
[node name="GridContainer" type="GridContainer" parent="."]
offset_left = 8.0
offset_top = 8.0
offset_right = 309.0
offset_bottom = 212.0
columns = 2
[node name="NameLabel" type="Label" parent="GridContainer"]
layout_mode = 2
text = "Name"
[node name="NameEdit" type="LineEdit" parent="GridContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
placeholder_text = "Terrain name"
[node name="ColorLabel" type="Label" parent="GridContainer"]
layout_mode = 2
text = "Color"
[node name="ColorPicker" type="ColorPickerButton" parent="GridContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
color = Color(1, 0.262745, 0.498039, 1)
edit_alpha = false
[node name="IconLabel" type="Label" parent="GridContainer"]
layout_mode = 2
text = "Icon"
[node name="IconEdit" type="LineEdit" parent="GridContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
placeholder_text = "Icon path (optional)"
[node name="TypeLabel" type="Label" parent="GridContainer"]
layout_mode = 2
text = "Mode"
[node name="TypeOption" type="OptionButton" parent="GridContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
item_count = 3
popup/item_0/text = "Match tiles"
popup/item_0/icon = ExtResource("2_ncc5p")
popup/item_0/id = 0
popup/item_1/text = "Match vertices"
popup/item_1/icon = ExtResource("3_0nvmi")
popup/item_1/id = 1
popup/item_2/text = "Category"
popup/item_2/icon = ExtResource("5_awp83")
popup/item_2/id = 2
[node name="CategoryLabel" type="Label" parent="GridContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 2
size_flags_vertical = 1
text = "Categories"
[node name="CategoryContainer" type="ScrollContainer" parent="GridContainer"]
unique_name_in_owner = true
visible = false
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
size_flags_vertical = 3
[node name="CategoryLayout" type="VBoxContainer" parent="GridContainer/CategoryContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 100)
layout_mode = 2
size_flags_vertical = 3
[connection signal="confirmed" from="." to="." method="_on_confirmed"]
[connection signal="item_selected" from="GridContainer/TypeOption" to="." method="_on_type_option_item_selected"]

View File

@@ -0,0 +1,190 @@
@tool
extends Node
var action_index := 0
var action_count := 0
var _current_action_index := 0
var _current_action_count := 0
func create_tile_restore_point(undo_manager: EditorUndoRedoManager, tm: TileMapLayer, cells: Array, and_surrounding_cells: bool = true) -> void:
if and_surrounding_cells:
cells = BetterTerrain._widen(tm, cells)
var restore := []
for c in cells:
restore.append([
c,
tm.get_cell_source_id(c),
tm.get_cell_atlas_coords(c),
tm.get_cell_alternative_tile(c)
])
undo_manager.add_undo_method(self, &"restore_tiles", tm, restore)
func create_tile_restore_point_area(undo_manager: EditorUndoRedoManager, tm: TileMapLayer, area: Rect2i, and_surrounding_cells: bool = true) -> void:
area.end += Vector2i.ONE
var restore := []
for y in range(area.position.y, area.end.y):
for x in range(area.position.x, area.end.x):
var c := Vector2i(x, y)
restore.append([
c,
tm.get_cell_source_id(c),
tm.get_cell_atlas_coords(c),
tm.get_cell_alternative_tile(c)
])
undo_manager.add_undo_method(self, &"restore_tiles", tm, restore)
if !and_surrounding_cells:
return
var edges := []
for x in range(area.position.x, area.end.x):
edges.append(Vector2i(x, area.position.y))
edges.append(Vector2i(x, area.end.y))
for y in range(area.position.y + 1, area.end.y - 1):
edges.append(Vector2i(area.position.x, y))
edges.append(Vector2i(area.end.x, y))
edges = BetterTerrain._widen_with_exclusion(tm, edges, area)
create_tile_restore_point(undo_manager, tm, edges, false)
func restore_tiles(tm: TileMapLayer, restore: Array) -> void:
for r in restore:
tm.set_cell(r[0], r[1], r[2], r[3])
func create_peering_restore_point(undo_manager: EditorUndoRedoManager, ts: TileSet) -> void:
var restore := []
for s in ts.get_source_count():
var source_id := ts.get_source_id(s)
var source := ts.get_source(source_id) as TileSetAtlasSource
if !source:
continue
for t in source.get_tiles_count():
var coord := source.get_tile_id(t)
for a in source.get_alternative_tiles_count(coord):
var alternate := source.get_alternative_tile_id(coord, a)
var td := source.get_tile_data(coord, alternate)
var tile_type := BetterTerrain.get_tile_terrain_type(td)
if tile_type == BetterTerrain.TileCategory.NON_TERRAIN:
continue
var peering_dict := {}
for c in BetterTerrain.tile_peering_keys(td):
peering_dict[c] = BetterTerrain.tile_peering_types(td, c)
var symmetry = BetterTerrain.get_tile_symmetry_type(td)
restore.append([source_id, coord, alternate, tile_type, peering_dict, symmetry])
undo_manager.add_undo_method(self, &"restore_peering", ts, restore)
func create_peering_restore_point_specific(undo_manager: EditorUndoRedoManager, ts: TileSet, protect: int) -> void:
var restore := []
for s in ts.get_source_count():
var source_id := ts.get_source_id(s)
var source := ts.get_source(source_id) as TileSetAtlasSource
if !source:
continue
for t in source.get_tiles_count():
var coord := source.get_tile_id(t)
for a in source.get_alternative_tiles_count(coord):
var alternate := source.get_alternative_tile_id(coord, a)
var td := source.get_tile_data(coord, alternate)
var tile_type := BetterTerrain.get_tile_terrain_type(td)
if tile_type == BetterTerrain.TileCategory.NON_TERRAIN:
continue
var to_restore : bool = tile_type == protect
var terrain := BetterTerrain.get_terrain(ts, tile_type)
var cells = BetterTerrain.data.get_terrain_peering_cells(ts, terrain.type)
for c in cells:
if protect in BetterTerrain.tile_peering_types(td, c):
to_restore = true
break
if !to_restore:
continue
var peering_dict := {}
for c in cells:
peering_dict[c] = BetterTerrain.tile_peering_types(td, c)
var symmetry = BetterTerrain.get_tile_symmetry_type(td)
restore.append([source_id, coord, alternate, tile_type, peering_dict, symmetry])
undo_manager.add_undo_method(self, &"restore_peering", ts, restore)
func create_peering_restore_point_tile(undo_manager: EditorUndoRedoManager, ts: TileSet, source_id: int, coord: Vector2i, alternate: int) -> void:
var source := ts.get_source(source_id) as TileSetAtlasSource
var td := source.get_tile_data(coord, alternate)
var tile_type := BetterTerrain.get_tile_terrain_type(td)
var restore := []
var peering_dict := {}
for c in BetterTerrain.tile_peering_keys(td):
peering_dict[c] = BetterTerrain.tile_peering_types(td, c)
var symmetry = BetterTerrain.get_tile_symmetry_type(td)
restore.append([source_id, coord, alternate, tile_type, peering_dict, symmetry])
undo_manager.add_undo_method(self, &"restore_peering", ts, restore)
func restore_peering(ts: TileSet, restore: Array) -> void:
for r in restore:
var source := ts.get_source(r[0]) as TileSetAtlasSource
var td := source.get_tile_data(r[1], r[2])
BetterTerrain.set_tile_terrain_type(ts, td, r[3])
var peering_types = r[4]
for peering in peering_types:
var types := BetterTerrain.tile_peering_types(td, peering)
for t in types:
BetterTerrain.remove_tile_peering_type(ts, td, peering, t)
for t in peering_types[peering]:
BetterTerrain.add_tile_peering_type(ts, td, peering, t)
var symmetry = r[5]
BetterTerrain.set_tile_symmetry_type(ts, td, symmetry)
func create_terrain_type_restore_point(undo_manager: EditorUndoRedoManager, ts: TileSet) -> void:
var count = BetterTerrain.terrain_count(ts)
var restore = []
for i in count:
restore.push_back(BetterTerrain.get_terrain(ts, i))
undo_manager.add_undo_method(self, &"restore_terrain", ts, restore)
func restore_terrain(ts: TileSet, restore: Array) -> void:
for i in restore.size():
var r = restore[i]
BetterTerrain.set_terrain(ts, i, r.name, r.color, r.type, r.categories, r.icon)
func add_do_method(undo_manager: EditorUndoRedoManager, object:Object, method:StringName, args:Array):
if action_index > _current_action_index:
_current_action_index = action_index
_current_action_count = action_count
if action_count > _current_action_count:
_current_action_count = action_count
undo_manager.add_do_method(self, "_do_method", object, method, args, action_count)
func _do_method(object:Object, method:StringName, args:Array, this_action_count:int):
if this_action_count >= _current_action_count:
object.callv(method, args)
func finish_action():
_current_action_count = 0

View File

@@ -0,0 +1 @@
uid://ds1kcnrnbywo6

View File

@@ -0,0 +1,896 @@
@tool
extends Control
signal paste_occurred
signal change_zoom_level(value)
signal terrain_updated(index)
@onready var checkerboard := get_theme_icon("Checkerboard", "EditorIcons")
@onready var paint_symmetry_icons := [
null,
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryMirror.svg"),
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryFlip.svg"),
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryReflect.svg"),
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryRotateClockwise.svg"),
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryRotateCounterClockwise.svg"),
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryRotate180.svg"),
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryRotateAll.svg"),
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryAll.svg"),
]
# Draw checkerboard and tiles with specific materials in
# individual canvas items via rendering server
var _canvas_item_map = {}
var _canvas_item_background : RID
var tileset: TileSet
var disabled_sources: Array[int] = []: set = set_disabled_sources
var paint := BetterTerrain.TileCategory.NON_TERRAIN
var paint_symmetry := BetterTerrain.SymmetryType.NONE
var highlighted_tile_part := { valid = false }
var zoom_level := 1.0
var tiles_size : Vector2
var tile_size : Vector2i
var tile_part_size : Vector2
var alternate_size : Vector2
var alternate_lookup := []
var initial_click : Vector2i
var prev_position : Vector2i
var current_position : Vector2i
var selection_start : Vector2i
var selection_end : Vector2i
var selection_rect : Rect2i
var selected_tile_states : Array[Dictionary] = []
var copied_tile_states : Array[Dictionary] = []
var staged_paste_tile_states : Array[Dictionary] = []
var pick_icon_terrain : int = -1
var pick_icon_terrain_cancel := false
var undo_manager : EditorUndoRedoManager
var terrain_undo
# Modes for painting
enum PaintMode {
NO_PAINT,
PAINT_TYPE,
PAINT_PEERING,
PAINT_SYMMETRY,
SELECT,
PASTE
}
var paint_mode := PaintMode.NO_PAINT
# Actual interactions for painting
enum PaintAction {
NO_ACTION,
DRAW_TYPE,
ERASE_TYPE,
DRAW_PEERING,
ERASE_PEERING,
DRAW_SYMMETRY,
ERASE_SYMMETRY,
SELECT,
PASTE
}
var paint_action := PaintAction.NO_ACTION
const ALTERNATE_TILE_MARGIN := 18
func _enter_tree() -> void:
_canvas_item_background = RenderingServer.canvas_item_create()
RenderingServer.canvas_item_set_parent(_canvas_item_background, get_canvas_item())
RenderingServer.canvas_item_set_draw_behind_parent(_canvas_item_background, true)
func _exit_tree() -> void:
RenderingServer.free_rid(_canvas_item_background)
for p in _canvas_item_map:
RenderingServer.free_rid(_canvas_item_map[p])
_canvas_item_map.clear()
func refresh_tileset(ts: TileSet) -> void:
tileset = ts
tiles_size = Vector2.ZERO
alternate_size = Vector2.ZERO
alternate_lookup = []
disabled_sources = []
if !tileset:
return
for s in tileset.get_source_count():
var source_id := tileset.get_source_id(s)
var source := tileset.get_source(source_id) as TileSetAtlasSource
if !source or !source.texture:
continue
tiles_size.x = max(tiles_size.x, source.texture.get_width())
tiles_size.y += source.texture.get_height()
tile_size = source.texture_region_size
tile_part_size = Vector2(tile_size) / 3.0
for t in source.get_tiles_count():
var coord := source.get_tile_id(t)
var alt_count := source.get_alternative_tiles_count(coord)
if alt_count <= 1:
continue
var rect := source.get_tile_texture_region(coord, 0)
alternate_lookup.append([rect.size, source_id, coord])
alternate_size.x = max(alternate_size.x, rect.size.x * (alt_count - 1))
alternate_size.y += rect.size.y
_on_zoom_value_changed(zoom_level)
func is_tile_in_source(source: TileSetAtlasSource, coord: Vector2i) -> bool:
var origin := source.get_tile_at_coords(coord)
if origin == Vector2i(-1, -1):
return false
# Animation frames are not needed
var size := source.get_tile_size_in_atlas(origin)
return coord.x < origin.x + size.x and coord.y < origin.y + size.y
func _build_tile_part_from_position(result: Dictionary, position: Vector2i, rect: Rect2) -> void:
result.rect = rect
var type := BetterTerrain.get_tile_terrain_type(result.data)
if type == BetterTerrain.TileCategory.NON_TERRAIN:
return
result.terrain_type = type
var normalize_position := (Vector2(position) - rect.position) / rect.size
var terrain := BetterTerrain.get_terrain(tileset, type)
if !terrain.valid:
return
for p in BetterTerrain.data.get_terrain_peering_cells(tileset, terrain.type):
var side_polygon = BetterTerrain.data.peering_polygon(tileset, terrain.type, p)
if Geometry2D.is_point_in_polygon(normalize_position, side_polygon):
result.peering = p
result.polygon = side_polygon
break
func tile_part_from_position(position: Vector2i) -> Dictionary:
if !tileset:
return { valid = false }
var offset := Vector2.ZERO
var alt_offset := Vector2.RIGHT * (zoom_level * tiles_size.x + ALTERNATE_TILE_MARGIN)
if Rect2(alt_offset, zoom_level * alternate_size).has_point(position):
for a in alternate_lookup:
if a[1] in disabled_sources:
continue
var next_offset_y = alt_offset.y + zoom_level * a[0].y
if position.y > next_offset_y:
alt_offset.y = next_offset_y
continue
var source := tileset.get_source(a[1]) as TileSetAtlasSource
if !source:
break
var count := source.get_alternative_tiles_count(a[2])
var index := int((position.x - alt_offset.x) / (zoom_level * a[0].x)) + 1
if index < count:
var alt_id := source.get_alternative_tile_id(a[2], index)
var target_rect := Rect2(
alt_offset + Vector2.RIGHT * (index - 1) * zoom_level * a[0].x,
zoom_level * a[0]
)
var result := {
valid = true,
source_id = a[1],
coord = a[2],
alternate = alt_id,
data = source.get_tile_data(a[2], alt_id)
}
_build_tile_part_from_position(result, position, target_rect)
return result
else:
for s in tileset.get_source_count():
var source_id := tileset.get_source_id(s)
if source_id in disabled_sources:
continue
var source := tileset.get_source(source_id) as TileSetAtlasSource
if !source || !source.texture:
continue
for t in source.get_tiles_count():
var coord := source.get_tile_id(t)
var rect := source.get_tile_texture_region(coord, 0)
var target_rect := Rect2(offset + zoom_level * rect.position, zoom_level * rect.size)
if !target_rect.has_point(position):
continue
var result := {
valid = true,
source_id = source_id,
coord = coord,
alternate = 0,
data = source.get_tile_data(coord, 0)
}
_build_tile_part_from_position(result, position, target_rect)
return result
offset.y += zoom_level * source.texture.get_height()
return { valid = false }
func tile_rect_from_position(position: Vector2i) -> Rect2:
if !tileset:
return Rect2(-1,-1,0,0)
var offset := Vector2.ZERO
var alt_offset := Vector2.RIGHT * (zoom_level * tiles_size.x + ALTERNATE_TILE_MARGIN)
if Rect2(alt_offset, zoom_level * alternate_size).has_point(position):
for a in alternate_lookup:
if a[1] in disabled_sources:
continue
var next_offset_y = alt_offset.y + zoom_level * a[0].y
if position.y > next_offset_y:
alt_offset.y = next_offset_y
continue
var source := tileset.get_source(a[1]) as TileSetAtlasSource
if !source:
break
var count := source.get_alternative_tiles_count(a[2])
var index := int((position.x - alt_offset.x) / (zoom_level * a[0].x)) + 1
if index < count:
var target_rect := Rect2(
alt_offset + Vector2.RIGHT * (index - 1) * zoom_level * a[0].x,
zoom_level * a[0]
)
return target_rect
else:
for s in tileset.get_source_count():
var source_id := tileset.get_source_id(s)
if source_id in disabled_sources:
continue
var source := tileset.get_source(source_id) as TileSetAtlasSource
if !source:
continue
for t in source.get_tiles_count():
var coord := source.get_tile_id(t)
var rect := source.get_tile_texture_region(coord, 0)
var target_rect := Rect2(offset + zoom_level * rect.position, zoom_level * rect.size)
if target_rect.has_point(position):
return target_rect
offset.y += zoom_level * source.texture.get_height()
return Rect2(-1,-1,0,0)
func tile_parts_from_rect(rect:Rect2) -> Array[Dictionary]:
if !tileset:
return []
var tiles:Array[Dictionary] = []
var offset := Vector2.ZERO
var alt_offset := Vector2.RIGHT * (zoom_level * tiles_size.x + ALTERNATE_TILE_MARGIN)
for s in tileset.get_source_count():
var source_id := tileset.get_source_id(s)
if source_id in disabled_sources:
continue
var source := tileset.get_source(source_id) as TileSetAtlasSource
if !source:
continue
for t in source.get_tiles_count():
var coord := source.get_tile_id(t)
var tile_rect := source.get_tile_texture_region(coord, 0)
var target_rect := Rect2(offset + zoom_level * tile_rect.position, zoom_level * tile_rect.size)
if target_rect.intersects(rect):
var result := {
valid = true,
source_id = source_id,
coord = coord,
alternate = 0,
data = source.get_tile_data(coord, 0)
}
var pos = target_rect.position + target_rect.size/2
_build_tile_part_from_position(result, pos, target_rect)
tiles.push_back(result)
var alt_count := source.get_alternative_tiles_count(coord)
for a in alt_count:
var alt_id := 0
if a == 0:
continue
target_rect = Rect2(alt_offset + zoom_level * (a - 1) * tile_rect.size.x * Vector2.RIGHT, zoom_level * tile_rect.size)
alt_id = source.get_alternative_tile_id(coord, a)
if target_rect.intersects(rect):
var td := source.get_tile_data(coord, alt_id)
var result := {
valid = true,
source_id = source_id,
coord = coord,
alternate = alt_id,
data = td
}
var pos = target_rect.position + target_rect.size/2
_build_tile_part_from_position(result, pos, target_rect)
tiles.push_back(result)
if alt_count > 1:
alt_offset.y += zoom_level * tile_rect.size.y
offset.y += zoom_level * source.texture.get_height()
return tiles
func _get_canvas_item(td: TileData) -> RID:
if !td.material:
return self.get_canvas_item()
if _canvas_item_map.has(td.material):
return _canvas_item_map[td.material]
var rid = RenderingServer.canvas_item_create()
RenderingServer.canvas_item_set_material(rid, td.material.get_rid())
RenderingServer.canvas_item_set_parent(rid, get_canvas_item())
RenderingServer.canvas_item_set_draw_behind_parent(rid, true)
RenderingServer.canvas_item_set_default_texture_filter(rid, RenderingServer.CANVAS_ITEM_TEXTURE_FILTER_NEAREST)
_canvas_item_map[td.material] = rid
return rid
func _draw_tile_data(texture: Texture2D, rect: Rect2, src_rect: Rect2, td: TileData, draw_sides: bool = true) -> void:
var flipped_rect := rect
if td.flip_h:
flipped_rect.size.x = -rect.size.x
if td.flip_v:
flipped_rect.size.y = -rect.size.y
RenderingServer.canvas_item_add_texture_rect_region(
_get_canvas_item(td),
flipped_rect,
texture.get_rid(),
src_rect,
td.modulate,
td.transpose
)
var type := BetterTerrain.get_tile_terrain_type(td)
if type == BetterTerrain.TileCategory.NON_TERRAIN:
draw_rect(rect, Color(0.1, 0.1, 0.1, 0.5), true)
return
var terrain := BetterTerrain.get_terrain(tileset, type)
if !terrain.valid:
return
var transform := Transform2D(0.0, rect.size, 0.0, rect.position)
var center_polygon = transform * BetterTerrain.data.peering_polygon(tileset, terrain.type, -1)
draw_colored_polygon(center_polygon, Color(terrain.color, 0.6))
if terrain.type == BetterTerrain.TerrainType.DECORATION:
center_polygon.append(center_polygon[0])
draw_polyline(center_polygon, Color.BLACK)
if paint < BetterTerrain.TileCategory.EMPTY or paint >= BetterTerrain.terrain_count(tileset):
return
if not draw_sides:
return
var paint_terrain := BetterTerrain.get_terrain(tileset, paint)
for p in BetterTerrain.data.get_terrain_peering_cells(tileset, terrain.type):
if paint in BetterTerrain.tile_peering_types(td, p):
var side_polygon = transform * BetterTerrain.data.peering_polygon(tileset, terrain.type, p)
draw_colored_polygon(side_polygon, Color(paint_terrain.color, 0.6))
if paint_terrain.type == BetterTerrain.TerrainType.DECORATION:
side_polygon.append(side_polygon[0])
draw_polyline(side_polygon, Color.BLACK)
func _draw_tile_symmetry(texture: Texture2D, rect: Rect2, src_rect: Rect2, td: TileData, draw_icon: bool = true) -> void:
var flipped_rect := rect
if td.flip_h:
flipped_rect.size.x = -rect.size.x
if td.flip_v:
flipped_rect.size.y = -rect.size.y
RenderingServer.canvas_item_add_texture_rect_region(
_get_canvas_item(td),
flipped_rect,
texture.get_rid(),
src_rect,
td.modulate,
td.transpose
)
if not draw_icon:
return
var symmetry_type = BetterTerrain.get_tile_symmetry_type(td)
if symmetry_type == 0:
return
var symmetry_icon = paint_symmetry_icons[symmetry_type]
RenderingServer.canvas_item_add_texture_rect_region(
_get_canvas_item(td),
rect,
symmetry_icon.get_rid(),
Rect2(Vector2.ZERO, symmetry_icon.get_size()),
Color(1,1,1,0.5)
)
func _draw() -> void:
if !tileset:
return
# Clear material-based render targets
RenderingServer.canvas_item_clear(_canvas_item_background)
for p in _canvas_item_map:
RenderingServer.canvas_item_clear(_canvas_item_map[p])
var offset := Vector2.ZERO
var alt_offset := Vector2.RIGHT * (zoom_level * tiles_size.x + ALTERNATE_TILE_MARGIN)
RenderingServer.canvas_item_add_texture_rect(
_canvas_item_background,
Rect2(alt_offset, zoom_level * alternate_size),
checkerboard.get_rid(),
true
)
for s in tileset.get_source_count():
var source_id := tileset.get_source_id(s)
if source_id in disabled_sources:
continue
var source := tileset.get_source(source_id) as TileSetAtlasSource
if !source or !source.texture:
continue
RenderingServer.canvas_item_add_texture_rect(
_canvas_item_background,
Rect2(offset, zoom_level * source.texture.get_size()),
checkerboard.get_rid(),
true
)
for t in source.get_tiles_count():
var coord := source.get_tile_id(t)
var rect := source.get_tile_texture_region(coord, 0)
var alt_count := source.get_alternative_tiles_count(coord)
var target_rect : Rect2
for a in alt_count:
var alt_id := 0
if a == 0:
target_rect = Rect2(offset + zoom_level * rect.position, zoom_level * rect.size)
else:
target_rect = Rect2(alt_offset + zoom_level * (a - 1) * rect.size.x * Vector2.RIGHT, zoom_level * rect.size)
alt_id = source.get_alternative_tile_id(coord, a)
var td := source.get_tile_data(coord, alt_id)
var drawing_current = BetterTerrain.get_tile_terrain_type(td) == paint
if paint_mode == PaintMode.PAINT_SYMMETRY:
_draw_tile_symmetry(source.texture, target_rect, rect, td, drawing_current)
else:
_draw_tile_data(source.texture, target_rect, rect, td)
if drawing_current:
draw_rect(target_rect.grow(-1), Color(0,0,0, 0.75), false, 1)
draw_rect(target_rect, Color(1,1,1, 0.75), false, 1)
if paint_mode == PaintMode.SELECT:
if selected_tile_states.any(func(v):
return v.part.data == td
):
draw_rect(target_rect.grow(-1), Color.DEEP_SKY_BLUE, false, 2)
if alt_count > 1:
alt_offset.y += zoom_level * rect.size.y
# Blank out unused or uninteresting tiles
var size := source.get_atlas_grid_size()
for y in size.y:
for x in size.x:
var pos := Vector2i(x, y)
if !is_tile_in_source(source, pos):
var atlas_pos := source.margins + pos * (source.separation + source.texture_region_size)
draw_rect(Rect2(offset + zoom_level * atlas_pos, zoom_level * source.texture_region_size), Color(0.0, 0.0, 0.0, 0.8), true)
offset.y += zoom_level * source.texture.get_height()
# Blank out unused alternate tile sections
alt_offset = Vector2.RIGHT * (zoom_level * tiles_size.x + ALTERNATE_TILE_MARGIN)
for a in alternate_lookup:
if a[1] in disabled_sources:
continue
var source := tileset.get_source(a[1]) as TileSetAtlasSource
if source:
var count := source.get_alternative_tiles_count(a[2]) - 1
var occupied_width = count * zoom_level * a[0].x
var area := Rect2(
alt_offset.x + occupied_width,
alt_offset.y,
zoom_level * alternate_size.x - occupied_width,
zoom_level * a[0].y
)
draw_rect(area, Color(0.0, 0.0, 0.0, 0.8), true)
alt_offset.y += zoom_level * a[0].y
if highlighted_tile_part.valid:
if paint_mode == PaintMode.PAINT_PEERING and highlighted_tile_part.has("polygon"):
var transform := Transform2D(0.0, highlighted_tile_part.rect.size - 2 * Vector2.ONE, 0.0, highlighted_tile_part.rect.position + Vector2.ONE)
draw_colored_polygon(transform * highlighted_tile_part.polygon, Color(Color.WHITE, 0.2))
if paint_mode != PaintMode.NO_PAINT:
var inner_rect := Rect2(highlighted_tile_part.rect.position + Vector2.ONE, highlighted_tile_part.rect.size - 2 * Vector2.ONE)
draw_rect(inner_rect, Color.WHITE, false)
if paint_mode == PaintMode.PAINT_SYMMETRY:
if paint_symmetry > 0:
var symmetry_icon = paint_symmetry_icons[paint_symmetry]
draw_texture_rect(symmetry_icon, highlighted_tile_part.rect, false, Color(0.5,0.75,1,0.5))
if paint_mode == PaintMode.SELECT:
draw_rect(selection_rect, Color.WHITE, false)
if paint_mode == PaintMode.PASTE:
if staged_paste_tile_states.size() > 0:
var base_rect = staged_paste_tile_states[0].base_rect
var paint_terrain := BetterTerrain.get_terrain(tileset, paint)
var paint_terrain_type = paint_terrain.type
if paint_terrain_type == BetterTerrain.TerrainType.CATEGORY:
paint_terrain_type = 0
for state in staged_paste_tile_states:
var staged_rect:Rect2 = state.base_rect
staged_rect.position -= base_rect.position + base_rect.size / 2
staged_rect.position *= zoom_level
staged_rect.size *= zoom_level
staged_rect.position += Vector2(current_position)
var real_rect = tile_rect_from_position(staged_rect.get_center())
if real_rect.position.x >= 0:
draw_rect(real_rect, Color(0,0,0, 0.3), true)
var transform := Transform2D(0.0, real_rect.size, 0.0, real_rect.position)
var tile_sides = BetterTerrain.data.get_terrain_peering_cells(tileset, paint_terrain_type)
for p in tile_sides:
if state.paint in BetterTerrain.tile_peering_types(state.part.data, p):
var side_polygon = BetterTerrain.data.peering_polygon(tileset, paint_terrain_type, p)
var color = Color(paint_terrain.color, 0.6)
draw_colored_polygon(transform * side_polygon, color)
draw_rect(staged_rect, Color.DEEP_PINK, false)
func delete_selection():
undo_manager.create_action("Delete tile terrain peering types", UndoRedo.MERGE_DISABLE, tileset)
for t in selected_tile_states:
for side in range(16):
var old_peering = BetterTerrain.tile_peering_types(t.part.data, side)
if old_peering.has(paint):
undo_manager.add_do_method(BetterTerrain, &"remove_tile_peering_type", tileset, t.part.data, side, paint)
undo_manager.add_undo_method(BetterTerrain, &"add_tile_peering_type", tileset, t.part.data, side, paint)
undo_manager.add_do_method(self, &"queue_redraw")
undo_manager.add_undo_method(self, &"queue_redraw")
undo_manager.commit_action()
func toggle_selection():
undo_manager.create_action("Toggle tile terrain", UndoRedo.MERGE_DISABLE, tileset, true)
for t in selected_tile_states:
var type := BetterTerrain.get_tile_terrain_type(t.part.data)
var goal := paint if paint != type else BetterTerrain.TileCategory.NON_TERRAIN
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"set_tile_terrain_type", [tileset, t.part.data, goal])
if goal == BetterTerrain.TileCategory.NON_TERRAIN:
terrain_undo.create_peering_restore_point_tile(
undo_manager,
tileset,
t.part.source_id,
t.part.coord,
t.part.alternate
)
else:
undo_manager.add_undo_method(BetterTerrain, &"set_tile_terrain_type", tileset, t.part.data, type)
terrain_undo.add_do_method(undo_manager, self, &"queue_redraw", [])
undo_manager.add_undo_method(self, &"queue_redraw")
undo_manager.commit_action()
terrain_undo.action_count += 1
func copy_selection():
copied_tile_states = selected_tile_states
func paste_selection():
staged_paste_tile_states = copied_tile_states
selected_tile_states = []
paint_mode = PaintMode.PASTE
paint_action = PaintAction.PASTE
paste_occurred.emit()
queue_redraw()
func set_disabled_sources(list):
disabled_sources = list
queue_redraw()
func emit_terrain_updated(index):
terrain_updated.emit(index)
func _gui_input(event) -> void:
if event is InputEventKey and event.is_pressed():
if event.keycode == KEY_DELETE and not event.echo:
accept_event()
delete_selection()
if event.keycode == KEY_ENTER and not event.echo:
accept_event()
toggle_selection()
if event.keycode == KEY_ESCAPE and not event.echo:
accept_event()
if paint_action == PaintAction.PASTE:
staged_paste_tile_states = []
paint_mode = PaintMode.SELECT
paint_action = PaintAction.NO_ACTION
selection_start = Vector2i(-1,-1)
if event.keycode == KEY_C and (event.ctrl_pressed or event.meta_pressed) and not event.echo:
accept_event()
copy_selection()
if event.keycode == KEY_X and (event.ctrl_pressed or event.meta_pressed) and not event.echo:
accept_event()
copy_selection()
delete_selection()
if event.keycode == KEY_V and (event.ctrl_pressed or event.meta_pressed) and not event.echo:
accept_event()
paste_selection()
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_WHEEL_UP and (event.ctrl_pressed or event.meta_pressed):
accept_event()
change_zoom_level.emit(zoom_level * 1.1)
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN and (event.ctrl_pressed or event.meta_pressed):
accept_event()
change_zoom_level.emit(zoom_level / 1.1)
var released : bool = event is InputEventMouseButton and (not event.pressed and (event.button_index == MOUSE_BUTTON_LEFT or event.button_index == MOUSE_BUTTON_RIGHT))
if released:
paint_action = PaintAction.NO_ACTION
if event is InputEventMouseMotion:
prev_position = current_position
current_position = event.position
var tile := tile_part_from_position(event.position)
if tile.valid != highlighted_tile_part.valid or\
(tile.valid and tile.data != highlighted_tile_part.data) or\
(tile.valid and tile.get("peering") != highlighted_tile_part.get("peering")) or\
event.button_mask & MOUSE_BUTTON_LEFT and paint_action == PaintAction.SELECT:
queue_redraw()
highlighted_tile_part = tile
var clicked : bool = event is InputEventMouseButton and (event.pressed and (event.button_index == MOUSE_BUTTON_LEFT or event.button_index == MOUSE_BUTTON_RIGHT))
if clicked:
initial_click = current_position
selection_start = Vector2i(-1,-1)
terrain_undo.action_index += 1
terrain_undo.action_count = 0
if released:
terrain_undo.finish_action()
selection_rect = Rect2i(0,0,0,0)
queue_redraw()
if paint_action == PaintAction.PASTE:
if event is InputEventMouseMotion:
queue_redraw()
if clicked:
if event.button_index == MOUSE_BUTTON_LEFT and staged_paste_tile_states.size() > 0:
undo_manager.create_action("Paste tile terrain peering types", UndoRedo.MERGE_DISABLE, tileset)
var base_rect = staged_paste_tile_states[0].base_rect
for p in staged_paste_tile_states:
var staged_rect:Rect2 = p.base_rect
staged_rect.position -= base_rect.position + base_rect.size / 2
staged_rect.position *= zoom_level
staged_rect.size *= zoom_level
staged_rect.position += Vector2(current_position)
var old_tile_part = tile_part_from_position(staged_rect.get_center())
var new_tile_state = p
if (not old_tile_part.valid) or (not new_tile_state.part.valid):
continue
for side in range(16):
var old_peering = BetterTerrain.tile_peering_types(old_tile_part.data, side)
var new_sides = new_tile_state.sides
if new_sides.has(side) and not old_peering.has(paint):
undo_manager.add_do_method(BetterTerrain, &"add_tile_peering_type", tileset, old_tile_part.data, side, paint)
undo_manager.add_undo_method(BetterTerrain, &"remove_tile_peering_type", tileset, old_tile_part.data, side, paint)
elif old_peering.has(paint) and not new_sides.has(side):
undo_manager.add_do_method(BetterTerrain, &"remove_tile_peering_type", tileset, old_tile_part.data, side, paint)
undo_manager.add_undo_method(BetterTerrain, &"add_tile_peering_type", tileset, old_tile_part.data, side, paint)
var old_symmetry = BetterTerrain.get_tile_symmetry_type(old_tile_part.data)
var new_symmetry = new_tile_state.symmetry
if new_symmetry != old_symmetry:
undo_manager.add_do_method(BetterTerrain, &"set_tile_symmetry_type", tileset, old_tile_part.data, new_symmetry)
undo_manager.add_undo_method(BetterTerrain, &"set_tile_symmetry_type", tileset, old_tile_part.data, old_symmetry)
undo_manager.add_do_method(self, &"queue_redraw")
undo_manager.add_undo_method(self, &"queue_redraw")
undo_manager.commit_action()
staged_paste_tile_states = []
paint_mode = PaintMode.SELECT
paint_action = PaintAction.SELECT
return
if clicked and pick_icon_terrain >= 0:
highlighted_tile_part = tile_part_from_position(current_position)
if !highlighted_tile_part.valid:
return
var t = BetterTerrain.get_terrain(tileset, paint)
var prev_icon = t.icon.duplicate()
var icon = {
source_id = highlighted_tile_part.source_id,
coord = highlighted_tile_part.coord
}
undo_manager.create_action("Edit terrain details", UndoRedo.MERGE_DISABLE, tileset)
undo_manager.add_do_method(BetterTerrain, &"set_terrain", tileset, paint, t.name, t.color, t.type, t.categories, icon)
undo_manager.add_do_method(self, &"emit_terrain_updated", paint)
undo_manager.add_undo_method(BetterTerrain, &"set_terrain", tileset, paint, t.name, t.color, t.type, t.categories, prev_icon)
undo_manager.add_undo_method(self, &"emit_terrain_updated", paint)
undo_manager.commit_action()
pick_icon_terrain = -1
return
if pick_icon_terrain_cancel:
pick_icon_terrain = -1
pick_icon_terrain_cancel = false
if paint != BetterTerrain.TileCategory.NON_TERRAIN and clicked:
paint_action = PaintAction.NO_ACTION
if highlighted_tile_part.valid:
match [paint_mode, event.button_index]:
[PaintMode.PAINT_TYPE, MOUSE_BUTTON_LEFT]: paint_action = PaintAction.DRAW_TYPE
[PaintMode.PAINT_TYPE, MOUSE_BUTTON_RIGHT]: paint_action = PaintAction.ERASE_TYPE
[PaintMode.PAINT_PEERING, MOUSE_BUTTON_LEFT]: paint_action = PaintAction.DRAW_PEERING
[PaintMode.PAINT_PEERING, MOUSE_BUTTON_RIGHT]: paint_action = PaintAction.ERASE_PEERING
[PaintMode.PAINT_SYMMETRY, MOUSE_BUTTON_LEFT]: paint_action = PaintAction.DRAW_SYMMETRY
[PaintMode.PAINT_SYMMETRY, MOUSE_BUTTON_RIGHT]: paint_action = PaintAction.ERASE_SYMMETRY
[PaintMode.SELECT, MOUSE_BUTTON_LEFT]: paint_action = PaintAction.SELECT
else:
match [paint_mode, event.button_index]:
[PaintMode.SELECT, MOUSE_BUTTON_LEFT]: paint_action = PaintAction.SELECT
if (clicked or event is InputEventMouseMotion) and paint_action != PaintAction.NO_ACTION:
if paint_action == PaintAction.SELECT:
if clicked:
selection_start = Vector2i(-1,-1)
queue_redraw()
if selection_start.x < 0:
selection_start = current_position
selection_end = current_position
selection_rect = Rect2i(selection_start, selection_end - selection_start).abs()
var selected_tile_parts = tile_parts_from_rect(selection_rect)
selected_tile_states = []
for t in selected_tile_parts:
var state := {
part = t,
base_rect = Rect2(t.rect.position / zoom_level, t.rect.size / zoom_level),
paint = paint,
sides = BetterTerrain.tile_peering_for_type(t.data, paint),
symmetry = BetterTerrain.get_tile_symmetry_type(t.data)
}
selected_tile_states.push_back(state)
else:
if !highlighted_tile_part.valid:
return
#slightly crude and non-optimal but way simpler than the "correct" solution
var current_position_vec2 = Vector2(current_position)
var prev_position_vec2 = Vector2(prev_position)
var mouse_dist = current_position_vec2.distance_to(prev_position_vec2)
var step_size = (tile_part_size.x * zoom_level)
var steps = ceil(mouse_dist / step_size) + 1
for i in range(steps):
var t = float(i) / steps
var check_position = prev_position_vec2.lerp(current_position_vec2, t)
highlighted_tile_part = tile_part_from_position(check_position)
if !highlighted_tile_part.valid:
continue
if paint_action == PaintAction.DRAW_TYPE or paint_action == PaintAction.ERASE_TYPE:
var type := BetterTerrain.get_tile_terrain_type(highlighted_tile_part.data)
var goal := paint if paint_action == PaintAction.DRAW_TYPE else BetterTerrain.TileCategory.NON_TERRAIN
if type != goal:
undo_manager.create_action("Set tile terrain type " + str(terrain_undo.action_index), UndoRedo.MERGE_ALL, tileset, true)
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"set_tile_terrain_type", [tileset, highlighted_tile_part.data, goal])
terrain_undo.add_do_method(undo_manager, self, &"queue_redraw", [])
if goal == BetterTerrain.TileCategory.NON_TERRAIN:
terrain_undo.create_peering_restore_point_tile(
undo_manager,
tileset,
highlighted_tile_part.source_id,
highlighted_tile_part.coord,
highlighted_tile_part.alternate
)
else:
undo_manager.add_undo_method(BetterTerrain, &"set_tile_terrain_type", tileset, highlighted_tile_part.data, type)
undo_manager.add_undo_method(self, &"queue_redraw")
undo_manager.commit_action()
terrain_undo.action_count += 1
elif paint_action == PaintAction.DRAW_PEERING:
if highlighted_tile_part.has("peering"):
if !(paint in BetterTerrain.tile_peering_types(highlighted_tile_part.data, highlighted_tile_part.peering)):
undo_manager.create_action("Set tile terrain peering type " + str(terrain_undo.action_index), UndoRedo.MERGE_ALL, tileset, true)
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"add_tile_peering_type", [tileset, highlighted_tile_part.data, highlighted_tile_part.peering, paint])
terrain_undo.add_do_method(undo_manager, self, &"queue_redraw", [])
undo_manager.add_undo_method(BetterTerrain, &"remove_tile_peering_type", tileset, highlighted_tile_part.data, highlighted_tile_part.peering, paint)
undo_manager.add_undo_method(self, &"queue_redraw")
undo_manager.commit_action()
terrain_undo.action_count += 1
elif paint_action == PaintAction.ERASE_PEERING:
if highlighted_tile_part.has("peering"):
if paint in BetterTerrain.tile_peering_types(highlighted_tile_part.data, highlighted_tile_part.peering):
undo_manager.create_action("Remove tile terrain peering type " + str(terrain_undo.action_index), UndoRedo.MERGE_ALL, tileset, true)
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"remove_tile_peering_type", [tileset, highlighted_tile_part.data, highlighted_tile_part.peering, paint])
terrain_undo.add_do_method(undo_manager, self, &"queue_redraw", [])
undo_manager.add_undo_method(BetterTerrain, &"add_tile_peering_type", tileset, highlighted_tile_part.data, highlighted_tile_part.peering, paint)
undo_manager.add_undo_method(self, &"queue_redraw")
undo_manager.commit_action()
terrain_undo.action_count += 1
elif paint_action == PaintAction.DRAW_SYMMETRY:
if paint == BetterTerrain.get_tile_terrain_type(highlighted_tile_part.data):
undo_manager.create_action("Set tile symmetry type " + str(terrain_undo.action_index), UndoRedo.MERGE_ALL, tileset, true)
var old_symmetry = BetterTerrain.get_tile_symmetry_type(highlighted_tile_part.data)
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"set_tile_symmetry_type", [tileset, highlighted_tile_part.data, paint_symmetry])
terrain_undo.add_do_method(undo_manager, self, &"queue_redraw", [])
undo_manager.add_undo_method(BetterTerrain, &"set_tile_symmetry_type", tileset, highlighted_tile_part.data, old_symmetry)
undo_manager.add_undo_method(self, &"queue_redraw")
undo_manager.commit_action()
terrain_undo.action_count += 1
elif paint_action == PaintAction.ERASE_SYMMETRY:
if paint == BetterTerrain.get_tile_terrain_type(highlighted_tile_part.data):
undo_manager.create_action("Remove tile symmetry type " + str(terrain_undo.action_index), UndoRedo.MERGE_ALL, tileset, true)
var old_symmetry = BetterTerrain.get_tile_symmetry_type(highlighted_tile_part.data)
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"set_tile_symmetry_type", [tileset, highlighted_tile_part.data, BetterTerrain.SymmetryType.NONE])
terrain_undo.add_do_method(undo_manager, self, &"queue_redraw", [])
undo_manager.add_undo_method(BetterTerrain, &"set_tile_symmetry_type", tileset, highlighted_tile_part.data, old_symmetry)
undo_manager.add_undo_method(self, &"queue_redraw")
undo_manager.commit_action()
terrain_undo.action_count += 1
func _on_zoom_value_changed(value) -> void:
zoom_level = value
custom_minimum_size.x = zoom_level * tiles_size.x
if alternate_size.x > 0:
custom_minimum_size.x += ALTERNATE_TILE_MARGIN + zoom_level * alternate_size.x
custom_minimum_size.y = zoom_level * max(tiles_size.y, alternate_size.y)
queue_redraw()
func clear_highlighted_tile() -> void:
highlighted_tile_part = { valid = false }
queue_redraw()

View File

@@ -0,0 +1 @@
uid://cpm7dq6r0n0sn

1
addons/better-terrain/icon.svg Executable file
View File

@@ -0,0 +1 @@
<svg height="128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="m83.636719 67.794922a20 20 0 0 1 -15.636719 15.800781v44.404297h40c11.08 0 20-8.92 20-20v-40.205078z" fill="#169318"/><path d="m0 67.794922v40.205078c0 11.08 8.92 20 20 20h40v-44.404297a20 20 0 0 1 -15.636719-15.800781z" fill="#993d16"/><path d="m68 0v44.404297a20 20 0 0 1 15.552734 15.390625h44.447266v-39.794922c0-11.08-8.92-20-20-20z" fill="#993d16"/><path d="m20 0c-11.08 0-20 8.92-20 20v39.794922h44.447266a20 20 0 0 1 15.552734-15.390625v-44.404297z" fill="#169318"/></svg>

After

Width:  |  Height:  |  Size: 554 B

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c66nal373iwgd"
path="res://.godot/imported/icon.svg-7d4870855c0daec5051feb4adbea0091.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icon.svg"
dest_files=["res://.godot/imported/icon.svg-7d4870855c0daec5051feb4adbea0091.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="6" y="6" width="4" height="4" fill="white"/>
<path d="M4.5 6.5L1.5 9.5M1.5 6.5L4.5 9.5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.5 6.5L11.5 9.5M11.5 6.5L14.5 9.5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.5 1.5L6.5 4.5M6.5 1.5L9.5 4.5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.5 11.5L6.5 14.5M6.5 11.5L9.5 14.5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 593 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://kmypxsqhynyv"
path="res://.godot/imported/Decoration.svg-03773e83cc849c7744ecf3d36eee0072.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/Decoration.svg"
dest_files=["res://.godot/imported/Decoration.svg-03773e83cc849c7744ecf3d36eee0072.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,12 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_952_3274)">
<path d="M8.68988 11.1648L12.7626 15.2375C13.446 15.9209 14.554 15.9209 15.2374 15.2375C15.9208 14.5541 15.9208 13.446 15.2374 12.7626L11.1648 8.68994C10.5815 9.72354 9.72348 10.5815 8.68988 11.1648Z" fill="#E0E0E0"/>
<path d="M11 1C11 0.447715 10.5523 0 10 0H7C6.44772 0 6 0.447715 6 1V4C6 4.55228 6.44772 5 7 5C7.55228 5 8 4.55228 8 4V3.05033C8.61889 3.68203 9 4.54703 9 5.50004C9 7.26328 7.69615 8.72198 6 8.96459V10.9776C8.80325 10.725 11 8.36906 11 5.50004C11 4.16979 10.5279 2.95059 9.74266 2H10C10.5523 2 11 1.55228 11 1Z" fill="#E0E0E0"/>
<path d="M0 10C0 10.5523 0.447715 11 1 11H4C4.55228 11 5 10.5523 5 10V7C5 6.44772 4.55228 6 4 6C3.44772 6 3 6.44772 3 7V7.94975C2.38111 7.31805 2 6.45305 2 5.50004C2 3.7368 3.30385 2.2781 5 2.03548V0.0224609C2.19675 0.275075 0 2.63102 0 5.50004C0 6.83026 0.472062 8.04943 1.25727 9H1C0.447715 9 0 9.44771 0 10Z" fill="#E0E0E0"/>
</g>
<defs>
<clipPath id="clip0_952_3274">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://co6gwwmog0pjy"
path="res://.godot/imported/EditSymmetry.svg-794172208a8d86bb609531b82199f095.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/EditSymmetry.svg"
dest_files=["res://.godot/imported/EditSymmetry.svg-794172208a8d86bb609531b82199f095.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v14h14v-14zm1 1h12v12h-12zm1 1v2.6660156h2.6660156v-2.6660156zm3.6679688 0v2.6660156h2.6640624v-2.6660156zm3.6660152 0v2.6660156h2.666016v-2.6660156zm-7.333984 3.6660156v2.6660156h2.6660156v-2.6660156zm7.333984 0v2.6660156h2.666016v-2.6660156zm-7.333984 3.6679684v2.666016h2.6660156v-2.666016zm3.6679688 0v2.666016h2.6640624v-2.666016zm3.6660152 0v2.666016h2.666016v-2.666016z" fill="#e0e0e0"/></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bo2cjv08jkvf8"
path="res://.godot/imported/EditTerrain.svg-f7ee950d68a391de33e4e8ddd76bf2ac.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/EditTerrain.svg"
dest_files=["res://.godot/imported/EditTerrain.svg-f7ee950d68a391de33e4e8ddd76bf2ac.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v14h14v-14zm1 1h12v12h-12zm4.6679688 4.6660156v2.6660156h2.6640624v-2.6660156z" fill="#e0e0e0"/></svg>

After

Width:  |  Height:  |  Size: 198 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c6lxq2y7mpb18"
path="res://.godot/imported/EditType.svg-e7b3005c6a8f21d5102295c55b564ad1.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/EditType.svg"
dest_files=["res://.godot/imported/EditType.svg-e7b3005c6a8f21d5102295c55b564ad1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v4h4v-4zm5 0v4h4v-4zm5 0v4h4v-4zm-10 5v4h4v-4zm5 0v4h4v-4zm5 0v4h4v-4zm-10 5v4h4v-4zm5 0v4h4v-4zm5 0v4h4v-4z" fill="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="7.55906"/></svg>

After

Width:  |  Height:  |  Size: 295 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d1h1p7pcwdnjk"
path="res://.godot/imported/MatchTiles.svg-38111e21a893bd8f161311f0d1968a40.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/MatchTiles.svg"
dest_files=["res://.godot/imported/MatchTiles.svg-38111e21a893bd8f161311f0d1968a40.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v6l6-6zm8 0 6 6v-6zm-1 1-6 6 6 6 6-6zm-7 7v6h6zm14 0-6 6h6z" fill="#fff" stroke-width="3.77952"/></svg>

After

Width:  |  Height:  |  Size: 199 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dfemy1g6okwlv"
path="res://.godot/imported/MatchVertices.svg-288fe47ee1089920379407d6abf1a06c.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/MatchVertices.svg"
dest_files=["res://.godot/imported/MatchVertices.svg-288fe47ee1089920379407d6abf1a06c.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1-2 2v10l2 2h10l2-2v-10l-2-2zm2.1992188 3.171875a1 1 0 0 1 .6796874.2929688l2.1210938 2.1210937 2.121094-2.1210937a1 1 0 0 1 1.414062 0 1 1 0 0 1 0 1.4140624l-2.1210935 2.1210938 2.1210935 2.121094a1 1 0 0 1 0 1.414062 1 1 0 0 1 -1.414062 0l-2.121094-2.1210935-2.1210938 2.1210935a1 1 0 0 1 -1.4140624 0 1 1 0 0 1 0-1.414062l2.1210937-2.121094-2.1210937-2.1210938a1 1 0 0 1 0-1.4140624 1 1 0 0 1 .734375-.2929688z" fill="#fffaff"/></svg>

After

Width:  |  Height:  |  Size: 532 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://1yr6yruwl63u"
path="res://.godot/imported/NonModifying.svg-4d16d471be4a8f1d3ba0c013ff629ee1.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/NonModifying.svg"
dest_files=["res://.godot/imported/NonModifying.svg-4d16d471be4a8f1d3ba0c013ff629ee1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,8 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="10.5" y="1.5" width="4" height="4" stroke="#D9D9D9"/>
<rect x="1" y="10" width="5" height="5" fill="#D9D9D9"/>
<path d="M7.5 3.5H5.5C4.39543 3.5 3.5 4.39543 3.5 5.5V6.5" stroke="#E0E0E0" stroke-linecap="square"/>
<path d="M8.5 12.5H10.5C11.6046 12.5 12.5 11.6046 12.5 10.5V9.5" stroke="#E0E0E0" stroke-linecap="square"/>
<path d="M10 10L12.5 7.5L15 10H10Z" fill="#E0E0E0"/>
<path d="M6 6L3.5 8.5L1 6H6Z" fill="#E0E0E0"/>
</svg>

After

Width:  |  Height:  |  Size: 533 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://y3xy6qdckht6"
path="res://.godot/imported/Replace.svg-7654df79fd42fc27133e4d3f81a4d56b.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/Replace.svg"
dest_files=["res://.godot/imported/Replace.svg-7654df79fd42fc27133e4d3f81a4d56b.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,12 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_513_3269)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.58578 3.4144C7.24015 2.76003 8.23514 2.6539 9 3.09599V4C9 4.80892 9.48728 5.5382 10.2346 5.84776C10.7544 6.06305 11.3288 6.04585 11.8228 5.82299L12.5858 6.58597C13.3668 7.36701 13.3668 8.63334 12.5858 9.41439L9.41421 12.586C8.75985 13.2403 7.76486 13.3465 7 12.9044V12C7 11.1911 6.51272 10.4618 5.76537 10.1522C5.24551 9.93691 4.67104 9.95416 4.17695 10.1771L3.41422 9.41439C2.63317 8.63334 2.63317 7.36701 3.41422 6.58597L6.58578 3.4144ZM8 9.00018C8.55229 9.00018 9 8.55246 9 8.00018C9 7.44789 8.55229 7.00018 8 7.00018C7.44772 7.00018 7 7.44789 7 8.00018C7 8.55246 7.44772 9.00018 8 9.00018ZM6 8.00018C6 8.55246 5.55229 9.00018 5 9.00018C4.44772 9.00018 4 8.55246 4 8.00018C4 7.44789 4.44772 7.00018 5 7.00018C5.55229 7.00018 6 7.44789 6 8.00018ZM11 9.00018C11.5523 9.00018 12 8.55246 12 8.00018C12 7.44789 11.5523 7.00018 11 7.00018C10.4477 7.00018 10 7.44789 10 8.00018C10 8.55246 10.4477 9.00018 11 9.00018Z" fill="white"/>
<path d="M5.42909 2.57732C5.92795 2.34034 6.14024 1.74383 5.90326 1.24497C5.66628 0.746113 5.06976 0.53382 4.57091 0.770801C1.87043 2.05366 0 4.80762 0 8.00043C0 10.1046 0.812938 12.0189 2.13978 13.4462L1.29289 14.2931C1.0069 14.5791 0.92134 15.0092 1.07612 15.3829C1.2309 15.7566 1.59554 16.0002 2 16.0002H5C5.55228 16.0002 6 15.5525 6 15.0002V12.0002C6 11.5958 5.75636 11.2311 5.38268 11.0764C5.00901 10.9216 4.57889 11.0071 4.29289 11.2931L3.55511 12.0309C2.58795 10.9651 2 9.5514 2 8.00043C2 5.60845 3.39967 3.5414 5.42909 2.57732Z" fill="white"/>
<path d="M11 0C10.4477 0 9.99999 0.447715 9.99999 1V4C9.99999 4.40446 10.2436 4.7691 10.6173 4.92388C10.991 5.07866 11.4211 4.9931 11.7071 4.70711L12.4448 3.96939C13.4119 5.03511 14 6.44912 14 8C14 10.392 12.6003 12.459 10.5709 13.4231C10.072 13.6601 9.85975 14.2566 10.0967 14.7555C10.3337 15.2543 10.9302 15.4666 11.4291 15.2296C14.1296 13.9468 16 11.1928 16 8C16 5.8956 15.1871 3.98156 13.8601 2.55412L14.7071 1.70711C14.9931 1.42111 15.0787 0.990991 14.9239 0.617317C14.7691 0.243642 14.4045 0 14 0H11Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_513_3269">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cs4mdmluiydj6"
path="res://.godot/imported/ShuffleRandom.svg-15ee49f7a06c55a1e95e1ed056732dc5.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/ShuffleRandom.svg"
dest_files=["res://.godot/imported/ShuffleRandom.svg-15ee49f7a06c55a1e95e1ed056732dc5.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="8" cy="8" r="6" stroke="#E0E0E0" stroke-width="2"/>
<circle cx="8" cy="8" r="3" fill="#E0E0E0"/>
</svg>

After

Width:  |  Height:  |  Size: 212 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cyjra4g05dwh"
path="res://.godot/imported/SymmetryAll.svg-cd6a02766f60c09344aa97e0325457c1.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/SymmetryAll.svg"
dest_files=["res://.godot/imported/SymmetryAll.svg-cd6a02766f60c09344aa97e0325457c1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 14L10 12M8 14L8 2M8 14L6 12M8 2L10 4M8 2L6 4" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 244 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dqmc1jp56or8m"
path="res://.godot/imported/SymmetryFlip.svg-ea11c1010d0643843f115093c045dc42.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/SymmetryFlip.svg"
dest_files=["res://.godot/imported/SymmetryFlip.svg-ea11c1010d0643843f115093c045dc42.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 8L4 10M2 8H14M2 8L4 6M14 8L12 10M14 8L12 6" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 242 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://5hm3bfj3dvej"
path="res://.godot/imported/SymmetryMirror.svg-0bf9d259572cc33d41c783e35586310a.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/SymmetryMirror.svg"
dest_files=["res://.godot/imported/SymmetryMirror.svg-0bf9d259572cc33d41c783e35586310a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,11 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_953_3297)">
<path d="M8 15L10 13M8 15L8 1M8 15L6 13M8 1L10 3M8 1L6 3" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1 8L3 10M1 8H15M1 8L3 6M15 8L13 10M15 8L13 6" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_953_3297">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 527 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cxoewno1cefua"
path="res://.godot/imported/SymmetryReflect.svg-39f88a51808c88d6cb37005ed1ddd254.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/SymmetryReflect.svg"
dest_files=["res://.godot/imported/SymmetryReflect.svg-39f88a51808c88d6cb37005ed1ddd254.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.87869 5.87857H3.05026V3.05014" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.46447 4.46424C6.41709 2.51162 9.58291 2.51162 11.5355 4.46424C12.0384 4.96707 12.4117 5.55035 12.6556 6.17265" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round"/>
<path d="M10.1213 10.1214H12.9497V12.9499" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.5355 11.5358C9.58291 13.4884 6.41709 13.4884 4.46447 11.5358C3.96164 11.0329 3.5883 10.4496 3.34444 9.82735" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 718 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://8mcycyl3e66r"
path="res://.godot/imported/SymmetryRotate180.svg-805113e1c31c7195ed5fec5febf455b9.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/SymmetryRotate180.svg"
dest_files=["res://.godot/imported/SymmetryRotate180.svg-805113e1c31c7195ed5fec5febf455b9.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.87869 5.87857H2.05026V3.05014" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.87869 11.1211L5.87869 13.9496L3.05026 13.9496" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.1213 4.87887L10.1213 2.05044L12.9497 2.05044" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.1213 10.1214H13.9497V12.9499" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 8C14 5.57851 12.5655 3.49205 10.5 2.54404M8 14C10.4215 14 12.508 12.5655 13.456 10.5M2 8C2 10.4215 3.43447 12.508 5.5 13.456M8 2C5.54028 2 3.42626 3.48012 2.5 5.59829" stroke="#E0E0E0" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 856 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b7fx4mk18lmls"
path="res://.godot/imported/SymmetryRotateAll.svg-959ef9f7a9c5b12d37b3a1c9ddcf2432.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/SymmetryRotateAll.svg"
dest_files=["res://.godot/imported/SymmetryRotateAll.svg-959ef9f7a9c5b12d37b3a1c9ddcf2432.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 9L12 11L14 9" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 13C4.23858 13 2 10.7614 2 8C2 5.23858 4.23858 3 7 3C9.76142 3 12 5.23858 12 8V10" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 366 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://baxhjy28r1iqj"
path="res://.godot/imported/SymmetryRotateClockwise.svg-9d1254877c31fcd2b5fd3dd58555e624.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/SymmetryRotateClockwise.svg"
dest_files=["res://.godot/imported/SymmetryRotateClockwise.svg-9d1254877c31fcd2b5fd3dd58555e624.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 9L4 11L2 9" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 13C11.7614 13 14 10.7614 14 8C14 5.23858 11.7614 3 9 3C6.23858 3 4 5.23858 4 8V10" stroke="#E0E0E0" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 364 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://csbwdkr6bc2db"
path="res://.godot/imported/SymmetryRotateCounterClockwise.svg-ba4f86a741d97c0ebfc0ae19d3460f6f.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/SymmetryRotateCounterClockwise.svg"
dest_files=["res://.godot/imported/SymmetryRotateCounterClockwise.svg-ba4f86a741d97c0ebfc0ae19d3460f6f.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1c-1.1046 0-2 .89543-2 2v10c0 1.1046.89543 2 2 2h10c1.1046 0 2-.89543 2-2v-10c0-1.1046-.89543-2-2-2zm4 2h2v6h-2zm0 8h2v2h-2z" fill="#ffdd65"/></svg>

After

Width:  |  Height:  |  Size: 243 B

View File

@@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b0es228gfcykd"
path="res://.godot/imported/Warning.svg-7bb0ec60ff2da2c7ebdba79b0dcdd006.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/Warning.svg"
dest_files=["res://.godot/imported/Warning.svg-7bb0ec60ff2da2c7ebdba79b0dcdd006.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.5 9C4.5 6.51472 6.51472 4.5 9 4.5C11.4853 4.5 13.5 6.51472 13.5 9C13.5 11.4853 11.4853 13.5 9 13.5C6.51472 13.5 4.5 11.4853 4.5 9ZM9 1.5C4.85786 1.5 1.5 4.85786 1.5 9C1.5 13.1421 4.85786 16.5 9 16.5C13.1421 16.5 16.5 13.1421 16.5 9C16.5 4.85786 13.1421 1.5 9 1.5ZM9 11.5C10.3807 11.5 11.5 10.3807 11.5 9C11.5 7.61929 10.3807 6.5 9 6.5C7.61929 6.5 6.5 7.61929 6.5 9C6.5 10.3807 7.61929 11.5 9 11.5Z" fill="white" stroke="black"/>
</svg>

After

Width:  |  Height:  |  Size: 544 B

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://iid5buh1t5j5"
path="res://.godot/imported/SymmetryAll.svg-c2902d14b54ee9a54b7986a2ea5e47a7.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/paint-symmetry/SymmetryAll.svg"
dest_files=["res://.godot/imported/SymmetryAll.svg-c2902d14b54ee9a54b7986a2ea5e47a7.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=4.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5 11.5854L10.5 6.41458C11.0277 6.60067 11.6386 6.4827 12.0607 6.06066C12.6464 5.47487 12.6464 4.52513 12.0607 3.93934L10.0607 1.93934C9.47487 1.35355 8.52513 1.35355 7.93934 1.93934L5.93934 3.93934C5.35355 4.52513 5.35355 5.47487 5.93934 6.06066C6.36138 6.4827 6.97234 6.60067 7.5 6.41458L7.5 11.5854C6.97234 11.3993 6.36138 11.5173 5.93934 11.9393C5.35355 12.5251 5.35355 13.4749 5.93934 14.0607L7.93934 16.0607C8.52513 16.6464 9.47487 16.6464 10.0607 16.0607L12.0607 14.0607C12.6464 13.4749 12.6464 12.5251 12.0607 11.9393C11.6386 11.5173 11.0277 11.3993 10.5 11.5854Z" fill="white" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 765 B

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://brro1lqnf3r5y"
path="res://.godot/imported/SymmetryFlip.svg-0de1b384a4706cad746bcf7b3b7f0c2d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/paint-symmetry/SymmetryFlip.svg"
dest_files=["res://.godot/imported/SymmetryFlip.svg-0de1b384a4706cad746bcf7b3b7f0c2d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=4.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.41458 10.5H11.5854C11.3993 11.0277 11.5173 11.6386 11.9393 12.0607C12.5251 12.6464 13.4749 12.6464 14.0607 12.0607L16.0607 10.0607C16.6464 9.47487 16.6464 8.52513 16.0607 7.93934L14.0607 5.93934C13.4749 5.35355 12.5251 5.35355 11.9393 5.93934C11.5173 6.36138 11.3993 6.97234 11.5854 7.5H6.41458C6.60067 6.97234 6.4827 6.36138 6.06066 5.93934C5.47487 5.35355 4.52513 5.35355 3.93934 5.93934L1.93934 7.93934C1.35355 8.52513 1.35355 9.47487 1.93934 10.0607L3.93934 12.0607C4.52513 12.6464 5.47487 12.6464 6.06066 12.0607C6.4827 11.6386 6.60067 11.0277 6.41458 10.5Z" fill="white" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 756 B

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dpf5p8xxn52cb"
path="res://.godot/imported/SymmetryMirror.svg-2ba85612b4c15f1a7eab344dc47f9a9a.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/paint-symmetry/SymmetryMirror.svg"
dest_files=["res://.godot/imported/SymmetryMirror.svg-2ba85612b4c15f1a7eab344dc47f9a9a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=4.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.35441 11.6456C6.52581 11.286 6.54587 10.8723 6.41458 10.5H7.5V11.5854C7.12774 11.4541 6.71401 11.4742 6.35441 11.6456ZM11.6456 11.6456C11.286 11.4742 10.8723 11.4541 10.5 11.5854V10.5H11.5854C11.4541 10.8723 11.4742 11.286 11.6456 11.6456ZM11.6456 6.35441C11.4742 6.71401 11.4541 7.12774 11.5854 7.5H10.5V6.41458C10.8723 6.54587 11.286 6.52581 11.6456 6.35441ZM6.35442 6.35442C6.71401 6.52582 7.12774 6.54587 7.5 6.41458V7.5H6.41458C6.54587 7.12774 6.52581 6.71401 6.35442 6.35442ZM5.64558 5.64558C5.08822 5.37993 4.40083 5.47785 3.93934 5.93934L1.93934 7.93934C1.35355 8.52513 1.35355 9.47487 1.93934 10.0607L3.93934 12.0607C4.40084 12.5222 5.08823 12.6201 5.64558 12.3544C5.37992 12.9118 5.47784 13.5992 5.93934 14.0607L7.93934 16.0607C8.52513 16.6464 9.47487 16.6464 10.0607 16.0607L12.0607 14.0607C12.5222 13.5992 12.6201 12.9118 12.3544 12.3544C12.9118 12.6201 13.5992 12.5222 14.0607 12.0607L16.0607 10.0607C16.6464 9.47487 16.6464 8.52513 16.0607 7.93934L14.0607 5.93934C13.5992 5.47784 12.9118 5.37992 12.3544 5.64559C12.6201 5.08823 12.5222 4.40084 12.0607 3.93934L10.0607 1.93934C9.77935 1.65804 9.39782 1.5 9 1.5C8.60217 1.5 8.22064 1.65804 7.93934 1.93934L5.93934 3.93934C5.47784 4.40083 5.37993 5.08823 5.64558 5.64558Z" fill="white" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d251v4pxpwsre"
path="res://.godot/imported/SymmetryReflect.svg-de65ca99c884ea9239bb60e11b7c0ca4.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/paint-symmetry/SymmetryReflect.svg"
dest_files=["res://.godot/imported/SymmetryReflect.svg-de65ca99c884ea9239bb60e11b7c0ca4.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=4.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.81404 5.70606C9.04953 5.26223 10.4851 5.53533 11.4749 6.52513C11.8288 6.87906 12.0891 7.28666 12.259 7.72015C12.5612 8.49147 13.4315 8.87172 14.2028 8.56946C14.9742 8.26721 15.3544 7.39691 15.0522 6.62559C14.7343 5.81449 14.2479 5.05553 13.5962 4.40381C11.3866 2.19423 7.98242 1.90803 5.46303 3.54507C5.25562 2.96524 4.70139 2.55036 4.05026 2.55036C3.22184 2.55036 2.55026 3.22194 2.55026 4.05036V6.87879C2.55026 7.70722 3.22184 8.37879 4.05026 8.37879H6.87869C7.70712 8.37879 8.37869 7.70722 8.37869 6.87879C8.37869 6.4041 8.1582 5.98092 7.81404 5.70606ZM10.186 12.2944C8.95048 12.7382 7.51492 12.4651 6.52513 11.4753C6.17119 11.1214 5.91091 10.7138 5.74104 10.2803C5.43878 9.50898 4.56848 9.12872 3.79716 9.43098C3.02584 9.73323 2.64559 10.6035 2.94784 11.3749C3.26569 12.186 3.75209 12.9449 4.40381 13.5966C6.61338 15.8062 10.0176 16.0924 12.537 14.4554C12.7444 15.0352 13.2986 15.4501 13.9497 15.4501C14.7782 15.4501 15.4497 14.7785 15.4497 13.9501V11.1217C15.4497 10.2932 14.7782 9.62165 13.9497 9.62165H11.1213C10.2929 9.62165 9.62131 10.2932 9.62131 11.1217C9.62131 11.5963 9.84181 12.0195 10.186 12.2944Z" fill="white" stroke="black" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c1bmbyb3ig0mx"
path="res://.godot/imported/SymmetryRotate180.svg-ff244f85658bd621d56af3cf4f7c7ebe.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/paint-symmetry/SymmetryRotate180.svg"
dest_files=["res://.godot/imported/SymmetryRotate180.svg-ff244f85658bd621d56af3cf4f7c7ebe.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=4.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.8216 4.27106C15.2019 3.99897 15.4497 3.55358 15.4497 3.05029C15.4497 2.22187 14.7782 1.55029 13.9497 1.55029H11.1213C10.2929 1.55029 9.62131 2.22187 9.62131 3.05029V5.87872C9.62131 6.70715 10.2929 7.37872 11.1213 7.37872C11.8172 7.37872 12.4024 6.90487 12.5718 6.26224C13.0101 6.83299 13.314 7.51086 13.4377 8.24808C13.5748 9.06509 14.3482 9.61629 15.1652 9.47922C15.9822 9.34216 16.5334 8.56872 16.3964 7.75172C16.1771 6.44496 15.6211 5.25423 14.8216 4.27106ZM6.26246 5.42796C6.83316 4.98981 7.51098 4.68586 8.24817 4.56218C9.06518 4.42511 9.61638 3.65168 9.47932 2.83467C9.34225 2.01766 8.56882 1.46646 7.75181 1.60353C6.44508 1.82276 5.2543 2.37868 4.27106 3.17821C3.99897 2.79787 3.55356 2.54999 3.05026 2.54999C2.22184 2.54999 1.55026 3.22156 1.55026 4.04999V6.87842C1.55026 7.70684 2.22184 8.37842 3.05026 8.37842H5.87869C6.70712 8.37842 7.37869 7.70685 7.37869 6.87842C7.37869 6.18264 6.90496 5.59751 6.26246 5.42796ZM6.87869 10.621C6.18279 10.621 5.59757 11.0949 5.42814 11.7376C4.98992 11.1668 4.68596 10.4889 4.56227 9.75172C4.42521 8.93471 3.65177 8.3835 2.83476 8.52057C2.01776 8.65764 1.46655 9.43107 1.60362 10.2481C1.82285 11.5548 2.37891 12.7455 3.17838 13.7287C2.7981 14.0008 2.55027 14.4461 2.55027 14.9494C2.55027 15.7778 3.22184 16.4494 4.05027 16.4494H6.87869C7.70712 16.4494 8.37869 15.7778 8.37869 14.9494L8.37869 12.121C8.37869 11.2926 7.70712 10.621 6.87869 10.621ZM11.7376 12.5718C11.1669 13.01 10.489 13.3139 9.75181 13.4376C8.9348 13.5747 8.3836 14.3481 8.52066 15.1651C8.65773 15.9821 9.43117 16.5333 10.2482 16.3963C11.555 16.177 12.7457 15.621 13.7289 14.8215C14.001 15.2018 14.4464 15.4497 14.9497 15.4497C15.7782 15.4497 16.4497 14.7781 16.4497 13.9497V11.1213C16.4497 10.2929 15.7782 9.62128 14.9497 9.62128H12.1213C11.2929 9.62128 10.6213 10.2929 10.6213 11.1213C10.6213 11.8171 11.0951 12.4022 11.7376 12.5718Z" fill="white" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bcky1dfn4umac"
path="res://.godot/imported/SymmetryRotateAll.svg-795a9b37a8f5df7e7376c9f762121b21.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/paint-symmetry/SymmetryRotateAll.svg"
dest_files=["res://.godot/imported/SymmetryRotateAll.svg-795a9b37a8f5df7e7376c9f762121b21.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=4.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 5.5C9.78974 5.5 11.2657 6.84334 11.4747 8.57674C10.9529 8.40317 10.3546 8.52404 9.93934 8.93934C9.35355 9.52513 9.35355 10.4749 9.93934 11.0607L11.9393 13.0607C12.5251 13.6464 13.4749 13.6464 14.0607 13.0607L16.0607 11.0607C16.6464 10.4749 16.6464 9.52513 16.0607 8.93934C15.6352 8.5139 15.0178 8.39745 14.4873 8.58997C14.2757 5.19115 11.4521 2.5 8 2.5C4.41015 2.5 1.5 5.41015 1.5 9C1.5 12.5899 4.41015 15.5 8 15.5C8.82843 15.5 9.5 14.8284 9.5 14C9.5 13.1716 8.82843 12.5 8 12.5C6.067 12.5 4.5 10.933 4.5 9C4.5 7.067 6.067 5.5 8 5.5Z" fill="white" stroke="black" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 703 B

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://def0fcqsn6s6x"
path="res://.godot/imported/SymmetryRotateClockwise.svg-e133d151dd3970411596d18bb133aece.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/paint-symmetry/SymmetryRotateClockwise.svg"
dest_files=["res://.godot/imported/SymmetryRotateClockwise.svg-e133d151dd3970411596d18bb133aece.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=4.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.51272 8.58997C2.98219 8.39745 2.36478 8.5139 1.93934 8.93934C1.35355 9.52513 1.35355 10.4749 1.93934 11.0607L3.93934 13.0607C4.52513 13.6464 5.47487 13.6464 6.06066 13.0607L8.06066 11.0607C8.64645 10.4749 8.64645 9.52513 8.06066 8.93934C7.64536 8.52404 7.04712 8.40317 6.52533 8.57674C6.73428 6.84334 8.21026 5.5 10 5.5C11.933 5.5 13.5 7.067 13.5 9C13.5 10.933 11.933 12.5 10 12.5C9.17157 12.5 8.5 13.1716 8.5 14C8.5 14.8284 9.17157 15.5 10 15.5C13.5899 15.5 16.5 12.5899 16.5 9C16.5 5.41015 13.5899 2.5 10 2.5C6.54787 2.5 3.72429 5.19115 3.51272 8.58997Z" fill="white" stroke="black" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 725 B

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ngej4qhkypb2"
path="res://.godot/imported/SymmetryRotateCounterClockwise.svg-b603f534dc5383de58f7e26cdf86fe8b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/better-terrain/icons/paint-symmetry/SymmetryRotateCounterClockwise.svg"
dest_files=["res://.godot/imported/SymmetryRotateCounterClockwise.svg-b603f534dc5383de58f7e26cdf86fe8b.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=4.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,7 @@
[plugin]
name="BetterTerrain"
description="This is a drop-in replacement for Godot 4's tilemap terrain system, offering more versatile and straightforward autotiling. It can be used with any existing TileMap or TileSet, either through the editor plugin, or directly via code."
author="Portponky"
version=""
script="TerrainPlugin.gd"