└── addons └── tile_bit_tools ├── LICENSE ├── README.md ├── controls ├── bit_data_draw │ ├── bit_data_draw.gd │ ├── bit_data_draw_node.gd │ └── bit_data_draw_node.tscn ├── icons │ ├── tile_bit_tools_16.svg │ └── tile_bit_tools_16.svg.import ├── shared │ ├── icon_button.gd │ ├── inspector_section_button.gd │ ├── inspector_section_button.tscn │ ├── template_info_list.gd │ └── template_info_list.tscn ├── tbt_plugin_control │ ├── popups │ │ ├── edit_template_dialog.gd │ │ ├── edit_template_dialog.tscn │ │ ├── save_template_dialog.gd │ │ ├── save_template_dialog.tscn │ │ └── template_dialog.gd │ ├── tbt_plugin_control.gd │ ├── tbt_plugin_control.tscn │ ├── template_manager.gd │ ├── theme_updater.gd │ └── tiles_manager.gd ├── tiles_inspector │ ├── template_section │ │ ├── selected_tag.gd │ │ ├── selected_tag.tscn │ │ ├── selected_tag_stylebox.tres │ │ ├── template_info_panel.gd │ │ ├── template_info_panel.tscn │ │ ├── templates_section.gd │ │ ├── templates_section.tscn │ │ ├── terrain_picker.gd │ │ └── terrain_picker.tscn │ ├── tiles_inspector.gd │ ├── tiles_inspector.tscn │ └── tool_buttons │ │ ├── tool_buttons.gd │ │ └── tool_buttons.tscn └── tiles_preview │ ├── terrain_opacity_slider.gd │ ├── tiles_preview.gd │ ├── tiles_preview.tscn │ ├── tiles_view.gd │ └── tiles_view.tscn ├── core ├── bit_data.gd ├── context.gd ├── editor_bit_data.gd ├── globals.gd ├── icons.gd ├── output.gd ├── template_bit_data.gd ├── template_loader.gd ├── template_tag_data.gd └── texts.gd ├── examples ├── godot3_2x2 │ ├── ABOUT.txt │ ├── godot3_2x2.png │ └── godot3_2x2.png.import ├── godot3_3x3_16_tiles │ ├── ABOUT.txt │ ├── godot3_3x3_16_tiles.png │ └── godot3_3x3_16_tiles.png.import ├── godot3_3x3_minimal │ ├── ABOUT.txt │ ├── godot3_3x3_minimal.png │ └── godot3_3x3_minimal.png.import ├── simple_tilesets │ ├── ABOUT.txt │ ├── simple_9_and_4.png │ ├── simple_9_and_4.png.import │ ├── simple_9_and_9.png │ └── simple_9_and_9.png.import ├── tilesetter_blob │ ├── ABOUT.txt │ ├── tilesetter_blob.png │ └── tilesetter_blob.png.import ├── tilesetter_wang │ ├── ABOUT.txt │ ├── tilesetter_wang.png │ └── tilesetter_wang.png.import └── tilesetter_wang_3_terrains │ ├── ABOUT.txt │ ├── tilesetter_wang_3_terrain.png │ └── tilesetter_wang_3_terrain.png.import ├── inspector_plugin.gd ├── plugin.cfg ├── plugin.gd └── templates ├── godot3_2x2.tres ├── godot3_3x3_16_tiles.tres ├── godot3_3x3_minimal.tres ├── simple_4-tile_(inside_corners).tres ├── simple_9-tile_(inside_corners).tres ├── simple_9-tile_(outside_corners).tres ├── tilepipe2_256_tile_16x16.tres ├── tilepipe2_256_tile_32x8.tres ├── tilesetter_blob.tres ├── tilesetter_wang.tres ├── tilesetter_wang_3-terrain.tres └── tilesetter_wang_3-terrain_transitions.tres /addons/tile_bit_tools/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 dandeliondino 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/README.md: -------------------------------------------------------------------------------- 1 | ![TileBitTools](https://raw.githubusercontent.com/dandeliondino/tile_bit_tools/main/assets/header.png) 2 | 3 | TileBitTools is a Godot 4 plugin for autotile templates and terrain bit editing. 4 | 5 | The terrain system in Godot 4 is powerful and extensible, and has a lot of untapped potential. The goal of this plugin is to enable fast iterations, to assist in migrating from Godot 3, and to speed up the learning process for new users. 6 | 7 | *Click on the screenshots below to expand* 8 | 9 | [![Animated gif demonstrating how to apply a template](https://raw.githubusercontent.com/dandeliondino/tile_bit_tools/main/assets/tutorials/apply_template_thumb.gif)](https://github.com/dandeliondino/tile_bit_tools/blob/main/assets/tutorials/apply_template.gif) 10 | [![Screenshot of a built-in template](https://raw.githubusercontent.com/dandeliondino/tile_bit_tools/main/assets/tips_and_examples_thumb.png)](https://github.com/dandeliondino/tile_bit_tools/blob/main/assets/tips_and_examples.png) 11 | [![Screenshot of the Save Template dialog window](https://raw.githubusercontent.com/dandeliondino/tile_bit_tools/main/assets/save_custom_templates_thumb.png)](https://github.com/dandeliondino/tile_bit_tools/blob/main/assets/save_custom_templates.png) 12 | [![Screenshots of setting up a 3-terrain tileset](https://raw.githubusercontent.com/dandeliondino/tile_bit_tools/main/assets/terrain_transitions_thumb.png)](https://github.com/dandeliondino/tile_bit_tools/blob/main/assets/terrain_transitions.png) 13 | 14 | ## Features 15 | - **Built-in autotile templates for all three Godot 4 terrain modes** 16 | - **3x3 minimal**, **3x3 16-tile** and **2x2** templates from Godot 3 documentation. 17 | - **Blob**, **Wang** and **Wang 3-terrain** templates to match Tilesetter's default export. 18 | - **Simple 9- and 4-tile** templates. These are modular corner-mode templates that match tile configurations commonly found in spritesheets. 19 | - **Tips and example tiles for all built-in templates** 20 | - **Terrain bit editing buttons** to make changes like 'Fill' and 'Clear' to multiple tiles or peering bits in one click 21 | - **Custom user template creation** 22 | - Save new templates from the terrain peering bits on existing tiles. Statistics and previews are automatically generated. 23 | - Use as a quick way to copy-paste terrain bits. 24 | - Or use to save complex, reusable templates to a shared directory accessible to all projects. 25 | - **Options in Project Settings** 26 | - Customize the template bit colors (default colors are from the color-blind-friendly 'bright' scheme from [Paul Tol](https://personal.sron.nl/~pault/)) 27 | - Customize which messages appear in the Output log 28 | - Customize the template save folder location 29 | 30 | 31 | ## Limitations 32 | - Even using Godot 3 autotile templates, tile placement will not work exactly the same as it did in Godot 3, as the core matching algorithm is different 33 | - Hex and isometric tiles are not supported 34 | - Alternative tiles are not supported 35 | 36 | 37 | ## How to use 38 | *Please back up your project before making any changes. Godot 4 is still new, and TileBitTools is even newer, so unexpected behavior may occur.* 39 | 40 | TileBitTools is located in the bottom TileSet editor, in the Select tab. To access any of its functions, the first step is to select tiles. 41 | 42 | See the following pages for detailed directions: 43 | 44 | - [Installation](https://github.com/dandeliondino/tile_bit_tools/wiki/1.-Installation) 45 | - [Bulk terrain editing buttons](https://github.com/dandeliondino/tile_bit_tools/wiki/2.-Bulk-Terrain-Editing-Buttons) 46 | - Templates 47 | - [Applying templates](https://github.com/dandeliondino/tile_bit_tools/wiki/3.1-Applying-Templates) 48 | - [Saving templates](https://github.com/dandeliondino/tile_bit_tools/wiki/3.2-Saving-Templates) - ***If you are saving a significant amount of data in your templates, please make sure they are being backed up and/or added to a version control system. There are rare cases of [template data being deleted on editor startup](https://github.com/dandeliondino/tile_bit_tools/issues/49).*** 49 | - [Editing templates](https://github.com/dandeliondino/tile_bit_tools/wiki/3.3-Editing-Templates) 50 | - [Warning regarding resource files](https://github.com/dandeliondino/tile_bit_tools/wiki/X.-Warning-Regarding-Resource-Files) 51 | 52 | 53 | ## Feedback 54 | Find a bug? -> [Known Bugs and Reports](https://github.com/dandeliondino/tile_bit_tools/issues/2) 55 | 56 | Have an autotile template that should be built-in? -> [Add More Built-in Terrain Templates](https://github.com/dandeliondino/tile_bit_tools/issues/4) 57 | 58 | Have an idea for a new feature? -> [Future Directions and Suggestions](https://github.com/dandeliondino/tile_bit_tools/issues/3) 59 | 60 | 61 | ## Credits 62 | Concept inspired by [Wareya's Godot Tile Setup Helper](https://github.com/wareya/godot-tile-setup-helper) for Godot 3.5 63 | 64 | Huge thanks to [YuriSizov's Godot Editor Theme Explorer](https://github.com/YuriSizov/godot-editor-theme-explorer) and [Zylann's Editor Debugger](https://github.com/Zylann/godot_editor_debugger_plugin) 65 | 66 | The example tilesets are adapted from [Kenney's Pixel Shmup](https://www.kenney.nl/assets/pixel-shmup) ([License: CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/)) 67 | 68 | The TileBitTools icon is modified from [Kenney's Game Icons](https://www.kenney.nl/assets/game-icons) ([License: CCO 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/)) 69 | 70 | The fonts in the header and images are [Lilita One](https://fonts.google.com/specimen/Lilita+One) (SIL Open Font License 1.1) and [Fira Code](https://github.com/tonsky/FiraCode) (SIL Open Font License 1.1). 71 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/bit_data_draw/bit_data_draw.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Control 3 | 4 | 5 | enum RectPoint { 6 | TOP_LEFT, 7 | TOP_CENTER, 8 | TOP_RIGHT, 9 | RIGHT_CENTER, 10 | BOTTOM_RIGHT, 11 | BOTTOM_CENTER, 12 | BOTTOM_LEFT, 13 | LEFT_CENTER, 14 | } 15 | 16 | var bit_shapes := { 17 | TileSet.TERRAIN_MODE_MATCH_SIDES: { 18 | BitData.TerrainBits.TOP_SIDE: 19 | func(rect : Rect2): return [ 20 | _get_rect_point(rect, RectPoint.TOP_LEFT), 21 | _get_rect_point(rect, RectPoint.TOP_RIGHT), 22 | _get_center_rect_point(rect, RectPoint.TOP_RIGHT), 23 | _get_center_rect_point(rect, RectPoint.TOP_LEFT), 24 | ], 25 | BitData.TerrainBits.RIGHT_SIDE: 26 | func(rect : Rect2): return [ 27 | _get_rect_point(rect, RectPoint.TOP_RIGHT), 28 | _get_rect_point(rect, RectPoint.BOTTOM_RIGHT), 29 | _get_center_rect_point(rect, RectPoint.BOTTOM_RIGHT), 30 | _get_center_rect_point(rect, RectPoint.TOP_RIGHT), 31 | ], 32 | BitData.TerrainBits.BOTTOM_SIDE: 33 | func(rect : Rect2): return [ 34 | _get_rect_point(rect, RectPoint.BOTTOM_RIGHT), 35 | _get_rect_point(rect, RectPoint.BOTTOM_LEFT), 36 | _get_center_rect_point(rect, RectPoint.BOTTOM_LEFT), 37 | _get_center_rect_point(rect, RectPoint.BOTTOM_RIGHT), 38 | ], 39 | BitData.TerrainBits.LEFT_SIDE: 40 | func(rect : Rect2): return [ 41 | _get_rect_point(rect, RectPoint.BOTTOM_LEFT), 42 | _get_rect_point(rect, RectPoint.TOP_LEFT), 43 | _get_center_rect_point(rect, RectPoint.TOP_LEFT), 44 | _get_center_rect_point(rect, RectPoint.BOTTOM_LEFT), 45 | ], 46 | BitData.TerrainBits.CENTER: 47 | func(rect : Rect2): return _get_center_rect(rect), #.grow(-3), 48 | }, 49 | TileSet.TERRAIN_MODE_MATCH_CORNERS: { 50 | BitData.TerrainBits.TOP_LEFT_CORNER: 51 | func(rect : Rect2): return [ 52 | _get_rect_point(rect, RectPoint.LEFT_CENTER), 53 | _get_rect_point(rect, RectPoint.TOP_LEFT), 54 | _get_rect_point(rect, RectPoint.TOP_CENTER), 55 | _get_center_rect_point(rect, RectPoint.TOP_CENTER), 56 | _get_center_rect_point(rect, RectPoint.TOP_LEFT), 57 | _get_center_rect_point(rect, RectPoint.LEFT_CENTER), 58 | ], 59 | BitData.TerrainBits.TOP_RIGHT_CORNER: 60 | func(rect : Rect2): return [ 61 | _get_rect_point(rect, RectPoint.TOP_CENTER), 62 | _get_rect_point(rect, RectPoint.TOP_RIGHT), 63 | _get_rect_point(rect, RectPoint.RIGHT_CENTER), 64 | _get_center_rect_point(rect, RectPoint.RIGHT_CENTER), 65 | _get_center_rect_point(rect, RectPoint.TOP_RIGHT), 66 | _get_center_rect_point(rect, RectPoint.TOP_CENTER), 67 | ], 68 | BitData.TerrainBits.CENTER: 69 | func(rect : Rect2): return _get_center_rect(rect), #.grow(-3), 70 | # func(rect : Rect2): 71 | # var center_rect := _get_center_rect(rect).grow(-1) 72 | # var radius := center_rect.size.x/2 73 | # return {"position": center_rect.get_center(), "radius": radius}, 74 | BitData.TerrainBits.BOTTOM_RIGHT_CORNER: 75 | func(rect : Rect2): return [ 76 | _get_rect_point(rect, RectPoint.RIGHT_CENTER), 77 | _get_rect_point(rect, RectPoint.BOTTOM_RIGHT), 78 | _get_rect_point(rect, RectPoint.BOTTOM_CENTER), 79 | _get_center_rect_point(rect, RectPoint.BOTTOM_CENTER), 80 | _get_center_rect_point(rect, RectPoint.BOTTOM_RIGHT), 81 | _get_center_rect_point(rect, RectPoint.RIGHT_CENTER), 82 | ], 83 | BitData.TerrainBits.BOTTOM_LEFT_CORNER: 84 | func(rect : Rect2): return [ 85 | _get_rect_point(rect, RectPoint.BOTTOM_CENTER), 86 | _get_rect_point(rect, RectPoint.BOTTOM_LEFT), 87 | _get_rect_point(rect, RectPoint.LEFT_CENTER), 88 | _get_center_rect_point(rect, RectPoint.LEFT_CENTER), 89 | _get_center_rect_point(rect, RectPoint.BOTTOM_LEFT), 90 | _get_center_rect_point(rect, RectPoint.BOTTOM_CENTER), 91 | ], 92 | }, 93 | TileSet.TERRAIN_MODE_MATCH_CORNERS_AND_SIDES: { 94 | BitData.TerrainBits.TOP_LEFT_CORNER: 95 | func(rect : Rect2): return Rect2( 96 | rect.position, 97 | _get_bit_size(rect) 98 | ), 99 | BitData.TerrainBits.TOP_SIDE: 100 | func(rect : Rect2): return Rect2( 101 | Vector2(rect.position.x + rect.size.x/3, rect.position.y), 102 | _get_bit_size(rect) 103 | ), 104 | BitData.TerrainBits.TOP_RIGHT_CORNER: 105 | func(rect : Rect2): return Rect2( 106 | Vector2(rect.position.x + 2*rect.size.x/3, rect.position.y), 107 | _get_bit_size(rect) 108 | ), 109 | BitData.TerrainBits.LEFT_SIDE: 110 | func(rect : Rect2): return Rect2( 111 | Vector2(rect.position.x, rect.position.y + rect.size.y/3), 112 | _get_bit_size(rect) 113 | ), 114 | BitData.TerrainBits.CENTER: 115 | func(rect : Rect2): return Rect2( 116 | Vector2(rect.position.x + rect.size.x/3, rect.position.y + rect.size.y/3), 117 | _get_bit_size(rect) 118 | ), 119 | BitData.TerrainBits.RIGHT_SIDE: 120 | func(rect : Rect2): return Rect2( 121 | Vector2(rect.position.x + 2*rect.size.x/3, rect.position.y + rect.size.y/3), 122 | _get_bit_size(rect) 123 | ), 124 | BitData.TerrainBits.BOTTOM_LEFT_CORNER: 125 | func(rect : Rect2): return Rect2( 126 | Vector2(rect.position.x, rect.position.y + 2*rect.size.y/3), 127 | _get_bit_size(rect) 128 | ), 129 | BitData.TerrainBits.BOTTOM_SIDE: 130 | func(rect : Rect2): return Rect2( 131 | Vector2(rect.position.x + rect.size.x/3, rect.position.y + 2*rect.size.y/3), 132 | _get_bit_size(rect) 133 | ), 134 | BitData.TerrainBits.BOTTOM_RIGHT_CORNER: 135 | func(rect : Rect2): return Rect2( 136 | Vector2(rect.position.x + 2*rect.size.x/3, rect.position.y + 2*rect.size.y/3), 137 | _get_bit_size(rect) 138 | ), 139 | } 140 | } 141 | 142 | const BitData := preload("res://addons/tile_bit_tools/core/bit_data.gd") 143 | 144 | var bit_data : BitData : 145 | set(value): 146 | bit_data = value 147 | _update_properties() 148 | queue_redraw() 149 | 150 | var draw_size : Vector2i : 151 | set(value): 152 | draw_size = value 153 | _update_properties() 154 | queue_redraw() 155 | 156 | var spacing := 5 157 | 158 | var atlas_rect : Rect2i 159 | var atlas_offset : Vector2i 160 | var tile_size : Vector2 161 | 162 | var terrain_set : int 163 | var terrain_mode : TileSet.TerrainMode 164 | var terrain_colors := {} 165 | 166 | 167 | 168 | 169 | func _update_properties() -> void: 170 | if !bit_data or draw_size == Vector2i.ZERO: 171 | return 172 | 173 | atlas_rect = bit_data.get_atlas_rect() 174 | 175 | # changing to floats avoids rounding errors/gaps 176 | tile_size = Vector2(draw_size) / Vector2(atlas_rect.size) 177 | atlas_offset = atlas_rect.position 178 | 179 | terrain_set = bit_data.terrain_set 180 | terrain_mode = bit_data.terrain_mode 181 | terrain_colors = bit_data.get_terrain_colors_dict() 182 | 183 | 184 | 185 | func _draw() -> void: 186 | if !bit_data or draw_size == Vector2i.ZERO: 187 | return 188 | 189 | for coords in bit_data.get_coordinates_list(): 190 | var adj_coords : Vector2 = coords - atlas_offset 191 | var tile_pos := adj_coords * tile_size 192 | var tile_rect := Rect2(tile_pos, tile_size).grow(-spacing) 193 | 194 | for bit in bit_data.get_terrain_bits_list(true): 195 | var terrain_index := bit_data.get_bit_terrain(coords, bit) 196 | var color = terrain_colors[terrain_index] 197 | var shape = bit_shapes[terrain_mode][bit].call(tile_rect) 198 | 199 | if typeof(shape) == TYPE_ARRAY: 200 | draw_colored_polygon(shape, color) 201 | elif typeof(shape) == TYPE_RECT2: 202 | draw_rect(shape, color) 203 | elif typeof(shape) == TYPE_DICTIONARY: 204 | draw_circle(shape.position, shape.radius, color) 205 | 206 | 207 | 208 | func _get_rect_point(rect : Rect2, point : RectPoint) -> Vector2: 209 | match point: 210 | RectPoint.TOP_LEFT: 211 | return rect.position 212 | RectPoint.TOP_CENTER: 213 | return Vector2(rect.get_center().x, rect.position.y) 214 | RectPoint.TOP_RIGHT: 215 | return Vector2(rect.end.x, rect.position.y) 216 | RectPoint.RIGHT_CENTER: 217 | return Vector2(rect.end.x, rect.get_center().y) 218 | RectPoint.BOTTOM_RIGHT: 219 | return rect.end 220 | RectPoint.BOTTOM_CENTER: 221 | return Vector2(rect.get_center().x, rect.end.y) 222 | RectPoint.BOTTOM_LEFT: 223 | return Vector2(rect.position.x, rect.end.y) 224 | RectPoint.LEFT_CENTER: 225 | return Vector2(rect.position.x, rect.get_center().y) 226 | return Vector2.ZERO 227 | 228 | func _get_center_rect_point(rect : Rect2, point : RectPoint) -> Vector2: 229 | return _get_rect_point(_get_center_rect(rect), point) 230 | 231 | func _get_center_rect(rect : Rect2) -> Rect2: 232 | return Rect2(rect.position + rect.size/3.0, rect.size/3.0) 233 | 234 | func _get_bit_size(rect : Rect2) -> Vector2: 235 | return rect.size/3.0 236 | 237 | 238 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/bit_data_draw/bit_data_draw_node.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends SubViewport 3 | 4 | const DEFAULT_TILE_SIZE := 16 5 | 6 | const BitData := preload("res://addons/tile_bit_tools/core/bit_data.gd") 7 | 8 | var drawing_size := Vector2i.ZERO 9 | var tile_size := 0 10 | var tile_spacing := 0 11 | 12 | @onready var bit_data_draw: Control = %BitDataDraw 13 | 14 | 15 | func get_bit_texture(bit_data : BitData) -> Texture2D: 16 | var tex_size := get_drawing_size(bit_data) 17 | size = tex_size 18 | bit_data_draw.draw_size = tex_size 19 | bit_data_draw.spacing = tile_spacing 20 | bit_data_draw.bit_data = bit_data 21 | render_target_update_mode = SubViewport.UPDATE_ONCE 22 | await RenderingServer.frame_post_draw 23 | var image := get_texture().get_image() 24 | return ImageTexture.create_from_image(image) 25 | 26 | 27 | func get_drawing_size(bit_data : BitData) -> Vector2i: 28 | if drawing_size != Vector2i.ZERO: 29 | return drawing_size 30 | var calc_tile_size : int = tile_size if tile_size > 0 else DEFAULT_TILE_SIZE 31 | return bit_data.get_atlas_rect().size * calc_tile_size 32 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/bit_data_draw/bit_data_draw_node.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/bit_data_draw/bit_data_draw.gd" id="1_2rdsg"] 4 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/bit_data_draw/bit_data_draw_node.gd" id="1_jpipc"] 5 | 6 | [node name="DrawViewport" type="SubViewport"] 7 | disable_3d = true 8 | gui_disable_input = true 9 | gui_snap_controls_to_pixels = false 10 | size = Vector2i(64, 96) 11 | render_target_update_mode = 1 12 | script = ExtResource("1_jpipc") 13 | 14 | [node name="BitDataDraw" type="Control" parent="."] 15 | unique_name_in_owner = true 16 | layout_mode = 3 17 | anchors_preset = 0 18 | offset_right = 40.0 19 | offset_bottom = 40.0 20 | script = ExtResource("1_2rdsg") 21 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/icons/tile_bit_tools_16.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 28 | 31 | 34 | 35 | 43 | 48 | 52 | 53 | 54 | 73 | 77 | 85 | 90 | 95 | 96 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/icons/tile_bit_tools_16.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cheop6l3022pn" 6 | path="res://.godot/imported/tile_bit_tools_16.svg-d3d960897b553a49b1b4c28f73a2a661.ctex" 7 | metadata={ 8 | "editor_dark_theme": true, 9 | "editor_scale": 2.0, 10 | "has_editor_variant": true, 11 | "vram_texture": false 12 | } 13 | 14 | [deps] 15 | 16 | source_file="res://addons/tile_bit_tools/controls/icons/tile_bit_tools_16.svg" 17 | dest_files=["res://.godot/imported/tile_bit_tools_16.svg-d3d960897b553a49b1b4c28f73a2a661.ctex"] 18 | 19 | [params] 20 | 21 | compress/mode=0 22 | compress/high_quality=false 23 | compress/lossy_quality=0.7 24 | compress/hdr_compression=1 25 | compress/normal_map=0 26 | compress/channel_pack=0 27 | mipmaps/generate=false 28 | mipmaps/limit=-1 29 | roughness/mode=0 30 | roughness/src_normal="" 31 | process/fix_alpha_border=true 32 | process/premult_alpha=false 33 | process/normal_map_invert_y=false 34 | process/hdr_as_srgb=false 35 | process/hdr_clamp_exposure=false 36 | process/size_limit=0 37 | detect_3d/compress_to=1 38 | svg/scale=1.0 39 | editor/scale_with_editor_scale=true 40 | editor/convert_colors_with_editor_theme=true 41 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/shared/icon_button.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Button 3 | 4 | 5 | const TBTPlugin := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd") 6 | 7 | ## property name from icons.gd 8 | @export var icon_name : String 9 | 10 | ## string of icon name from editor theme 11 | @export var editor_name : String 12 | 13 | var tbt : TBTPlugin 14 | 15 | func _tbt_ready() -> void: 16 | if icon_name != "": 17 | icon = tbt.icons.get_icon_by_name(icon_name) 18 | elif editor_name != "": 19 | icon = tbt.icons.get_icon_by_editor_name(editor_name) 20 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/shared/inspector_section_button.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Button 3 | 4 | 5 | const LABEL_MARGIN_ADJUST := -6 6 | 7 | const TBTPlugin := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd") 8 | 9 | 10 | @export var label_text := "Inspector Section" : 11 | set(value): 12 | label_text = value 13 | if is_instance_valid(label): 14 | _update_label_text() 15 | 16 | @export var expand_container_path : NodePath 17 | 18 | var tbt : TBTPlugin 19 | 20 | var expand_container : Control 21 | 22 | var icon_expanded 23 | var icon_collapsed 24 | 25 | var normal_color : Color 26 | var hover_color : Color 27 | 28 | @onready var background_rect: ColorRect = %BackgroundRect 29 | @onready var arrow_rect: TextureRect = %ArrowRect 30 | @onready var label: Label = %Label 31 | @onready var arrow_margin_container: MarginContainer = $ArrowMarginContainer 32 | @onready var label_margin_container: MarginContainer = $LabelMarginContainer 33 | 34 | 35 | 36 | func _ready() -> void: 37 | _update_label_text() 38 | 39 | 40 | func _tbt_ready() -> void: 41 | expand_container = get_node_or_null(expand_container_path) 42 | if expand_container == null: 43 | tbt.output.error("Expand container null") 44 | return 45 | 46 | icon_expanded = tbt.icons.get_icon(tbt.icons.ARROW_EXPANDED) 47 | icon_collapsed = tbt.icons.get_icon(tbt.icons.ARROW_COLLAPSED) 48 | _toggle_expanded(false) 49 | 50 | normal_color = tbt.base_control.get_theme_color("prop_subsection", "Editor") 51 | normal_color.a = normal_color.a * 0.4 52 | hover_color = normal_color.lightened(0.2) 53 | _toggle_hover(false) 54 | 55 | var left_margin = round(2 * tbt.interface.get_editor_scale()) 56 | arrow_margin_container.set("theme_override_constants/margin_left", left_margin) 57 | 58 | var label_margin = left_margin + icon_expanded.get_size().x + LABEL_MARGIN_ADJUST 59 | label_margin_container.set("theme_override_constants/margin_left", label_margin) 60 | 61 | 62 | 63 | func _update_label_text() -> void: 64 | label.text = label_text 65 | 66 | 67 | func _toggle_expanded(value : bool) -> void: 68 | if value: 69 | if is_instance_valid(expand_container): 70 | expand_container.show() 71 | arrow_rect.texture = icon_expanded 72 | else: 73 | if is_instance_valid(expand_container): 74 | expand_container.hide() 75 | arrow_rect.texture = icon_collapsed 76 | 77 | 78 | func _toggle_hover(value : bool) -> void: 79 | if value: 80 | background_rect.color = hover_color 81 | else: 82 | background_rect.color = normal_color 83 | 84 | 85 | func _on_mouse_entered() -> void: 86 | _toggle_hover(true) 87 | 88 | 89 | func _on_mouse_exited() -> void: 90 | _toggle_hover(false) 91 | 92 | 93 | func _on_toggled(p_button_pressed: bool) -> void: 94 | _toggle_expanded(p_button_pressed) 95 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/shared/inspector_section_button.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://dkmdolef567aa"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/shared/inspector_section_button.gd" id="1_nbqv8"] 4 | 5 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_mwoex"] 6 | 7 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_kmslc"] 8 | 9 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_e3ufk"] 10 | 11 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_lkr7c"] 12 | 13 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_bxs77"] 14 | 15 | [node name="InspectorSectionButton" type="Button" groups=["TBTSectionButton"]] 16 | custom_minimum_size = Vector2(0, 34) 17 | anchors_preset = 10 18 | anchor_right = 1.0 19 | grow_horizontal = 2 20 | theme_override_styles/normal = SubResource("StyleBoxEmpty_mwoex") 21 | theme_override_styles/hover = SubResource("StyleBoxEmpty_kmslc") 22 | theme_override_styles/pressed = SubResource("StyleBoxEmpty_e3ufk") 23 | theme_override_styles/disabled = SubResource("StyleBoxEmpty_lkr7c") 24 | theme_override_styles/focus = SubResource("StyleBoxEmpty_bxs77") 25 | toggle_mode = true 26 | flat = true 27 | script = ExtResource("1_nbqv8") 28 | 29 | [node name="BackgroundRect" type="ColorRect" parent="."] 30 | unique_name_in_owner = true 31 | layout_mode = 1 32 | anchors_preset = 15 33 | anchor_right = 1.0 34 | anchor_bottom = 1.0 35 | grow_horizontal = 2 36 | grow_vertical = 2 37 | color = Color(0.47451, 0.47451, 0.47451, 1) 38 | 39 | [node name="ArrowMarginContainer" type="MarginContainer" parent="."] 40 | layout_mode = 1 41 | anchors_preset = 15 42 | anchor_right = 1.0 43 | anchor_bottom = 1.0 44 | grow_horizontal = 2 45 | grow_vertical = 2 46 | 47 | [node name="ArrowRect" type="TextureRect" parent="ArrowMarginContainer"] 48 | unique_name_in_owner = true 49 | layout_mode = 2 50 | size_flags_horizontal = 0 51 | size_flags_vertical = 4 52 | stretch_mode = 2 53 | 54 | [node name="LabelMarginContainer" type="MarginContainer" parent="."] 55 | layout_mode = 1 56 | anchors_preset = 15 57 | anchor_right = 1.0 58 | anchor_bottom = 1.0 59 | grow_horizontal = 2 60 | grow_vertical = 2 61 | 62 | [node name="Label" type="Label" parent="LabelMarginContainer" groups=["TBTSectionLabel"]] 63 | unique_name_in_owner = true 64 | layout_mode = 2 65 | text = "Inspector Section" 66 | 67 | [connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] 68 | [connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] 69 | [connection signal="toggled" from="." to="." method="_on_toggled"] 70 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/shared/template_info_list.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends ItemList 3 | 4 | const TBTPlugin := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd") 5 | 6 | var tbt : TBTPlugin 7 | 8 | var show_custom_tags := true 9 | 10 | func update(template_bit_data : TBTPlugin.TemplateBitData) -> void: 11 | var item_list := [] 12 | item_list.append({"text": "Tiles: %s" % template_bit_data.get_tile_count()}) 13 | item_list.append({"text": "Terrains: %s" % template_bit_data.get_terrain_count()}) 14 | 15 | var template_tag_data := preload("res://addons/tile_bit_tools/core/template_tag_data.gd").new() 16 | for tag in template_tag_data.tags.values(): 17 | if tag.get_test_result(template_bit_data): 18 | item_list.append({"text": tag.text, "icon": tag.get_icon(tbt.base_control)}) 19 | 20 | if show_custom_tags: 21 | for custom_tag_text in template_bit_data.get_custom_tags(): 22 | var tag := template_tag_data.TemplateTag.new(custom_tag_text) 23 | item_list.append({"text": tag.text, "icon": tag.get_icon(tbt.base_control)}) 24 | 25 | clear() 26 | for item in item_list: 27 | if item.has("icon"): 28 | var _err := add_item(item.text, item.icon, false) 29 | else: 30 | var _err := add_item(item.text, null, false) 31 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/shared/template_info_list.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://bscknm10ko78t"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/shared/template_info_list.gd" id="1_6r84n"] 4 | 5 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_n7nl4"] 6 | 7 | [node name="TemplateInfoList" type="ItemList"] 8 | size_flags_horizontal = 3 9 | focus_mode = 0 10 | mouse_filter = 2 11 | theme_override_styles/panel = SubResource("StyleBoxEmpty_n7nl4") 12 | auto_height = true 13 | item_count = 2 14 | item_0/text = "Tiles: 15" 15 | item_0/selectable = false 16 | item_1/text = "Terrains: 2" 17 | item_1/selectable = false 18 | script = ExtResource("1_6r84n") 19 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tbt_plugin_control/popups/edit_template_dialog.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends "res://addons/tile_bit_tools/controls/tbt_plugin_control/popups/template_dialog.gd" 3 | 4 | 5 | var save_path : String 6 | 7 | @onready var folder_label: Label = %FolderLabel 8 | 9 | 10 | 11 | func _setup_connections() -> void: 12 | var _err := tbt.edit_template_requested.connect(_on_edit_template_requested) 13 | 14 | 15 | 16 | func _setup_edit_dialog(p_template_bit_data : TBTPlugin.TemplateBitData) -> void: 17 | template_bit_data = p_template_bit_data 18 | save_path = template_bit_data.resource_path 19 | 20 | show_dialog() 21 | 22 | 23 | 24 | # overriden 25 | func _setup_initial_values() -> void: 26 | name_edit.text = template_bit_data.template_name 27 | description_edit.text = template_bit_data.template_description 28 | tags_edit.text = _custom_tags_to_string() 29 | _update_folder_label() 30 | 31 | 32 | # unable to get selecting option button item to work... 33 | # using label instead 34 | func _update_folder_label() -> void: 35 | var path = save_path.rsplit("/", true, 1)[0] + "/" 36 | for i in range(tbt.template_manager.template_folder_paths.size()): 37 | var folder_path : Dictionary = tbt.template_manager.template_folder_paths[i] 38 | if path == folder_path.path: 39 | folder_label.text = folder_path.name 40 | folder_label.tooltip_text = folder_path.tooltip + "\n" + folder_path.path 41 | return 42 | folder_label.text = path # if cannot find saved path, list path itself 43 | folder_label.tooltip_text = "" 44 | 45 | 46 | 47 | func _custom_tags_to_string() -> String: 48 | return ", ".join(template_bit_data._custom_tags) 49 | 50 | 51 | # overriden 52 | func _get_save_path(_valid_only := true) -> String: 53 | return save_path 54 | 55 | 56 | 57 | 58 | func _on_edit_template_requested(p_template_bit_data : TBTPlugin.TemplateBitData) -> void: 59 | if p_template_bit_data != null: 60 | _setup_edit_dialog(p_template_bit_data) 61 | else: 62 | tbt.output.warning("Edit requested without bit data") 63 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tbt_plugin_control/popups/edit_template_dialog.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://cyxr6y64lkue6"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://job5gqt1xbrh" path="res://addons/tile_bit_tools/controls/tbt_plugin_control/popups/save_template_dialog.tscn" id="1_5jw1t"] 4 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/tbt_plugin_control/popups/edit_template_dialog.gd" id="2_bgxlq"] 5 | 6 | [node name="EditTemplateDialog" instance=ExtResource("1_5jw1t")] 7 | title = "Edit Terrain Template" 8 | visible = false 9 | script = ExtResource("2_bgxlq") 10 | metadata/_edit_pinned_properties_ = [&"title"] 11 | 12 | [node name="FolderLabel" type="Label" parent="BackgroundPanel/MarginContainer/VBoxContainer/ForegroundPanel/MarginContainer/VBoxContainer/BottomPanel/MarginContainer/VBoxContainer/HBoxContainer6" index="1" groups=["TBTSubPropertyLabel"]] 13 | unique_name_in_owner = true 14 | layout_mode = 2 15 | size_flags_horizontal = 3 16 | size_flags_stretch_ratio = 2.0 17 | mouse_filter = 0 18 | text = "(save path)" 19 | 20 | [node name="FolderOptionButton" parent="BackgroundPanel/MarginContainer/VBoxContainer/ForegroundPanel/MarginContainer/VBoxContainer/BottomPanel/MarginContainer/VBoxContainer/HBoxContainer6" index="2"] 21 | visible = false 22 | metadata/_edit_pinned_properties_ = [&"visible"] 23 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tbt_plugin_control/popups/save_template_dialog.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends "res://addons/tile_bit_tools/controls/tbt_plugin_control/popups/template_dialog.gd" 3 | 4 | 5 | 6 | 7 | 8 | func _setup_connections() -> void: 9 | var _err := tbt.save_template_requested.connect(_on_save_template_requested) 10 | 11 | 12 | func _setup_save_dialog() -> void: 13 | template_bit_data = TBTPlugin.TemplateBitData.new() 14 | 15 | var result := template_bit_data.load_editor_bit_data(tbt.context.bit_data) 16 | if result != OK: 17 | tbt.output.user("Cannot create template from editor data", result) 18 | # TODO: message_box_requested not implemented 19 | # tbt.message_box_requested.emit("Cannot create template from editor data (ERR %s)" % result) 20 | hide() 21 | 22 | show_dialog() 23 | 24 | 25 | 26 | 27 | 28 | # overriden 29 | func _setup_initial_values() -> void: 30 | name_edit.text = DEFAULT_TEMPLATE_NAME 31 | description_edit.text = DEFAULT_TEMPLATE_DESCRIPTION 32 | tags_edit.text = DEFAULT_TEMPLATE_TAGS 33 | 34 | 35 | 36 | # overriden 37 | func _get_save_path(_valid_only := true) -> String: 38 | var dir := _get_save_dir() 39 | var file_name := _get_file_name(dir) 40 | var path : String = dir.get_current_dir() + "/" + file_name 41 | 42 | if !path.is_absolute_path(): 43 | tbt.output.error("Unable to get valid save path: %s" % path) 44 | return "" 45 | 46 | return path 47 | 48 | 49 | func _get_save_dir() -> DirAccess: 50 | var folder_id := folder_option_button.get_selected_id() 51 | var path : String = tbt.template_manager.template_folder_paths[folder_id].path 52 | return DirAccess.open(path) 53 | 54 | 55 | func _get_file_name(dir : DirAccess) -> String: 56 | var template_name := name_edit.text 57 | if template_name == "": 58 | template_name = DEFAULT_TEMPLATE_NAME 59 | 60 | var s := template_name.to_lower().replace(" ", "_").validate_filename() 61 | var extension := ".tres" 62 | var file_name := s + extension 63 | 64 | var suffix := 0 65 | 66 | while dir.file_exists(file_name): 67 | suffix += 1 68 | file_name = s + str(suffix).pad_zeros(2) + extension 69 | 70 | return file_name 71 | 72 | 73 | 74 | func _on_save_template_requested() -> void: 75 | _setup_save_dialog() 76 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tbt_plugin_control/popups/template_dialog.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Window 3 | 4 | const MIN_WIDTH_PROPORTION := 0.3 5 | const MIN_HEIGHT_PROPORTION := 0.3 6 | 7 | 8 | # class extended by SaveTemplateDialog and EditTemplateDialog 9 | 10 | const DEFAULT_TEMPLATE_NAME := "User Template" 11 | const DEFAULT_TEMPLATE_DESCRIPTION := "" 12 | const DEFAULT_TEMPLATE_TAGS := "" 13 | 14 | var INFO_LABEL_TEXT := "Tiles: {tile_count}\nTerrains: {terrain_count}" 15 | 16 | 17 | const TBTPlugin := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd") 18 | 19 | const BitDataDrawNode := preload("res://addons/tile_bit_tools/controls/bit_data_draw/bit_data_draw_node.gd") 20 | 21 | var template_bit_data : TBTPlugin.TemplateBitData 22 | 23 | 24 | var tbt : TBTPlugin 25 | 26 | @onready var name_edit: LineEdit = %NameEdit 27 | @onready var description_edit: TextEdit = %DescriptionEdit 28 | @onready var tags_edit: LineEdit = %TagsEdit 29 | 30 | 31 | @onready var preview_rect: TextureRect = %PreviewRect 32 | 33 | @onready var folder_option_button: OptionButton = %FolderOptionButton 34 | @onready var template_info_list: ItemList = %TemplateInfoList 35 | 36 | 37 | func _ready() -> void: 38 | hide() 39 | var _err := close_requested.connect(close_dialog) 40 | template_info_list.show_custom_tags = false 41 | 42 | 43 | func _tbt_ready() -> void: 44 | _setup_connections() 45 | 46 | 47 | func _setup_connections() -> void: 48 | # override this 49 | pass 50 | 51 | 52 | # call after get template_bit_data 53 | func show_dialog() -> void: 54 | _setup_dialog() 55 | popup_centered() 56 | 57 | 58 | # connected to buttons/closing dialog, 59 | # do not need to call 60 | func close_dialog() -> void: 61 | template_bit_data = null 62 | hide() 63 | 64 | 65 | # ---------------------------------- 66 | # COMMON SETUP 67 | # ---------------------------------- 68 | 69 | func _setup_dialog() -> void: 70 | _set_size() 71 | _setup_texture() 72 | template_info_list.update(template_bit_data) 73 | _setup_folders_button() 74 | _setup_initial_values() 75 | 76 | 77 | func _set_size() -> void: 78 | set("min_size", Vector2i(Vector2(get_tree().root.size) * Vector2(MIN_WIDTH_PROPORTION, MIN_HEIGHT_PROPORTION))) 79 | # set("max_size", get_tree().root.size * 0.75) 80 | 81 | 82 | func _setup_texture() -> void: 83 | var bit_data_draw_node : BitDataDrawNode = tbt.template_manager.get_bit_data_draw() 84 | preview_rect.texture = await bit_data_draw_node.get_bit_texture(template_bit_data) 85 | 86 | 87 | 88 | func _setup_folders_button() -> void: 89 | folder_option_button.clear() 90 | 91 | for i in range(tbt.template_manager.template_folder_paths.size()): 92 | var folder_path : Dictionary = tbt.template_manager.template_folder_paths[i] 93 | if folder_path.type != tbt.G.TemplateTypes.USER: 94 | continue 95 | if !folder_path.has("name"): 96 | continue 97 | folder_option_button.add_item(folder_path.name, i) 98 | 99 | var tooltip : String = folder_path.tooltip + "\n" + folder_path.path 100 | var idx := folder_option_button.get_item_index(i) 101 | folder_option_button.set_item_tooltip(idx, tooltip) 102 | 103 | folder_option_button.get_popup().add_to_group("TBTPopupMenu") 104 | 105 | 106 | 107 | 108 | func _setup_initial_values() -> void: 109 | # override this 110 | # name 111 | # description 112 | # tags 113 | # save path label 114 | pass 115 | 116 | 117 | # --------------------------- 118 | # SAVE FUNCTIONS 119 | # --------------------------- 120 | 121 | func _save() -> void: 122 | _update_template_bit_data() 123 | 124 | var path := _get_save_path() 125 | var result := ResourceSaver.save(template_bit_data, path) 126 | 127 | if result != OK: 128 | tbt.output.user("Error saving template", tbt.G.Errors.FAILED) 129 | tbt.output.debug("ResourceSaver error: %s" % result) 130 | close_dialog() 131 | return 132 | 133 | tbt.output.user("Saved user template '%s' to %s " % [template_bit_data.template_name, path]) 134 | tbt.templates_update_requested.emit() 135 | close_dialog() 136 | 137 | 138 | func _update_template_bit_data() -> void: 139 | if name_edit.text == "": 140 | template_bit_data.template_name = DEFAULT_TEMPLATE_NAME 141 | else: 142 | template_bit_data.template_name = name_edit.text 143 | template_bit_data.template_description = description_edit.text 144 | template_bit_data._custom_tags = _get_custom_tags() 145 | template_bit_data.version = tbt.G.VERSION 146 | 147 | 148 | func _get_custom_tags() -> Array: 149 | var tags := [] 150 | var tag_texts := tags_edit.text.split(",") 151 | for text in tag_texts: 152 | text = text.strip_edges() 153 | if text == "": 154 | continue 155 | tags.append(text) 156 | return tags 157 | 158 | 159 | func _get_save_path() -> String: 160 | # override this function 161 | return "" 162 | 163 | 164 | 165 | 166 | 167 | 168 | func _on_save_button_pressed() -> void: 169 | _save() 170 | 171 | 172 | func _on_cancel_button_pressed() -> void: 173 | close_dialog() 174 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Node 3 | 4 | 5 | signal tiles_inspector_added 6 | signal tiles_inspector_removed 7 | 8 | signal templates_updated 9 | signal templates_update_requested 10 | 11 | signal save_template_requested 12 | signal edit_template_requested(template_bit_data) 13 | signal message_box_requested(msg) 14 | 15 | signal preview_updated(preview_bit_data) 16 | signal reset_requested 17 | signal apply_changes_requested 18 | 19 | signal tiles_preview_collapse_requested 20 | signal tiles_preview_expand_requested 21 | 22 | signal theme_update_requested(node) 23 | 24 | const TBT_PROPERTY_NAME := "tbt" 25 | const TBT_READY_METHOD := "_tbt_ready" 26 | const TILES_INSPECTOR_ADDED_METHOD := "_tiles_inspector_added" 27 | const TILES_INSPECTOR_REMOVED_METHOD := "_tiles_inspector_removed" 28 | 29 | const G := preload("res://addons/tile_bit_tools/core/globals.gd") 30 | const Texts := preload("res://addons/tile_bit_tools/core/texts.gd") 31 | const Icons := preload("res://addons/tile_bit_tools/core/icons.gd") 32 | const Output := preload("res://addons/tile_bit_tools/core/output.gd") 33 | 34 | const BitData := preload("res://addons/tile_bit_tools/core/bit_data.gd") 35 | const EditorBitData := preload("res://addons/tile_bit_tools/core/editor_bit_data.gd") 36 | const TemplateBitData := preload("res://addons/tile_bit_tools/core/template_bit_data.gd") 37 | 38 | const TemplateManager := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/template_manager.gd") 39 | const TilesManager := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/tiles_manager.gd") 40 | const ThemeUpdater := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/theme_updater.gd") 41 | const Context := preload("res://addons/tile_bit_tools/core/context.gd") 42 | 43 | var output : Output = Output.new() 44 | var texts : Texts = Texts.new() 45 | var icons : Icons 46 | 47 | var interface : EditorInterface 48 | var atlas_source_editor : Node 49 | var base_control : Control 50 | 51 | var template_manager : TemplateManager 52 | var tiles_manager : TilesManager 53 | var theme_updater : ThemeUpdater 54 | var dialog_windows := [] 55 | 56 | var context : Context : 57 | get: 58 | if !_is_reference_valid(context): 59 | return null 60 | return context 61 | 62 | 63 | var tiles_inspector : Control : 64 | get: 65 | if !_is_reference_valid(tiles_inspector): 66 | return null 67 | return tiles_inspector 68 | 69 | 70 | var tiles_preview : Control : 71 | get: 72 | if !_is_reference_valid(tiles_preview): 73 | return null 74 | return tiles_preview 75 | 76 | 77 | func _ready() -> void: 78 | set_process_input(false) 79 | var _err := child_entered_tree.connect(_assign_child_by_class) 80 | _setup_debug_signals() 81 | _setup_children() 82 | 83 | 84 | func setup(p_interface : EditorInterface, p_atlas_source_editor : Node, p_tiles_preview : Control) -> void: 85 | interface = p_interface 86 | base_control = interface.get_base_control() 87 | atlas_source_editor = p_atlas_source_editor 88 | 89 | icons = Icons.new(base_control) 90 | 91 | # here instead of _ready() so base_control is not null 92 | _inject_tbt_reference(self) 93 | 94 | tiles_preview = p_tiles_preview 95 | _inject_tbt_reference(tiles_preview, true) 96 | 97 | 98 | func notify_tiles_inspector_added(p_tiles_inspector : Control) -> void: 99 | tiles_inspector = p_tiles_inspector 100 | _inject_tbt_reference(tiles_inspector, true) 101 | _call_subtree(self, TILES_INSPECTOR_ADDED_METHOD) 102 | tiles_inspector_added.emit() 103 | var _err := tiles_inspector.visibility_changed.connect(_on_tiles_inspector_visibility_changed) 104 | _setup_dynamic_containers() 105 | set_process_input(true) 106 | tiles_preview_expand_requested.emit() 107 | 108 | 109 | func notify_tiles_inspector_removed() -> void: 110 | set_process_input(false) 111 | _call_subtree(self, TILES_INSPECTOR_REMOVED_METHOD) 112 | tiles_inspector_removed.emit() 113 | 114 | func is_dialog_popped_up() -> bool: 115 | for dialog in dialog_windows: 116 | if dialog.visible: 117 | return true 118 | return false 119 | 120 | 121 | func _setup_dynamic_containers() -> void: 122 | for node in get_tree().get_nodes_in_group(G.GROUP_DYNAMIC_CONTAINER): 123 | if !node.child_entered_tree.is_connected(_on_dynamic_container_child_added): 124 | var _err := node.child_entered_tree.connect(_on_dynamic_container_child_added) 125 | 126 | 127 | func _on_dynamic_container_child_added(node : Node) -> void: 128 | _inject_tbt_reference(node, true) 129 | theme_update_requested.emit(node) 130 | 131 | 132 | func _on_tiles_inspector_visibility_changed() -> void: 133 | if !is_instance_valid(tiles_inspector) or !tiles_inspector.is_visible_in_tree(): 134 | tiles_preview.hide() 135 | set_process_input(false) 136 | else: 137 | tiles_preview.show() 138 | set_process_input(true) 139 | 140 | 141 | # while tiles inspector is visible, watch for mouse clicks and 142 | # collapse preview panel for clicks outside of this plugin 143 | func _input(event: InputEvent) -> void: 144 | if not event is InputEventMouseButton: 145 | return 146 | if not event.button_index in [MOUSE_BUTTON_LEFT, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MIDDLE]: 147 | return 148 | 149 | if is_dialog_popped_up(): 150 | return 151 | 152 | var mouse_position = base_control.get_global_mouse_position() 153 | 154 | if tiles_inspector.get_parent_control().get_global_rect().has_point(mouse_position): 155 | if tiles_inspector.get_global_rect().has_point(mouse_position): 156 | if !tiles_preview.expanded: 157 | tiles_preview_expand_requested.emit() 158 | else: 159 | tiles_preview_collapse_requested.emit() 160 | return 161 | 162 | if !tiles_preview.expanded: 163 | return 164 | 165 | if tiles_preview.get_parent_control().get_global_rect().has_point(mouse_position): 166 | if !tiles_preview.get_mouse_input_rect().has_point(mouse_position): 167 | tiles_preview_collapse_requested.emit() 168 | 169 | 170 | func _setup_children() -> void: 171 | for child in _get_children_recursive(self): 172 | _assign_child_by_class(child) 173 | 174 | 175 | func _assign_child_by_class(child : Node) -> void: 176 | match child.get_script(): 177 | TemplateManager: 178 | template_manager = child 179 | TilesManager: 180 | tiles_manager = child 181 | ThemeUpdater: 182 | theme_updater = child 183 | Context: 184 | context = child 185 | _: 186 | if child is Window: 187 | dialog_windows.append(child) 188 | 189 | 190 | 191 | func _inject_tbt_reference(node : Node, include_parent := false) -> void: 192 | _set_subtree(node, TBT_PROPERTY_NAME, self, include_parent) 193 | _call_subtree(node, TBT_READY_METHOD, include_parent) 194 | 195 | 196 | func _set_subtree(parent : Node, property_name : String, value : Variant, include_parent := false) -> void: 197 | var nodes_to_set := _get_children_recursive(parent) 198 | if include_parent: 199 | nodes_to_set.append(parent) 200 | 201 | for node in nodes_to_set: 202 | node.set(property_name, value) 203 | 204 | 205 | func _call_subtree(parent : Node, method_name : String, include_parent := false) -> void: 206 | var nodes_to_call := _get_children_recursive(parent) 207 | if include_parent: 208 | nodes_to_call.append(parent) 209 | 210 | for node in nodes_to_call: 211 | if node.has_method(method_name): 212 | node.call(method_name) 213 | 214 | 215 | func _get_children_recursive(node : Node) -> Array: 216 | return node.find_children("*", "", true, false) 217 | 218 | 219 | func _is_reference_valid(node) -> bool: 220 | if !is_instance_valid(node): 221 | return false 222 | if !node.is_inside_tree(): 223 | output.warning("%s is not inside tree" % node) 224 | return false 225 | if node.is_queued_for_deletion(): 226 | output.warning("%s is queued for deletion" % node) 227 | return false 228 | return true 229 | # return is_instance_valid(node) && node.is_inside_tree() && !node.is_queued_for_deletion() 230 | 231 | 232 | func _setup_debug_signals() -> void: 233 | for signal_data in get_signal_list(): 234 | if signal_data.args.size() == 0: 235 | var _err := connect(signal_data.name, _on_signal_emitted.bind(signal_data.name)) 236 | else: 237 | var _err := connect(signal_data.name, _on_signal_emitted_with_arg.bind(signal_data.name)) 238 | 239 | 240 | func _on_signal_emitted(signal_name := "") -> void: 241 | output.debug("[TBTPlugin] Signal emitted: %s" % signal_name) 242 | 243 | 244 | func _on_signal_emitted_with_arg(arg = null, signal_name := "") -> void: 245 | output.debug("[TBTPlugin] Signal emitted: %s (%s)" % [signal_name, arg]) 246 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=8 format=3 uid="uid://bx6k2su2g3pj"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd" id="1_6xv2q"] 4 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/tbt_plugin_control/template_manager.gd" id="2_aj7pp"] 5 | [ext_resource type="PackedScene" uid="uid://job5gqt1xbrh" path="res://addons/tile_bit_tools/controls/tbt_plugin_control/popups/save_template_dialog.tscn" id="3_p4ion"] 6 | [ext_resource type="PackedScene" uid="uid://cyxr6y64lkue6" path="res://addons/tile_bit_tools/controls/tbt_plugin_control/popups/edit_template_dialog.tscn" id="4_qymya"] 7 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/tbt_plugin_control/tiles_manager.gd" id="5_v5epd"] 8 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/tbt_plugin_control/theme_updater.gd" id="6_mie0u"] 9 | 10 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3by6e"] 11 | content_margin_left = 24.0 12 | content_margin_top = 24.0 13 | content_margin_right = 24.0 14 | content_margin_bottom = 24.0 15 | bg_color = Color(0.21, 0.24, 0.29, 1) 16 | border_width_top = 48 17 | border_color = Color(0.21, 0.24, 0.29, 1) 18 | corner_detail = 5 19 | expand_margin_top = 48.0 20 | shadow_color = Color(0, 0, 0, 0.3) 21 | shadow_size = 8 22 | anti_aliasing = false 23 | 24 | [node name="TBTPlugin" type="Control"] 25 | layout_mode = 3 26 | anchors_preset = 0 27 | script = ExtResource("1_6xv2q") 28 | 29 | [node name="TemplateManager" type="Node" parent="."] 30 | script = ExtResource("2_aj7pp") 31 | 32 | [node name="TilesManager" type="Node" parent="."] 33 | script = ExtResource("5_v5epd") 34 | 35 | [node name="ThemeUpdater" type="Control" parent="."] 36 | anchors_preset = 0 37 | script = ExtResource("6_mie0u") 38 | 39 | [node name="SaveDialogContainer" type="Control" parent="."] 40 | anchors_preset = 0 41 | 42 | [node name="SaveTemplateDialog" parent="SaveDialogContainer" instance=ExtResource("3_p4ion")] 43 | visible = false 44 | theme_override_styles/embedded_border = SubResource("StyleBoxFlat_3by6e") 45 | 46 | [node name="EditDialogContainer" type="Control" parent="."] 47 | anchors_preset = 0 48 | 49 | [node name="EditTemplateDialog" parent="EditDialogContainer" instance=ExtResource("4_qymya")] 50 | theme_override_styles/embedded_border = SubResource("StyleBoxFlat_3by6e") 51 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tbt_plugin_control/template_manager.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Node 3 | 4 | const TEMPLATE_PREVIEW_TILE_WIDTH := 16 5 | const TEMPLATE_PREVIEW_TILE_SPACING := 0 6 | 7 | const TBTPlugin := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd") 8 | 9 | const TemplateLoader := preload("res://addons/tile_bit_tools/core/template_loader.gd") 10 | const BitDataDrawNode := preload("res://addons/tile_bit_tools/controls/bit_data_draw/bit_data_draw_node.tscn") 11 | 12 | var template_loader : TemplateLoader 13 | var bit_data_draw_node 14 | 15 | var tbt : TBTPlugin 16 | 17 | 18 | @onready var template_folder_paths : Array 19 | 20 | 21 | 22 | func _tbt_ready() -> void: 23 | var _err := tbt.templates_update_requested.connect(update_templates) 24 | 25 | _setup_bit_data_draw() 26 | 27 | # call deferred so editor will not pause on activating plugin 28 | _load_templates.call_deferred() 29 | 30 | 31 | func _setup_bit_data_draw() -> void: 32 | bit_data_draw_node = BitDataDrawNode.instantiate() 33 | add_child(bit_data_draw_node) 34 | bit_data_draw_node.tile_size = TEMPLATE_PREVIEW_TILE_WIDTH 35 | bit_data_draw_node.tile_spacing = TEMPLATE_PREVIEW_TILE_SPACING 36 | 37 | 38 | func _load_templates() -> void: 39 | _update_template_folder_paths() 40 | 41 | for folder_path in template_folder_paths: 42 | tbt.output.debug("Loading templates in %s: %s" % [folder_path.name, folder_path.path]) 43 | if !DirAccess.dir_exists_absolute(folder_path.path): 44 | tbt.output.debug("making path to template folder: %s" % folder_path.path) 45 | var _err := DirAccess.make_dir_recursive_absolute(folder_path.path) 46 | 47 | template_loader = TemplateLoader.new(template_folder_paths) 48 | _create_template_textures.call_deferred() 49 | tbt.output.info("%s templates loaded" % template_loader.get_templates().size()) 50 | 51 | 52 | func _create_template_textures() -> void: 53 | for template in template_loader.get_templates(): 54 | template.preview_texture = await bit_data_draw_node.get_bit_texture(template) 55 | # tbt.output.debug("Created preview texture for %s" % template.template_name) 56 | 57 | 58 | func update_templates() -> void: 59 | _load_templates() 60 | tbt.templates_updated.emit() 61 | 62 | 63 | func get_bit_data_draw() -> SubViewport: 64 | return bit_data_draw_node 65 | 66 | 67 | func get_user_templates_path() -> String: 68 | var path : String = ProjectSettings.get_setting(TBTPlugin.G.Settings.user_templates_path.path) 69 | var dir := DirAccess.open(path) 70 | if dir: 71 | return dir.get_current_dir() 72 | return "" 73 | 74 | 75 | func _update_template_folder_paths() -> void: 76 | template_folder_paths = [ 77 | { 78 | "type": TBTPlugin.G.TemplateTypes.BUILT_IN, 79 | "name": "Built-in Templates Folder", 80 | "path": TBTPlugin.G.BUILTIN_TEMPLATES_PATH, 81 | }, 82 | { 83 | "type": TBTPlugin.G.TemplateTypes.USER, 84 | "name": "Project Templates Folder", 85 | "tooltip": "Templates saved here will only be available to this project", 86 | "path": TBTPlugin.G.PROJECT_TEMPLATES_PATH, 87 | }, 88 | { 89 | "type": TBTPlugin.G.TemplateTypes.USER, 90 | "name": "Shared Templates Folder", 91 | "tooltip": "Templates saved here will be available to all projects on this computer", 92 | "path": OS.get_data_dir() + TBTPlugin.G.GODOT_TEMPLATES_FOLDER, 93 | }, 94 | # default is the same as project templates folder 95 | { 96 | "type": TBTPlugin.G.TemplateTypes.USER, 97 | "name": "User Templates Folder", 98 | "tooltip": "Template will be saved to the folder set in Project Settings -> Tile Bit Tools", 99 | "path": get_user_templates_path(), 100 | }, 101 | ] 102 | 103 | for i in range(template_folder_paths.size()-1, -1, -1): 104 | if template_folder_paths[i].path == "": 105 | template_folder_paths.remove_at(i) 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tbt_plugin_control/tiles_manager.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Node 3 | 4 | 5 | 6 | enum TerrainChanges {NONE, ERASE, FILL, BITS, TEMPLATE} 7 | 8 | const TBTPlugin := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd") 9 | 10 | # TODO: move to texts; currently unused 11 | var terrain_changes_texts := { 12 | TerrainChanges.NONE: "", 13 | TerrainChanges.ERASE: "Erase terrain", 14 | TerrainChanges.FILL: "Fill terrain", 15 | TerrainChanges.BITS: "Set terrain bits", 16 | TerrainChanges.TEMPLATE: "Apply terrain template", 17 | } 18 | 19 | 20 | var preview_bit_data : TBTPlugin.EditorBitData = null 21 | var current_terrain_change := TerrainChanges.NONE 22 | 23 | var tbt : TBTPlugin 24 | 25 | 26 | 27 | func _tbt_ready() -> void: 28 | var _err := tbt.reset_requested.connect(_on_reset_requested) 29 | _err = tbt.apply_changes_requested.connect(_on_apply_changes_requested) 30 | 31 | 32 | func get_preview_terrain_set() -> int: 33 | if preview_bit_data == null: 34 | return TBTPlugin.BitData.NULL_TERRAIN_SET 35 | return preview_bit_data.terrain_set 36 | 37 | func has_preview() -> bool: 38 | return preview_bit_data != null 39 | 40 | 41 | func has_preview_terrain_set() -> bool: 42 | if preview_bit_data == null: 43 | return false 44 | return preview_bit_data.terrain_set != preview_bit_data.NULL_TERRAIN_SET 45 | 46 | 47 | func clear_preview() -> void: 48 | preview_bit_data = null 49 | _emit_preview_updated() 50 | 51 | 52 | func erase_terrains() -> void: 53 | preview_bit_data = _get_new_preview_data() 54 | preview_bit_data.clear_all_tile_terrains() 55 | current_terrain_change = TerrainChanges.ERASE 56 | _emit_preview_updated() 57 | 58 | 59 | func fill_terrain(terrain_set : int, terrain_id : int) -> void: 60 | preview_bit_data = _get_new_preview_data() 61 | var terrain_mode : int = tbt.context.tile_set.get_terrain_set_mode(terrain_set) 62 | preview_bit_data.fill_all_tile_terrains(terrain_set, terrain_mode, terrain_id) 63 | current_terrain_change = TerrainChanges.FILL 64 | _emit_preview_updated() 65 | 66 | 67 | # sets specific terrain bit of all selected tiles 68 | # adds on to any changes already made 69 | func set_terrain_bits(terrain_bit : int, terrain_id : int) -> void: 70 | if !preview_bit_data: 71 | preview_bit_data = _get_new_preview_data() 72 | preview_bit_data.set_all_bit_terrains(terrain_bit, terrain_id) 73 | current_terrain_change = TerrainChanges.BITS 74 | _emit_preview_updated() 75 | 76 | 77 | func apply_template_terrains(template : TBTPlugin.TemplateBitData, terrain_set : int, terrain_mapping : Dictionary) -> void: 78 | if terrain_mapping.is_empty(): 79 | preview_bit_data = null 80 | _emit_preview_updated() 81 | return 82 | 83 | preview_bit_data = _get_new_preview_data() 84 | var result := preview_bit_data.apply_template_bit_data(template, terrain_set, terrain_mapping) 85 | if result != OK: 86 | preview_bit_data = null 87 | current_terrain_change = TerrainChanges.NONE 88 | tbt.output.error("Unable to apply template data", result) 89 | _emit_preview_updated() 90 | return 91 | 92 | current_terrain_change = TerrainChanges.TEMPLATE 93 | _emit_preview_updated() 94 | 95 | 96 | ## Applies the changes to TileData object 97 | ## including terrain_set, terrain and terrain peering bits 98 | ## There is no undo 99 | func apply_bit_data() -> void: 100 | if !preview_bit_data: 101 | return 102 | 103 | tbt.output.info(terrain_changes_texts[current_terrain_change]) 104 | 105 | if current_terrain_change == TerrainChanges.ERASE: 106 | tbt.output.user("Erasing terrain set assignments may cause error spam of Condition 'terrain_set < 0' is true. Data should save without corruption. Please ignore.") 107 | 108 | for coords in tbt.context.tiles.keys(): 109 | var tile_data : TileData = tbt.context.tiles[coords] 110 | tile_data.terrain_set = preview_bit_data.terrain_set 111 | tile_data.terrain = preview_bit_data.get_tile_terrain(coords) 112 | for cell_neighbor in preview_bit_data.get_terrain_bits_list(): 113 | var terrain := preview_bit_data.get_bit_terrain(coords, cell_neighbor) 114 | tile_data.set_terrain_peering_bit(cell_neighbor, terrain) 115 | 116 | 117 | 118 | func _emit_preview_updated() -> void: 119 | tbt.preview_updated.emit(preview_bit_data) 120 | 121 | 122 | func _get_new_preview_data() -> TBTPlugin.EditorBitData: 123 | var copy : TBTPlugin.EditorBitData = tbt.context.bit_data.make_copy() 124 | return copy 125 | 126 | 127 | func _on_reset_requested() -> void: 128 | tbt.output.debug("reset requested from tiles manager") 129 | clear_preview() 130 | 131 | 132 | func _on_apply_changes_requested() -> void: 133 | apply_bit_data() 134 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_inspector/template_section/selected_tag.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PanelContainer 3 | 4 | signal tag_removed 5 | 6 | const TemplateTagData := preload("res://addons/tile_bit_tools/core/template_tag_data.gd") 7 | const TemplateTag := TemplateTagData.TemplateTag 8 | 9 | var tag : TemplateTag 10 | 11 | @onready var icon_rect: TextureRect = %IconRect 12 | @onready var label: Label = %Label 13 | @onready var remove_button: Button = %RemoveButton 14 | 15 | 16 | func _ready() -> void: 17 | var _err := remove_button.pressed.connect(_on_remove_button_pressed) 18 | 19 | 20 | func setup(p_tag : TemplateTag, base_control : Control) -> void: 21 | tag = p_tag 22 | 23 | label.text = tag.text 24 | 25 | icon_rect.texture = tag.get_icon(base_control) 26 | 27 | var stylebox : StyleBoxFlat = base_control.get_theme_stylebox("selected", "ItemList").duplicate(true) 28 | stylebox.content_margin_left = 6 29 | stylebox.content_margin_right = 2 30 | stylebox.content_margin_top = 0 31 | stylebox.content_margin_bottom = 0 32 | set("theme_override_styles/panel", stylebox) 33 | 34 | 35 | # TODO: implement in future? 36 | # if tag.color: 37 | # var stylebox : StyleBoxFlat = get("theme_override_styles/panel") 38 | # stylebox.bg_color = tag.color 39 | 40 | 41 | func _on_remove_button_pressed() -> void: 42 | tag_removed.emit() 43 | queue_free() 44 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_inspector/template_section/selected_tag.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://bco7r2se8g2hn"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/tiles_inspector/template_section/selected_tag.gd" id="1_n8esd"] 4 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/shared/icon_button.gd" id="2_gjwm3"] 5 | 6 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_4fhay"] 7 | 8 | [node name="SelectedTag" type="PanelContainer"] 9 | script = ExtResource("1_n8esd") 10 | 11 | [node name="HBoxContainer" type="HBoxContainer" parent="."] 12 | layout_mode = 2 13 | 14 | [node name="IconRect" type="TextureRect" parent="HBoxContainer"] 15 | unique_name_in_owner = true 16 | layout_mode = 2 17 | stretch_mode = 3 18 | 19 | [node name="Label" type="Label" parent="HBoxContainer" groups=["TBTSubPropertyLabel"]] 20 | unique_name_in_owner = true 21 | layout_mode = 2 22 | text = "Mode: Corners and Sides" 23 | 24 | [node name="RemoveButton" type="Button" parent="HBoxContainer"] 25 | unique_name_in_owner = true 26 | layout_mode = 2 27 | theme_override_styles/focus = SubResource("StyleBoxEmpty_4fhay") 28 | flat = true 29 | script = ExtResource("2_gjwm3") 30 | editor_name = "Close" 31 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_inspector/template_section/selected_tag_stylebox.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="StyleBoxFlat" format=3 uid="uid://c1i723a04fsg7"] 2 | 3 | [resource] 4 | resource_local_to_scene = true 5 | content_margin_left = 8.0 6 | content_margin_right = 2.0 7 | bg_color = Color(0.239216, 0.258824, 0.278431, 1) 8 | corner_radius_top_left = 8 9 | corner_radius_top_right = 8 10 | corner_radius_bottom_right = 8 11 | corner_radius_bottom_left = 8 12 | corner_detail = 16 13 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_inspector/template_section/template_info_panel.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PanelContainer 3 | 4 | const TBTPlugin := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd") 5 | 6 | 7 | var info_label_text := "Type: {type}\nMode: {terrain_mode}\nTiles: {tile_count}\nTerrains: {terrain_count}" 8 | 9 | var texts := preload("res://addons/tile_bit_tools/core/texts.gd").new() 10 | var example_folder_path : String 11 | 12 | var template_bit_data : TBTPlugin.TemplateBitData 13 | 14 | var tbt : TBTPlugin 15 | 16 | 17 | @onready var template_info_list: ItemList = $MarginContainer/VBoxContainer/HBoxContainer/TemplateInfoList 18 | 19 | 20 | @onready var template_rect: TextureRect = %TemplateRect 21 | 22 | @onready var description_container: PanelContainer = %DescriptionContainer 23 | @onready var expand_button: Button = %ExpandButton 24 | @onready var description_label: RichTextLabel = %DescriptionLabel 25 | 26 | @onready var example_button: Button = %ExampleButton 27 | @onready var edit_button: Button = %EditButton 28 | @onready var remove_button: Button = %RemoveButton 29 | 30 | 31 | func _ready() -> void: 32 | hide() 33 | 34 | 35 | func _tbt_ready() -> void: 36 | _toggle_description_expand_button(false) 37 | 38 | 39 | func update(p_template_bit_data : TBTPlugin.TemplateBitData) -> void: 40 | template_bit_data = p_template_bit_data 41 | 42 | if !template_bit_data: 43 | hide() 44 | return 45 | 46 | show() 47 | 48 | if template_bit_data.template_description == "": 49 | description_container.hide() 50 | else: 51 | description_container.show() 52 | 53 | description_label.text = template_bit_data.template_description 54 | 55 | template_info_list.update(template_bit_data) 56 | 57 | 58 | if template_bit_data.built_in: 59 | edit_button.hide() 60 | remove_button.hide() 61 | else: 62 | edit_button.show() 63 | remove_button.show() 64 | 65 | if template_bit_data.example_folder_path != "" && DirAccess.dir_exists_absolute(template_bit_data.example_folder_path): 66 | example_folder_path = template_bit_data.example_folder_path 67 | example_button.show() 68 | else: 69 | example_folder_path = "" 70 | example_button.hide() 71 | 72 | template_rect.texture = template_bit_data.preview_texture 73 | 74 | 75 | 76 | 77 | 78 | 79 | func _toggle_description_expand_button(value : bool) -> void: 80 | if value: 81 | expand_button.icon = tbt.icons.get_icon(tbt.icons.ARROW_EXPANDED) 82 | description_label.fit_content = true 83 | else: 84 | expand_button.icon = tbt.icons.get_icon(tbt.icons.ARROW_COLLAPSED) 85 | description_label.fit_content = false 86 | 87 | 88 | 89 | 90 | func _open_example_folder() -> void: 91 | var path := ProjectSettings.globalize_path(example_folder_path) 92 | var _err := OS.shell_open(path) 93 | 94 | 95 | # TODO: move to template_manager? make more generalized? 96 | func _remove_template() -> void: 97 | var confirm_dialog := ConfirmationDialog.new() 98 | confirm_dialog.title = "Confirm Delete Template" 99 | confirm_dialog.dialog_text = "Really delete template '%s'?" % template_bit_data.template_name 100 | confirm_dialog.ok_button_text = "Delete (no undo)" 101 | var _err := confirm_dialog.confirmed.connect(_on_delete_confirmed) 102 | add_child(confirm_dialog) 103 | confirm_dialog.popup_centered() 104 | 105 | 106 | func _on_delete_confirmed() -> void: 107 | var path := template_bit_data.resource_path 108 | var _err := DirAccess.remove_absolute(path) 109 | tbt.output.user("Deleted user template '%s'" % template_bit_data.template_name) 110 | tbt.templates_update_requested.emit() 111 | 112 | 113 | func _on_remove_button_pressed() -> void: 114 | _remove_template() 115 | 116 | 117 | func _on_edit_button_pressed() -> void: 118 | tbt.edit_template_requested.emit(template_bit_data) 119 | 120 | 121 | func _on_example_button_pressed() -> void: 122 | _open_example_folder() 123 | 124 | 125 | func _on_expand_button_toggled(button_pressed: bool) -> void: 126 | _toggle_description_expand_button(button_pressed) 127 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_inspector/template_section/template_info_panel.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://bxhlehm73ligy"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/tiles_inspector/template_section/template_info_panel.gd" id="1_arkoa"] 4 | [ext_resource type="PackedScene" uid="uid://bscknm10ko78t" path="res://addons/tile_bit_tools/controls/shared/template_info_list.tscn" id="2_jg88n"] 5 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/shared/icon_button.gd" id="5_7b1jd"] 6 | 7 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_kuq0c"] 8 | 9 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_kei5m"] 10 | 11 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_v7bmg"] 12 | 13 | [node name="TemplateInfoPanel" type="PanelContainer" groups=["TBTSubinspectorOverlayPanel"]] 14 | visible = false 15 | custom_minimum_size = Vector2(0, 48) 16 | anchors_preset = 10 17 | anchor_right = 1.0 18 | offset_bottom = 92.0 19 | grow_horizontal = 2 20 | size_flags_horizontal = 3 21 | script = ExtResource("1_arkoa") 22 | 23 | [node name="MarginContainer" type="MarginContainer" parent="."] 24 | layout_mode = 2 25 | theme_override_constants/margin_left = 8 26 | theme_override_constants/margin_top = 8 27 | theme_override_constants/margin_right = 8 28 | theme_override_constants/margin_bottom = 8 29 | 30 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] 31 | layout_mode = 2 32 | mouse_filter = 2 33 | 34 | [node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] 35 | layout_mode = 2 36 | mouse_filter = 2 37 | 38 | [node name="TemplateInfoList" parent="MarginContainer/VBoxContainer/HBoxContainer" instance=ExtResource("2_jg88n")] 39 | layout_mode = 2 40 | 41 | [node name="TemplateRect" type="TextureRect" parent="MarginContainer/VBoxContainer/HBoxContainer"] 42 | unique_name_in_owner = true 43 | texture_filter = 1 44 | texture_repeat = 1 45 | layout_mode = 2 46 | size_flags_horizontal = 3 47 | expand_mode = 1 48 | stretch_mode = 5 49 | 50 | [node name="DescriptionContainer" type="PanelContainer" parent="MarginContainer/VBoxContainer" groups=["TBTInspectorMessagePanel"]] 51 | unique_name_in_owner = true 52 | layout_mode = 2 53 | theme_override_styles/panel = SubResource("StyleBoxEmpty_kuq0c") 54 | 55 | [node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer/VBoxContainer/DescriptionContainer"] 56 | layout_mode = 2 57 | 58 | [node name="ExpandButton" type="Button" parent="MarginContainer/VBoxContainer/DescriptionContainer/HBoxContainer2"] 59 | unique_name_in_owner = true 60 | layout_mode = 2 61 | size_flags_horizontal = 0 62 | size_flags_vertical = 0 63 | theme_override_styles/focus = SubResource("StyleBoxEmpty_kei5m") 64 | toggle_mode = true 65 | flat = true 66 | 67 | [node name="DescriptionLabel" type="RichTextLabel" parent="MarginContainer/VBoxContainer/DescriptionContainer/HBoxContainer2"] 68 | unique_name_in_owner = true 69 | layout_mode = 2 70 | size_flags_horizontal = 3 71 | theme_override_styles/normal = SubResource("StyleBoxEmpty_v7bmg") 72 | bbcode_enabled = true 73 | text = "Description of the template. Description of the template. Description of the template. Description of the template. Description of the template. Description of the template. Description of the template. Description of the template. Description of the template. Description of the template. Description of the template. " 74 | scroll_active = false 75 | 76 | [node name="Buttons" type="HBoxContainer" parent="MarginContainer"] 77 | layout_mode = 2 78 | size_flags_vertical = 0 79 | alignment = 2 80 | 81 | [node name="ExampleButton" type="Button" parent="MarginContainer/Buttons"] 82 | unique_name_in_owner = true 83 | layout_mode = 2 84 | tooltip_text = "Open example files folder" 85 | script = ExtResource("5_7b1jd") 86 | editor_name = "Image" 87 | 88 | [node name="EditButton" type="Button" parent="MarginContainer/Buttons"] 89 | unique_name_in_owner = true 90 | layout_mode = 2 91 | tooltip_text = "Edit User Template" 92 | script = ExtResource("5_7b1jd") 93 | editor_name = "Edit" 94 | 95 | [node name="RemoveButton" type="Button" parent="MarginContainer/Buttons"] 96 | unique_name_in_owner = true 97 | layout_mode = 2 98 | tooltip_text = "Delete User Template" 99 | script = ExtResource("5_7b1jd") 100 | editor_name = "Remove" 101 | 102 | [connection signal="toggled" from="MarginContainer/VBoxContainer/DescriptionContainer/HBoxContainer2/ExpandButton" to="." method="_on_expand_button_toggled"] 103 | [connection signal="pressed" from="MarginContainer/Buttons/ExampleButton" to="." method="_on_example_button_pressed"] 104 | [connection signal="pressed" from="MarginContainer/Buttons/EditButton" to="." method="_on_edit_button_pressed"] 105 | [connection signal="pressed" from="MarginContainer/Buttons/RemoveButton" to="." method="_on_remove_button_pressed"] 106 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_inspector/template_section/templates_section.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Control 3 | 4 | const NULL_TERRAIN_SET_INDEX := 0 5 | 6 | const NULL_OPTION_ID := 999 7 | 8 | const TBTPlugin := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd") 9 | 10 | 11 | var TerrainPicker := preload("res://addons/tile_bit_tools/controls/tiles_inspector/template_section/terrain_picker.tscn") 12 | var SelectedTag := preload("res://addons/tile_bit_tools/controls/tiles_inspector/template_section/selected_tag.tscn") 13 | 14 | var templates := {} 15 | 16 | var terrain_pickers := [] 17 | var terrain_sets := {} 18 | 19 | var selected_tags := [] 20 | var selected_template_id := -1 21 | var selected_template : TBTPlugin.TemplateBitData 22 | var selected_terrain_set := -1 23 | 24 | var tbt : TBTPlugin 25 | 26 | @onready var tags_menu_button: MenuButton = %TagsMenuButton 27 | @onready var tags_popup := tags_menu_button.get_popup() 28 | 29 | @onready var tags_container: HFlowContainer = %TagsContainer 30 | 31 | @onready var templates_option_button: OptionButton = %TemplatesOptionButton 32 | @onready var template_info_panel: PanelContainer = %TemplateInfoPanel 33 | @onready var terrain_set_option_button: OptionButton = %TerrainSetOptionButton 34 | @onready var terrain_pickers_container: VBoxContainer = %TerrainPickersContainer 35 | 36 | @onready var terrain_container: PanelContainer = %TerrainContainer 37 | 38 | @onready var template_section_panel: PanelContainer = $TemplateSectionPanel 39 | 40 | 41 | 42 | func _tbt_ready() -> void: 43 | 44 | var _err := tbt.templates_updated.connect(_on_templates_updated) 45 | _err = tbt.reset_requested.connect(_on_reset_requested) 46 | 47 | _err = templates_option_button.item_selected.connect(_on_template_selected) 48 | _err = terrain_set_option_button.item_selected.connect(_on_terrain_set_selected) 49 | 50 | _err = tags_popup.id_pressed.connect(_add_tag) 51 | _reset_tags() 52 | 53 | 54 | # -------------------------------------- 55 | # TAGS 56 | # -------------------------------------- 57 | 58 | func _reset_tags() -> void: 59 | for child in tags_container.get_children(): 60 | child.queue_free() 61 | 62 | selected_tags.clear() 63 | _update_tags_popup() 64 | 65 | 66 | func _update_tags_popup() -> void: 67 | tags_popup.clear() 68 | var item_list := tbt.template_manager.template_loader.get_tags_item_list(true, true, selected_tags) 69 | 70 | if item_list.size() == 0: 71 | tags_menu_button.disabled = true 72 | tags_menu_button.tooltip_text = "No additional filters available" 73 | return 74 | 75 | tags_menu_button.disabled = false 76 | tags_menu_button.tooltip_text = "" 77 | 78 | for item in item_list: 79 | var item_text : String = item.text + " (Templates: %s)" % item.count 80 | var icon = item.tag.get_icon(tbt.base_control) 81 | if icon: 82 | tags_popup.add_icon_item(icon, item_text, item.id) 83 | else: 84 | tags_popup.add_item(item_text, item.id) 85 | 86 | _update_templates_option_button() 87 | 88 | 89 | func _add_tag(tag_id : int) -> void: 90 | selected_tags.append(tag_id) 91 | var tag_control := SelectedTag.instantiate() 92 | var tag := tbt.template_manager.template_loader.get_tag(tag_id) 93 | tags_container.add_child(tag_control) 94 | tag_control.setup(tag, tbt.base_control) 95 | tag_control.tag_removed.connect(_on_tag_removed.bind(tag_id)) 96 | _update_tags_popup() 97 | 98 | 99 | func _on_tag_removed(tag_id : int) -> void: 100 | selected_tags.erase(tag_id) 101 | _update_tags_popup() 102 | 103 | 104 | 105 | # -------------------------------------- 106 | # TEMPLATES 107 | # -------------------------------------- 108 | 109 | func _clear_templates_option_button() -> void: 110 | templates_option_button.clear() 111 | selected_template_id = -1 112 | 113 | 114 | func _update_templates_option_button() -> void: 115 | templates_option_button.clear() 116 | templates_option_button.add_item("", NULL_OPTION_ID) 117 | 118 | var templates_list := tbt.template_manager.template_loader.get_templates_item_list(selected_tags) 119 | for item in templates_list: 120 | templates_option_button.add_item(item.text, item.id) 121 | 122 | _force_select_template(NULL_OPTION_ID) 123 | 124 | 125 | func _force_select_template(id : int) -> void: 126 | var index := templates_option_button.get_item_index(id) 127 | templates_option_button.select(index) 128 | _on_template_selected(index) # selecting via script does not emit signal 129 | 130 | 131 | func _on_template_selected(index : int) -> void: 132 | selected_template_id = templates_option_button.get_item_id(index) 133 | selected_template = tbt.template_manager.template_loader.get_template(selected_template_id) 134 | _update_template_panel() 135 | _update_terrain_sets() 136 | _update_terrain_pickers_from_template() 137 | _update_template_info_panel() 138 | tbt.tiles_manager.clear_preview() 139 | 140 | 141 | func _update_template_info_panel() -> void: 142 | template_info_panel.update(selected_template) 143 | 144 | 145 | func _update_template_panel() -> void: 146 | if selected_template: 147 | template_section_panel.add_to_group("TBTSubinspectorPanel") 148 | tbt.theme_update_requested.emit(template_section_panel) 149 | else: 150 | template_section_panel.remove_from_group("TBTSubinspectorPanel") 151 | template_section_panel.set("theme_override_styles/panel", StyleBoxEmpty.new()) 152 | 153 | 154 | 155 | 156 | 157 | 158 | # -------------------------------------- 159 | # TERRAIN SET 160 | # -------------------------------------- 161 | 162 | 163 | func _update_terrain_sets() -> void: 164 | if selected_template_id == NULL_OPTION_ID: 165 | terrain_container.hide() 166 | else: 167 | terrain_container.show() 168 | _update_terrain_set_option_button() 169 | 170 | 171 | func _update_terrain_set_option_button() -> void: 172 | var item_list := [] 173 | if selected_template: 174 | item_list = tbt.context.get_terrain_sets_item_list(selected_template.terrain_mode) 175 | 176 | _update_option_button(terrain_set_option_button, item_list, false) 177 | 178 | if item_list.size() == 0: 179 | terrain_set_option_button.disabled = true 180 | terrain_set_option_button.tooltip_text = "No terrain sets found matching template mode. Create a new one in the TileSet." 181 | else: 182 | terrain_set_option_button.disabled = false 183 | terrain_set_option_button.tooltip_text = "" 184 | _force_select_terrain_set(item_list[0].id) 185 | 186 | 187 | func _force_select_terrain_set(id : int) -> void: 188 | var index := terrain_set_option_button.get_item_index(id) 189 | terrain_set_option_button.select(index) 190 | _on_terrain_set_selected(index) # selecting via script does not emit signal 191 | 192 | 193 | func _on_terrain_set_selected(index : int) -> void: 194 | selected_terrain_set = terrain_set_option_button.get_item_id(index) 195 | _update_terrain_pickers_from_terrain_set() 196 | 197 | 198 | # -------------------------------------- 199 | # TERRAINS 200 | # -------------------------------------- 201 | 202 | 203 | func _update_terrain_pickers_from_template() -> void: 204 | for child in terrain_pickers_container.get_children(): 205 | terrain_pickers_container.remove_child(child) 206 | 207 | if !selected_template: 208 | return 209 | 210 | for i in range(selected_template.get_terrain_count()): 211 | terrain_pickers.append(_add_terrain_picker(i)) 212 | 213 | 214 | func _update_terrain_pickers_from_terrain_set() -> void: 215 | for terrain_picker in terrain_pickers: 216 | terrain_picker.terrain_set = selected_terrain_set 217 | 218 | 219 | func _add_terrain_picker(index : int) -> Control: 220 | var terrain_picker := TerrainPicker.instantiate() 221 | terrain_pickers_container.add_child(terrain_picker) 222 | terrain_picker.setup(tbt, index, selected_template.get_terrain_color(index), selected_template.terrain_mode) 223 | var _err := terrain_set_option_button.item_selected.connect(terrain_picker._on_terrain_set_changed) 224 | terrain_picker.item_selected.connect(_request_preview) 225 | terrain_picker.terrain_set = selected_terrain_set 226 | return terrain_picker 227 | 228 | 229 | 230 | func _request_preview() -> void: 231 | var terrain_mapping := _get_terrain_mapping() 232 | tbt.tiles_manager.apply_template_terrains(selected_template, selected_terrain_set, terrain_mapping) 233 | 234 | 235 | 236 | func _update_option_button(option_button : OptionButton, item_list : Array, add_empty_item := false) -> void: 237 | 238 | option_button.clear() 239 | if add_empty_item: 240 | option_button.add_item("", NULL_OPTION_ID) 241 | 242 | for item in item_list: 243 | if item.has("icon"): 244 | option_button.add_icon_item(item.icon, item.text, item.id) 245 | else: 246 | option_button.add_item(item.text, item.id) 247 | 248 | 249 | 250 | ## Returns a dictionary 251 | ## key = template terrain id 252 | ## value = tileset terrain id 253 | func _get_terrain_mapping() -> Dictionary: 254 | var mapping := {} 255 | var has_data := false 256 | 257 | for picker in terrain_pickers: 258 | var terrain_id : int = picker.get_selected_item_id() 259 | if terrain_id != -1: 260 | has_data = true 261 | mapping[picker.index] = picker.get_selected_item_id() 262 | 263 | if has_data: 264 | return mapping 265 | else: 266 | return {} 267 | 268 | 269 | 270 | func _on_templates_updated() -> void: 271 | _reset_tags() 272 | 273 | 274 | func _on_reset_requested() -> void: 275 | _reset_tags() 276 | 277 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_inspector/template_section/templates_section.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://dknch2tbr32yh"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/tiles_inspector/template_section/templates_section.gd" id="1_i6wsq"] 4 | [ext_resource type="PackedScene" uid="uid://bxhlehm73ligy" path="res://addons/tile_bit_tools/controls/tiles_inspector/template_section/template_info_panel.tscn" id="5_tlsqj"] 5 | 6 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_t8sf7"] 7 | 8 | [node name="TemplatesContainer" type="VBoxContainer"] 9 | anchors_preset = 10 10 | anchor_right = 1.0 11 | offset_bottom = 26.0 12 | grow_horizontal = 2 13 | script = ExtResource("1_i6wsq") 14 | 15 | [node name="HBoxContainer2" type="HBoxContainer" parent="."] 16 | layout_mode = 2 17 | 18 | [node name="TagsMenuButton" type="MenuButton" parent="HBoxContainer2"] 19 | unique_name_in_owner = true 20 | layout_mode = 2 21 | size_flags_horizontal = 3 22 | text = "Select Tag to Filter..." 23 | 24 | [node name="TagsContainer" type="HFlowContainer" parent="." groups=["TBTDynamicContainer"]] 25 | unique_name_in_owner = true 26 | layout_mode = 2 27 | 28 | [node name="TemplateSectionPanel" type="PanelContainer" parent="."] 29 | layout_mode = 2 30 | size_flags_horizontal = 3 31 | theme_override_styles/panel = SubResource("StyleBoxEmpty_t8sf7") 32 | 33 | [node name="VBoxContainer" type="VBoxContainer" parent="TemplateSectionPanel"] 34 | layout_mode = 2 35 | theme_override_constants/separation = 0 36 | 37 | [node name="HBoxContainer" type="HBoxContainer" parent="TemplateSectionPanel/VBoxContainer"] 38 | layout_mode = 2 39 | 40 | [node name="Label" type="Label" parent="TemplateSectionPanel/VBoxContainer/HBoxContainer"] 41 | layout_mode = 2 42 | size_flags_horizontal = 3 43 | text = "Template" 44 | 45 | [node name="TemplatesOptionButton" type="OptionButton" parent="TemplateSectionPanel/VBoxContainer/HBoxContainer"] 46 | unique_name_in_owner = true 47 | layout_mode = 2 48 | size_flags_horizontal = 3 49 | item_count = 4 50 | selected = 0 51 | popup/item_0/text = "" 52 | popup/item_0/id = 999 53 | popup/item_1/text = "2x2" 54 | popup/item_1/id = 0 55 | popup/item_2/text = "3x3 Minimal" 56 | popup/item_2/id = 1 57 | popup/item_3/text = "Generic 16 Tiles" 58 | popup/item_3/id = 2 59 | 60 | [node name="MarginContainer" type="MarginContainer" parent="TemplateSectionPanel/VBoxContainer"] 61 | layout_mode = 2 62 | theme_override_constants/margin_left = 8 63 | theme_override_constants/margin_top = 0 64 | theme_override_constants/margin_right = 8 65 | theme_override_constants/margin_bottom = 8 66 | 67 | [node name="VBoxContainer" type="VBoxContainer" parent="TemplateSectionPanel/VBoxContainer/MarginContainer"] 68 | layout_mode = 2 69 | 70 | [node name="TemplateInfoPanel" parent="TemplateSectionPanel/VBoxContainer/MarginContainer/VBoxContainer" instance=ExtResource("5_tlsqj")] 71 | unique_name_in_owner = true 72 | layout_mode = 2 73 | 74 | [node name="TerrainContainer" type="PanelContainer" parent="TemplateSectionPanel/VBoxContainer/MarginContainer/VBoxContainer" groups=["TBTSubinspectorPropertiesPanel"]] 75 | unique_name_in_owner = true 76 | layout_mode = 2 77 | 78 | [node name="MarginContainer" type="MarginContainer" parent="TemplateSectionPanel/VBoxContainer/MarginContainer/VBoxContainer/TerrainContainer"] 79 | layout_mode = 2 80 | theme_override_constants/margin_left = 16 81 | 82 | [node name="VBoxContainer" type="VBoxContainer" parent="TemplateSectionPanel/VBoxContainer/MarginContainer/VBoxContainer/TerrainContainer/MarginContainer"] 83 | layout_mode = 2 84 | 85 | [node name="HBoxContainer" type="HBoxContainer" parent="TemplateSectionPanel/VBoxContainer/MarginContainer/VBoxContainer/TerrainContainer/MarginContainer/VBoxContainer"] 86 | layout_mode = 2 87 | 88 | [node name="Label" type="Label" parent="TemplateSectionPanel/VBoxContainer/MarginContainer/VBoxContainer/TerrainContainer/MarginContainer/VBoxContainer/HBoxContainer" groups=["TBTPropertyLabel"]] 89 | layout_mode = 2 90 | size_flags_horizontal = 3 91 | text = "Terrain Set" 92 | 93 | [node name="TerrainSetOptionButton" type="OptionButton" parent="TemplateSectionPanel/VBoxContainer/MarginContainer/VBoxContainer/TerrainContainer/MarginContainer/VBoxContainer/HBoxContainer"] 94 | unique_name_in_owner = true 95 | layout_mode = 2 96 | size_flags_horizontal = 3 97 | tooltip_text = "No terrain sets found matching template mode. Create a new one in the TileSet." 98 | disabled = true 99 | fit_to_longest_item = false 100 | 101 | [node name="MarginContainer" type="MarginContainer" parent="TemplateSectionPanel/VBoxContainer/MarginContainer/VBoxContainer/TerrainContainer/MarginContainer/VBoxContainer"] 102 | layout_mode = 2 103 | theme_override_constants/margin_left = 16 104 | 105 | [node name="TerrainPickersContainer" type="VBoxContainer" parent="TemplateSectionPanel/VBoxContainer/MarginContainer/VBoxContainer/TerrainContainer/MarginContainer/VBoxContainer/MarginContainer" groups=["TBTDynamicContainer"]] 106 | unique_name_in_owner = true 107 | layout_mode = 2 108 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_inspector/template_section/terrain_picker.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Control 3 | 4 | signal item_selected 5 | 6 | 7 | const TBTPlugin := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd") 8 | 9 | var EMPTY_ITEM_ID := -99 10 | 11 | var terrain_label_text := "Terrain %s" 12 | 13 | var index : int 14 | var color : Color 15 | 16 | var terrain_set : int : 17 | set(value): 18 | terrain_set = value 19 | _update_picker() 20 | 21 | var tbt : TBTPlugin 22 | var template_mode : TileSet.TerrainMode 23 | 24 | @onready var terrain_label: Label = %TerrainLabel 25 | @onready var terrain_color_rect: ColorRect = %TerrainColorRect 26 | @onready var terrain_option_button: OptionButton = %TerrainOptionButton 27 | 28 | 29 | func setup(p_tbt : TBTPlugin, p_index : int, p_color : Color, p_mode : TileSet.TerrainMode) -> void: 30 | tbt = p_tbt 31 | index = p_index 32 | color = p_color 33 | template_mode = p_mode 34 | _update_label() 35 | 36 | 37 | func _ready() -> void: 38 | var _err := terrain_option_button.item_selected.connect(_emit_item_selected) 39 | 40 | 41 | func get_selected_item_id() -> int: 42 | var id := terrain_option_button.get_selected_id() 43 | # id cannot be -1 in optionlist, so must be converted 44 | if id == EMPTY_ITEM_ID: 45 | return -1 46 | return id 47 | 48 | 49 | func disable_picker(value : bool) -> void: 50 | if value: 51 | terrain_option_button.disabled = true 52 | else: 53 | terrain_option_button.disabled = false 54 | 55 | 56 | func _update_label() -> void: 57 | terrain_label.text = terrain_label_text % index 58 | terrain_color_rect.color = color 59 | 60 | 61 | func _update_picker() -> void: 62 | terrain_option_button.clear() 63 | terrain_option_button.add_item(tbt.texts.EMPTY_ITEM, EMPTY_ITEM_ID) 64 | 65 | if terrain_set == -1: 66 | disable_picker(true) 67 | return 68 | 69 | var terrain_set_mode := tbt.context.tile_set.get_terrain_set_mode(terrain_set) 70 | if terrain_set_mode != template_mode: 71 | disable_picker(true) 72 | return 73 | 74 | disable_picker(false) 75 | 76 | var terrains_count := tbt.context.tile_set.get_terrains_count(terrain_set) 77 | 78 | for i in range(terrains_count): 79 | var terrain_name := tbt.context.tile_set.get_terrain_name(terrain_set, i) 80 | var terrain_color := tbt.context.tile_set.get_terrain_color(terrain_set, i) 81 | var icon : ImageTexture = tbt.context.get_terrain_icon(terrain_color) 82 | terrain_option_button.add_icon_item(icon, terrain_name, i) 83 | 84 | 85 | func _emit_item_selected(_index := -1) -> void: 86 | item_selected.emit() 87 | 88 | 89 | func _on_terrain_set_changed(id : int) -> void: 90 | terrain_set = id 91 | 92 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_inspector/template_section/terrain_picker.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://1s77a4hdeubd"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/tiles_inspector/template_section/terrain_picker.gd" id="1_wswd4"] 4 | 5 | [node name="TerrainPicker" type="HBoxContainer"] 6 | anchors_preset = 10 7 | anchor_right = 1.0 8 | offset_bottom = 26.0 9 | grow_horizontal = 2 10 | script = ExtResource("1_wswd4") 11 | 12 | [node name="HBoxContainer" type="HBoxContainer" parent="."] 13 | layout_mode = 2 14 | size_flags_horizontal = 3 15 | theme_override_constants/separation = 12 16 | 17 | [node name="TerrainLabel" type="Label" parent="HBoxContainer" groups=["TBTPropertyLabel"]] 18 | unique_name_in_owner = true 19 | layout_mode = 2 20 | size_flags_horizontal = 0 21 | text = "Terrain 1" 22 | 23 | [node name="TerrainColorRect" type="ColorRect" parent="HBoxContainer"] 24 | unique_name_in_owner = true 25 | custom_minimum_size = Vector2(16, 16) 26 | layout_mode = 2 27 | size_flags_horizontal = 4 28 | size_flags_vertical = 4 29 | 30 | [node name="TerrainOptionButton" type="OptionButton" parent="."] 31 | unique_name_in_owner = true 32 | layout_mode = 2 33 | size_flags_horizontal = 3 34 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_inspector/tiles_inspector.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Control 3 | 4 | const TBTPlugin := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd") 5 | 6 | var tbt : TBTPlugin 7 | 8 | var ready_complete := false 9 | 10 | @onready var save_button: Button = %SaveButton 11 | 12 | func _ready() -> void: 13 | ready_complete = true 14 | 15 | 16 | func _tbt_ready() -> void: 17 | if tbt.context.bit_data.has_terrain_set(): 18 | save_button.disabled = false 19 | save_button.tooltip_text = "" 20 | else: 21 | save_button.disabled = true 22 | save_button.tooltip_text = "No terrain data to save in selected tiles" 23 | 24 | 25 | 26 | func _on_save_button_pressed() -> void: 27 | tbt.save_template_requested.emit() 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_inspector/tiles_inspector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://bijiosgd4td"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/tiles_inspector/tiles_inspector.gd" id="1_hyl5p"] 4 | [ext_resource type="Texture2D" uid="uid://cheop6l3022pn" path="res://addons/tile_bit_tools/controls/icons/tile_bit_tools_16.svg" id="3_pojdm"] 5 | [ext_resource type="PackedScene" uid="uid://dkmdolef567aa" path="res://addons/tile_bit_tools/controls/shared/inspector_section_button.tscn" id="4_2qwfq"] 6 | [ext_resource type="PackedScene" uid="uid://b5nbqr8ah0ay2" path="res://addons/tile_bit_tools/controls/tiles_inspector/tool_buttons/tool_buttons.tscn" id="4_vjqmp"] 7 | [ext_resource type="PackedScene" uid="uid://dknch2tbr32yh" path="res://addons/tile_bit_tools/controls/tiles_inspector/template_section/templates_section.tscn" id="6_xgtwo"] 8 | 9 | [node name="TilesInspector" type="VBoxContainer"] 10 | custom_minimum_size = Vector2(0, 64) 11 | anchors_preset = 15 12 | anchor_right = 1.0 13 | anchor_bottom = 1.0 14 | grow_horizontal = 2 15 | grow_vertical = 2 16 | theme_override_constants/separation = 0 17 | script = ExtResource("1_hyl5p") 18 | 19 | [node name="CategoryPanel" type="Panel" parent="." groups=["TBTCategoryPanel"]] 20 | custom_minimum_size = Vector2(0, 43) 21 | layout_mode = 2 22 | size_flags_vertical = 4 23 | 24 | [node name="HBoxContainer" type="HBoxContainer" parent="CategoryPanel"] 25 | layout_mode = 1 26 | anchors_preset = 14 27 | anchor_top = 0.5 28 | anchor_right = 1.0 29 | anchor_bottom = 0.5 30 | offset_top = -16.0 31 | offset_bottom = 16.0 32 | grow_horizontal = 2 33 | grow_vertical = 2 34 | size_flags_vertical = 4 35 | alignment = 1 36 | 37 | [node name="TextureRect" type="TextureRect" parent="CategoryPanel/HBoxContainer"] 38 | layout_mode = 2 39 | size_flags_horizontal = 4 40 | size_flags_vertical = 4 41 | texture = ExtResource("3_pojdm") 42 | stretch_mode = 3 43 | 44 | [node name="CategoryLabel" type="Label" parent="CategoryPanel/HBoxContainer" groups=["TBTCategoryLabel"]] 45 | layout_mode = 2 46 | text = "TileBitTools" 47 | 48 | [node name="MarginContainer3" type="MarginContainer" parent="."] 49 | layout_mode = 2 50 | theme_override_constants/margin_left = 8 51 | theme_override_constants/margin_top = 8 52 | theme_override_constants/margin_right = 8 53 | theme_override_constants/margin_bottom = 8 54 | 55 | [node name="ToolButtons" parent="MarginContainer3" instance=ExtResource("4_vjqmp")] 56 | layout_mode = 2 57 | 58 | [node name="ApplyTemplateSectionButton" parent="." instance=ExtResource("4_2qwfq")] 59 | custom_minimum_size = Vector2(0, 43) 60 | layout_mode = 2 61 | label_text = "Apply Terrain Template" 62 | expand_container_path = NodePath("../MarginContainer") 63 | 64 | [node name="MarginContainer" type="MarginContainer" parent="."] 65 | visible = false 66 | layout_mode = 2 67 | theme_override_constants/margin_left = 16 68 | 69 | [node name="TemplatesContainer" parent="MarginContainer" instance=ExtResource("6_xgtwo")] 70 | layout_mode = 2 71 | 72 | [node name="SaveTemplateSectionButton" parent="." instance=ExtResource("4_2qwfq")] 73 | custom_minimum_size = Vector2(0, 43) 74 | layout_mode = 2 75 | label_text = "Save Terrain Template" 76 | expand_container_path = NodePath("../MarginContainer2") 77 | 78 | [node name="MarginContainer2" type="MarginContainer" parent="."] 79 | layout_mode = 2 80 | theme_override_constants/margin_left = 16 81 | 82 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer2"] 83 | layout_mode = 2 84 | 85 | [node name="PanelContainer" type="PanelContainer" parent="MarginContainer2/VBoxContainer" groups=["TBTInspectorMessagePanel"]] 86 | layout_mode = 2 87 | 88 | [node name="MarginContainer" type="MarginContainer" parent="MarginContainer2/VBoxContainer/PanelContainer"] 89 | layout_mode = 2 90 | theme_override_constants/margin_left = 8 91 | theme_override_constants/margin_top = 8 92 | theme_override_constants/margin_right = 8 93 | theme_override_constants/margin_bottom = 8 94 | 95 | [node name="Label" type="Label" parent="MarginContainer2/VBoxContainer/PanelContainer/MarginContainer"] 96 | layout_mode = 2 97 | text = "Save terrain from selected tiles as a user template. 98 | 99 | Choose to save in a project-specific or shared folder. Or set a custom location in Project Settings -> Addons -> TileBitTools." 100 | horizontal_alignment = 1 101 | vertical_alignment = 1 102 | autowrap_mode = 2 103 | 104 | [node name="SaveButton" type="Button" parent="MarginContainer2/VBoxContainer" groups=["TBTTextButton"]] 105 | unique_name_in_owner = true 106 | layout_mode = 2 107 | text = "Save Template" 108 | 109 | [connection signal="pressed" from="MarginContainer2/VBoxContainer/SaveButton" to="." method="_on_save_button_pressed"] 110 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_inspector/tool_buttons/tool_buttons.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Control 3 | 4 | const TERRAIN_POPUP := "TerrainPopup" 5 | 6 | const TBTPlugin := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd") 7 | 8 | var EMPTY_TERRAIN_ID := 99 9 | 10 | var fill_menu_items := {} 11 | 12 | var bit_button_bit_id : int 13 | var bit_button_terrain_id : int 14 | 15 | var tbt : TBTPlugin 16 | 17 | 18 | @onready var fill_button: MenuButton = %FillButton 19 | @onready var bit_button: MenuButton = %BitButton 20 | @onready var bit_button_popup := bit_button.get_popup() 21 | @onready var clear_button: Button = %ClearButton 22 | 23 | 24 | func _tbt_ready() -> void: 25 | var _err := tbt.preview_updated.connect(_on_preview_updated) 26 | bit_button_popup.submenu_popup_delay = 0.0 27 | _setup_fill_button() 28 | _update_buttons() 29 | 30 | 31 | func _setup_fill_button() -> void: 32 | var _err := fill_button.get_popup().id_pressed.connect(_on_fill_button_popup_id_pressed) 33 | fill_button.custom_minimum_size.x = fill_button.size.x + 22 34 | _populate_fill_menu() 35 | 36 | 37 | func _update_buttons() -> void: 38 | _update_bit_button() 39 | _update_clear_button() 40 | 41 | 42 | func _update_clear_button() -> void: 43 | if tbt.tiles_manager.has_preview(): 44 | if tbt.tiles_manager.has_preview_terrain_set(): 45 | _enable_clear_button() 46 | else: 47 | _disable_clear_button() 48 | elif tbt.context.bit_data.has_terrain_set(): 49 | _enable_clear_button() 50 | else: 51 | _disable_clear_button() 52 | 53 | 54 | 55 | func _disable_clear_button() -> void: 56 | clear_button.disabled = true 57 | clear_button.tooltip_text = "No terrain data to clear" 58 | 59 | 60 | func _enable_clear_button() -> void: 61 | clear_button.disabled = false 62 | clear_button.tooltip_text = "Clears all terrain data from selected tiles" 63 | 64 | # Creating individual sub-menus, as cannot tell which item is selected in PopupMenu() 65 | # PopupMenu.get_current_index() does not appear to exist in 4.0 66 | # https://github.com/godotengine/godot/pull/38520 67 | func _update_bit_button() -> void: 68 | bit_button_popup.clear() 69 | for child in bit_button_popup.get_children(): 70 | child.queue_free() 71 | 72 | var terrain_bits_list : Array 73 | var terrain_set := tbt.BitData.NULL_TERRAIN_SET 74 | 75 | if tbt.tiles_manager.has_preview_terrain_set(): 76 | terrain_set = tbt.tiles_manager.get_preview_terrain_set() 77 | terrain_bits_list = tbt.tiles_manager.preview_bit_data.get_terrain_bits_list(true) 78 | elif tbt.context.bit_data.has_terrain_set(): 79 | terrain_set = tbt.context.bit_data.terrain_set 80 | terrain_bits_list = tbt.context.bit_data.get_terrain_bits_list(true) 81 | else: 82 | _disable_bit_button() 83 | return 84 | 85 | _enable_bit_button() 86 | 87 | var terrains_item_list := tbt.context.get_terrains_item_list(terrain_set) 88 | 89 | for terrain_bit in terrain_bits_list: 90 | var terrain_bit_name : String = tbt.texts.TERRAIN_BIT_TEXTS[terrain_bit] 91 | bit_button_popup.add_item(terrain_bit_name, terrain_bit) 92 | var idx := bit_button_popup.get_item_index(terrain_bit) 93 | 94 | var terrain_popup_name := _create_terrain_popup(terrain_bit, terrains_item_list) 95 | 96 | bit_button_popup.set_item_submenu(idx, terrain_popup_name) 97 | 98 | 99 | func _disable_bit_button() -> void: 100 | bit_button.disabled = true 101 | bit_button.tooltip_text = "Tiles must have a Terrain Set assigned" 102 | 103 | 104 | func _enable_bit_button() -> void: 105 | bit_button.disabled = false 106 | bit_button.tooltip_text = "Set a single terrain bit in selected tiles" 107 | 108 | 109 | func _create_terrain_popup(terrain_bit : int, item_list : Array) -> String: 110 | var terrain_popup := PopupMenu.new() 111 | terrain_popup.name = TERRAIN_POPUP + str(terrain_bit) 112 | bit_button_popup.add_child(terrain_popup) 113 | var _err := terrain_popup.id_pressed.connect(_on_bit_button_terrain_id_pressed.bind(terrain_bit)) 114 | 115 | for item in item_list: 116 | terrain_popup.add_icon_item(item.icon, item.text, item.id) 117 | terrain_popup.add_item("", EMPTY_TERRAIN_ID) 118 | 119 | return terrain_popup.name 120 | 121 | 122 | 123 | 124 | 125 | 126 | func _populate_fill_menu() -> void: 127 | fill_button.get_popup().clear() 128 | 129 | var terrain_sets_count := tbt.context.tile_set.get_terrain_sets_count() 130 | if terrain_sets_count == 1: 131 | _populate_fill_menu_terrains(0) 132 | else: 133 | for i in range(terrain_sets_count): 134 | var id := fill_menu_items.size() 135 | fill_button.get_popup().add_item("Terrain Set [%s]" % i, id) 136 | fill_button.get_popup().set_item_disabled(id, true) 137 | fill_menu_items[id] = null 138 | _populate_fill_menu_terrains(i) 139 | 140 | 141 | func _populate_fill_menu_terrains(terrain_set : int) -> void: 142 | for i in range(tbt.context.tile_set.get_terrains_count(terrain_set)): 143 | var terrain_name := tbt.context.tile_set.get_terrain_name(terrain_set, i) 144 | var terrain_color := tbt.context.tile_set.get_terrain_color(terrain_set, i) 145 | var icon := _get_icon(terrain_color) 146 | var id := fill_menu_items.size() 147 | fill_button.get_popup().add_icon_item(icon, terrain_name, id) 148 | fill_menu_items[id] = {"terrain_set": terrain_set, "terrain": i} 149 | 150 | 151 | func _get_icon(p_color : Color) -> ImageTexture: 152 | var image := Image.create(16, 16, false, Image.FORMAT_RGB8) 153 | image.fill(p_color) 154 | return ImageTexture.create_from_image(image) 155 | 156 | 157 | 158 | func _on_fill_button_popup_id_pressed(id : int) -> void: 159 | if fill_menu_items[id] == null: 160 | return 161 | var terrain_set : int = fill_menu_items[id].terrain_set 162 | var terrain : int = fill_menu_items[id].terrain 163 | tbt.tiles_manager.fill_terrain(terrain_set, terrain) 164 | 165 | 166 | func _on_bit_button_terrain_id_pressed(terrain_id : int, terrain_bit : int) -> void: 167 | # print("terrain_bit=%s, terrain_id=%s" % [terrain_bit, terrain_id]) 168 | if terrain_id == EMPTY_TERRAIN_ID: 169 | terrain_id = -1 170 | tbt.tiles_manager.set_terrain_bits(terrain_bit, terrain_id) 171 | 172 | 173 | func _on_erase_button_pressed() -> void: 174 | tbt.tiles_manager.erase_terrains() 175 | 176 | func _on_preview_updated(_arg) -> void: 177 | _update_buttons() 178 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_inspector/tool_buttons/tool_buttons.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://b5nbqr8ah0ay2"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/tiles_inspector/tool_buttons/tool_buttons.gd" id="1_8r32g"] 4 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/shared/icon_button.gd" id="3_hprs6"] 5 | 6 | [node name="ToolButtons" type="HFlowContainer"] 7 | anchors_preset = 10 8 | anchor_right = 1.0 9 | offset_bottom = 149.0 10 | grow_horizontal = 2 11 | theme_override_constants/h_separation = 16 12 | alignment = 1 13 | script = ExtResource("1_8r32g") 14 | 15 | [node name="FillButton" type="MenuButton" parent="." groups=["TBTToolButton"]] 16 | unique_name_in_owner = true 17 | custom_minimum_size = Vector2(123, 0) 18 | layout_mode = 2 19 | size_flags_horizontal = 4 20 | tooltip_text = "Fill all terrain bits in selected tiles" 21 | focus_mode = 2 22 | text = "Fill" 23 | flat = false 24 | alignment = 0 25 | script = ExtResource("3_hprs6") 26 | editor_name = "Bucket" 27 | 28 | [node name="BitButton" type="MenuButton" parent="." groups=["TBTToolButton"]] 29 | unique_name_in_owner = true 30 | custom_minimum_size = Vector2(123, 0) 31 | layout_mode = 2 32 | size_flags_horizontal = 4 33 | tooltip_text = "Fill all terrain bits with a single terrain" 34 | focus_mode = 2 35 | text = "Set Bits" 36 | flat = false 37 | alignment = 0 38 | script = ExtResource("3_hprs6") 39 | editor_name = "EditKey" 40 | 41 | [node name="ClearButton" type="Button" parent="." groups=["TBTToolButton"]] 42 | unique_name_in_owner = true 43 | layout_mode = 2 44 | size_flags_horizontal = 4 45 | size_flags_vertical = 0 46 | text = "Clear" 47 | alignment = 0 48 | script = ExtResource("3_hprs6") 49 | editor_name = "Clear" 50 | 51 | [connection signal="pressed" from="ClearButton" to="." method="_on_erase_button_pressed"] 52 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_preview/terrain_opacity_slider.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends HSlider 3 | 4 | 5 | # exported nodes do not work in tool mode 6 | #@onready var current_bit_data_draw: Control = %CurrentBitDataDraw 7 | #@onready var preview_bit_data_draw: Control = %PreviewBitDataDraw 8 | # 9 | # 10 | # 11 | #func _ready() -> void: 12 | # value_changed.connect(_on_value_changed) 13 | # _update() 14 | # 15 | # 16 | #func _update() -> void: 17 | # _update_draw_layer_opacity(current_bit_data_draw) 18 | # _update_draw_layer_opacity(preview_bit_data_draw) 19 | # 20 | # 21 | #func _update_draw_layer_opacity(control : Control) -> void: 22 | # control.modulate.a = value 23 | # control.get_viewport().render_target_update_mode = SubViewport.UPDATE_ONCE 24 | # 25 | #func _on_value_changed(value : float) -> void: 26 | # _update() 27 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_preview/tiles_preview.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Control 3 | 4 | const EMPTY_RECT := Rect2i(0,0,0,0) 5 | const FRAME_0 := 0 6 | 7 | const BACK_PANEL_MARGIN := 12 8 | 9 | const DEFAULT_TERRAIN_OPACITY := 0.75 10 | 11 | const TBTPlugin := preload("res://addons/tile_bit_tools/controls/tbt_plugin_control/tbt_plugin_control.gd") 12 | 13 | 14 | var tbt : TBTPlugin 15 | 16 | var preview_bit_data : TBTPlugin.EditorBitData 17 | 18 | var tile_rects : Dictionary 19 | 20 | var base_image : Image 21 | 22 | var image_crop_rect : Rect2i 23 | 24 | var initial_height : float 25 | 26 | var expanded := true 27 | 28 | 29 | @onready var back_panel: Panel = %BackPanel 30 | @onready var v_split_container: VSplitContainer = %VSplitContainer 31 | @onready var front_container: Container = %FrontContainer 32 | 33 | 34 | @onready var collapsed_controls: Control = %CollapsedControls 35 | @onready var expanded_controls: Control = %ExpandedControls 36 | 37 | 38 | @onready var preview_container: Container = %PreviewContainer 39 | 40 | @onready var current_tiles_view: Container = %CurrentTilesView 41 | @onready var preview_tiles_view: Container = %PreviewTilesView 42 | @onready var terrain_opacity_slider: HSlider = %TerrainOpacitySlider 43 | 44 | @onready var placeholder_label: RichTextLabel = %PlaceholderLabel 45 | 46 | @onready var reset_button: Button = %ResetButton 47 | @onready var apply_button: Button = %ApplyButton 48 | 49 | var ready_complete := false 50 | 51 | 52 | 53 | func _ready() -> void: 54 | ready_complete = true 55 | var _err := front_container.resized.connect(_on_front_container_resized) 56 | _connect_opacity_slider() 57 | _toggle_expanded_state(true) 58 | 59 | 60 | func _tbt_ready() -> void: 61 | var _err := tbt.tiles_inspector_added.connect(_on_tiles_inspector_added) 62 | _err = tbt.tiles_inspector_removed.connect(_on_tiles_inspector_removed) 63 | _err = tbt.preview_updated.connect(_on_preview_updated) 64 | _err = tbt.tiles_preview_expand_requested.connect(_on_tiles_preview_expand_requested) 65 | _err = tbt.tiles_preview_collapse_requested.connect(_on_tiles_preview_collapse_requested) 66 | 67 | 68 | 69 | # returns control to determine if mouse click is 70 | # in panel 71 | func get_mouse_input_rect() -> Rect2: 72 | var rect := back_panel.get_global_rect() 73 | rect.position = rect.position - Vector2(0, 6) 74 | rect.size = rect.size + Vector2(0, 6) 75 | return rect 76 | 77 | 78 | func _setup_textures() -> void: 79 | _update_base_texture() 80 | _update_current_terrain() 81 | _update_preview_terrain() 82 | 83 | 84 | func _clear_data() -> void: 85 | base_image = null 86 | preview_bit_data = null 87 | tile_rects.clear() 88 | image_crop_rect = EMPTY_RECT 89 | _update_preview_terrain() 90 | 91 | 92 | func _update_base_texture() -> void: 93 | _update_image_crop_rect() 94 | _create_base_image() 95 | var texture := ImageTexture.create_from_image(base_image) 96 | current_tiles_view.setup_base_tiles(texture, image_crop_rect.size) 97 | preview_tiles_view.setup_base_tiles(texture, image_crop_rect.size) 98 | 99 | 100 | func _connect_opacity_slider() -> void: 101 | var _err := terrain_opacity_slider.value_changed.connect(current_tiles_view.set_terrain_overlay_opacity) 102 | _err = terrain_opacity_slider.value_changed.connect(preview_tiles_view.set_terrain_overlay_opacity) 103 | terrain_opacity_slider.value = DEFAULT_TERRAIN_OPACITY 104 | terrain_opacity_slider.value_changed.emit(DEFAULT_TERRAIN_OPACITY) 105 | 106 | 107 | func _update_current_terrain() -> void: 108 | current_tiles_view.set_bit_data(tbt.context.bit_data) 109 | 110 | 111 | 112 | func _update_preview_terrain() -> void: 113 | if preview_bit_data: 114 | placeholder_label.hide() 115 | reset_button.show() 116 | preview_tiles_view.show() 117 | preview_tiles_view.set_bit_data(preview_bit_data) 118 | reset_button.disabled = false 119 | apply_button.disabled = false 120 | 121 | else: 122 | placeholder_label.show() 123 | reset_button.hide() 124 | preview_tiles_view.hide() 125 | preview_tiles_view.set_bit_data(null) 126 | reset_button.disabled = true 127 | apply_button.disabled = true 128 | 129 | 130 | 131 | 132 | func _create_base_image() -> void: 133 | var texture := tbt.context.source.texture 134 | base_image = texture.get_image().get_region(image_crop_rect) 135 | 136 | 137 | func _update_image_crop_rect() -> void: 138 | image_crop_rect = EMPTY_RECT 139 | 140 | for coords in tbt.context.tiles.keys(): 141 | var rect := tbt.context.source.get_tile_texture_region(coords, FRAME_0) 142 | if image_crop_rect == EMPTY_RECT: 143 | image_crop_rect = rect 144 | else: 145 | image_crop_rect = image_crop_rect.merge(rect) 146 | 147 | 148 | func _on_preview_updated(bit_data : TBTPlugin.EditorBitData) -> void: 149 | preview_bit_data = bit_data 150 | _update_preview_terrain() 151 | 152 | 153 | func _on_front_container_resized() -> void: 154 | var front_height := front_container.size.y 155 | back_panel.custom_minimum_size.y = front_height + BACK_PANEL_MARGIN 156 | 157 | 158 | func _toggle_expanded_state(p_expanded : bool) -> void: 159 | if expanded == p_expanded: 160 | return 161 | expanded = p_expanded 162 | expanded_controls.visible = expanded 163 | collapsed_controls.visible = !expanded 164 | 165 | 166 | func _on_reset_button_pressed() -> void: 167 | tbt.reset_requested.emit() 168 | 169 | 170 | func _on_apply_button_pressed() -> void: 171 | tbt.apply_changes_requested.emit() 172 | 173 | 174 | func _on_expand_button_pressed() -> void: 175 | _toggle_expanded_state(true) 176 | 177 | 178 | func _on_collapse_button_pressed() -> void: 179 | _toggle_expanded_state(false) 180 | 181 | 182 | func _on_tiles_inspector_added() -> void: 183 | show() 184 | _setup_textures() 185 | 186 | 187 | func _on_tiles_inspector_removed() -> void: 188 | hide() 189 | _clear_data() 190 | 191 | 192 | func _on_tiles_preview_expand_requested() -> void: 193 | _toggle_expanded_state(true) 194 | 195 | 196 | func _on_tiles_preview_collapse_requested() -> void: 197 | _toggle_expanded_state(false) 198 | 199 | 200 | func _on_front_panel_collapsed_gui_input(event: InputEvent) -> void: 201 | if not event is InputEventMouseButton or !event.is_pressed(): 202 | return 203 | if event.button_index != MOUSE_BUTTON_LEFT: 204 | return 205 | _toggle_expanded_state(true) 206 | 207 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_preview/tiles_view.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Control 3 | 4 | 5 | 6 | const BitData := preload("res://addons/tile_bit_tools/core/bit_data.gd") 7 | 8 | var upscale_by_max_size := { 9 | 250 : 8, 10 | 500 : 4, 11 | 1000 : 2, 12 | } 13 | 14 | 15 | 16 | @onready var base_tiles_rect: TextureRect = %BaseTilesRect 17 | @onready var terrain_viewport: SubViewport = %TerrainViewport 18 | @onready var bit_data_draw: Control = %BitDataDraw 19 | @onready var terrain_overlay_rect: TextureRect = %TerrainOverlayRect 20 | 21 | 22 | func _ready() -> void: 23 | terrain_overlay_rect.texture = terrain_viewport.get_texture() 24 | 25 | 26 | func setup_base_tiles(texture : Texture2D, base_size : Vector2i) -> void: 27 | base_tiles_rect.texture = texture 28 | var upscale_factor := _get_upscale_factor(base_size) 29 | terrain_viewport.size = base_size * upscale_factor 30 | bit_data_draw.draw_size = base_size * upscale_factor 31 | 32 | 33 | func set_terrain_overlay_opacity(value : float) -> void: 34 | terrain_overlay_rect.modulate.a = value 35 | 36 | 37 | func set_bit_data(bit_data : BitData) -> void: 38 | bit_data_draw.bit_data = bit_data 39 | terrain_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE 40 | 41 | 42 | func _get_upscale_factor(base_size : Vector2i) -> int: 43 | for max_size in upscale_by_max_size.keys(): 44 | if base_size.x <= max_size && base_size.y <= max_size: 45 | return upscale_by_max_size[max_size] 46 | return 1 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/controls/tiles_preview/tiles_view.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://rt2lmg0mecbq"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/tiles_preview/tiles_view.gd" id="1_7t85k"] 4 | [ext_resource type="Script" path="res://addons/tile_bit_tools/controls/bit_data_draw/bit_data_draw.gd" id="1_kiwr1"] 5 | 6 | [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_d1ndd"] 7 | 8 | [node name="TilesView" type="PanelContainer"] 9 | size_flags_horizontal = 3 10 | theme_override_styles/panel = SubResource("StyleBoxEmpty_d1ndd") 11 | script = ExtResource("1_7t85k") 12 | 13 | [node name="TerrainViewport" type="SubViewport" parent="."] 14 | unique_name_in_owner = true 15 | disable_3d = true 16 | transparent_bg = true 17 | gui_snap_controls_to_pixels = false 18 | size = Vector2i(300, 300) 19 | render_target_update_mode = 1 20 | 21 | [node name="BitDataDraw" type="Control" parent="TerrainViewport"] 22 | unique_name_in_owner = true 23 | layout_mode = 3 24 | anchors_preset = 15 25 | anchor_right = 1.0 26 | anchor_bottom = 1.0 27 | grow_horizontal = 2 28 | grow_vertical = 2 29 | script = ExtResource("1_kiwr1") 30 | 31 | [node name="BaseTilesRect" type="TextureRect" parent="."] 32 | unique_name_in_owner = true 33 | layout_mode = 2 34 | stretch_mode = 5 35 | 36 | [node name="TerrainOverlayRect" type="TextureRect" parent="."] 37 | unique_name_in_owner = true 38 | layout_mode = 2 39 | expand_mode = 1 40 | stretch_mode = 5 41 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/core/bit_data.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Resource 3 | 4 | 5 | ## Redefined CellNeighbors enum to include center tile (terrain). 6 | ## Allows tile.terrain not to require separate logic when iterating. 7 | enum TerrainBits { 8 | CENTER=99, 9 | TOP_LEFT_CORNER=TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER, 10 | TOP_SIDE=TileSet.CELL_NEIGHBOR_TOP_SIDE, 11 | TOP_RIGHT_CORNER=TileSet.CELL_NEIGHBOR_TOP_RIGHT_CORNER, 12 | RIGHT_SIDE=TileSet.CELL_NEIGHBOR_RIGHT_SIDE, 13 | BOTTOM_RIGHT_CORNER=TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, 14 | BOTTOM_SIDE=TileSet.CELL_NEIGHBOR_BOTTOM_SIDE, 15 | BOTTOM_LEFT_CORNER=TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, 16 | LEFT_SIDE=TileSet.CELL_NEIGHBOR_LEFT_SIDE, 17 | } 18 | 19 | const NULL_TERRAIN_INDEX := -1 20 | const NULL_TERRAIN_SET := -1 21 | const NULL_TERRAIN_MODE := -1 22 | 23 | const BitData := preload("res://addons/tile_bit_tools/core/bit_data.gd") 24 | 25 | var CellNeighborsByMode := { 26 | TileSet.TerrainMode.TERRAIN_MODE_MATCH_CORNERS_AND_SIDES: [ 27 | TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER, 28 | TileSet.CELL_NEIGHBOR_TOP_SIDE, 29 | TileSet.CELL_NEIGHBOR_TOP_RIGHT_CORNER, 30 | TileSet.CELL_NEIGHBOR_RIGHT_SIDE, 31 | TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, 32 | TileSet.CELL_NEIGHBOR_BOTTOM_SIDE, 33 | TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, 34 | TileSet.CELL_NEIGHBOR_LEFT_SIDE, 35 | ], 36 | TileSet.TerrainMode.TERRAIN_MODE_MATCH_CORNERS: [ 37 | TileSet.CELL_NEIGHBOR_TOP_LEFT_CORNER, 38 | TileSet.CELL_NEIGHBOR_TOP_RIGHT_CORNER, 39 | TileSet.CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, 40 | TileSet.CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, 41 | ], 42 | TileSet.TerrainMode.TERRAIN_MODE_MATCH_SIDES: [ 43 | TileSet.CELL_NEIGHBOR_TOP_SIDE, 44 | TileSet.CELL_NEIGHBOR_RIGHT_SIDE, 45 | TileSet.CELL_NEIGHBOR_BOTTOM_SIDE, 46 | TileSet.CELL_NEIGHBOR_LEFT_SIDE, 47 | ], 48 | } 49 | 50 | 51 | enum _TileKeys {TERRAIN, PEERING_BITS} 52 | 53 | 54 | # _tiles[coords : Vector2i][_TileKey] 55 | # TERRAIN = terrain_index 56 | # PEERING_BITS = Dictionary of {CellNeighbors : terrain_index} 57 | @export var _tiles := {} 58 | 59 | @export var terrain_set := NULL_TERRAIN_SET 60 | @export var terrain_mode := TileSet.TerrainMode.TERRAIN_MODE_MATCH_CORNERS_AND_SIDES 61 | 62 | 63 | 64 | # ITERATOR HELPERS 65 | 66 | # returns terrain bits in terrain_set's mode 67 | # center bit, if requested, will be listed first 68 | func get_terrain_bits_list(include_center_bit := false) -> Array: 69 | var list : Array = CellNeighborsByMode[terrain_mode].duplicate() 70 | if include_center_bit: 71 | list.push_front(TerrainBits.CENTER) 72 | return list 73 | 74 | # returns all terrain bits regardless of terrain_set's mode 75 | func get_all_terrain_bits(include_center_bit := false) -> Array: 76 | var list : Array = CellNeighborsByMode[TileSet.TerrainMode.TERRAIN_MODE_MATCH_CORNERS_AND_SIDES].duplicate() 77 | if include_center_bit: 78 | list.push_front(TerrainBits.CENTER) 79 | return list 80 | 81 | 82 | func get_coordinates_list() -> Array: 83 | return _tiles.keys() 84 | 85 | 86 | # RESOURCE DATA 87 | func has_data() -> bool: 88 | return get_tile_count() > 0 89 | 90 | func has_terrain_set() -> bool: 91 | return terrain_set != NULL_TERRAIN_SET 92 | 93 | 94 | func has_tile(coords : Vector2i) -> bool: 95 | return _tiles.has(coords) 96 | 97 | 98 | func get_tile_count() -> int: 99 | return _tiles.size() 100 | 101 | 102 | func get_terrain_count() -> int: 103 | return get_terrains().size() 104 | 105 | 106 | func get_terrains() -> Array: 107 | var terrains := [] 108 | for coords in get_coordinates_list(): 109 | for bit in get_terrain_bits_list(true): 110 | var terrain_index := get_bit_terrain(coords, bit) 111 | if !terrains.has(terrain_index): 112 | terrains.append(terrain_index) 113 | return terrains 114 | 115 | 116 | # ATLAS RECT 117 | func get_atlas_offset() -> Vector2i: 118 | return get_atlas_rect().position 119 | 120 | 121 | func get_atlas_rect() -> Rect2i: 122 | return _get_rect_from_points(_tiles.keys()) 123 | 124 | 125 | func _get_rect_from_points(p_points : Array) -> Rect2i: 126 | var points := p_points.duplicate(true) 127 | points.sort_custom(func(a,b): return a.x < b.x) 128 | var x : int = points.front().x 129 | var width : int = points.back().x + 1 - x 130 | points.sort_custom(func(a,b): return a.y < b.y) 131 | var y : int = points.front().y 132 | var height : int = points.back().y + 1 - y 133 | return Rect2i(x,y,width,height) 134 | 135 | 136 | 137 | # TERRAIN DATA 138 | func set_tile_terrain(coords : Vector2i, terrain_index : int) -> void: 139 | _tiles[coords][_TileKeys.TERRAIN] = terrain_index 140 | 141 | 142 | func get_tile_terrain(coords : Vector2i) -> int: 143 | return _tiles[coords].get(_TileKeys.TERRAIN, NULL_TERRAIN_INDEX) 144 | 145 | 146 | func set_bit_terrain(coords : Vector2i, bit : TerrainBits, terrain_index : int) -> void: 147 | if bit == TerrainBits.CENTER: 148 | set_tile_terrain(coords, terrain_index) 149 | return 150 | _tiles[coords][_TileKeys.PEERING_BITS][bit] = terrain_index 151 | 152 | 153 | func get_bit_terrain(coords : Vector2i, bit : TerrainBits) -> int: 154 | if bit == TerrainBits.CENTER: 155 | return get_tile_terrain(coords) 156 | return _tiles[coords][_TileKeys.PEERING_BITS].get(bit, NULL_TERRAIN_INDEX) 157 | 158 | 159 | func get_bit_color(coords : Vector2i, bit : TerrainBits) -> Color: 160 | var terrain_index := get_bit_terrain(coords, bit) 161 | return get_terrain_color(terrain_index) 162 | 163 | 164 | func get_terrain_color(_terrain_index : int) -> Color: 165 | # override this function 166 | return Color.BLACK 167 | 168 | 169 | func get_terrain_colors_dict() -> Dictionary: 170 | var d := {} 171 | for i in get_terrains(): 172 | d[i] = get_terrain_color(i) 173 | return d 174 | 175 | 176 | 177 | func _add_tile(coords : Vector2i, terrain_index := NULL_TERRAIN_INDEX) -> void: 178 | assert(!_tiles.has(coords)) 179 | 180 | _tiles[coords] = { 181 | _TileKeys.TERRAIN: terrain_index, 182 | _TileKeys.PEERING_BITS: {}, 183 | } 184 | 185 | 186 | # TERRAIN MANIPULATION 187 | 188 | ## Sets all tiles' terrain and peering_bits to terrain_index 189 | func fill_all_tile_terrains(p_terrain_set : int, p_terrain_mode : TileSet.TerrainMode, terrain_index : int) -> void: 190 | terrain_set = p_terrain_set 191 | terrain_mode = p_terrain_mode 192 | 193 | for coords in get_coordinates_list(): 194 | fill_tile_terrains(coords, terrain_index) 195 | 196 | 197 | ## Sets a single tile's terrain and peering_bits to terrain_index 198 | func fill_tile_terrains(coords : Vector2i, terrain_index : int) -> void: 199 | _clear_tile_peering_bits(coords) 200 | set_tile_terrain(coords, terrain_index) 201 | 202 | if terrain_index == NULL_TERRAIN_INDEX: 203 | return 204 | 205 | for bit in get_terrain_bits_list(): 206 | set_bit_terrain(coords, bit, terrain_index) 207 | 208 | 209 | func clear_all_tile_terrains() -> void: 210 | terrain_set = NULL_TERRAIN_SET 211 | # do not need to clear terrain_mode as it is only used internally 212 | 213 | for coords in get_coordinates_list(): 214 | clear_tile_terrains(coords) 215 | 216 | 217 | func clear_tile_terrains(coords : Vector2i) -> void: 218 | set_tile_terrain(coords, NULL_TERRAIN_INDEX) 219 | _clear_tile_peering_bits(coords) 220 | 221 | 222 | func replace_all_tile_terrains(old_terrain_index : int, new_terrain_index : int) -> void: 223 | for coords in get_coordinates_list(): 224 | replace_tile_terrains(coords, old_terrain_index, new_terrain_index) 225 | 226 | 227 | func replace_tile_terrains(coords : Vector2i, old_terrain_index : int, new_terrain_index : int) -> void: 228 | # includes tile.terrain 229 | for bit in get_terrain_bits_list(true): 230 | if get_bit_terrain(coords, bit) == old_terrain_index: 231 | set_bit_terrain(coords, bit, new_terrain_index) 232 | 233 | 234 | func set_all_bit_terrains(terrain_bit : TerrainBits, terrain_index : int) -> void: 235 | for coords in get_coordinates_list(): 236 | set_bit_terrain(coords, terrain_bit, terrain_index) 237 | 238 | 239 | 240 | func _clear_tile_peering_bits(coords : Vector2i) -> void: 241 | _tiles[coords][_TileKeys.PEERING_BITS].clear() 242 | 243 | 244 | 245 | 246 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/core/context.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Node 3 | 4 | 5 | const G := preload("res://addons/tile_bit_tools/core/globals.gd") 6 | const EditorBitData := preload("res://addons/tile_bit_tools/core/editor_bit_data.gd") 7 | 8 | const Icons := preload("res://addons/tile_bit_tools/core/icons.gd") 9 | 10 | #var _print := preload("res://addons/tile_bit_tools/core/output.gd").new() 11 | var texts := preload("res://addons/tile_bit_tools/core/texts.gd").new() 12 | 13 | 14 | var tile_set : TileSet 15 | var source : TileSetAtlasSource 16 | var tiles : Dictionary 17 | 18 | var bit_data : EditorBitData 19 | 20 | var base_control : Control 21 | 22 | # {index : terrain_mode} 23 | var terrain_sets := {} 24 | 25 | # {terrain_set : [{ 26 | # "index": terrain_index, 27 | # "color": color, 28 | # "name": name, 29 | #},...]} 30 | var terrains_by_set := {} 31 | 32 | var ready_complete := false 33 | 34 | 35 | func _ready() -> void: 36 | ready_complete = true 37 | 38 | 39 | func finish_setup() -> G.Errors: 40 | var validate_result := validate() 41 | if validate_result != OK: 42 | return validate_result 43 | 44 | bit_data = EditorBitData.new() 45 | var bit_data_result := bit_data.load_from_tile_data(tiles, tile_set) 46 | if bit_data_result != OK: 47 | return bit_data_result 48 | 49 | _populate_terrain_sets() 50 | _populate_terrains() 51 | 52 | return G.Errors.OK 53 | 54 | 55 | func validate() -> G.Errors: 56 | if tile_set.tile_shape != TileSet.TILE_SHAPE_SQUARE: 57 | return G.Errors.UNSUPPORTED_SHAPE 58 | return G.Errors.OK 59 | 60 | 61 | func _populate_terrain_sets() -> void: 62 | for i in range(tile_set.get_terrain_sets_count()): 63 | terrain_sets[i] = tile_set.get_terrain_set_mode(i) 64 | 65 | 66 | func _populate_terrains() -> void: 67 | for terrain_set in terrain_sets.keys(): 68 | terrains_by_set[terrain_set] = [] 69 | for i in range(tile_set.get_terrains_count(terrain_set)): 70 | terrains_by_set[terrain_set].append({ 71 | "id": i, 72 | "text": tile_set.get_terrain_name(terrain_set, i), 73 | "color": tile_set.get_terrain_color(terrain_set, i), 74 | "icon": get_terrain_icon(tile_set.get_terrain_color(terrain_set, i)) 75 | }) 76 | 77 | 78 | func get_terrain_icon(color : Color) -> ImageTexture: 79 | var image := Image.create(16, 16, false, Image.FORMAT_RGB8) 80 | image.fill(color) 81 | return ImageTexture.create_from_image(image) 82 | 83 | 84 | func get_terrain_sets() -> Array: 85 | return terrain_sets.keys() 86 | 87 | 88 | func get_terrain_sets_by_mode(terrain_mode : TileSet.TerrainMode) -> Array: 89 | var terrain_set_list := [] 90 | for index in terrain_sets.keys(): 91 | if terrain_sets[index] == terrain_mode: 92 | terrain_set_list.append(index) 93 | return terrain_set_list 94 | 95 | 96 | func get_terrain_sets_item_list(terrain_mode : TileSet.TerrainMode) -> Array: 97 | var icons := Icons.new(base_control) 98 | 99 | var terrain_set_list := [] 100 | for index in terrain_sets.keys(): 101 | if terrain_sets[index] == terrain_mode: 102 | terrain_set_list.append({ 103 | "id": index, 104 | "terrain_mode": terrain_mode, 105 | "text": "[%s]" % [index], 106 | "icon": icons.get_icon(icons.TERRAIN_MODE_ICONS[terrain_mode]), 107 | }) 108 | return terrain_set_list 109 | 110 | 111 | func get_terrains_item_list(terrain_set : int) -> Array: 112 | return terrains_by_set[terrain_set] 113 | 114 | 115 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/core/editor_bit_data.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends "res://addons/tile_bit_tools/core/bit_data.gd" 3 | 4 | const G := preload("res://addons/tile_bit_tools/core/globals.gd") 5 | const TemplateBitData := preload("res://addons/tile_bit_tools/core/template_bit_data.gd") 6 | const EditorBitData := preload("res://addons/tile_bit_tools/core/editor_bit_data.gd") 7 | 8 | var output := preload("res://addons/tile_bit_tools/core/output.gd").new() 9 | 10 | var tile_set : TileSet 11 | 12 | 13 | 14 | func make_copy() -> EditorBitData: 15 | var bit_data_copy := self.duplicate() 16 | bit_data_copy._tiles = _tiles.duplicate(true) 17 | bit_data_copy.tile_set = tile_set 18 | return bit_data_copy 19 | 20 | 21 | 22 | 23 | 24 | func get_terrain_color(terrain_index : int) -> Color: 25 | if terrain_index == NULL_TERRAIN_INDEX: 26 | return Color.TRANSPARENT 27 | return tile_set.get_terrain_color(terrain_set, terrain_index) 28 | 29 | 30 | 31 | 32 | ## terrain_mapping: {template terrain_index : tile_set terrain_index} 33 | func apply_template_bit_data(template_bit_data : TemplateBitData, p_terrain_set : int, terrain_mapping : Dictionary) -> G.Errors: 34 | clear_all_tile_terrains() 35 | 36 | terrain_set = p_terrain_set 37 | terrain_mode = template_bit_data.terrain_mode 38 | if tile_set.get_terrain_set_mode(terrain_set) != terrain_mode: 39 | return G.Errors.FAILED 40 | 41 | var offset := get_atlas_offset() 42 | 43 | for coords in get_coordinates_list(): 44 | var template_coords = coords - offset 45 | if !template_bit_data.has_tile(template_coords): 46 | continue 47 | for bit in get_terrain_bits_list(true): 48 | var template_terrain_index := template_bit_data.get_bit_terrain(template_coords, bit) 49 | var terrain_index = terrain_mapping[template_terrain_index] 50 | set_bit_terrain(coords, bit, terrain_index) 51 | 52 | return G.Errors.OK 53 | 54 | 55 | 56 | ## Loads TileData into BitData resource; 57 | ## p_tiles = Dict{coords : TileData} 58 | func load_from_tile_data(p_tiles : Dictionary, p_tile_set : TileSet) -> G.Errors: 59 | # only allow loading into empty resource 60 | if has_data(): 61 | return G.Errors.FAILED 62 | if p_tiles.size() == 0: 63 | return G.Errors.MISSING_TILES 64 | 65 | tile_set = p_tile_set 66 | 67 | var result := _load_tiles(p_tiles) 68 | if terrain_set == NULL_TERRAIN_SET: 69 | # if there is only one terrain set, assign it to the bit data 70 | # this allows using the set bits button on tiles that don't have 71 | # any terrain data yet 72 | var terrain_sets_count := tile_set.get_terrain_sets_count() 73 | if terrain_sets_count == 1: 74 | terrain_set = 0 75 | 76 | return result 77 | 78 | 79 | func _load_tiles(p_tiles : Dictionary) -> G.Errors: 80 | # output.user("Fetching current terrain bits now. Unassigned terrain bits will result in [i]Condition '!is_valid_terrain_peering_bit(p_peering_bit)' is true.[/i] Please ignore.") 81 | 82 | for coords in p_tiles.keys(): 83 | _add_tile(coords) 84 | 85 | var tile_data : TileData = p_tiles[coords] 86 | if tile_data.terrain_set == NULL_TERRAIN_SET: 87 | # editor does not allow adding terrain without terrain set 88 | # so TileData will not have any terrain data 89 | continue 90 | 91 | var error := _load_terrain_set(tile_data) 92 | if error: 93 | return error 94 | 95 | _load_terrain(coords, tile_data) 96 | 97 | return G.Errors.OK 98 | 99 | 100 | func _load_terrain_set(tile_data : TileData) -> G.Errors: 101 | # output.debug("terrain_set=%s, TileData terrain_set=%s" % [terrain_set, tile_data.terrain_set]) 102 | 103 | if terrain_set == NULL_TERRAIN_SET: 104 | terrain_set = tile_data.terrain_set 105 | # output.debug("Assigning terrain_set => %s" % tile_data.terrain_set) 106 | terrain_mode = tile_set.get_terrain_set_mode(terrain_set) 107 | return G.Errors.OK 108 | 109 | if terrain_set != tile_data.terrain_set: 110 | # output.debug("Multiple terrain sets") 111 | return G.Errors.MULTIPLE_TERRAIN_SETS 112 | 113 | return G.Errors.OK 114 | 115 | 116 | # if a peering bit is unset, get_terrain_peering_bit() sometimes causes error spam 117 | # of "!is_valid_terrain_peering_bit(p_peering_bit)" is true" 118 | func _load_terrain(coords : Vector2i, tile_data : TileData) -> void: 119 | set_tile_terrain(coords, tile_data.terrain) 120 | 121 | for bit in get_terrain_bits_list(): 122 | set_bit_terrain(coords, bit, tile_data.get_terrain_peering_bit(bit)) 123 | 124 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/core/globals.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | 3 | const VERSION := "1.1.0" 4 | 5 | # ------------------------ 6 | # ENUMS 7 | # ------------------------ 8 | 9 | enum Errors { 10 | NULL_ERROR = -999, 11 | OK = Error.OK, 12 | FAILED = Error.FAILED, 13 | MISSING_TILE_SET = 900, 14 | MISSING_SOURCE = 901, 15 | MISSING_TILES = 902, 16 | MISSING_BIT_DATA = 903, 17 | INVALID_TBT_PLUGIN_CONTROL=904, 18 | INVALID_TILES_PREVIEW=905, 19 | MULTIPLE_TERRAIN_SETS = 910, 20 | UNSUPPORTED_SHAPE = 911, 21 | 22 | } 23 | 24 | 25 | enum TemplateTypes {BUILT_IN, USER} 26 | 27 | 28 | 29 | # ------------------------ 30 | # PATHS 31 | # ------------------------ 32 | 33 | const BUILTIN_TEMPLATES_PATH := "res://addons/tile_bit_tools/templates/" 34 | const GODOT_TEMPLATES_FOLDER := "/Godot/tile_bit_tools_templates/" 35 | const PROJECT_TEMPLATES_PATH := "user://addons/tile_bit_tools/templates/" 36 | 37 | # ------------------------ 38 | # USER SETTINGS 39 | # ------------------------ 40 | 41 | const PROJECT_SETTINGS_PATH := "addons/tile_bit_tools/" 42 | 43 | const Settings := { 44 | "user_templates_path": { 45 | "path": PROJECT_SETTINGS_PATH + "paths/user_templates_path", 46 | "default": "", 47 | "type": TYPE_STRING, 48 | "hint_string": PROPERTY_HINT_DIR, 49 | }, 50 | "output_show_user": { 51 | "path": PROJECT_SETTINGS_PATH + "output/show_user_messages", 52 | "default": true, 53 | "type": TYPE_BOOL, 54 | }, 55 | "output_show_info": { 56 | "path": PROJECT_SETTINGS_PATH + "output/show_info_messages", 57 | "default": false, 58 | "type": TYPE_BOOL, 59 | }, 60 | "output_show_debug": { 61 | "path": PROJECT_SETTINGS_PATH + "output/show_debug_messages", 62 | "default": false, 63 | "type": TYPE_BOOL, 64 | }, 65 | # Default colors from the "bright" color scheme at 66 | # https://personal.sron.nl/~pault/ 67 | "colors_terrain_01": { 68 | "path": PROJECT_SETTINGS_PATH + "colors/template_terrain_1", 69 | "default": Color("AA3377"), # pink 70 | "type": TYPE_COLOR, 71 | }, 72 | "colors_terrain_02": { 73 | "path": PROJECT_SETTINGS_PATH + "colors/template_terrain_2", 74 | "default": Color("CCBB44"), # yellow 75 | "type": TYPE_COLOR, 76 | }, 77 | "colors_terrain_03": { 78 | "path": PROJECT_SETTINGS_PATH + "colors/template_terrain_3", 79 | "default": Color("228833"), # green 80 | "type": TYPE_COLOR, 81 | }, 82 | "colors_terrain_04": { 83 | "path": PROJECT_SETTINGS_PATH + "colors/template_terrain_4", 84 | "default": Color("66ccee"), # cyan 85 | "type": TYPE_COLOR, 86 | }, 87 | } 88 | 89 | 90 | # ------------------------ 91 | # GROUPS 92 | # ------------------------ 93 | 94 | 95 | const GROUP_DYNAMIC_CONTAINER := "TBTDynamicContainer" 96 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/core/icons.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | 3 | 4 | const TERRAIN_MODE_ICONS := { 5 | TileSet.TerrainMode.TERRAIN_MODE_MATCH_CORNERS_AND_SIDES : ["TerrainMatchCornersAndSides", "EditorIcons"], 6 | TileSet.TerrainMode.TERRAIN_MODE_MATCH_CORNERS : ["TerrainMatchCorners", "EditorIcons"], 7 | TileSet.TerrainMode.TERRAIN_MODE_MATCH_SIDES : ["TerrainMatchSides", "EditorIcons"], 8 | } 9 | 10 | const EXPAND_PANEL := ["ExpandTree", "EditorIcons"] 11 | const COLLAPSE_PANEL := ["CollapseTree", "EditorIcons"] 12 | 13 | const ARROW_EXPANDED := ["arrow", "Tree"] 14 | const ARROW_COLLAPSED := ["arrow_collapsed", "Tree"] 15 | 16 | 17 | var control : Control 18 | 19 | func _init(p_control : Control) -> void: 20 | control = p_control 21 | 22 | 23 | func get_icon(icon_data : Array) -> Texture2D: 24 | return control.get_theme_icon(icon_data[0], icon_data[1]) 25 | 26 | 27 | func get_icon_by_name(icon_name : String) -> Texture2D: 28 | var icon_data = get(icon_name) 29 | if icon_data == null: 30 | return null 31 | return get_icon(icon_data) 32 | 33 | 34 | func get_icon_by_editor_name(editor_name : String) -> Texture2D: 35 | return control.get_theme_icon(editor_name, "EditorIcons") 36 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/core/output.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | 3 | 4 | 5 | enum MessageTypes {USER, INFO, DEBUG} 6 | 7 | 8 | const OUTPUT_TEMPLATE := "[color=palegreen]TileBitTools:[/color] %s" 9 | const COLOR_TEMPLATE := "[color={color}]{msg}[/color]" 10 | 11 | const ERROR_TEXT_TEMPLATE := "{error_text} (ERR {error})" 12 | const ERROR_CODE_TEMPLATE := "(ERR {error})" 13 | const OK_CODE_TEMPLATE := "(OK)" 14 | 15 | const MESSAGE_ERROR_TEMPLATE := "{msg} ({error_string})" 16 | 17 | const DEFAULT_ERROR_TEXT := "An error occurred" 18 | const ERROR_COLOR := "salmon" 19 | const WARNING_COLOR := "yellow" 20 | const OK_COLOR := "palegreen" 21 | 22 | const G := preload("res://addons/tile_bit_tools/core/globals.gd") 23 | 24 | var texts := preload("res://addons/tile_bit_tools/core/texts.gd").new() 25 | 26 | 27 | var message_type_settings := { 28 | MessageTypes.USER: G.Settings.output_show_user.path, 29 | MessageTypes.INFO: G.Settings.output_show_info.path, 30 | MessageTypes.DEBUG: G.Settings.output_show_debug.path, 31 | } 32 | 33 | var message_type_text_color := { 34 | MessageTypes.INFO: "webgray", 35 | MessageTypes.DEBUG: "palegoldenrod", 36 | } 37 | 38 | 39 | func user(msg, p_error := G.Errors.NULL_ERROR) -> void: 40 | _print_msg(msg, p_error, MessageTypes.USER) 41 | 42 | 43 | func info(msg : String, p_error := G.Errors.NULL_ERROR) -> void: 44 | _print_msg(msg, p_error, MessageTypes.INFO) 45 | 46 | 47 | func debug(msg : String, p_error := G.Errors.NULL_ERROR) -> void: 48 | _print_msg(msg, p_error, MessageTypes.DEBUG) 49 | 50 | 51 | func error(msg : String, p_error : int = G.Errors.NULL_ERROR) -> void: 52 | if p_error != G.Errors.NULL_ERROR: 53 | msg = ERROR_TEXT_TEMPLATE.format({ 54 | "error_text": msg, 55 | "error": error, 56 | }) 57 | msg = COLOR_TEMPLATE.format({ 58 | "color": ERROR_COLOR, 59 | "msg": msg, 60 | }) 61 | 62 | _print_msg(msg, G.Errors.NULL_ERROR, MessageTypes.DEBUG) 63 | 64 | 65 | func warning(msg : String, p_error : int = G.Errors.NULL_ERROR) -> void: 66 | if p_error != G.Errors.NULL_ERROR: 67 | msg = ERROR_TEXT_TEMPLATE.format({ 68 | "error_text": msg, 69 | "error": error, 70 | }) 71 | msg = COLOR_TEMPLATE.format({ 72 | "color": WARNING_COLOR, 73 | "msg": msg, 74 | }) 75 | 76 | _print_msg(msg, G.Errors.NULL_ERROR, MessageTypes.DEBUG) 77 | 78 | 79 | 80 | 81 | 82 | func _print_msg(msg, p_error : G.Errors, msg_type : MessageTypes) -> void: 83 | if !_is_message_type_enabled(msg_type): 84 | return 85 | 86 | if msg is G.Errors: 87 | _print_error(msg, msg_type) 88 | return 89 | 90 | if p_error != G.Errors.NULL_ERROR: 91 | msg = MESSAGE_ERROR_TEMPLATE.format({ 92 | "msg": msg, 93 | "error_string": _get_error_string(p_error, true), 94 | }) 95 | 96 | _format_and_print(msg, msg_type) 97 | 98 | 99 | 100 | func _print_error(p_error : G.Errors, msg_type : MessageTypes) -> void: 101 | var msg := _get_error_string(p_error) 102 | _format_and_print(msg, msg_type) 103 | 104 | 105 | func _format_and_print(msg : String, msg_type : MessageTypes) -> void: 106 | msg = _format_color(msg, msg_type) 107 | print_rich(OUTPUT_TEMPLATE % msg) 108 | 109 | 110 | func _format_color(msg : String, msg_type : MessageTypes) -> String: 111 | if message_type_text_color.has(msg_type): 112 | msg = COLOR_TEMPLATE.format({ 113 | "color": message_type_text_color[msg_type], 114 | "msg": msg, 115 | }) 116 | 117 | return msg 118 | 119 | 120 | func _format_error(msg : String, p_error : G.Errors) -> String: 121 | msg = "TileBitTools: %s" % msg 122 | msg = msg + "(ERR %s)" % p_error if p_error != -1 else msg 123 | return msg 124 | 125 | 126 | func _is_message_type_enabled(msg_type : MessageTypes) -> bool: 127 | var value = ProjectSettings.get_setting(message_type_settings[msg_type]) 128 | if value == null: 129 | return false 130 | return value 131 | 132 | 133 | func _get_error_string(p_error : G.Errors, skip_text_if_null := false) -> String: 134 | var error_text : String 135 | if skip_text_if_null: 136 | error_text = texts.ERROR_TEXTS.get(error, "") 137 | else: 138 | error_text = texts.ERROR_TEXTS.get(error, DEFAULT_ERROR_TEXT) 139 | 140 | var error_template : String 141 | if error_text == "": 142 | error_template = ERROR_CODE_TEMPLATE 143 | else: 144 | error_template = ERROR_TEXT_TEMPLATE 145 | 146 | var s := error_template.format({ 147 | "error_text": error_text, 148 | "error": error, 149 | }) 150 | 151 | var error_color : String 152 | if p_error == G.Errors.OK: 153 | error_color = OK_COLOR 154 | else: 155 | error_color = ERROR_COLOR 156 | 157 | return COLOR_TEMPLATE.format({ 158 | "color": error_color, 159 | "msg": s, 160 | }) 161 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/core/template_bit_data.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends "res://addons/tile_bit_tools/core/bit_data.gd" 3 | 4 | const additional_colors := [Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN, Color.REBECCA_PURPLE, Color.WHEAT, Color.AQUA, Color.SALMON, Color.CHOCOLATE, Color.VIOLET, Color.CADET_BLUE, Color.NAVY_BLUE, Color.MAGENTA, Color.LAWN_GREEN, Color.KHAKI, Color.HOT_PINK, Color.FIREBRICK, Color.DARK_OLIVE_GREEN, Color.SADDLE_BROWN] 5 | 6 | const G := preload("res://addons/tile_bit_tools/core/globals.gd") 7 | const EditorBitData := preload("res://addons/tile_bit_tools/core/editor_bit_data.gd") 8 | const TemplateBitData := preload("res://addons/tile_bit_tools/core/template_bit_data.gd") 9 | 10 | var template_tag_data := preload("res://addons/tile_bit_tools/core/template_tag_data.gd").new() 11 | 12 | @export var version : String 13 | @export var template_name : String 14 | @export var template_description : String 15 | @export var _custom_tags := [] 16 | @export var template_terrain_count : int 17 | @export var example_folder_path : String 18 | 19 | var built_in := false 20 | var preview_texture : Texture2D 21 | 22 | var terrain_colors := { 23 | -1: Color.TRANSPARENT, 24 | 0: ProjectSettings.get_setting(G.Settings.colors_terrain_01.path) if ProjectSettings.get_setting(G.Settings.colors_terrain_01.path) != null else additional_colors[0], 25 | 1: ProjectSettings.get_setting(G.Settings.colors_terrain_02.path) if ProjectSettings.get_setting(G.Settings.colors_terrain_02.path) != null else additional_colors[1], 26 | 2: ProjectSettings.get_setting(G.Settings.colors_terrain_03.path) if ProjectSettings.get_setting(G.Settings.colors_terrain_03.path) != null else additional_colors[2], 27 | 3: ProjectSettings.get_setting(G.Settings.colors_terrain_04.path) if ProjectSettings.get_setting(G.Settings.colors_terrain_04.path) != null else additional_colors[3], 28 | } 29 | 30 | 31 | ## Attempts to return the template terrain color set in Project Settings 32 | ## If index is too high, returns a color constant from additional_colors 33 | func get_terrain_color(terrain_index : int) -> Color: 34 | return terrain_colors.get(terrain_index, additional_colors[terrain_index]) 35 | 36 | 37 | func get_custom_tags() -> Array: 38 | return _custom_tags 39 | 40 | 41 | func load_editor_bit_data(bit_data : EditorBitData) -> G.Errors: 42 | var result := _validate_editor_source(bit_data) 43 | if result != OK: 44 | return result 45 | 46 | # terrain_set will remain NULL_TERRAIN_SET (-1) 47 | terrain_mode = bit_data.terrain_mode 48 | var terrain_mapping := _get_terrain_mapping(bit_data) 49 | _load_tiles(bit_data, terrain_mapping) 50 | 51 | return G.Errors.OK 52 | 53 | 54 | func _validate_editor_source(bit_data : EditorBitData) -> G.Errors: 55 | if has_data(): 56 | return G.Errors.FAILED 57 | if bit_data.get_tile_count() == 0: 58 | return G.Errors.FAILED 59 | if bit_data.get_terrain_count() == 0: 60 | return G.Errors.FAILED 61 | return G.Errors.OK 62 | 63 | 64 | ## Sort terrains by frequency and return mapping 65 | ## with highest frequency = 0 66 | func _get_terrain_mapping(bit_data : EditorBitData) -> Dictionary: 67 | var terrains_by_count := {} 68 | for coords in bit_data.get_coordinates_list(): 69 | var terrain_index : int = bit_data.get_tile_terrain(coords) 70 | if terrains_by_count.has(terrain_index): 71 | terrains_by_count[terrain_index] += 1 72 | else: 73 | terrains_by_count[terrain_index] = 0 74 | var terrains := terrains_by_count.keys().duplicate() 75 | terrains.sort_custom(func(a, b): return terrains_by_count[a] > terrains_by_count[b]) 76 | var terrain_mapping := {} 77 | for i in range(terrains.size()): 78 | terrain_mapping[terrains[i]] = i 79 | return terrain_mapping 80 | 81 | 82 | func _load_tiles(bit_data : EditorBitData, terrain_mapping : Dictionary) -> void: 83 | var offset := bit_data.get_atlas_offset() 84 | var next_template_terrain := terrain_mapping.size() 85 | 86 | for coords in bit_data.get_coordinates_list(): 87 | var template_coords : Vector2i = coords - offset 88 | _add_tile(template_coords) 89 | 90 | for bit in bit_data.get_terrain_bits_list(true): 91 | var editor_terrain := bit_data.get_bit_terrain(coords, bit) 92 | if !terrain_mapping.has(editor_terrain): 93 | var template_terrain := next_template_terrain 94 | terrain_mapping[editor_terrain] = template_terrain 95 | next_template_terrain += 1 96 | 97 | set_bit_terrain(template_coords, bit, terrain_mapping[editor_terrain]) 98 | 99 | template_terrain_count = terrain_mapping.keys().size() 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/core/template_loader.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | 3 | 4 | 5 | const TAG_NOT_FOUND := null 6 | 7 | const G := preload("res://addons/tile_bit_tools/core/globals.gd") 8 | 9 | const TemplateBitData := preload("res://addons/tile_bit_tools/core/template_bit_data.gd") 10 | const TemplateTagData := preload("res://addons/tile_bit_tools/core/template_tag_data.gd") 11 | const TemplateTag := TemplateTagData.TemplateTag 12 | 13 | var _template_tag_data := TemplateTagData.new() 14 | 15 | 16 | 17 | var output := preload("res://addons/tile_bit_tools/core/output.gd").new() 18 | 19 | var _templates := [] 20 | var _tags := [] 21 | 22 | # {tag_id : [template_id, ...]} 23 | var _tags_to_templates := {} 24 | # {template_id : [tag_id, ...]} 25 | var _templates_to_tags := {} 26 | 27 | # {text : tag_id} 28 | var _custom_tags := {} 29 | 30 | var _display_tag_ids := [] 31 | 32 | 33 | 34 | func _init(template_folder_paths : Array) -> void: 35 | _load_auto_tags() 36 | _load_templates(template_folder_paths) 37 | _setup_sorted_tag_ids() 38 | 39 | 40 | func _load_templates(template_folder_paths : Array) -> void: 41 | var paths_parsed := [] 42 | 43 | for folder_path in template_folder_paths: 44 | # don't load from duplicate folders 45 | if folder_path in paths_parsed: 46 | continue 47 | paths_parsed.append(folder_path) 48 | 49 | _load_templates_in_directory( 50 | folder_path.path, 51 | folder_path.type == G.TemplateTypes.BUILT_IN 52 | ) 53 | 54 | 55 | #func print_templates_by_tag() -> void: 56 | # for tag_id in _tags_to_templates.keys(): 57 | # var tag : TemplateTag = _tags[tag_id] 58 | # print() 59 | # print(tag.text) 60 | # for template_id in _tags_to_templates[tag_id]: 61 | # var template : TemplateBitData = _templates[template_id] 62 | # print(template.template_name) 63 | 64 | 65 | 66 | # takes the auto tags in specified order (skipping any not in list), 67 | # then adds custom tags alphabetically 68 | func _setup_sorted_tag_ids() -> void: 69 | for tag_enum_id in _template_tag_data.tag_display: 70 | var tag_id := get_tag_id(_template_tag_data.tags[tag_enum_id]) 71 | if tag_id == -1: 72 | output.error("Unable to find auto tag %s" % tag_enum_id) 73 | continue 74 | _display_tag_ids.append(tag_id) 75 | 76 | var sorted_custom_tag_texts := _custom_tags.keys().duplicate() 77 | sorted_custom_tag_texts.sort_custom(func(a,b): return a.to_lower() < b.to_lower()) 78 | 79 | for tag_text in sorted_custom_tag_texts: 80 | _display_tag_ids.append(_custom_tags[tag_text]) 81 | 82 | 83 | func get_tag(tag_id : int) -> TemplateTag: 84 | return _tags[tag_id] 85 | 86 | 87 | func get_tag_id(tag : TemplateTag) -> int: 88 | return _tags.find(tag) 89 | 90 | 91 | func get_tags_item_list(use_display_list := false, exclude_unused := false, filtered_tags := []) -> Array: 92 | if use_display_list: 93 | return _get_tags_item_list(_display_tag_ids, exclude_unused, false, filtered_tags) 94 | else: 95 | return _get_tags_item_list(range(_tags.size()), exclude_unused, true, filtered_tags) 96 | 97 | 98 | func get_template(template_id : int) -> TemplateBitData: 99 | if template_id in range(0, _templates.size()): 100 | return _templates[template_id] 101 | return null 102 | 103 | func get_templates() -> Array: 104 | return _templates 105 | 106 | func _get_tags_item_list(tag_ids : Array, exclude_unused := false, sort_alphabetically := false, filtered_tags := []) -> Array: 107 | var item_list := [] 108 | for tag_id in tag_ids: 109 | if tag_id in filtered_tags: 110 | continue 111 | 112 | var item := _get_tag_as_item(tag_id, exclude_unused, filtered_tags) 113 | if item.is_empty(): 114 | continue 115 | item_list.append(item) 116 | 117 | if sort_alphabetically: 118 | item_list.sort_custom(func(a,b): return a["text"].to_lower() < b["text"].to_lower()) 119 | 120 | return item_list 121 | 122 | 123 | func _get_tag_as_item(tag_id : int, skip_if_unused := false, filtered_tags := []) -> Dictionary: 124 | var tags := filtered_tags.duplicate() 125 | tags.append(tag_id) 126 | var count : int = _get_filtered_templates_list(tags).size() 127 | if skip_if_unused && count == 0: 128 | return {} 129 | 130 | var tag : TemplateTag = _tags[tag_id] 131 | 132 | return({ 133 | "id": tag_id, 134 | "text": tag.text, 135 | "count": count, 136 | "tag": tag, 137 | # "color": tag.color, # TODO: implement in future? 138 | }) 139 | 140 | 141 | func _get_all_template_ids() -> Array: 142 | return range(_templates.size()) 143 | 144 | 145 | func _get_filtered_templates_list(filter_by_tags := []) -> Array: 146 | var filtered_templates := _get_all_template_ids() 147 | 148 | for tag_id in filter_by_tags: 149 | var templates_by_tag : Array = _tags_to_templates[tag_id].duplicate() 150 | filtered_templates = filtered_templates.filter(func(id): return templates_by_tag.has(id)) 151 | if filtered_templates.is_empty(): 152 | return [] 153 | 154 | return filtered_templates 155 | 156 | 157 | 158 | func get_templates_item_list(filter_by_tags := []) -> Array: 159 | var item_list := [] 160 | 161 | for template_id in _get_filtered_templates_list(filter_by_tags): 162 | var template : TemplateBitData = _templates[template_id] 163 | var text := template.template_name 164 | item_list.append({"id": template_id, "text": text}) 165 | 166 | # sort alphabetically 167 | item_list.sort_custom(func(a, b): return a["text"].to_lower() < b["text"].to_lower()) 168 | 169 | return item_list 170 | 171 | 172 | 173 | func _load_auto_tags() -> void: 174 | for tag in _template_tag_data.tags.values(): 175 | var _id := _add_tag(tag) 176 | 177 | 178 | func _load_templates_in_directory(path : String, mark_as_built_in := false) -> void: 179 | var dir := DirAccess.open(path) 180 | if !dir: 181 | output.user("No directory found at %s" % path) 182 | return 183 | for file in dir.get_files(): 184 | if !file.ends_with("tres"): 185 | continue 186 | 187 | # use get_current_dir() instead of path + file to normalize slashes 188 | var file_path := dir.get_current_dir() + "/" + file 189 | 190 | var template := load(file_path) 191 | if !template or template.get_script() != TemplateBitData: 192 | output.user("Error loading template at %s" % file_path) 193 | continue 194 | 195 | 196 | if mark_as_built_in: 197 | template.built_in = true 198 | 199 | var _id := _add_template(template) 200 | 201 | 202 | func _get_or_add_custom_tag(tag_text : String) -> int: 203 | # output.debug("_get_or_add_custom_tag(): %s" % tag_text) 204 | 205 | var custom_tag_id = _custom_tags.get(tag_text, null) 206 | # output.debug("custom_tag_id=%s" % custom_tag_id) 207 | if custom_tag_id != TAG_NOT_FOUND: 208 | return custom_tag_id 209 | 210 | var tag := TemplateTag.new(tag_text) 211 | tag.custom_tag = true 212 | var tag_id := _add_tag(tag) 213 | _custom_tags[tag_text] = tag_id 214 | return tag_id 215 | 216 | 217 | # adds tag, returns id 218 | func _add_tag(tag : TemplateTag) -> int: 219 | _tags.append(tag) 220 | var tag_id := _tags.size() - 1 221 | _tags_to_templates[tag_id] = [] 222 | return tag_id 223 | 224 | 225 | # adds template, assigns its _tags, returns id 226 | func _add_template(template : TemplateBitData) -> int: 227 | _templates.append(template) 228 | var template_id := _templates.size() - 1 229 | _templates_to_tags[template_id] = [] 230 | _assign_template_auto_tags(template_id, template) 231 | _assign_template_custom_tags(template_id, template) 232 | return template_id 233 | 234 | 235 | func _assign_template_auto_tags(template_id : int, template : TemplateBitData) -> void: 236 | for tag_id in range(_tags.size()): 237 | var tag : TemplateTag = _tags[tag_id] 238 | if !tag.has_test(): 239 | continue 240 | if tag.get_test_result(template): 241 | _assign_template_to_tag(template_id, tag_id) 242 | 243 | 244 | func _assign_template_custom_tags(template_id : int, template : TemplateBitData) -> void: 245 | for tag_text in template.get_custom_tags(): 246 | # output.debug("Evaluating custom tag [%s] for template %s" % [tag_text, template.template_name]) 247 | var tag_id := _get_or_add_custom_tag(tag_text) 248 | # output.debug("tag_id=%s" % tag_id) 249 | _assign_template_to_tag(template_id, tag_id) 250 | 251 | 252 | func _assign_template_to_tag(template_id : int, tag_id : int) -> void: 253 | if !_tags_to_templates[tag_id].has(template_id): 254 | _tags_to_templates[tag_id].append(template_id) 255 | if !_templates_to_tags[template_id].has(tag_id): 256 | _templates_to_tags[template_id].append(tag_id) 257 | 258 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/core/template_tag_data.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | 3 | const SEPARATOR := 0 4 | 5 | const TemplateBitData := preload("res://addons/tile_bit_tools/core/template_bit_data.gd") 6 | 7 | enum Tags { 8 | BUILT_IN, 9 | USER, 10 | ONE_OR_TWO_TERRAINS, 11 | THREE_PLUS_TERRAINS, 12 | MATCH_CORNERS_AND_SIDES, 13 | MATCH_CORNERS, 14 | MATCH_SIDES, 15 | } 16 | 17 | 18 | class TemplateTag: 19 | var test_func : Callable 20 | var text : String 21 | var color : Color 22 | var icon := "" 23 | var custom_tag := false 24 | 25 | var id : int 26 | 27 | var custom_tag_icons := { 28 | "Godot 3": "Godot", 29 | "Incomplete Autotile": "NodeWarning", 30 | "Plugin Required": "NodeWarning", 31 | "TilePipe2": "TileSet", 32 | "Tilesetter": "TileSet", 33 | 34 | } 35 | 36 | func _init(p_text : String, p_test_func = null, p_icon = null, p_color = null): 37 | text = p_text 38 | if p_test_func: 39 | test_func = p_test_func 40 | if p_icon: 41 | icon = p_icon 42 | if p_color: 43 | color = p_color 44 | 45 | if icon == "": 46 | icon = custom_tag_icons.get(text, "") 47 | 48 | 49 | func get_template_has_tag(bit_data : TemplateBitData) -> bool: 50 | if test_func: 51 | return test_func.call(bit_data) 52 | else: 53 | return text in bit_data._custom_tags 54 | 55 | func get_test_result(bit_data : TemplateBitData) -> bool: 56 | return test_func.call(bit_data) 57 | 58 | func get_icon(base_control : Control) -> Texture2D: 59 | if icon == "": 60 | return null 61 | if icon.is_absolute_path(): 62 | return load(icon) 63 | return base_control.get_theme_icon(icon, "EditorIcons") 64 | 65 | func has_test() -> bool: 66 | if test_func: 67 | return true 68 | return false 69 | 70 | 71 | 72 | var tags := { 73 | Tags.BUILT_IN: TemplateTag.new( 74 | "Type: Built-In", 75 | func(bit_data: TemplateBitData): 76 | return bit_data.built_in, 77 | "Tools", 78 | null, 79 | ), 80 | Tags.USER: TemplateTag.new( 81 | "Type: User", 82 | func(bit_data: TemplateBitData): 83 | return !bit_data.built_in, 84 | "File", 85 | Color.YELLOW, 86 | ), 87 | # Tags.ONE_OR_TWO_TERRAINS: TemplateTag.new( 88 | # "Terrains: 1-2", 89 | # func(bit_data: TemplateBitData): 90 | # return bit_data.template_terrain_count <= 2, 91 | # null, 92 | # null, 93 | # ), 94 | # Tags.THREE_PLUS_TERRAINS: TemplateTag.new( 95 | # "Terrains: 3+", 96 | # func(bit_data: TemplateBitData): 97 | # return bit_data.template_terrain_count >= 3, 98 | # null, 99 | # null, 100 | # ), 101 | Tags.MATCH_CORNERS_AND_SIDES: TemplateTag.new( 102 | "Mode: Corners and Sides", 103 | func(bit_data: TemplateBitData): 104 | return bit_data.terrain_mode == TileSet.TERRAIN_MODE_MATCH_CORNERS_AND_SIDES, 105 | "TerrainMatchCornersAndSides", 106 | null, 107 | ), 108 | Tags.MATCH_CORNERS: TemplateTag.new( 109 | "Mode: Corners", 110 | func(bit_data: TemplateBitData): 111 | return bit_data.terrain_mode == TileSet.TERRAIN_MODE_MATCH_CORNERS, 112 | "TerrainMatchCorners", 113 | null, 114 | ), 115 | Tags.MATCH_SIDES: TemplateTag.new( 116 | "Mode: Sides", 117 | func(bit_data: TemplateBitData): 118 | return bit_data.terrain_mode == TileSet.TERRAIN_MODE_MATCH_SIDES, 119 | "TerrainMatchSides", 120 | null, 121 | ), 122 | } 123 | 124 | var tag_display := [ 125 | Tags.BUILT_IN, 126 | Tags.USER, 127 | Tags.MATCH_CORNERS_AND_SIDES, 128 | Tags.MATCH_CORNERS, 129 | Tags.MATCH_SIDES, 130 | # Tags.ONE_OR_TWO_TERRAINS, 131 | # Tags.THREE_PLUS_TERRAINS, 132 | ] 133 | 134 | 135 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/core/texts.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | 3 | const G := preload("res://addons/tile_bit_tools/core/globals.gd") 4 | const BitData := preload("res://addons/tile_bit_tools/core/bit_data.gd") 5 | 6 | 7 | const ERROR_TEXTS := { 8 | # G.Errors.OK: "result=OK", 9 | G.Errors.MISSING_TILE_SET: "TileSet not found", 10 | G.Errors.MISSING_SOURCE: "TileSetAtlasSource not found", 11 | G.Errors.MISSING_TILES: "TileData not found", 12 | G.Errors.MISSING_BIT_DATA: "TileData failed to parse", 13 | G.Errors.INVALID_TBT_PLUGIN_CONTROL: "TBTPluginControl invalid", 14 | G.Errors.INVALID_TILES_PREVIEW: "TilesPreview invalid", 15 | G.Errors.MULTIPLE_TERRAIN_SETS: "Selected tiles contain more than one terrain set", 16 | G.Errors.UNSUPPORTED_SHAPE: "Current tile shape is not supported", 17 | } 18 | 19 | 20 | const TERRAIN_MODE_TEXTS := { 21 | TileSet.TerrainMode.TERRAIN_MODE_MATCH_CORNERS_AND_SIDES : "Corners and Sides", 22 | TileSet.TerrainMode.TERRAIN_MODE_MATCH_CORNERS : "Corners", 23 | TileSet.TerrainMode.TERRAIN_MODE_MATCH_SIDES : "Sides", 24 | } 25 | 26 | const TERRAIN_BIT_TEXTS := { 27 | BitData.TerrainBits.TOP_LEFT_CORNER: "Top Left Corner", 28 | BitData.TerrainBits.TOP_SIDE: "Top Side", 29 | BitData.TerrainBits.TOP_RIGHT_CORNER: "Top Right Corner", 30 | BitData.TerrainBits.RIGHT_SIDE: "Right Side", 31 | BitData.TerrainBits.BOTTOM_RIGHT_CORNER: "Bottom Right Corner", 32 | BitData.TerrainBits.BOTTOM_SIDE: "Bottom Side", 33 | BitData.TerrainBits.BOTTOM_LEFT_CORNER: "Bottom Left Corner", 34 | BitData.TerrainBits.LEFT_SIDE: "Left Side", 35 | BitData.TerrainBits.CENTER: "Tile Terrain (Center)", 36 | } 37 | 38 | 39 | const EMPTY_ITEM := "" 40 | 41 | const WELCOME_MESSAGE := "Welcome to TileBitTools. Please report bugs or suggestions to github.com/dandeliondino/tile_bit_tools. To change which messages you see here, go to Project Settings -> General -> Addons -> Tile Bit Tools (make sure 'Advanced' is enabled)" 42 | const WELCOME_MESSAGE2 := "Note: Please back up your project before applying any changes." 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/godot3_2x2/ABOUT.txt: -------------------------------------------------------------------------------- 1 | Tile size: 16px 2 | Credits: Tiles adapted from Kenney's Pixel Shmup (https://www.kenney.nl/assets/pixel-shmup) 3 | License: CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/) -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/godot3_2x2/godot3_2x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dandeliondino/tile_bit_tools/0bbf10f27d492cdc5f7e227cf7946a705998e0d4/addons/tile_bit_tools/examples/godot3_2x2/godot3_2x2.png -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/godot3_2x2/godot3_2x2.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://c8jir47kkrfm" 6 | path="res://.godot/imported/godot3_2x2.png-aba8871ac93d513184f6173778d73edf.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/tile_bit_tools/examples/godot3_2x2/godot3_2x2.png" 14 | dest_files=["res://.godot/imported/godot3_2x2.png-aba8871ac93d513184f6173778d73edf.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/godot3_3x3_16_tiles/ABOUT.txt: -------------------------------------------------------------------------------- 1 | Tile size: 16px 2 | Credits: Tiles adapted from Kenney's Pixel Shmup (https://www.kenney.nl/assets/pixel-shmup) 3 | License: CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/) -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/godot3_3x3_16_tiles/godot3_3x3_16_tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dandeliondino/tile_bit_tools/0bbf10f27d492cdc5f7e227cf7946a705998e0d4/addons/tile_bit_tools/examples/godot3_3x3_16_tiles/godot3_3x3_16_tiles.png -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/godot3_3x3_16_tiles/godot3_3x3_16_tiles.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://pm2rgubsav6m" 6 | path="res://.godot/imported/godot3_3x3_16_tiles.png-ba2f03c9d65060269dfa240bc35aff35.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/tile_bit_tools/examples/godot3_3x3_16_tiles/godot3_3x3_16_tiles.png" 14 | dest_files=["res://.godot/imported/godot3_3x3_16_tiles.png-ba2f03c9d65060269dfa240bc35aff35.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/godot3_3x3_minimal/ABOUT.txt: -------------------------------------------------------------------------------- 1 | Tile size: 16px 2 | Credits: Tiles adapted from Kenney's Pixel Shmup (https://www.kenney.nl/assets/pixel-shmup) 3 | License: CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/) -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/godot3_3x3_minimal/godot3_3x3_minimal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dandeliondino/tile_bit_tools/0bbf10f27d492cdc5f7e227cf7946a705998e0d4/addons/tile_bit_tools/examples/godot3_3x3_minimal/godot3_3x3_minimal.png -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/godot3_3x3_minimal/godot3_3x3_minimal.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://d5ylbtvxy6ud" 6 | path="res://.godot/imported/godot3_3x3_minimal.png-5315b0ad1f1d21ffddf7293efb5522ab.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/tile_bit_tools/examples/godot3_3x3_minimal/godot3_3x3_minimal.png" 14 | dest_files=["res://.godot/imported/godot3_3x3_minimal.png-5315b0ad1f1d21ffddf7293efb5522ab.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/simple_tilesets/ABOUT.txt: -------------------------------------------------------------------------------- 1 | Tile size: 16px 2 | Credits: Tiles adapted from Kenney's Pixel Shmup (https://www.kenney.nl/assets/pixel-shmup) 3 | License: CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/) -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/simple_tilesets/simple_9_and_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dandeliondino/tile_bit_tools/0bbf10f27d492cdc5f7e227cf7946a705998e0d4/addons/tile_bit_tools/examples/simple_tilesets/simple_9_and_4.png -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/simple_tilesets/simple_9_and_4.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://d3rqw1kgi0m2l" 6 | path="res://.godot/imported/simple_9_and_4.png-ecaee0bcf09165d94bf9236cc212e887.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/tile_bit_tools/examples/simple_tilesets/simple_9_and_4.png" 14 | dest_files=["res://.godot/imported/simple_9_and_4.png-ecaee0bcf09165d94bf9236cc212e887.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/simple_tilesets/simple_9_and_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dandeliondino/tile_bit_tools/0bbf10f27d492cdc5f7e227cf7946a705998e0d4/addons/tile_bit_tools/examples/simple_tilesets/simple_9_and_9.png -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/simple_tilesets/simple_9_and_9.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://eie8ec231nbx" 6 | path="res://.godot/imported/simple_9_and_9.png-dd97711fb9ab25ea25c4a4b71be2c566.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/tile_bit_tools/examples/simple_tilesets/simple_9_and_9.png" 14 | dest_files=["res://.godot/imported/simple_9_and_9.png-dd97711fb9ab25ea25c4a4b71be2c566.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/tilesetter_blob/ABOUT.txt: -------------------------------------------------------------------------------- 1 | Tile size: 16px 2 | Credits: Tiles adapted from Kenney's Pixel Shmup (https://www.kenney.nl/assets/pixel-shmup) 3 | License: CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/) -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/tilesetter_blob/tilesetter_blob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dandeliondino/tile_bit_tools/0bbf10f27d492cdc5f7e227cf7946a705998e0d4/addons/tile_bit_tools/examples/tilesetter_blob/tilesetter_blob.png -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/tilesetter_blob/tilesetter_blob.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://207wwupug0tm" 6 | path="res://.godot/imported/tilesetter_blob.png-d6e1fabbd7fb279f1ee63f1ada8e0733.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/tile_bit_tools/examples/tilesetter_blob/tilesetter_blob.png" 14 | dest_files=["res://.godot/imported/tilesetter_blob.png-d6e1fabbd7fb279f1ee63f1ada8e0733.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/tilesetter_wang/ABOUT.txt: -------------------------------------------------------------------------------- 1 | Tile size: 16px 2 | Credits: Tiles adapted from Kenney's Pixel Shmup (https://www.kenney.nl/assets/pixel-shmup) 3 | License: CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/) -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/tilesetter_wang/tilesetter_wang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dandeliondino/tile_bit_tools/0bbf10f27d492cdc5f7e227cf7946a705998e0d4/addons/tile_bit_tools/examples/tilesetter_wang/tilesetter_wang.png -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/tilesetter_wang/tilesetter_wang.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://djfsinobli1lr" 6 | path="res://.godot/imported/tilesetter_wang.png-d3f83fc7b5a9a46f3842d791cf7bf362.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/tile_bit_tools/examples/tilesetter_wang/tilesetter_wang.png" 14 | dest_files=["res://.godot/imported/tilesetter_wang.png-d3f83fc7b5a9a46f3842d791cf7bf362.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/tilesetter_wang_3_terrains/ABOUT.txt: -------------------------------------------------------------------------------- 1 | Tile size: 16px 2 | Credits: Tiles adapted from Kenney's Pixel Shmup (https://www.kenney.nl/assets/pixel-shmup) 3 | License: CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/) -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/tilesetter_wang_3_terrains/tilesetter_wang_3_terrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dandeliondino/tile_bit_tools/0bbf10f27d492cdc5f7e227cf7946a705998e0d4/addons/tile_bit_tools/examples/tilesetter_wang_3_terrains/tilesetter_wang_3_terrain.png -------------------------------------------------------------------------------- /addons/tile_bit_tools/examples/tilesetter_wang_3_terrains/tilesetter_wang_3_terrain.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://3tn7f0bc7wtl" 6 | path="res://.godot/imported/tilesetter_wang_3_terrain.png-ccf897dbb6ca5ad03088ec812e1fa06a.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/tile_bit_tools/examples/tilesetter_wang_3_terrains/tilesetter_wang_3_terrain.png" 14 | dest_files=["res://.godot/imported/tilesetter_wang_3_terrain.png-ccf897dbb6ca5ad03088ec812e1fa06a.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="TileBitTools" 4 | description="A Godot 4 plugin for autotile templates and terrain bit editing." 5 | author="dandeliondino" 6 | version="1.1.0" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | const G := preload("res://addons/tile_bit_tools/core/globals.gd") 5 | 6 | const PLUGIN_NAME := "tile_bit_tools" 7 | 8 | var texts := preload("res://addons/tile_bit_tools/core/texts.gd").new() 9 | var output := preload("res://addons/tile_bit_tools/core/output.gd").new() 10 | 11 | var plugin : EditorInspectorPlugin 12 | 13 | 14 | func _enter_tree() -> void: 15 | output.debug("plugin.gd : _enter_tree()") 16 | output.info("Initializing TileBitTools v%s..." % G.VERSION) 17 | 18 | _setup_project_settings() 19 | 20 | plugin = preload("inspector_plugin.gd").new() 21 | add_inspector_plugin(plugin) 22 | var result : G.Errors = plugin.setup(get_editor_interface()) 23 | if result != OK: 24 | output.user("Unable to initialize, disabling plugin") 25 | get_editor_interface().set_plugin_enabled(PLUGIN_NAME, false) 26 | return 27 | output.info("Initialization complete") 28 | output.user(texts.WELCOME_MESSAGE) 29 | output.user(texts.WELCOME_MESSAGE2) 30 | 31 | 32 | func _clear() -> void: 33 | output.debug("plugin.gd : _clear()") 34 | if plugin: 35 | plugin.clean_up() 36 | 37 | 38 | func _exit_tree() -> void: 39 | output.debug("plugin.gd : _exit_tree()") 40 | output.info("Cleaning up...") 41 | if plugin: 42 | plugin.clean_up() 43 | remove_inspector_plugin(plugin) 44 | 45 | 46 | 47 | 48 | func _setup_project_settings() -> void: 49 | for key in G.Settings.keys(): 50 | var setting : Dictionary = G.Settings[key] 51 | if !ProjectSettings.has_setting(setting.path): 52 | ProjectSettings.set(setting.path, setting.default) 53 | 54 | ProjectSettings.set_initial_value(setting.path, setting.default) 55 | ProjectSettings.add_property_info({ 56 | "name": setting.path, 57 | "type": setting.type, 58 | "hint_string": setting.get("hint_string", null), 59 | }) 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/templates/godot3_2x2.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=2 format=3 uid="uid://dghepno5umy5j"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/core/template_bit_data.gd" id="1_03oml"] 4 | 5 | [resource] 6 | script = ExtResource("1_03oml") 7 | version = "0.1.0" 8 | template_name = "2x2 (Godot 3)" 9 | template_description = "Like all corner-matching templates, it can be used for simple blocks of terrain or diagonal paths or walls. It cannot be drawn in a single-tile wide line. This configuration is based on the Godot 3 documentation (https://docs.godotengine.org/en/3.5/tutorials/2d/using_tilemaps.html)." 10 | _custom_tags = ["Godot 3", "TilePipe2"] 11 | template_terrain_count = 2 12 | example_folder_path = "res://addons/tile_bit_tools/examples/godot3_2x2/" 13 | _tiles = { 14 | Vector2i(0, 0): { 15 | 0: 0, 16 | 1: { 17 | 3: 1, 18 | 7: 0, 19 | 11: 1, 20 | 15: 1 21 | } 22 | }, 23 | Vector2i(0, 1): { 24 | 0: 0, 25 | 1: { 26 | 3: 0, 27 | 7: 1, 28 | 11: 0, 29 | 15: 1 30 | } 31 | }, 32 | Vector2i(0, 2): { 33 | 0: 0, 34 | 1: { 35 | 3: 1, 36 | 7: 1, 37 | 11: 1, 38 | 15: 0 39 | } 40 | }, 41 | Vector2i(0, 3): { 42 | 0: 1, 43 | 1: { 44 | 3: 1, 45 | 7: 1, 46 | 11: 1, 47 | 15: 1 48 | } 49 | }, 50 | Vector2i(1, 0): { 51 | 0: 0, 52 | 1: { 53 | 3: 0, 54 | 7: 1, 55 | 11: 1, 56 | 15: 0 57 | } 58 | }, 59 | Vector2i(1, 1): { 60 | 0: 0, 61 | 1: { 62 | 3: 0, 63 | 7: 0, 64 | 11: 1, 65 | 15: 0 66 | } 67 | }, 68 | Vector2i(1, 2): { 69 | 0: 0, 70 | 1: { 71 | 3: 1, 72 | 7: 1, 73 | 11: 0, 74 | 15: 0 75 | } 76 | }, 77 | Vector2i(1, 3): { 78 | 0: 0, 79 | 1: { 80 | 3: 0, 81 | 7: 1, 82 | 11: 1, 83 | 15: 1 84 | } 85 | }, 86 | Vector2i(2, 0): { 87 | 0: 0, 88 | 1: { 89 | 3: 0, 90 | 7: 0, 91 | 11: 0, 92 | 15: 1 93 | } 94 | }, 95 | Vector2i(2, 1): { 96 | 0: 0, 97 | 1: { 98 | 3: 0, 99 | 7: 0, 100 | 11: 0, 101 | 15: 0 102 | } 103 | }, 104 | Vector2i(2, 2): { 105 | 0: 0, 106 | 1: { 107 | 3: 0, 108 | 7: 1, 109 | 11: 0, 110 | 15: 0 111 | } 112 | }, 113 | Vector2i(2, 3): { 114 | 0: 0, 115 | 1: { 116 | 3: 1, 117 | 7: 0, 118 | 11: 1, 119 | 15: 0 120 | } 121 | }, 122 | Vector2i(3, 0): { 123 | 0: 0, 124 | 1: { 125 | 3: 0, 126 | 7: 0, 127 | 11: 1, 128 | 15: 1 129 | } 130 | }, 131 | Vector2i(3, 1): { 132 | 0: 0, 133 | 1: { 134 | 3: 1, 135 | 7: 0, 136 | 11: 0, 137 | 15: 0 138 | } 139 | }, 140 | Vector2i(3, 2): { 141 | 0: 0, 142 | 1: { 143 | 3: 1, 144 | 7: 0, 145 | 11: 0, 146 | 15: 1 147 | } 148 | }, 149 | Vector2i(3, 3): { 150 | 0: 0, 151 | 1: { 152 | 3: 1, 153 | 7: 1, 154 | 11: 0, 155 | 15: 1 156 | } 157 | } 158 | } 159 | terrain_set = -1 160 | terrain_mode = 1 161 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/templates/godot3_3x3_16_tiles.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=2 format=3 uid="uid://dvhege7ynlv7t"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/core/template_bit_data.gd" id="1_4fue2"] 4 | 5 | [resource] 6 | script = ExtResource("1_4fue2") 7 | version = "0.1.0" 8 | template_name = "3x3 16 Tiles (Godot 3)" 9 | template_description = "Like all side-matching templates, it can be used for paths or fences in straight lines. It cannot make diagonal lines or shapes wider than one tile. This configuration is based on the Godot 3 documentation (https://docs.godotengine.org/en/3.5/tutorials/2d/using_tilemaps.html)." 10 | _custom_tags = ["Godot 3"] 11 | template_terrain_count = 2 12 | example_folder_path = "res://addons/tile_bit_tools/examples/godot3_3x3_16_tiles/" 13 | _tiles = { 14 | Vector2i(0, 0): { 15 | 0: 0, 16 | 1: { 17 | 0: 1, 18 | 4: 0, 19 | 8: 1, 20 | 12: 1 21 | } 22 | }, 23 | Vector2i(0, 1): { 24 | 0: 0, 25 | 1: { 26 | 0: 1, 27 | 4: 0, 28 | 8: 1, 29 | 12: 0 30 | } 31 | }, 32 | Vector2i(0, 2): { 33 | 0: 0, 34 | 1: { 35 | 0: 1, 36 | 4: 1, 37 | 8: 1, 38 | 12: 0 39 | } 40 | }, 41 | Vector2i(0, 3): { 42 | 0: 0, 43 | 1: { 44 | 0: 1, 45 | 4: 1, 46 | 8: 1, 47 | 12: 1 48 | } 49 | }, 50 | Vector2i(1, 0): { 51 | 0: 0, 52 | 1: { 53 | 0: 0, 54 | 4: 0, 55 | 8: 1, 56 | 12: 1 57 | } 58 | }, 59 | Vector2i(1, 1): { 60 | 0: 0, 61 | 1: { 62 | 0: 0, 63 | 4: 0, 64 | 8: 1, 65 | 12: 0 66 | } 67 | }, 68 | Vector2i(1, 2): { 69 | 0: 0, 70 | 1: { 71 | 0: 0, 72 | 4: 1, 73 | 8: 1, 74 | 12: 0 75 | } 76 | }, 77 | Vector2i(1, 3): { 78 | 0: 0, 79 | 1: { 80 | 0: 0, 81 | 4: 1, 82 | 8: 1, 83 | 12: 1 84 | } 85 | }, 86 | Vector2i(2, 0): { 87 | 0: 0, 88 | 1: { 89 | 0: 0, 90 | 4: 0, 91 | 8: 0, 92 | 12: 1 93 | } 94 | }, 95 | Vector2i(2, 1): { 96 | 0: 0, 97 | 1: { 98 | 0: 0, 99 | 4: 0, 100 | 8: 0, 101 | 12: 0 102 | } 103 | }, 104 | Vector2i(2, 2): { 105 | 0: 0, 106 | 1: { 107 | 0: 0, 108 | 4: 1, 109 | 8: 0, 110 | 12: 0 111 | } 112 | }, 113 | Vector2i(2, 3): { 114 | 0: 0, 115 | 1: { 116 | 0: 0, 117 | 4: 1, 118 | 8: 0, 119 | 12: 1 120 | } 121 | }, 122 | Vector2i(3, 0): { 123 | 0: 0, 124 | 1: { 125 | 0: 1, 126 | 4: 0, 127 | 8: 0, 128 | 12: 1 129 | } 130 | }, 131 | Vector2i(3, 1): { 132 | 0: 0, 133 | 1: { 134 | 0: 1, 135 | 4: 0, 136 | 8: 0, 137 | 12: 0 138 | } 139 | }, 140 | Vector2i(3, 2): { 141 | 0: 0, 142 | 1: { 143 | 0: 1, 144 | 4: 1, 145 | 8: 0, 146 | 12: 0 147 | } 148 | }, 149 | Vector2i(3, 3): { 150 | 0: 0, 151 | 1: { 152 | 0: 1, 153 | 4: 1, 154 | 8: 0, 155 | 12: 1 156 | } 157 | } 158 | } 159 | terrain_set = -1 160 | terrain_mode = 2 161 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/templates/godot3_3x3_minimal.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=2 format=3 uid="uid://bxsdvyv6ld25w"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/core/template_bit_data.gd" id="1_dl8o4"] 4 | 5 | [resource] 6 | script = ExtResource("1_dl8o4") 7 | version = "0.1.0" 8 | template_name = "3x3 Minimal (Godot 3)" 9 | template_description = "Like all corner-and-side-matching templates, this 47/48-tile template has flexible uses and can make complex shapes. It is good for terrains, paths and walls. This configuration is based on the Godot 3 documentation (https://docs.godotengine.org/en/3.5/tutorials/2d/using_tilemaps.html)." 10 | _custom_tags = ["Godot 3", "TilePipe2"] 11 | template_terrain_count = 2 12 | example_folder_path = "res://addons/tile_bit_tools/examples/godot3_3x3_minimal/" 13 | _tiles = { 14 | Vector2i(0, 0): { 15 | 0: 0, 16 | 1: { 17 | 0: 1, 18 | 3: 1, 19 | 4: 0, 20 | 7: 1, 21 | 8: 1, 22 | 11: 1, 23 | 12: 1, 24 | 15: 1 25 | } 26 | }, 27 | Vector2i(0, 1): { 28 | 0: 0, 29 | 1: { 30 | 0: 1, 31 | 3: 1, 32 | 4: 0, 33 | 7: 1, 34 | 8: 1, 35 | 11: 1, 36 | 12: 0, 37 | 15: 1 38 | } 39 | }, 40 | Vector2i(0, 2): { 41 | 0: 0, 42 | 1: { 43 | 0: 1, 44 | 3: 1, 45 | 4: 1, 46 | 7: 1, 47 | 8: 1, 48 | 11: 1, 49 | 12: 0, 50 | 15: 1 51 | } 52 | }, 53 | Vector2i(0, 3): { 54 | 0: 0, 55 | 1: { 56 | 0: 1, 57 | 3: 1, 58 | 4: 1, 59 | 7: 1, 60 | 8: 1, 61 | 11: 1, 62 | 12: 1, 63 | 15: 1 64 | } 65 | }, 66 | Vector2i(1, 0): { 67 | 0: 0, 68 | 1: { 69 | 0: 0, 70 | 3: 1, 71 | 4: 0, 72 | 7: 1, 73 | 8: 1, 74 | 11: 1, 75 | 12: 1, 76 | 15: 1 77 | } 78 | }, 79 | Vector2i(1, 1): { 80 | 0: 0, 81 | 1: { 82 | 0: 0, 83 | 3: 1, 84 | 4: 0, 85 | 7: 1, 86 | 8: 1, 87 | 11: 1, 88 | 12: 0, 89 | 15: 1 90 | } 91 | }, 92 | Vector2i(1, 2): { 93 | 0: 0, 94 | 1: { 95 | 0: 0, 96 | 3: 1, 97 | 4: 1, 98 | 7: 1, 99 | 8: 1, 100 | 11: 1, 101 | 12: 0, 102 | 15: 1 103 | } 104 | }, 105 | Vector2i(1, 3): { 106 | 0: 0, 107 | 1: { 108 | 0: 0, 109 | 3: 1, 110 | 4: 1, 111 | 7: 1, 112 | 8: 1, 113 | 11: 1, 114 | 12: 1, 115 | 15: 1 116 | } 117 | }, 118 | Vector2i(2, 0): { 119 | 0: 0, 120 | 1: { 121 | 0: 0, 122 | 3: 1, 123 | 4: 0, 124 | 7: 1, 125 | 8: 0, 126 | 11: 1, 127 | 12: 1, 128 | 15: 1 129 | } 130 | }, 131 | Vector2i(2, 1): { 132 | 0: 0, 133 | 1: { 134 | 0: 0, 135 | 3: 1, 136 | 4: 0, 137 | 7: 1, 138 | 8: 0, 139 | 11: 1, 140 | 12: 0, 141 | 15: 1 142 | } 143 | }, 144 | Vector2i(2, 2): { 145 | 0: 0, 146 | 1: { 147 | 0: 0, 148 | 3: 1, 149 | 4: 1, 150 | 7: 1, 151 | 8: 0, 152 | 11: 1, 153 | 12: 0, 154 | 15: 1 155 | } 156 | }, 157 | Vector2i(2, 3): { 158 | 0: 0, 159 | 1: { 160 | 0: 0, 161 | 3: 1, 162 | 4: 1, 163 | 7: 1, 164 | 8: 0, 165 | 11: 1, 166 | 12: 1, 167 | 15: 1 168 | } 169 | }, 170 | Vector2i(3, 0): { 171 | 0: 0, 172 | 1: { 173 | 0: 1, 174 | 3: 1, 175 | 4: 0, 176 | 7: 1, 177 | 8: 0, 178 | 11: 1, 179 | 12: 1, 180 | 15: 1 181 | } 182 | }, 183 | Vector2i(3, 1): { 184 | 0: 0, 185 | 1: { 186 | 0: 1, 187 | 3: 1, 188 | 4: 0, 189 | 7: 1, 190 | 8: 0, 191 | 11: 1, 192 | 12: 0, 193 | 15: 1 194 | } 195 | }, 196 | Vector2i(3, 2): { 197 | 0: 0, 198 | 1: { 199 | 0: 1, 200 | 3: 1, 201 | 4: 1, 202 | 7: 1, 203 | 8: 0, 204 | 11: 1, 205 | 12: 0, 206 | 15: 1 207 | } 208 | }, 209 | Vector2i(3, 3): { 210 | 0: 0, 211 | 1: { 212 | 0: 1, 213 | 3: 1, 214 | 4: 1, 215 | 7: 1, 216 | 8: 0, 217 | 11: 1, 218 | 12: 1, 219 | 15: 1 220 | } 221 | }, 222 | Vector2i(4, 0): { 223 | 0: 0, 224 | 1: { 225 | 0: 0, 226 | 3: 1, 227 | 4: 0, 228 | 7: 1, 229 | 8: 0, 230 | 11: 0, 231 | 12: 0, 232 | 15: 1 233 | } 234 | }, 235 | Vector2i(4, 1): { 236 | 0: 0, 237 | 1: { 238 | 0: 0, 239 | 3: 0, 240 | 4: 0, 241 | 7: 1, 242 | 8: 1, 243 | 11: 1, 244 | 12: 0, 245 | 15: 1 246 | } 247 | }, 248 | Vector2i(4, 2): { 249 | 0: 0, 250 | 1: { 251 | 0: 0, 252 | 3: 1, 253 | 4: 0, 254 | 7: 1, 255 | 8: 1, 256 | 11: 1, 257 | 12: 0, 258 | 15: 0 259 | } 260 | }, 261 | Vector2i(4, 3): { 262 | 0: 0, 263 | 1: { 264 | 0: 0, 265 | 3: 1, 266 | 4: 0, 267 | 7: 0, 268 | 8: 0, 269 | 11: 1, 270 | 12: 0, 271 | 15: 1 272 | } 273 | }, 274 | Vector2i(5, 0): { 275 | 0: 0, 276 | 1: { 277 | 0: 0, 278 | 3: 0, 279 | 4: 0, 280 | 7: 1, 281 | 8: 0, 282 | 11: 1, 283 | 12: 1, 284 | 15: 1 285 | } 286 | }, 287 | Vector2i(5, 1): { 288 | 0: 0, 289 | 1: { 290 | 0: 0, 291 | 3: 0, 292 | 4: 0, 293 | 7: 0, 294 | 8: 0, 295 | 11: 1, 296 | 12: 0, 297 | 15: 0 298 | } 299 | }, 300 | Vector2i(5, 2): { 301 | 0: 0, 302 | 1: { 303 | 0: 0, 304 | 3: 0, 305 | 4: 0, 306 | 7: 1, 307 | 8: 0, 308 | 11: 0, 309 | 12: 0, 310 | 15: 0 311 | } 312 | }, 313 | Vector2i(5, 3): { 314 | 0: 0, 315 | 1: { 316 | 0: 0, 317 | 3: 1, 318 | 4: 1, 319 | 7: 1, 320 | 8: 0, 321 | 11: 1, 322 | 12: 0, 323 | 15: 0 324 | } 325 | }, 326 | Vector2i(6, 0): { 327 | 0: 0, 328 | 1: { 329 | 0: 0, 330 | 3: 1, 331 | 4: 0, 332 | 7: 0, 333 | 8: 0, 334 | 11: 1, 335 | 12: 1, 336 | 15: 1 337 | } 338 | }, 339 | Vector2i(6, 1): { 340 | 0: 0, 341 | 1: { 342 | 0: 0, 343 | 3: 0, 344 | 4: 0, 345 | 7: 0, 346 | 8: 0, 347 | 11: 0, 348 | 12: 0, 349 | 15: 1 350 | } 351 | }, 352 | Vector2i(6, 2): { 353 | 0: 0, 354 | 1: { 355 | 0: 0, 356 | 3: 1, 357 | 4: 0, 358 | 7: 0, 359 | 8: 0, 360 | 11: 0, 361 | 12: 0, 362 | 15: 0 363 | } 364 | }, 365 | Vector2i(6, 3): { 366 | 0: 0, 367 | 1: { 368 | 0: 0, 369 | 3: 1, 370 | 4: 1, 371 | 7: 1, 372 | 8: 0, 373 | 11: 0, 374 | 12: 0, 375 | 15: 1 376 | } 377 | }, 378 | Vector2i(7, 0): { 379 | 0: 0, 380 | 1: { 381 | 0: 0, 382 | 3: 1, 383 | 4: 0, 384 | 7: 1, 385 | 8: 0, 386 | 11: 1, 387 | 12: 0, 388 | 15: 0 389 | } 390 | }, 391 | Vector2i(7, 1): { 392 | 0: 0, 393 | 1: { 394 | 0: 1, 395 | 3: 1, 396 | 4: 0, 397 | 7: 0, 398 | 8: 0, 399 | 11: 1, 400 | 12: 0, 401 | 15: 1 402 | } 403 | }, 404 | Vector2i(7, 2): { 405 | 0: 0, 406 | 1: { 407 | 0: 1, 408 | 3: 1, 409 | 4: 0, 410 | 7: 1, 411 | 8: 0, 412 | 11: 0, 413 | 12: 0, 414 | 15: 1 415 | } 416 | }, 417 | Vector2i(7, 3): { 418 | 0: 0, 419 | 1: { 420 | 0: 0, 421 | 3: 0, 422 | 4: 0, 423 | 7: 1, 424 | 8: 0, 425 | 11: 1, 426 | 12: 0, 427 | 15: 1 428 | } 429 | }, 430 | Vector2i(8, 0): { 431 | 0: 0, 432 | 1: { 433 | 0: 0, 434 | 3: 0, 435 | 4: 0, 436 | 7: 1, 437 | 8: 1, 438 | 11: 1, 439 | 12: 1, 440 | 15: 1 441 | } 442 | }, 443 | Vector2i(8, 1): { 444 | 0: 0, 445 | 1: { 446 | 0: 0, 447 | 3: 0, 448 | 4: 0, 449 | 7: 1, 450 | 8: 1, 451 | 11: 1, 452 | 12: 0, 453 | 15: 0 454 | } 455 | }, 456 | Vector2i(8, 2): { 457 | 0: 0, 458 | 1: { 459 | 0: 0, 460 | 3: 0, 461 | 4: 0, 462 | 7: 1, 463 | 8: 0, 464 | 11: 1, 465 | 12: 0, 466 | 15: 0 467 | } 468 | }, 469 | Vector2i(8, 3): { 470 | 0: 0, 471 | 1: { 472 | 0: 0, 473 | 3: 1, 474 | 4: 1, 475 | 7: 1, 476 | 8: 1, 477 | 11: 1, 478 | 12: 0, 479 | 15: 0 480 | } 481 | }, 482 | Vector2i(9, 0): { 483 | 0: 0, 484 | 1: { 485 | 0: 0, 486 | 3: 0, 487 | 4: 0, 488 | 7: 0, 489 | 8: 0, 490 | 11: 1, 491 | 12: 0, 492 | 15: 1 493 | } 494 | }, 495 | Vector2i(9, 1): { 496 | 0: 0, 497 | 1: { 498 | 0: 0, 499 | 3: 1, 500 | 4: 0, 501 | 7: 0, 502 | 8: 0, 503 | 11: 1, 504 | 12: 0, 505 | 15: 0 506 | } 507 | }, 508 | Vector2i(9, 2): { 509 | 0: 0, 510 | 1: { 511 | 0: 0, 512 | 3: 0, 513 | 4: 0, 514 | 7: 0, 515 | 8: 0, 516 | 11: 0, 517 | 12: 0, 518 | 15: 0 519 | } 520 | }, 521 | Vector2i(9, 3): { 522 | 0: 0, 523 | 1: { 524 | 0: 0, 525 | 3: 1, 526 | 4: 1, 527 | 7: 1, 528 | 8: 0, 529 | 11: 0, 530 | 12: 0, 531 | 15: 0 532 | } 533 | }, 534 | Vector2i(10, 0): { 535 | 0: 0, 536 | 1: { 537 | 0: 0, 538 | 3: 0, 539 | 4: 0, 540 | 7: 0, 541 | 8: 0, 542 | 11: 1, 543 | 12: 1, 544 | 15: 1 545 | } 546 | }, 547 | Vector2i(10, 1): { 548 | 0: 1, 549 | 1: { 550 | 0: 1, 551 | 3: 1, 552 | 4: 1, 553 | 7: 1, 554 | 8: 1, 555 | 11: 1, 556 | 12: 1, 557 | 15: 1 558 | } 559 | }, 560 | Vector2i(10, 2): { 561 | 0: 0, 562 | 1: { 563 | 0: 0, 564 | 3: 0, 565 | 4: 0, 566 | 7: 1, 567 | 8: 0, 568 | 11: 0, 569 | 12: 0, 570 | 15: 1 571 | } 572 | }, 573 | Vector2i(10, 3): { 574 | 0: 0, 575 | 1: { 576 | 0: 0, 577 | 3: 1, 578 | 4: 0, 579 | 7: 1, 580 | 8: 0, 581 | 11: 0, 582 | 12: 0, 583 | 15: 0 584 | } 585 | }, 586 | Vector2i(11, 0): { 587 | 0: 0, 588 | 1: { 589 | 0: 1, 590 | 3: 1, 591 | 4: 0, 592 | 7: 0, 593 | 8: 0, 594 | 11: 1, 595 | 12: 1, 596 | 15: 1 597 | } 598 | }, 599 | Vector2i(11, 1): { 600 | 0: 0, 601 | 1: { 602 | 0: 0, 603 | 3: 1, 604 | 4: 0, 605 | 7: 0, 606 | 8: 0, 607 | 11: 0, 608 | 12: 0, 609 | 15: 1 610 | } 611 | }, 612 | Vector2i(11, 2): { 613 | 0: 0, 614 | 1: { 615 | 0: 1, 616 | 3: 1, 617 | 4: 0, 618 | 7: 0, 619 | 8: 0, 620 | 11: 0, 621 | 12: 0, 622 | 15: 1 623 | } 624 | }, 625 | Vector2i(11, 3): { 626 | 0: 0, 627 | 1: { 628 | 0: 1, 629 | 3: 1, 630 | 4: 1, 631 | 7: 1, 632 | 8: 0, 633 | 11: 0, 634 | 12: 0, 635 | 15: 1 636 | } 637 | } 638 | } 639 | terrain_set = -1 640 | terrain_mode = 0 641 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/templates/simple_4-tile_(inside_corners).tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=2 format=3 uid="uid://c5o8gekgy74pn"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/core/template_bit_data.gd" id="1_5cbea"] 4 | 5 | [resource] 6 | script = ExtResource("1_5cbea") 7 | version = "0.2.0" 8 | template_name = "Simple 4-Tile (Inside Corners)" 9 | template_description = "Use with Simple 9-Tile (Outside Corners). These simple autotiles are commonly found in spritesheets. Combining sets of inside and outside corners allows drawing a variety of over-lapping rectangles. However, the diagonal corners found in a full set of Corner tiles will be missing. And, like all Corner-mode autotiles, single-tile lines are not possible." 10 | _custom_tags = ["Incomplete Autotile", "Simple"] 11 | template_terrain_count = 2 12 | example_folder_path = "res://addons/tile_bit_tools/examples/simple_tilesets/" 13 | _tiles = { 14 | Vector2i(0, 0): { 15 | 0: 0, 16 | 1: { 17 | 3: 1, 18 | 7: 0, 19 | 11: 0, 20 | 15: 0 21 | } 22 | }, 23 | Vector2i(0, 1): { 24 | 0: 0, 25 | 1: { 26 | 3: 0, 27 | 7: 0, 28 | 11: 0, 29 | 15: 1 30 | } 31 | }, 32 | Vector2i(1, 0): { 33 | 0: 0, 34 | 1: { 35 | 3: 0, 36 | 7: 1, 37 | 11: 0, 38 | 15: 0 39 | } 40 | }, 41 | Vector2i(1, 1): { 42 | 0: 0, 43 | 1: { 44 | 3: 0, 45 | 7: 0, 46 | 11: 1, 47 | 15: 0 48 | } 49 | } 50 | } 51 | terrain_set = -1 52 | terrain_mode = 1 53 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/templates/simple_9-tile_(inside_corners).tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=2 format=3 uid="uid://4x5kx54f54be"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/core/template_bit_data.gd" id="1_00o4j"] 4 | 5 | [resource] 6 | script = ExtResource("1_00o4j") 7 | version = "0.2.0" 8 | template_name = "Simple 9-Tile (Inside Corners)" 9 | template_description = "Use with Simple 9-Tile (Outside Corners). These simple autotiles are commonly found in spritesheets. Combining sets of inside and outside corners allows drawing a variety of over-lapping rectangles. However, the diagonal corners found in a full set of Corner tiles will be missing. And, like all Corner-mode autotiles, single-tile lines are not possible." 10 | _custom_tags = ["Incomplete Autotile", "Simple"] 11 | template_terrain_count = 2 12 | example_folder_path = "res://addons/tile_bit_tools/examples/simple_tilesets/" 13 | _tiles = { 14 | Vector2i(0, 0): { 15 | 0: 0, 16 | 1: { 17 | 3: 1, 18 | 7: 0, 19 | 11: 0, 20 | 15: 0 21 | } 22 | }, 23 | Vector2i(0, 1): { 24 | 0: 0, 25 | 1: { 26 | 3: 1, 27 | 7: 0, 28 | 11: 0, 29 | 15: 1 30 | } 31 | }, 32 | Vector2i(0, 2): { 33 | 0: 0, 34 | 1: { 35 | 3: 0, 36 | 7: 0, 37 | 11: 0, 38 | 15: 1 39 | } 40 | }, 41 | Vector2i(1, 0): { 42 | 0: 0, 43 | 1: { 44 | 3: 1, 45 | 7: 1, 46 | 11: 0, 47 | 15: 0 48 | } 49 | }, 50 | Vector2i(1, 1): { 51 | 0: 1, 52 | 1: { 53 | 3: 1, 54 | 7: 1, 55 | 11: 1, 56 | 15: 1 57 | } 58 | }, 59 | Vector2i(1, 2): { 60 | 0: 0, 61 | 1: { 62 | 3: 0, 63 | 7: 0, 64 | 11: 1, 65 | 15: 1 66 | } 67 | }, 68 | Vector2i(2, 0): { 69 | 0: 0, 70 | 1: { 71 | 3: 0, 72 | 7: 1, 73 | 11: 0, 74 | 15: 0 75 | } 76 | }, 77 | Vector2i(2, 1): { 78 | 0: 0, 79 | 1: { 80 | 3: 0, 81 | 7: 1, 82 | 11: 1, 83 | 15: 0 84 | } 85 | }, 86 | Vector2i(2, 2): { 87 | 0: 0, 88 | 1: { 89 | 3: 0, 90 | 7: 0, 91 | 11: 1, 92 | 15: 0 93 | } 94 | } 95 | } 96 | terrain_set = -1 97 | terrain_mode = 1 98 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/templates/simple_9-tile_(outside_corners).tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=2 format=3 uid="uid://cs67cjf5v5e8m"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/core/template_bit_data.gd" id="1_5bw30"] 4 | 5 | [resource] 6 | script = ExtResource("1_5bw30") 7 | version = "0.2.0" 8 | template_name = "Simple 9-Tile (Outside Corners)" 9 | template_description = "Use with Simple 4- or 9-Tile (Inside Corners). These simple autotiles are commonly found in spritesheets. Combining sets of inside and outside corners allows drawing a variety of over-lapping rectangles. However, the diagonal corners found in a full set of Corner tiles will be missing. And, like all Corner-mode autotiles, single-tile lines are not possible." 10 | _custom_tags = ["Incomplete Autotile", "Simple"] 11 | template_terrain_count = 2 12 | example_folder_path = "res://addons/tile_bit_tools/examples/simple_tilesets/" 13 | _tiles = { 14 | Vector2i(0, 0): { 15 | 0: 0, 16 | 1: { 17 | 3: 0, 18 | 7: 1, 19 | 11: 1, 20 | 15: 1 21 | } 22 | }, 23 | Vector2i(0, 1): { 24 | 0: 0, 25 | 1: { 26 | 3: 0, 27 | 7: 1, 28 | 11: 1, 29 | 15: 0 30 | } 31 | }, 32 | Vector2i(0, 2): { 33 | 0: 0, 34 | 1: { 35 | 3: 1, 36 | 7: 1, 37 | 11: 1, 38 | 15: 0 39 | } 40 | }, 41 | Vector2i(1, 0): { 42 | 0: 0, 43 | 1: { 44 | 3: 0, 45 | 7: 0, 46 | 11: 1, 47 | 15: 1 48 | } 49 | }, 50 | Vector2i(1, 1): { 51 | 0: 0, 52 | 1: { 53 | 3: 0, 54 | 7: 0, 55 | 11: 0, 56 | 15: 0 57 | } 58 | }, 59 | Vector2i(1, 2): { 60 | 0: 0, 61 | 1: { 62 | 3: 1, 63 | 7: 1, 64 | 11: 0, 65 | 15: 0 66 | } 67 | }, 68 | Vector2i(2, 0): { 69 | 0: 0, 70 | 1: { 71 | 3: 1, 72 | 7: 0, 73 | 11: 1, 74 | 15: 1 75 | } 76 | }, 77 | Vector2i(2, 1): { 78 | 0: 0, 79 | 1: { 80 | 3: 1, 81 | 7: 0, 82 | 11: 0, 83 | 15: 1 84 | } 85 | }, 86 | Vector2i(2, 2): { 87 | 0: 0, 88 | 1: { 89 | 3: 1, 90 | 7: 1, 91 | 11: 0, 92 | 15: 1 93 | } 94 | } 95 | } 96 | terrain_set = -1 97 | terrain_mode = 1 98 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/templates/tilesetter_blob.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=2 format=3 uid="uid://b304wbf3x68vn"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/core/template_bit_data.gd" id="1_xoo0e"] 4 | 5 | [resource] 6 | script = ExtResource("1_xoo0e") 7 | version = "0.2.0" 8 | template_name = "Blob (Tilesetter)" 9 | template_description = "Corners-and-Sides autotile in Tilesetter's layout. In Tilesetter, select the center tile, build borders for 'Blob', then select the tiles and export as 'Image'. Does not include the stray single tile. Select that tile separately, and click 'Fill'." 10 | _custom_tags = ["Tilesetter"] 11 | template_terrain_count = 2 12 | example_folder_path = "res://addons/tile_bit_tools/examples/tilesetter_blob/" 13 | _tiles = { 14 | Vector2i(0, 0): { 15 | 0: 0, 16 | 1: { 17 | 0: 0, 18 | 3: 0, 19 | 4: 0, 20 | 7: 1, 21 | 8: 1, 22 | 11: 1, 23 | 12: 1, 24 | 15: 1 25 | } 26 | }, 27 | Vector2i(0, 1): { 28 | 0: 0, 29 | 1: { 30 | 0: 0, 31 | 3: 0, 32 | 4: 0, 33 | 7: 1, 34 | 8: 1, 35 | 11: 1, 36 | 12: 0, 37 | 15: 0 38 | } 39 | }, 40 | Vector2i(0, 2): { 41 | 0: 0, 42 | 1: { 43 | 0: 0, 44 | 3: 1, 45 | 4: 1, 46 | 7: 1, 47 | 8: 1, 48 | 11: 1, 49 | 12: 0, 50 | 15: 0 51 | } 52 | }, 53 | Vector2i(0, 3): { 54 | 0: 0, 55 | 1: { 56 | 0: 0, 57 | 3: 1, 58 | 4: 1, 59 | 7: 1, 60 | 8: 1, 61 | 11: 1, 62 | 12: 1, 63 | 15: 1 64 | } 65 | }, 66 | Vector2i(1, 0): { 67 | 0: 0, 68 | 1: { 69 | 0: 0, 70 | 3: 0, 71 | 4: 0, 72 | 7: 0, 73 | 8: 0, 74 | 11: 1, 75 | 12: 1, 76 | 15: 1 77 | } 78 | }, 79 | Vector2i(1, 1): { 80 | 0: 0, 81 | 1: { 82 | 0: 0, 83 | 3: 0, 84 | 4: 0, 85 | 7: 0, 86 | 8: 0, 87 | 11: 0, 88 | 12: 0, 89 | 15: 0 90 | } 91 | }, 92 | Vector2i(1, 2): { 93 | 0: 0, 94 | 1: { 95 | 0: 0, 96 | 3: 1, 97 | 4: 1, 98 | 7: 1, 99 | 8: 0, 100 | 11: 0, 101 | 12: 0, 102 | 15: 0 103 | } 104 | }, 105 | Vector2i(1, 3): { 106 | 0: 0, 107 | 1: { 108 | 0: 0, 109 | 3: 1, 110 | 4: 1, 111 | 7: 1, 112 | 8: 0, 113 | 11: 1, 114 | 12: 1, 115 | 15: 1 116 | } 117 | }, 118 | Vector2i(2, 0): { 119 | 0: 0, 120 | 1: { 121 | 0: 1, 122 | 3: 1, 123 | 4: 0, 124 | 7: 0, 125 | 8: 0, 126 | 11: 1, 127 | 12: 1, 128 | 15: 1 129 | } 130 | }, 131 | Vector2i(2, 1): { 132 | 0: 0, 133 | 1: { 134 | 0: 1, 135 | 3: 1, 136 | 4: 0, 137 | 7: 0, 138 | 8: 0, 139 | 11: 0, 140 | 12: 0, 141 | 15: 1 142 | } 143 | }, 144 | Vector2i(2, 2): { 145 | 0: 0, 146 | 1: { 147 | 0: 1, 148 | 3: 1, 149 | 4: 1, 150 | 7: 1, 151 | 8: 0, 152 | 11: 0, 153 | 12: 0, 154 | 15: 1 155 | } 156 | }, 157 | Vector2i(2, 3): { 158 | 0: 0, 159 | 1: { 160 | 0: 1, 161 | 3: 1, 162 | 4: 1, 163 | 7: 1, 164 | 8: 0, 165 | 11: 1, 166 | 12: 1, 167 | 15: 1 168 | } 169 | }, 170 | Vector2i(3, 0): { 171 | 0: 0, 172 | 1: { 173 | 0: 1, 174 | 3: 1, 175 | 4: 0, 176 | 7: 1, 177 | 8: 1, 178 | 11: 1, 179 | 12: 1, 180 | 15: 1 181 | } 182 | }, 183 | Vector2i(3, 1): { 184 | 0: 0, 185 | 1: { 186 | 0: 1, 187 | 3: 1, 188 | 4: 0, 189 | 7: 1, 190 | 8: 1, 191 | 11: 1, 192 | 12: 0, 193 | 15: 1 194 | } 195 | }, 196 | Vector2i(3, 2): { 197 | 0: 0, 198 | 1: { 199 | 0: 1, 200 | 3: 1, 201 | 4: 1, 202 | 7: 1, 203 | 8: 1, 204 | 11: 1, 205 | 12: 0, 206 | 15: 1 207 | } 208 | }, 209 | Vector2i(3, 3): { 210 | 0: 0, 211 | 1: { 212 | 0: 1, 213 | 3: 1, 214 | 4: 1, 215 | 7: 1, 216 | 8: 1, 217 | 11: 1, 218 | 12: 1, 219 | 15: 1 220 | } 221 | }, 222 | Vector2i(4, 0): { 223 | 0: 0, 224 | 1: { 225 | 0: 0, 226 | 3: 1, 227 | 4: 0, 228 | 7: 1, 229 | 8: 1, 230 | 11: 1, 231 | 12: 1, 232 | 15: 1 233 | } 234 | }, 235 | Vector2i(4, 1): { 236 | 0: 0, 237 | 1: { 238 | 0: 0, 239 | 3: 1, 240 | 4: 0, 241 | 7: 1, 242 | 8: 1, 243 | 11: 1, 244 | 12: 0, 245 | 15: 0 246 | } 247 | }, 248 | Vector2i(4, 2): { 249 | 0: 0, 250 | 1: { 251 | 0: 0, 252 | 3: 0, 253 | 4: 0, 254 | 7: 1, 255 | 8: 1, 256 | 11: 1, 257 | 12: 0, 258 | 15: 1 259 | } 260 | }, 261 | Vector2i(4, 3): { 262 | 0: 0, 263 | 1: { 264 | 0: 0, 265 | 3: 1, 266 | 4: 1, 267 | 7: 1, 268 | 8: 1, 269 | 11: 1, 270 | 12: 0, 271 | 15: 1 272 | } 273 | }, 274 | Vector2i(4, 4): { 275 | 0: 0, 276 | 1: { 277 | 0: 0, 278 | 3: 1, 279 | 4: 0, 280 | 7: 1, 281 | 8: 1, 282 | 11: 1, 283 | 12: 0, 284 | 15: 1 285 | } 286 | }, 287 | Vector2i(5, 0): { 288 | 0: 0, 289 | 1: { 290 | 0: 0, 291 | 3: 1, 292 | 4: 0, 293 | 7: 0, 294 | 8: 0, 295 | 11: 1, 296 | 12: 1, 297 | 15: 1 298 | } 299 | }, 300 | Vector2i(5, 1): { 301 | 0: 0, 302 | 1: { 303 | 0: 0, 304 | 3: 1, 305 | 4: 0, 306 | 7: 0, 307 | 8: 0, 308 | 11: 0, 309 | 12: 0, 310 | 15: 0 311 | } 312 | }, 313 | Vector2i(5, 2): { 314 | 0: 0, 315 | 1: { 316 | 0: 0, 317 | 3: 0, 318 | 4: 0, 319 | 7: 0, 320 | 8: 0, 321 | 11: 0, 322 | 12: 0, 323 | 15: 1 324 | } 325 | }, 326 | Vector2i(5, 3): { 327 | 0: 0, 328 | 1: { 329 | 0: 0, 330 | 3: 1, 331 | 4: 1, 332 | 7: 1, 333 | 8: 0, 334 | 11: 0, 335 | 12: 0, 336 | 15: 1 337 | } 338 | }, 339 | Vector2i(5, 4): { 340 | 0: 0, 341 | 1: { 342 | 0: 0, 343 | 3: 1, 344 | 4: 0, 345 | 7: 0, 346 | 8: 0, 347 | 11: 0, 348 | 12: 0, 349 | 15: 1 350 | } 351 | }, 352 | Vector2i(6, 0): { 353 | 0: 0, 354 | 1: { 355 | 0: 0, 356 | 3: 0, 357 | 4: 0, 358 | 7: 1, 359 | 8: 0, 360 | 11: 1, 361 | 12: 1, 362 | 15: 1 363 | } 364 | }, 365 | Vector2i(6, 1): { 366 | 0: 0, 367 | 1: { 368 | 0: 0, 369 | 3: 0, 370 | 4: 0, 371 | 7: 1, 372 | 8: 0, 373 | 11: 0, 374 | 12: 0, 375 | 15: 0 376 | } 377 | }, 378 | Vector2i(6, 2): { 379 | 0: 0, 380 | 1: { 381 | 0: 0, 382 | 3: 0, 383 | 4: 0, 384 | 7: 0, 385 | 8: 0, 386 | 11: 1, 387 | 12: 0, 388 | 15: 0 389 | } 390 | }, 391 | Vector2i(6, 3): { 392 | 0: 0, 393 | 1: { 394 | 0: 0, 395 | 3: 1, 396 | 4: 1, 397 | 7: 1, 398 | 8: 0, 399 | 11: 1, 400 | 12: 0, 401 | 15: 0 402 | } 403 | }, 404 | Vector2i(6, 4): { 405 | 0: 0, 406 | 1: { 407 | 0: 0, 408 | 3: 0, 409 | 4: 0, 410 | 7: 1, 411 | 8: 0, 412 | 11: 1, 413 | 12: 0, 414 | 15: 0 415 | } 416 | }, 417 | Vector2i(7, 0): { 418 | 0: 0, 419 | 1: { 420 | 0: 1, 421 | 3: 1, 422 | 4: 0, 423 | 7: 1, 424 | 8: 0, 425 | 11: 1, 426 | 12: 1, 427 | 15: 1 428 | } 429 | }, 430 | Vector2i(7, 1): { 431 | 0: 0, 432 | 1: { 433 | 0: 1, 434 | 3: 1, 435 | 4: 0, 436 | 7: 1, 437 | 8: 0, 438 | 11: 0, 439 | 12: 0, 440 | 15: 1 441 | } 442 | }, 443 | Vector2i(7, 2): { 444 | 0: 0, 445 | 1: { 446 | 0: 1, 447 | 3: 1, 448 | 4: 0, 449 | 7: 0, 450 | 8: 0, 451 | 11: 1, 452 | 12: 0, 453 | 15: 1 454 | } 455 | }, 456 | Vector2i(7, 3): { 457 | 0: 0, 458 | 1: { 459 | 0: 1, 460 | 3: 1, 461 | 4: 1, 462 | 7: 1, 463 | 8: 0, 464 | 11: 1, 465 | 12: 0, 466 | 15: 1 467 | } 468 | }, 469 | Vector2i(7, 4): { 470 | 0: 0, 471 | 1: { 472 | 0: 1, 473 | 3: 1, 474 | 4: 0, 475 | 7: 1, 476 | 8: 0, 477 | 11: 1, 478 | 12: 0, 479 | 15: 1 480 | } 481 | }, 482 | Vector2i(8, 0): { 483 | 0: 0, 484 | 1: { 485 | 0: 0, 486 | 3: 1, 487 | 4: 0, 488 | 7: 1, 489 | 8: 0, 490 | 11: 1, 491 | 12: 1, 492 | 15: 1 493 | } 494 | }, 495 | Vector2i(8, 1): { 496 | 0: 0, 497 | 1: { 498 | 0: 0, 499 | 3: 1, 500 | 4: 0, 501 | 7: 1, 502 | 8: 0, 503 | 11: 0, 504 | 12: 0, 505 | 15: 0 506 | } 507 | }, 508 | Vector2i(8, 2): { 509 | 0: 0, 510 | 1: { 511 | 0: 0, 512 | 3: 0, 513 | 4: 0, 514 | 7: 0, 515 | 8: 0, 516 | 11: 1, 517 | 12: 0, 518 | 15: 1 519 | } 520 | }, 521 | Vector2i(8, 3): { 522 | 0: 0, 523 | 1: { 524 | 0: 0, 525 | 3: 1, 526 | 4: 1, 527 | 7: 1, 528 | 8: 0, 529 | 11: 1, 530 | 12: 0, 531 | 15: 1 532 | } 533 | }, 534 | Vector2i(8, 4): { 535 | 0: 0, 536 | 1: { 537 | 0: 0, 538 | 3: 1, 539 | 4: 0, 540 | 7: 1, 541 | 8: 0, 542 | 11: 1, 543 | 12: 0, 544 | 15: 1 545 | } 546 | }, 547 | Vector2i(9, 0): { 548 | 0: 0, 549 | 1: { 550 | 0: 0, 551 | 3: 0, 552 | 4: 0, 553 | 7: 1, 554 | 8: 0, 555 | 11: 0, 556 | 12: 0, 557 | 15: 1 558 | } 559 | }, 560 | Vector2i(9, 1): { 561 | 0: 0, 562 | 1: { 563 | 0: 0, 564 | 3: 1, 565 | 4: 0, 566 | 7: 0, 567 | 8: 0, 568 | 11: 1, 569 | 12: 0, 570 | 15: 0 571 | } 572 | }, 573 | Vector2i(9, 2): { 574 | 0: 0, 575 | 1: { 576 | 0: 0, 577 | 3: 0, 578 | 4: 0, 579 | 7: 1, 580 | 8: 0, 581 | 11: 1, 582 | 12: 0, 583 | 15: 1 584 | } 585 | }, 586 | Vector2i(9, 3): { 587 | 0: 0, 588 | 1: { 589 | 0: 0, 590 | 3: 1, 591 | 4: 0, 592 | 7: 1, 593 | 8: 0, 594 | 11: 1, 595 | 12: 0, 596 | 15: 0 597 | } 598 | }, 599 | Vector2i(10, 2): { 600 | 0: 0, 601 | 1: { 602 | 0: 0, 603 | 3: 1, 604 | 4: 0, 605 | 7: 0, 606 | 8: 0, 607 | 11: 1, 608 | 12: 0, 609 | 15: 1 610 | } 611 | }, 612 | Vector2i(10, 3): { 613 | 0: 0, 614 | 1: { 615 | 0: 0, 616 | 3: 1, 617 | 4: 0, 618 | 7: 1, 619 | 8: 0, 620 | 11: 0, 621 | 12: 0, 622 | 15: 1 623 | } 624 | } 625 | } 626 | terrain_set = -1 627 | terrain_mode = 0 628 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/templates/tilesetter_wang.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=2 format=3 uid="uid://oenc7os41oe3"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/core/template_bit_data.gd" id="1_dbw6t"] 4 | 5 | [resource] 6 | script = ExtResource("1_dbw6t") 7 | version = "0.2.0" 8 | template_name = "Wang (Tilesetter)" 9 | template_description = "Corners autotile in Tilesetter's layout. In Tilesetter, select the center tile, build borders for 'Wang', then select the tiles and export as 'Image'. Does not include the stray single tile. Select that tile separately, and click 'Fill'." 10 | _custom_tags = ["Tilesetter"] 11 | template_terrain_count = 2 12 | example_folder_path = "res://addons/tile_bit_tools/examples/tilesetter_wang/" 13 | _tiles = { 14 | Vector2i(0, 0): { 15 | 0: 0, 16 | 1: { 17 | 3: 0, 18 | 7: 1, 19 | 11: 1, 20 | 15: 1 21 | } 22 | }, 23 | Vector2i(0, 1): { 24 | 0: 0, 25 | 1: { 26 | 3: 0, 27 | 7: 1, 28 | 11: 1, 29 | 15: 0 30 | } 31 | }, 32 | Vector2i(0, 2): { 33 | 0: 0, 34 | 1: { 35 | 3: 1, 36 | 7: 1, 37 | 11: 1, 38 | 15: 0 39 | } 40 | }, 41 | Vector2i(1, 0): { 42 | 0: 0, 43 | 1: { 44 | 3: 0, 45 | 7: 0, 46 | 11: 1, 47 | 15: 1 48 | } 49 | }, 50 | Vector2i(1, 1): { 51 | 0: 0, 52 | 1: { 53 | 3: 0, 54 | 7: 0, 55 | 11: 0, 56 | 15: 0 57 | } 58 | }, 59 | Vector2i(1, 2): { 60 | 0: 0, 61 | 1: { 62 | 3: 1, 63 | 7: 1, 64 | 11: 0, 65 | 15: 0 66 | } 67 | }, 68 | Vector2i(2, 0): { 69 | 0: 0, 70 | 1: { 71 | 3: 1, 72 | 7: 0, 73 | 11: 1, 74 | 15: 1 75 | } 76 | }, 77 | Vector2i(2, 1): { 78 | 0: 0, 79 | 1: { 80 | 3: 1, 81 | 7: 0, 82 | 11: 0, 83 | 15: 1 84 | } 85 | }, 86 | Vector2i(2, 2): { 87 | 0: 0, 88 | 1: { 89 | 3: 1, 90 | 7: 1, 91 | 11: 0, 92 | 15: 1 93 | } 94 | }, 95 | Vector2i(3, 0): { 96 | 0: 0, 97 | 1: { 98 | 3: 1, 99 | 7: 0, 100 | 11: 0, 101 | 15: 0 102 | } 103 | }, 104 | Vector2i(3, 1): { 105 | 0: 0, 106 | 1: { 107 | 3: 0, 108 | 7: 0, 109 | 11: 0, 110 | 15: 1 111 | } 112 | }, 113 | Vector2i(3, 2): { 114 | 0: 0, 115 | 1: { 116 | 3: 0, 117 | 7: 1, 118 | 11: 0, 119 | 15: 1 120 | } 121 | }, 122 | Vector2i(4, 0): { 123 | 0: 0, 124 | 1: { 125 | 3: 0, 126 | 7: 1, 127 | 11: 0, 128 | 15: 0 129 | } 130 | }, 131 | Vector2i(4, 1): { 132 | 0: 0, 133 | 1: { 134 | 3: 0, 135 | 7: 0, 136 | 11: 1, 137 | 15: 0 138 | } 139 | }, 140 | Vector2i(4, 2): { 141 | 0: 0, 142 | 1: { 143 | 3: 1, 144 | 7: 0, 145 | 11: 1, 146 | 15: 0 147 | } 148 | } 149 | } 150 | terrain_set = -1 151 | terrain_mode = 1 152 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/templates/tilesetter_wang_3-terrain.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=2 format=3 uid="uid://da0d3b3822j63"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/core/template_bit_data.gd" id="1_hxqib"] 4 | 5 | [resource] 6 | script = ExtResource("1_hxqib") 7 | version = "0.2.0" 8 | template_name = "Wang 3-Terrain (Tilesetter)" 9 | template_description = "Full autotile for 3-terrain Wang tilesets made in Tilesetter. In Tilesetter, select the center tiles for all three terrains at once, build borders for 'Wang', then select the tiles and export as 'Image'." 10 | _custom_tags = ["Tilesetter"] 11 | template_terrain_count = 3 12 | example_folder_path = "res://addons/tile_bit_tools/examples/tilesetter_wang_3_terrains/" 13 | _tiles = { 14 | Vector2i(0, 1): { 15 | 0: 1, 16 | 1: { 17 | 3: 1, 18 | 7: 1, 19 | 11: 1, 20 | 15: 1 21 | } 22 | }, 23 | Vector2i(0, 4): { 24 | 0: 2, 25 | 1: { 26 | 3: 2, 27 | 7: 2, 28 | 11: 2, 29 | 15: 2 30 | } 31 | }, 32 | Vector2i(1, 0): { 33 | 0: 0, 34 | 1: { 35 | 3: 0, 36 | 7: 1, 37 | 11: 1, 38 | 15: 1 39 | } 40 | }, 41 | Vector2i(1, 1): { 42 | 0: 0, 43 | 1: { 44 | 3: 0, 45 | 7: 1, 46 | 11: 1, 47 | 15: 0 48 | } 49 | }, 50 | Vector2i(1, 2): { 51 | 0: 0, 52 | 1: { 53 | 3: 1, 54 | 7: 1, 55 | 11: 1, 56 | 15: 0 57 | } 58 | }, 59 | Vector2i(1, 3): { 60 | 0: 0, 61 | 1: { 62 | 3: 0, 63 | 7: 2, 64 | 11: 2, 65 | 15: 2 66 | } 67 | }, 68 | Vector2i(1, 4): { 69 | 0: 0, 70 | 1: { 71 | 3: 0, 72 | 7: 2, 73 | 11: 2, 74 | 15: 0 75 | } 76 | }, 77 | Vector2i(1, 5): { 78 | 0: 0, 79 | 1: { 80 | 3: 2, 81 | 7: 2, 82 | 11: 2, 83 | 15: 0 84 | } 85 | }, 86 | Vector2i(1, 6): { 87 | 0: 1, 88 | 1: { 89 | 3: 1, 90 | 7: 2, 91 | 11: 2, 92 | 15: 2 93 | } 94 | }, 95 | Vector2i(1, 7): { 96 | 0: 1, 97 | 1: { 98 | 3: 1, 99 | 7: 2, 100 | 11: 2, 101 | 15: 1 102 | } 103 | }, 104 | Vector2i(1, 8): { 105 | 0: 1, 106 | 1: { 107 | 3: 2, 108 | 7: 2, 109 | 11: 2, 110 | 15: 1 111 | } 112 | }, 113 | Vector2i(2, 0): { 114 | 0: 0, 115 | 1: { 116 | 3: 0, 117 | 7: 0, 118 | 11: 1, 119 | 15: 1 120 | } 121 | }, 122 | Vector2i(2, 1): { 123 | 0: 0, 124 | 1: { 125 | 3: 0, 126 | 7: 0, 127 | 11: 0, 128 | 15: 0 129 | } 130 | }, 131 | Vector2i(2, 2): { 132 | 0: 0, 133 | 1: { 134 | 3: 1, 135 | 7: 1, 136 | 11: 0, 137 | 15: 0 138 | } 139 | }, 140 | Vector2i(2, 3): { 141 | 0: 0, 142 | 1: { 143 | 3: 0, 144 | 7: 0, 145 | 11: 2, 146 | 15: 2 147 | } 148 | }, 149 | Vector2i(2, 5): { 150 | 0: 0, 151 | 1: { 152 | 3: 2, 153 | 7: 2, 154 | 11: 0, 155 | 15: 0 156 | } 157 | }, 158 | Vector2i(2, 6): { 159 | 0: 1, 160 | 1: { 161 | 3: 1, 162 | 7: 1, 163 | 11: 2, 164 | 15: 2 165 | } 166 | }, 167 | Vector2i(2, 8): { 168 | 0: 1, 169 | 1: { 170 | 3: 2, 171 | 7: 2, 172 | 11: 1, 173 | 15: 1 174 | } 175 | }, 176 | Vector2i(3, 0): { 177 | 0: 0, 178 | 1: { 179 | 3: 1, 180 | 7: 0, 181 | 11: 1, 182 | 15: 1 183 | } 184 | }, 185 | Vector2i(3, 1): { 186 | 0: 0, 187 | 1: { 188 | 3: 1, 189 | 7: 0, 190 | 11: 0, 191 | 15: 1 192 | } 193 | }, 194 | Vector2i(3, 2): { 195 | 0: 0, 196 | 1: { 197 | 3: 1, 198 | 7: 1, 199 | 11: 0, 200 | 15: 1 201 | } 202 | }, 203 | Vector2i(3, 3): { 204 | 0: 0, 205 | 1: { 206 | 3: 2, 207 | 7: 0, 208 | 11: 2, 209 | 15: 2 210 | } 211 | }, 212 | Vector2i(3, 4): { 213 | 0: 0, 214 | 1: { 215 | 3: 2, 216 | 7: 0, 217 | 11: 0, 218 | 15: 2 219 | } 220 | }, 221 | Vector2i(3, 5): { 222 | 0: 0, 223 | 1: { 224 | 3: 2, 225 | 7: 2, 226 | 11: 0, 227 | 15: 2 228 | } 229 | }, 230 | Vector2i(3, 6): { 231 | 0: 1, 232 | 1: { 233 | 3: 2, 234 | 7: 1, 235 | 11: 2, 236 | 15: 2 237 | } 238 | }, 239 | Vector2i(3, 7): { 240 | 0: 1, 241 | 1: { 242 | 3: 2, 243 | 7: 1, 244 | 11: 1, 245 | 15: 2 246 | } 247 | }, 248 | Vector2i(3, 8): { 249 | 0: 1, 250 | 1: { 251 | 3: 2, 252 | 7: 2, 253 | 11: 1, 254 | 15: 2 255 | } 256 | }, 257 | Vector2i(4, 0): { 258 | 0: 0, 259 | 1: { 260 | 3: 1, 261 | 7: 0, 262 | 11: 0, 263 | 15: 0 264 | } 265 | }, 266 | Vector2i(4, 1): { 267 | 0: 0, 268 | 1: { 269 | 3: 0, 270 | 7: 0, 271 | 11: 0, 272 | 15: 1 273 | } 274 | }, 275 | Vector2i(4, 2): { 276 | 0: 0, 277 | 1: { 278 | 3: 0, 279 | 7: 1, 280 | 11: 0, 281 | 15: 1 282 | } 283 | }, 284 | Vector2i(4, 3): { 285 | 0: 0, 286 | 1: { 287 | 3: 2, 288 | 7: 0, 289 | 11: 0, 290 | 15: 0 291 | } 292 | }, 293 | Vector2i(4, 4): { 294 | 0: 0, 295 | 1: { 296 | 3: 0, 297 | 7: 0, 298 | 11: 0, 299 | 15: 2 300 | } 301 | }, 302 | Vector2i(4, 5): { 303 | 0: 0, 304 | 1: { 305 | 3: 0, 306 | 7: 2, 307 | 11: 0, 308 | 15: 2 309 | } 310 | }, 311 | Vector2i(4, 6): { 312 | 0: 1, 313 | 1: { 314 | 3: 2, 315 | 7: 1, 316 | 11: 1, 317 | 15: 1 318 | } 319 | }, 320 | Vector2i(4, 7): { 321 | 0: 1, 322 | 1: { 323 | 3: 1, 324 | 7: 1, 325 | 11: 1, 326 | 15: 2 327 | } 328 | }, 329 | Vector2i(4, 8): { 330 | 0: 1, 331 | 1: { 332 | 3: 1, 333 | 7: 2, 334 | 11: 1, 335 | 15: 2 336 | } 337 | }, 338 | Vector2i(5, 0): { 339 | 0: 0, 340 | 1: { 341 | 3: 0, 342 | 7: 1, 343 | 11: 0, 344 | 15: 0 345 | } 346 | }, 347 | Vector2i(5, 1): { 348 | 0: 0, 349 | 1: { 350 | 3: 0, 351 | 7: 0, 352 | 11: 1, 353 | 15: 0 354 | } 355 | }, 356 | Vector2i(5, 2): { 357 | 0: 0, 358 | 1: { 359 | 3: 1, 360 | 7: 0, 361 | 11: 1, 362 | 15: 0 363 | } 364 | }, 365 | Vector2i(5, 3): { 366 | 0: 0, 367 | 1: { 368 | 3: 0, 369 | 7: 2, 370 | 11: 0, 371 | 15: 0 372 | } 373 | }, 374 | Vector2i(5, 4): { 375 | 0: 0, 376 | 1: { 377 | 3: 0, 378 | 7: 0, 379 | 11: 2, 380 | 15: 0 381 | } 382 | }, 383 | Vector2i(5, 5): { 384 | 0: 0, 385 | 1: { 386 | 3: 2, 387 | 7: 0, 388 | 11: 2, 389 | 15: 0 390 | } 391 | }, 392 | Vector2i(5, 6): { 393 | 0: 1, 394 | 1: { 395 | 3: 1, 396 | 7: 2, 397 | 11: 1, 398 | 15: 1 399 | } 400 | }, 401 | Vector2i(5, 7): { 402 | 0: 1, 403 | 1: { 404 | 3: 1, 405 | 7: 1, 406 | 11: 2, 407 | 15: 1 408 | } 409 | }, 410 | Vector2i(5, 8): { 411 | 0: 1, 412 | 1: { 413 | 3: 2, 414 | 7: 1, 415 | 11: 2, 416 | 15: 1 417 | } 418 | }, 419 | Vector2i(6, 6): { 420 | 0: 0, 421 | 1: { 422 | 3: 2, 423 | 7: 0, 424 | 11: 1, 425 | 15: 1 426 | } 427 | }, 428 | Vector2i(6, 7): { 429 | 0: 0, 430 | 1: { 431 | 3: 1, 432 | 7: 1, 433 | 11: 0, 434 | 15: 2 435 | } 436 | }, 437 | Vector2i(6, 8): { 438 | 0: 0, 439 | 1: { 440 | 3: 0, 441 | 7: 1, 442 | 11: 1, 443 | 15: 2 444 | } 445 | }, 446 | Vector2i(6, 9): { 447 | 0: 0, 448 | 1: { 449 | 3: 2, 450 | 7: 1, 451 | 11: 1, 452 | 15: 0 453 | } 454 | }, 455 | Vector2i(6, 10): { 456 | 0: 0, 457 | 1: { 458 | 3: 2, 459 | 7: 2, 460 | 11: 1, 461 | 15: 0 462 | } 463 | }, 464 | Vector2i(6, 11): { 465 | 0: 0, 466 | 1: { 467 | 3: 0, 468 | 7: 1, 469 | 11: 2, 470 | 15: 2 471 | } 472 | }, 473 | Vector2i(7, 6): { 474 | 0: 0, 475 | 1: { 476 | 3: 0, 477 | 7: 2, 478 | 11: 1, 479 | 15: 1 480 | } 481 | }, 482 | Vector2i(7, 7): { 483 | 0: 0, 484 | 1: { 485 | 3: 1, 486 | 7: 1, 487 | 11: 2, 488 | 15: 0 489 | } 490 | }, 491 | Vector2i(7, 8): { 492 | 0: 0, 493 | 1: { 494 | 3: 1, 495 | 7: 0, 496 | 11: 2, 497 | 15: 1 498 | } 499 | }, 500 | Vector2i(7, 9): { 501 | 0: 0, 502 | 1: { 503 | 3: 1, 504 | 7: 2, 505 | 11: 0, 506 | 15: 1 507 | } 508 | }, 509 | Vector2i(7, 10): { 510 | 0: 0, 511 | 1: { 512 | 3: 2, 513 | 7: 2, 514 | 11: 0, 515 | 15: 1 516 | } 517 | }, 518 | Vector2i(7, 11): { 519 | 0: 0, 520 | 1: { 521 | 3: 1, 522 | 7: 0, 523 | 11: 2, 524 | 15: 2 525 | } 526 | }, 527 | Vector2i(8, 6): { 528 | 0: 0, 529 | 1: { 530 | 3: 1, 531 | 7: 0, 532 | 11: 1, 533 | 15: 2 534 | } 535 | }, 536 | Vector2i(8, 7): { 537 | 0: 0, 538 | 1: { 539 | 3: 2, 540 | 7: 1, 541 | 11: 0, 542 | 15: 1 543 | } 544 | }, 545 | Vector2i(8, 8): { 546 | 0: 0, 547 | 1: { 548 | 3: 0, 549 | 7: 2, 550 | 11: 1, 551 | 15: 2 552 | } 553 | }, 554 | Vector2i(8, 9): { 555 | 0: 0, 556 | 1: { 557 | 3: 2, 558 | 7: 1, 559 | 11: 2, 560 | 15: 0 561 | } 562 | }, 563 | Vector2i(8, 10): { 564 | 0: 0, 565 | 1: { 566 | 3: 2, 567 | 7: 0, 568 | 11: 1, 569 | 15: 0 570 | } 571 | }, 572 | Vector2i(8, 11): { 573 | 0: 0, 574 | 1: { 575 | 3: 0, 576 | 7: 1, 577 | 11: 0, 578 | 15: 2 579 | } 580 | }, 581 | Vector2i(9, 6): { 582 | 0: 0, 583 | 1: { 584 | 3: 0, 585 | 7: 1, 586 | 11: 2, 587 | 15: 1 588 | } 589 | }, 590 | Vector2i(9, 7): { 591 | 0: 0, 592 | 1: { 593 | 3: 1, 594 | 7: 2, 595 | 11: 1, 596 | 15: 0 597 | } 598 | }, 599 | Vector2i(9, 8): { 600 | 0: 0, 601 | 1: { 602 | 3: 2, 603 | 7: 0, 604 | 11: 2, 605 | 15: 1 606 | } 607 | }, 608 | Vector2i(9, 9): { 609 | 0: 0, 610 | 1: { 611 | 3: 1, 612 | 7: 2, 613 | 11: 0, 614 | 15: 2 615 | } 616 | }, 617 | Vector2i(9, 10): { 618 | 0: 0, 619 | 1: { 620 | 3: 0, 621 | 7: 2, 622 | 11: 0, 623 | 15: 1 624 | } 625 | }, 626 | Vector2i(9, 11): { 627 | 0: 0, 628 | 1: { 629 | 3: 1, 630 | 7: 0, 631 | 11: 2, 632 | 15: 0 633 | } 634 | }, 635 | Vector2i(10, 6): { 636 | 0: 0, 637 | 1: { 638 | 3: 2, 639 | 7: 0, 640 | 11: 1, 641 | 15: 2 642 | } 643 | }, 644 | Vector2i(10, 7): { 645 | 0: 0, 646 | 1: { 647 | 3: 2, 648 | 7: 1, 649 | 11: 0, 650 | 15: 2 651 | } 652 | }, 653 | Vector2i(10, 8): { 654 | 0: 0, 655 | 1: { 656 | 3: 0, 657 | 7: 0, 658 | 11: 1, 659 | 15: 2 660 | } 661 | }, 662 | Vector2i(10, 9): { 663 | 0: 0, 664 | 1: { 665 | 3: 2, 666 | 7: 1, 667 | 11: 0, 668 | 15: 0 669 | } 670 | }, 671 | Vector2i(10, 10): { 672 | 0: 0, 673 | 1: { 674 | 3: 0, 675 | 7: 2, 676 | 11: 1, 677 | 15: 0 678 | } 679 | }, 680 | Vector2i(10, 11): { 681 | 0: 0, 682 | 1: { 683 | 3: 0, 684 | 7: 1, 685 | 11: 2, 686 | 15: 0 687 | } 688 | }, 689 | Vector2i(11, 6): { 690 | 0: 0, 691 | 1: { 692 | 3: 0, 693 | 7: 2, 694 | 11: 2, 695 | 15: 1 696 | } 697 | }, 698 | Vector2i(11, 7): { 699 | 0: 0, 700 | 1: { 701 | 3: 1, 702 | 7: 2, 703 | 11: 2, 704 | 15: 0 705 | } 706 | }, 707 | Vector2i(11, 8): { 708 | 0: 0, 709 | 1: { 710 | 3: 0, 711 | 7: 0, 712 | 11: 2, 713 | 15: 1 714 | } 715 | }, 716 | Vector2i(11, 9): { 717 | 0: 0, 718 | 1: { 719 | 3: 1, 720 | 7: 2, 721 | 11: 0, 722 | 15: 0 723 | } 724 | }, 725 | Vector2i(11, 10): { 726 | 0: 0, 727 | 1: { 728 | 3: 2, 729 | 7: 0, 730 | 11: 0, 731 | 15: 1 732 | } 733 | }, 734 | Vector2i(11, 11): { 735 | 0: 0, 736 | 1: { 737 | 3: 1, 738 | 7: 0, 739 | 11: 0, 740 | 15: 2 741 | } 742 | } 743 | } 744 | terrain_set = -1 745 | terrain_mode = 1 746 | -------------------------------------------------------------------------------- /addons/tile_bit_tools/templates/tilesetter_wang_3-terrain_transitions.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=2 format=3 uid="uid://cd8m38mk5xhiy"] 2 | 3 | [ext_resource type="Script" path="res://addons/tile_bit_tools/core/template_bit_data.gd" id="1_ubh4a"] 4 | 5 | [resource] 6 | script = ExtResource("1_ubh4a") 7 | version = "0.2.0" 8 | template_name = "Wang 3-Terrain Transition (Tilesetter)" 9 | template_description = "Transition tiles for 3-terrain Wang tilesets made in Tilesetter. Use in conjunction with 'Tilesetter Wang' to achieve the same results as 'Tilesetter Wang 3-Terrain'." 10 | _custom_tags = ["Tilesetter", "Incomplete Autotile"] 11 | template_terrain_count = 3 12 | example_folder_path = "res://addons/tile_bit_tools/examples/tilesetter_wang_3_terrains/" 13 | _tiles = { 14 | Vector2i(0, 0): { 15 | 0: 0, 16 | 1: { 17 | 3: 2, 18 | 7: 0, 19 | 11: 1, 20 | 15: 1 21 | } 22 | }, 23 | Vector2i(0, 1): { 24 | 0: 0, 25 | 1: { 26 | 3: 1, 27 | 7: 1, 28 | 11: 0, 29 | 15: 2 30 | } 31 | }, 32 | Vector2i(0, 2): { 33 | 0: 0, 34 | 1: { 35 | 3: 0, 36 | 7: 1, 37 | 11: 1, 38 | 15: 2 39 | } 40 | }, 41 | Vector2i(0, 3): { 42 | 0: 0, 43 | 1: { 44 | 3: 2, 45 | 7: 1, 46 | 11: 1, 47 | 15: 0 48 | } 49 | }, 50 | Vector2i(0, 4): { 51 | 0: 0, 52 | 1: { 53 | 3: 2, 54 | 7: 2, 55 | 11: 1, 56 | 15: 0 57 | } 58 | }, 59 | Vector2i(0, 5): { 60 | 0: 0, 61 | 1: { 62 | 3: 0, 63 | 7: 1, 64 | 11: 2, 65 | 15: 2 66 | } 67 | }, 68 | Vector2i(1, 0): { 69 | 0: 0, 70 | 1: { 71 | 3: 0, 72 | 7: 2, 73 | 11: 1, 74 | 15: 1 75 | } 76 | }, 77 | Vector2i(1, 1): { 78 | 0: 0, 79 | 1: { 80 | 3: 1, 81 | 7: 1, 82 | 11: 2, 83 | 15: 0 84 | } 85 | }, 86 | Vector2i(1, 2): { 87 | 0: 0, 88 | 1: { 89 | 3: 1, 90 | 7: 0, 91 | 11: 2, 92 | 15: 1 93 | } 94 | }, 95 | Vector2i(1, 3): { 96 | 0: 0, 97 | 1: { 98 | 3: 1, 99 | 7: 2, 100 | 11: 0, 101 | 15: 1 102 | } 103 | }, 104 | Vector2i(1, 4): { 105 | 0: 0, 106 | 1: { 107 | 3: 2, 108 | 7: 2, 109 | 11: 0, 110 | 15: 1 111 | } 112 | }, 113 | Vector2i(1, 5): { 114 | 0: 0, 115 | 1: { 116 | 3: 1, 117 | 7: 0, 118 | 11: 2, 119 | 15: 2 120 | } 121 | }, 122 | Vector2i(2, 0): { 123 | 0: 0, 124 | 1: { 125 | 3: 1, 126 | 7: 0, 127 | 11: 1, 128 | 15: 2 129 | } 130 | }, 131 | Vector2i(2, 1): { 132 | 0: 0, 133 | 1: { 134 | 3: 2, 135 | 7: 1, 136 | 11: 0, 137 | 15: 1 138 | } 139 | }, 140 | Vector2i(2, 2): { 141 | 0: 0, 142 | 1: { 143 | 3: 0, 144 | 7: 2, 145 | 11: 1, 146 | 15: 2 147 | } 148 | }, 149 | Vector2i(2, 3): { 150 | 0: 0, 151 | 1: { 152 | 3: 2, 153 | 7: 1, 154 | 11: 2, 155 | 15: 0 156 | } 157 | }, 158 | Vector2i(2, 4): { 159 | 0: 0, 160 | 1: { 161 | 3: 2, 162 | 7: 0, 163 | 11: 1, 164 | 15: 0 165 | } 166 | }, 167 | Vector2i(2, 5): { 168 | 0: 0, 169 | 1: { 170 | 3: 0, 171 | 7: 1, 172 | 11: 0, 173 | 15: 2 174 | } 175 | }, 176 | Vector2i(3, 0): { 177 | 0: 0, 178 | 1: { 179 | 3: 0, 180 | 7: 1, 181 | 11: 2, 182 | 15: 1 183 | } 184 | }, 185 | Vector2i(3, 1): { 186 | 0: 0, 187 | 1: { 188 | 3: 1, 189 | 7: 2, 190 | 11: 1, 191 | 15: 0 192 | } 193 | }, 194 | Vector2i(3, 2): { 195 | 0: 0, 196 | 1: { 197 | 3: 2, 198 | 7: 0, 199 | 11: 2, 200 | 15: 1 201 | } 202 | }, 203 | Vector2i(3, 3): { 204 | 0: 0, 205 | 1: { 206 | 3: 1, 207 | 7: 2, 208 | 11: 0, 209 | 15: 2 210 | } 211 | }, 212 | Vector2i(3, 4): { 213 | 0: 0, 214 | 1: { 215 | 3: 0, 216 | 7: 2, 217 | 11: 0, 218 | 15: 1 219 | } 220 | }, 221 | Vector2i(3, 5): { 222 | 0: 0, 223 | 1: { 224 | 3: 1, 225 | 7: 0, 226 | 11: 2, 227 | 15: 0 228 | } 229 | }, 230 | Vector2i(4, 0): { 231 | 0: 0, 232 | 1: { 233 | 3: 2, 234 | 7: 0, 235 | 11: 1, 236 | 15: 2 237 | } 238 | }, 239 | Vector2i(4, 1): { 240 | 0: 0, 241 | 1: { 242 | 3: 2, 243 | 7: 1, 244 | 11: 0, 245 | 15: 2 246 | } 247 | }, 248 | Vector2i(4, 2): { 249 | 0: 0, 250 | 1: { 251 | 3: 0, 252 | 7: 0, 253 | 11: 1, 254 | 15: 2 255 | } 256 | }, 257 | Vector2i(4, 3): { 258 | 0: 0, 259 | 1: { 260 | 3: 2, 261 | 7: 1, 262 | 11: 0, 263 | 15: 0 264 | } 265 | }, 266 | Vector2i(4, 4): { 267 | 0: 0, 268 | 1: { 269 | 3: 0, 270 | 7: 2, 271 | 11: 1, 272 | 15: 0 273 | } 274 | }, 275 | Vector2i(4, 5): { 276 | 0: 0, 277 | 1: { 278 | 3: 0, 279 | 7: 1, 280 | 11: 2, 281 | 15: 0 282 | } 283 | }, 284 | Vector2i(5, 0): { 285 | 0: 0, 286 | 1: { 287 | 3: 0, 288 | 7: 2, 289 | 11: 2, 290 | 15: 1 291 | } 292 | }, 293 | Vector2i(5, 1): { 294 | 0: 0, 295 | 1: { 296 | 3: 1, 297 | 7: 2, 298 | 11: 2, 299 | 15: 0 300 | } 301 | }, 302 | Vector2i(5, 2): { 303 | 0: 0, 304 | 1: { 305 | 3: 0, 306 | 7: 0, 307 | 11: 2, 308 | 15: 1 309 | } 310 | }, 311 | Vector2i(5, 3): { 312 | 0: 0, 313 | 1: { 314 | 3: 1, 315 | 7: 2, 316 | 11: 0, 317 | 15: 0 318 | } 319 | }, 320 | Vector2i(5, 4): { 321 | 0: 0, 322 | 1: { 323 | 3: 2, 324 | 7: 0, 325 | 11: 0, 326 | 15: 1 327 | } 328 | }, 329 | Vector2i(5, 5): { 330 | 0: 0, 331 | 1: { 332 | 3: 1, 333 | 7: 0, 334 | 11: 0, 335 | 15: 2 336 | } 337 | } 338 | } 339 | terrain_set = -1 340 | terrain_mode = 1 341 | --------------------------------------------------------------------------------