added the game
258
addons/better-terrain/BetterTerrain.cs
Executable 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";
|
||||
}
|
||||
}
|
1159
addons/better-terrain/BetterTerrain.gd
Normal file
1
addons/better-terrain/BetterTerrain.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://b5wvl76qnagcj
|
598
addons/better-terrain/BetterTerrainData.gd
Executable 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
|
1
addons/better-terrain/BetterTerrainData.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://dns30obk1hpvd
|
72
addons/better-terrain/TerrainPlugin.gd
Executable 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)
|
1
addons/better-terrain/TerrainPlugin.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://bj2cm2q4tdgno
|
20
addons/better-terrain/Watcher.gd
Executable 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()
|
||||
|
1
addons/better-terrain/Watcher.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://d3rqd2eagpoe0
|
939
addons/better-terrain/editor/Dock.gd
Executable 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)
|
1
addons/better-terrain/editor/Dock.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://ynajlxcomlkc
|
399
addons/better-terrain/editor/Dock.tscn
Executable 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"]
|
185
addons/better-terrain/editor/TerrainEntry.gd
Executable 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()
|
1
addons/better-terrain/editor/TerrainEntry.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://c2qfovpuj58b7
|
114
addons/better-terrain/editor/TerrainEntry.tscn
Executable 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"]
|
85
addons/better-terrain/editor/TerrainProperties.gd
Executable 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
|
1
addons/better-terrain/editor/TerrainProperties.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://j81f0xo4p36y
|
96
addons/better-terrain/editor/TerrainProperties.tscn
Normal 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"]
|
190
addons/better-terrain/editor/TerrainUndo.gd
Executable 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
|
1
addons/better-terrain/editor/TerrainUndo.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://ds1kcnrnbywo6
|
896
addons/better-terrain/editor/TileView.gd
Executable 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()
|
1
addons/better-terrain/editor/TileView.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cpm7dq6r0n0sn
|
1
addons/better-terrain/icon.svg
Executable 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 |
43
addons/better-terrain/icon.svg.import
Normal 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
|
7
addons/better-terrain/icons/Decoration.svg
Executable 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 |
44
addons/better-terrain/icons/Decoration.svg.import
Normal 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
|
12
addons/better-terrain/icons/EditSymmetry.svg
Executable 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 |
44
addons/better-terrain/icons/EditSymmetry.svg.import
Normal 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
|
1
addons/better-terrain/icons/EditTerrain.svg
Executable 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 |
44
addons/better-terrain/icons/EditTerrain.svg.import
Normal 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
|
1
addons/better-terrain/icons/EditType.svg
Executable 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 |
44
addons/better-terrain/icons/EditType.svg.import
Normal 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
|
1
addons/better-terrain/icons/MatchTiles.svg
Executable 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 |
44
addons/better-terrain/icons/MatchTiles.svg.import
Normal 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
|
1
addons/better-terrain/icons/MatchVertices.svg
Executable 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 |
44
addons/better-terrain/icons/MatchVertices.svg.import
Normal 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
|
1
addons/better-terrain/icons/NonModifying.svg
Executable 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 |
44
addons/better-terrain/icons/NonModifying.svg.import
Normal 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
|
8
addons/better-terrain/icons/Replace.svg
Executable 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 |
44
addons/better-terrain/icons/Replace.svg.import
Normal 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
|
12
addons/better-terrain/icons/ShuffleRandom.svg
Executable 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 |
44
addons/better-terrain/icons/ShuffleRandom.svg.import
Normal 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
|
4
addons/better-terrain/icons/SymmetryAll.svg
Executable 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 |
44
addons/better-terrain/icons/SymmetryAll.svg.import
Normal 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
|
3
addons/better-terrain/icons/SymmetryFlip.svg
Executable 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 |
44
addons/better-terrain/icons/SymmetryFlip.svg.import
Normal 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
|
3
addons/better-terrain/icons/SymmetryMirror.svg
Executable 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 |
44
addons/better-terrain/icons/SymmetryMirror.svg.import
Normal 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
|
11
addons/better-terrain/icons/SymmetryReflect.svg
Executable 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 |
44
addons/better-terrain/icons/SymmetryReflect.svg.import
Normal 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
|
6
addons/better-terrain/icons/SymmetryRotate180.svg
Executable 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 |
44
addons/better-terrain/icons/SymmetryRotate180.svg.import
Normal 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
|
7
addons/better-terrain/icons/SymmetryRotateAll.svg
Executable 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 |
44
addons/better-terrain/icons/SymmetryRotateAll.svg.import
Normal 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
|
4
addons/better-terrain/icons/SymmetryRotateClockwise.svg
Executable 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 |
@@ -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
|
4
addons/better-terrain/icons/SymmetryRotateCounterClockwise.svg
Executable 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 |
@@ -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
|
1
addons/better-terrain/icons/Warning.svg
Executable 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 |
44
addons/better-terrain/icons/Warning.svg.import
Normal 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
|
3
addons/better-terrain/icons/paint-symmetry/SymmetryAll.svg
Executable 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 |
@@ -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
|
3
addons/better-terrain/icons/paint-symmetry/SymmetryFlip.svg
Executable 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 |
@@ -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
|
3
addons/better-terrain/icons/paint-symmetry/SymmetryMirror.svg
Executable 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 |
@@ -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
|
3
addons/better-terrain/icons/paint-symmetry/SymmetryReflect.svg
Executable 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 |
@@ -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
|
3
addons/better-terrain/icons/paint-symmetry/SymmetryRotate180.svg
Executable 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 |
@@ -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
|
3
addons/better-terrain/icons/paint-symmetry/SymmetryRotateAll.svg
Executable 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 |
@@ -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
|
3
addons/better-terrain/icons/paint-symmetry/SymmetryRotateClockwise.svg
Executable 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 |
@@ -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
|
@@ -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 |
@@ -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
|
7
addons/better-terrain/plugin.cfg
Executable 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"
|