├── .gitattributes ├── .gitignore ├── EquipmentPro.ttf ├── GUI ├── GUI.gd ├── GUI.tscn ├── Theme.tres ├── arrow-click.png ├── arrow-click.png.import ├── arrow-hover.png ├── arrow-hover.png.import ├── arrow.png └── arrow.png.import ├── Generator ├── CellDrawer.gd ├── CellDrawer.tscn ├── CellularAutomata.gd ├── ColorFiller.gd ├── ColorSchemeGenerator.gd ├── GroupDrawer.gd ├── GroupDrawer.tscn ├── MapGenerator.gd ├── NameGenerator.gd └── SpriteGenerator.gd ├── LICENSE ├── addons └── HTML5FileExchange │ ├── HTML5FileExchange.gd │ ├── plugin.cfg │ └── plugin.gd ├── default_env.tres ├── icon.png ├── icon.png.import └── project.godot /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Godot-specific ignores 3 | .import/ 4 | export.cfg 5 | export_presets.cfg 6 | 7 | # Mono-specific ignores 8 | .mono/ 9 | -------------------------------------------------------------------------------- /EquipmentPro.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deep-Fold/SpriteGenerator/841d76d4a91cf7c345d0c33fe01d5cb06259736f/EquipmentPro.ttf -------------------------------------------------------------------------------- /GUI/GUI.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | onready var sprite_generator = preload("res://Generator/SpriteGenerator.gd").new() 4 | onready var name_generator = preload("res://Generator/NameGenerator.gd").new() 5 | onready var group_drawer = preload("res://Generator/GroupDrawer.tscn") 6 | 7 | 8 | var seed_list = [] 9 | var seed_index = 0 10 | var outline = true 11 | var lifetime = 0 12 | var size = Vector2(45,45) 13 | 14 | func _process(delta): 15 | lifetime += delta * 0.025 16 | $ColorRect.color = Color.from_hsv(fmod(lifetime, 1.0), 0.2, 0.8) 17 | 18 | func _ready(): 19 | seed_list.append(_get_next_seed()) 20 | _redraw() 21 | 22 | func _input(event): 23 | if event.is_action_pressed("ui_right"): 24 | _shift_seeds(1) 25 | if event.is_action_pressed("ui_left"): 26 | _shift_seeds(-1) 27 | 28 | func _shift_seeds(shift): 29 | seed_index += shift 30 | seed_index = max(seed_index, 0) 31 | if seed_index == 0: 32 | $Left.visible = false 33 | else: 34 | $Left.visible = true 35 | 36 | 37 | if seed_index >= seed_list.size(): 38 | seed_list.append(_get_next_seed()) 39 | _redraw() 40 | 41 | var draw_rect = Vector2(400, 400) 42 | 43 | func _redraw(): 44 | for c in $CenterContainer/Control.get_children(): 45 | c.queue_free() 46 | 47 | var gd = _get_group_drawer(false) 48 | 49 | $CenterContainer/Control.add_child(gd) 50 | $Label.text = name_generator.get_name() 51 | 52 | 53 | func _get_group_drawer(var pixel_perfect = false): 54 | var sprite_groups = sprite_generator.get_sprite(seed_list[seed_index], size, 12, outline) 55 | var gd = group_drawer.instance() 56 | gd.groups = sprite_groups.groups 57 | gd.negative_groups = sprite_groups.negative_groups 58 | 59 | var draw_size = min((draw_rect.x / size.x), (draw_rect.y / size.y)) 60 | if pixel_perfect: 61 | gd.draw_size = 1 62 | else: 63 | gd.draw_size = draw_size 64 | gd.position = Vector2(-draw_size *size.x*0.5, -draw_size *size.y*0.5) 65 | 66 | return gd 67 | 68 | func _get_next_seed(): 69 | randomize() 70 | return randi() 71 | 72 | 73 | func _on_Left_pressed(): 74 | _shift_seeds(-1) 75 | 76 | 77 | func _on_Right_pressed(): 78 | _shift_seeds(1) 79 | 80 | 81 | 82 | func _on_CloseSettings_pressed(): 83 | $Settings.visible = false 84 | $OpenSettings.visible = true 85 | $ExportPanel.visible = true 86 | 87 | 88 | func _on_ToggleOutline_pressed(): 89 | outline = !outline 90 | if outline: 91 | $Settings/VBoxContainer/HBoxContainer3/ToggleOutline.text = "On" 92 | else: 93 | $Settings/VBoxContainer/HBoxContainer3/ToggleOutline.text = "Off" 94 | 95 | 96 | func _on_OpenSettings_pressed(): 97 | $OpenSettings.visible = false 98 | $ExportPanel.visible = false 99 | $Settings.visible = true 100 | 101 | 102 | func _on_ExportButton_pressed(): 103 | for c in $Viewport.get_children(): 104 | c.queue_free() 105 | var gd = _get_group_drawer(true) 106 | gd.disable_movement() 107 | gd.position = Vector2(1,1) 108 | $Viewport.add_child(gd) 109 | 110 | yield(get_tree(), "idle_frame") 111 | yield(get_tree(), "idle_frame") 112 | export_image() 113 | 114 | 115 | func export_image(): 116 | var img = Image.new() 117 | img.create(size.x + 2, size.y + 2, false, Image.FORMAT_RGBA8) 118 | var viewport_img = $Viewport.get_texture().get_data() 119 | 120 | img.blit_rect(viewport_img, Rect2(0,0,size.x+2,size.y+2), Vector2(0,0)) 121 | 122 | save_image(img) 123 | 124 | func save_image(img): 125 | if OS.get_name() == "HTML5" and OS.has_feature('JavaScript'): 126 | var filesaver = get_tree().root.get_node("/root/HTML5File") 127 | filesaver.save_image(img, $Label.text) 128 | else: 129 | if OS.get_name() == "OSX": 130 | img.save_png("user://" + $Label.text + ".png") 131 | else: 132 | img.save_png("res://" + $Label.text + ".png") 133 | 134 | 135 | func _on_Height_value_changed(value): 136 | size.y = clamp(round(value), 10, 128) 137 | 138 | 139 | func _on_Width_value_changed(value): 140 | size.x = clamp(round(value), 10, 128) 141 | -------------------------------------------------------------------------------- /GUI/GUI.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=10 format=2] 2 | 3 | [ext_resource path="res://GUI/arrow.png" type="Texture" id=1] 4 | [ext_resource path="res://GUI/arrow-click.png" type="Texture" id=2] 5 | [ext_resource path="res://GUI/arrow-hover.png" type="Texture" id=3] 6 | [ext_resource path="res://GUI/Theme.tres" type="Theme" id=4] 7 | [ext_resource path="res://EquipmentPro.ttf" type="DynamicFontData" id=5] 8 | [ext_resource path="res://GUI/GUI.gd" type="Script" id=6] 9 | 10 | [sub_resource type="DynamicFont" id=1] 11 | size = 80 12 | font_data = ExtResource( 5 ) 13 | 14 | [sub_resource type="DynamicFont" id=2] 15 | size = 40 16 | font_data = ExtResource( 5 ) 17 | 18 | [sub_resource type="ViewportTexture" id=3] 19 | viewport_path = NodePath("Viewport") 20 | 21 | [node name="GUI" type="Control"] 22 | anchor_right = 1.0 23 | anchor_bottom = 1.0 24 | script = ExtResource( 6 ) 25 | __meta__ = { 26 | "_edit_use_anchors_": false 27 | } 28 | 29 | [node name="ColorRect" type="ColorRect" parent="."] 30 | anchor_right = 1.0 31 | anchor_bottom = 1.0 32 | color = Color( 1, 0.81, 0.813167, 1 ) 33 | __meta__ = { 34 | "_edit_use_anchors_": false 35 | } 36 | 37 | [node name="Label" type="Label" parent="."] 38 | modulate = Color( 0, 0, 0, 1 ) 39 | anchor_right = 1.0 40 | margin_top = 10.0 41 | margin_bottom = 120.0 42 | custom_fonts/font = SubResource( 1 ) 43 | text = "Test" 44 | align = 1 45 | autowrap = true 46 | __meta__ = { 47 | "_edit_use_anchors_": false 48 | } 49 | 50 | [node name="CenterContainer" type="CenterContainer" parent="."] 51 | anchor_right = 1.0 52 | anchor_bottom = 1.0 53 | margin_top = 70.0 54 | __meta__ = { 55 | "_edit_use_anchors_": false 56 | } 57 | 58 | [node name="Control" type="Control" parent="CenterContainer"] 59 | margin_left = 512.0 60 | margin_top = 265.0 61 | margin_right = 512.0 62 | margin_bottom = 265.0 63 | 64 | [node name="Right" type="TextureButton" parent="."] 65 | anchor_left = 1.0 66 | anchor_right = 1.0 67 | anchor_bottom = 1.0 68 | margin_left = -174.0 69 | margin_top = 200.0 70 | margin_right = -64.0 71 | margin_bottom = -200.0 72 | mouse_default_cursor_shape = 2 73 | texture_normal = ExtResource( 3 ) 74 | texture_pressed = ExtResource( 2 ) 75 | texture_hover = ExtResource( 1 ) 76 | expand = true 77 | stretch_mode = 5 78 | __meta__ = { 79 | "_edit_use_anchors_": false 80 | } 81 | 82 | [node name="Left" type="TextureButton" parent="."] 83 | visible = false 84 | anchor_left = 1.0 85 | anchor_right = 1.0 86 | anchor_bottom = 1.0 87 | margin_left = -850.0 88 | margin_top = 200.0 89 | margin_right = -740.0 90 | margin_bottom = -200.0 91 | rect_scale = Vector2( -1, 1 ) 92 | mouse_default_cursor_shape = 2 93 | texture_normal = ExtResource( 3 ) 94 | texture_pressed = ExtResource( 2 ) 95 | texture_hover = ExtResource( 1 ) 96 | expand = true 97 | stretch_mode = 5 98 | __meta__ = { 99 | "_edit_use_anchors_": false 100 | } 101 | 102 | [node name="TextureButton" type="TextureButton" parent="."] 103 | margin_right = 40.0 104 | margin_bottom = 40.0 105 | __meta__ = { 106 | "_edit_use_anchors_": false 107 | } 108 | 109 | [node name="OpenSettings" type="Panel" parent="."] 110 | self_modulate = Color( 0, 0, 0, 0.733333 ) 111 | anchor_top = 1.0 112 | anchor_bottom = 1.0 113 | margin_top = -104.0 114 | margin_right = 265.0 115 | margin_bottom = -52.0 116 | theme = ExtResource( 4 ) 117 | __meta__ = { 118 | "_edit_use_anchors_": false 119 | } 120 | 121 | [node name="OpenSettings" type="Button" parent="OpenSettings"] 122 | anchor_right = 1.0 123 | anchor_bottom = 1.0 124 | margin_left = 20.0 125 | margin_right = 20.0 126 | mouse_default_cursor_shape = 2 127 | theme = ExtResource( 4 ) 128 | text = "Settings" 129 | flat = true 130 | align = 0 131 | __meta__ = { 132 | "_edit_use_anchors_": false 133 | } 134 | 135 | [node name="ExportPanel" type="Panel" parent="."] 136 | self_modulate = Color( 0, 0, 0, 0.733333 ) 137 | anchor_top = 1.0 138 | anchor_bottom = 1.0 139 | margin_top = -52.0 140 | margin_right = 265.0 141 | theme = ExtResource( 4 ) 142 | __meta__ = { 143 | "_edit_use_anchors_": false 144 | } 145 | 146 | [node name="ExportButton" type="Button" parent="ExportPanel"] 147 | margin_left = 20.0 148 | margin_top = -2.0 149 | margin_right = 265.0 150 | margin_bottom = 44.0 151 | mouse_default_cursor_shape = 2 152 | theme = ExtResource( 4 ) 153 | text = "Export PNG" 154 | flat = true 155 | align = 0 156 | __meta__ = { 157 | "_edit_use_anchors_": false 158 | } 159 | 160 | [node name="Settings" type="Panel" parent="."] 161 | visible = false 162 | self_modulate = Color( 1, 1, 1, 0.733333 ) 163 | anchor_top = 1.0 164 | anchor_bottom = 1.0 165 | margin_top = -233.0 166 | margin_right = 265.0 167 | theme = ExtResource( 4 ) 168 | __meta__ = { 169 | "_edit_group_": true, 170 | "_edit_use_anchors_": false 171 | } 172 | 173 | [node name="VBoxContainer" type="VBoxContainer" parent="Settings"] 174 | anchor_right = 1.0 175 | anchor_bottom = 1.0 176 | margin_left = 20.0 177 | margin_right = -10.0 178 | __meta__ = { 179 | "_edit_use_anchors_": false 180 | } 181 | 182 | [node name="HBoxContainer4" type="HBoxContainer" parent="Settings/VBoxContainer"] 183 | margin_right = 235.0 184 | margin_bottom = 46.0 185 | 186 | [node name="Label" type="Label" parent="Settings/VBoxContainer/HBoxContainer4"] 187 | margin_top = 3.0 188 | margin_right = 201.0 189 | margin_bottom = 43.0 190 | size_flags_horizontal = 3 191 | custom_fonts/font = SubResource( 2 ) 192 | text = "Settings" 193 | 194 | [node name="CloseSettings" type="Button" parent="Settings/VBoxContainer/HBoxContainer4"] 195 | margin_left = 205.0 196 | margin_right = 235.0 197 | margin_bottom = 46.0 198 | mouse_default_cursor_shape = 2 199 | theme = ExtResource( 4 ) 200 | text = "x" 201 | flat = true 202 | 203 | [node name="HSeparator" type="HSeparator" parent="Settings/VBoxContainer"] 204 | margin_top = 50.0 205 | margin_right = 235.0 206 | margin_bottom = 70.0 207 | rect_min_size = Vector2( 0, 20 ) 208 | 209 | [node name="HBoxContainer" type="HBoxContainer" parent="Settings/VBoxContainer"] 210 | margin_top = 74.0 211 | margin_right = 235.0 212 | margin_bottom = 124.0 213 | 214 | [node name="Label2" type="Label" parent="Settings/VBoxContainer/HBoxContainer"] 215 | margin_top = 5.0 216 | margin_right = 115.0 217 | margin_bottom = 45.0 218 | size_flags_horizontal = 3 219 | size_flags_stretch_ratio = 0.7 220 | custom_fonts/font = SubResource( 2 ) 221 | text = "Width:" 222 | 223 | [node name="Width" type="SpinBox" parent="Settings/VBoxContainer/HBoxContainer"] 224 | margin_left = 119.0 225 | margin_right = 235.0 226 | margin_bottom = 50.0 227 | size_flags_horizontal = 3 228 | size_flags_vertical = 3 229 | size_flags_stretch_ratio = 0.7 230 | theme = ExtResource( 4 ) 231 | min_value = 10.0 232 | max_value = 128.0 233 | value = 45.0 234 | rounded = true 235 | 236 | [node name="HBoxContainer2" type="HBoxContainer" parent="Settings/VBoxContainer"] 237 | margin_top = 128.0 238 | margin_right = 235.0 239 | margin_bottom = 178.0 240 | 241 | [node name="Label2" type="Label" parent="Settings/VBoxContainer/HBoxContainer2"] 242 | margin_top = 5.0 243 | margin_right = 115.0 244 | margin_bottom = 45.0 245 | size_flags_horizontal = 3 246 | size_flags_stretch_ratio = 0.7 247 | custom_fonts/font = SubResource( 2 ) 248 | text = "Height:" 249 | 250 | [node name="Height" type="SpinBox" parent="Settings/VBoxContainer/HBoxContainer2"] 251 | margin_left = 119.0 252 | margin_right = 235.0 253 | margin_bottom = 50.0 254 | size_flags_horizontal = 3 255 | size_flags_vertical = 3 256 | size_flags_stretch_ratio = 0.7 257 | theme = ExtResource( 4 ) 258 | min_value = 10.0 259 | max_value = 128.0 260 | value = 45.0 261 | 262 | [node name="HBoxContainer3" type="HBoxContainer" parent="Settings/VBoxContainer"] 263 | margin_top = 182.0 264 | margin_right = 235.0 265 | margin_bottom = 228.0 266 | 267 | [node name="Label" type="Label" parent="Settings/VBoxContainer/HBoxContainer3"] 268 | margin_top = 3.0 269 | margin_right = 114.0 270 | margin_bottom = 43.0 271 | size_flags_horizontal = 3 272 | size_flags_stretch_ratio = 0.7 273 | custom_fonts/font = SubResource( 2 ) 274 | text = "Outline:" 275 | 276 | [node name="ToggleOutline" type="Button" parent="Settings/VBoxContainer/HBoxContainer3"] 277 | margin_left = 118.0 278 | margin_right = 235.0 279 | margin_bottom = 46.0 280 | mouse_default_cursor_shape = 2 281 | size_flags_horizontal = 3 282 | theme = ExtResource( 4 ) 283 | text = "On" 284 | flat = true 285 | 286 | [node name="Viewport" type="Viewport" parent="."] 287 | size = Vector2( 130, 130 ) 288 | own_world = true 289 | transparent_bg = true 290 | hdr = false 291 | disable_3d = true 292 | usage = 0 293 | render_target_v_flip = true 294 | 295 | [node name="TextureRect" type="TextureRect" parent="."] 296 | modulate = Color( 1, 1, 1, 0.0392157 ) 297 | margin_right = 40.0 298 | margin_bottom = 40.0 299 | rect_scale = Vector2( 0.01, 0.01 ) 300 | texture = SubResource( 3 ) 301 | __meta__ = { 302 | "_edit_use_anchors_": false 303 | } 304 | 305 | [connection signal="pressed" from="Right" to="." method="_on_Right_pressed"] 306 | [connection signal="pressed" from="Left" to="." method="_on_Left_pressed"] 307 | [connection signal="pressed" from="OpenSettings/OpenSettings" to="." method="_on_OpenSettings_pressed"] 308 | [connection signal="pressed" from="ExportPanel/ExportButton" to="." method="_on_ExportButton_pressed"] 309 | [connection signal="pressed" from="Settings/VBoxContainer/HBoxContainer4/CloseSettings" to="." method="_on_CloseSettings_pressed"] 310 | [connection signal="value_changed" from="Settings/VBoxContainer/HBoxContainer/Width" to="." method="_on_Width_value_changed"] 311 | [connection signal="value_changed" from="Settings/VBoxContainer/HBoxContainer2/Height" to="." method="_on_Height_value_changed"] 312 | [connection signal="pressed" from="Settings/VBoxContainer/HBoxContainer3/ToggleOutline" to="." method="_on_ToggleOutline_pressed"] 313 | -------------------------------------------------------------------------------- /GUI/Theme.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Theme" load_steps=5 format=2] 2 | 3 | [sub_resource type="DynamicFontData" id=1] 4 | font_path = "res://EquipmentPro.ttf" 5 | 6 | [sub_resource type="DynamicFont" id=2] 7 | size = 40 8 | font_data = SubResource( 1 ) 9 | 10 | [sub_resource type="DynamicFont" id=3] 11 | size = 40 12 | extra_spacing_space = -6 13 | font_data = SubResource( 1 ) 14 | 15 | [sub_resource type="StyleBoxFlat" id=4] 16 | bg_color = Color( 0, 0, 0, 1 ) 17 | 18 | [resource] 19 | Button/colors/font_color = Color( 1, 1, 1, 1 ) 20 | Button/colors/font_color_disabled = Color( 0.9, 0.9, 0.9, 0.2 ) 21 | Button/colors/font_color_hover = Color( 0.0313726, 1, 0, 1 ) 22 | Button/colors/font_color_pressed = Color( 0.133333, 0.133333, 0.133333, 1 ) 23 | Button/constants/hseparation = 2 24 | Button/fonts/font = SubResource( 2 ) 25 | Button/styles/disabled = null 26 | Button/styles/focus = null 27 | Button/styles/hover = null 28 | Button/styles/normal = null 29 | Button/styles/pressed = null 30 | LineEdit/colors/clear_button_color = Color( 0.88, 0.88, 0.88, 1 ) 31 | LineEdit/colors/clear_button_color_pressed = Color( 1, 1, 1, 1 ) 32 | LineEdit/colors/cursor_color = Color( 0.94, 0.94, 0.94, 1 ) 33 | LineEdit/colors/font_color = Color( 0.88, 0.88, 0.88, 1 ) 34 | LineEdit/colors/font_color_selected = Color( 0, 0, 0, 1 ) 35 | LineEdit/colors/font_color_uneditable = Color( 0.88, 0.88, 0.88, 0.5 ) 36 | LineEdit/colors/selection_color = Color( 0.49, 0.49, 0.49, 1 ) 37 | LineEdit/constants/minimum_spaces = 12 38 | LineEdit/fonts/font = SubResource( 3 ) 39 | LineEdit/icons/clear = null 40 | LineEdit/styles/focus = null 41 | LineEdit/styles/normal = null 42 | LineEdit/styles/read_only = null 43 | Panel/styles/panel = SubResource( 4 ) 44 | SpinBox/icons/updown = null 45 | -------------------------------------------------------------------------------- /GUI/arrow-click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deep-Fold/SpriteGenerator/841d76d4a91cf7c345d0c33fe01d5cb06259736f/GUI/arrow-click.png -------------------------------------------------------------------------------- /GUI/arrow-click.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/arrow-click.png-f4f51747c25c66d893d26130f3aca57f.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://GUI/arrow-click.png" 13 | dest_files=[ "res://.import/arrow-click.png-f4f51747c25c66d893d26130f3aca57f.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=false 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /GUI/arrow-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deep-Fold/SpriteGenerator/841d76d4a91cf7c345d0c33fe01d5cb06259736f/GUI/arrow-hover.png -------------------------------------------------------------------------------- /GUI/arrow-hover.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/arrow-hover.png-140bb8bfeb2a041e3b156fc785e24f89.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://GUI/arrow-hover.png" 13 | dest_files=[ "res://.import/arrow-hover.png-140bb8bfeb2a041e3b156fc785e24f89.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=false 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /GUI/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deep-Fold/SpriteGenerator/841d76d4a91cf7c345d0c33fe01d5cb06259736f/GUI/arrow.png -------------------------------------------------------------------------------- /GUI/arrow.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/arrow.png-76d0e96a4df842b70055e6a9a35f05ec.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://GUI/arrow.png" 13 | dest_files=[ "res://.import/arrow.png-76d0e96a4df842b70055e6a9a35f05ec.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=false 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /Generator/CellDrawer.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | var cells = [] 4 | var draw_size = 6 5 | var speed = 5 6 | var lifetime = 0 7 | var is_eye = false 8 | var amplitude = 0 9 | var movement = true 10 | 11 | func _ready(): 12 | amplitude = (lifetime % 5 + 2) * 5.0 13 | 14 | func set_cells(c): 15 | cells = c 16 | 17 | update() 18 | 19 | func _draw(): 20 | var average = Vector2() 21 | var size = 0 22 | var eye_cutoff = 0.0 23 | if is_eye: 24 | for c in cells: 25 | size += 1 26 | average += c.position 27 | eye_cutoff = sqrt(float(size)) * 0.3 28 | 29 | average = average / cells.size() 30 | 31 | for c in cells: 32 | draw_rect(Rect2(c.position.x*draw_size, c.position.y*draw_size, draw_size, draw_size), c.color) 33 | 34 | if is_eye && average.distance_to(c.position) < eye_cutoff: 35 | draw_rect(Rect2(c.position.x*draw_size, c.position.y*draw_size, draw_size, draw_size), c.color.darkened(0.85)) 36 | 37 | func set_speed(s): 38 | speed = s 39 | 40 | func _process(delta): 41 | if movement: 42 | lifetime += delta * 4.0 43 | position.y = sin(lifetime) * 20 44 | 45 | func set_eye(): 46 | is_eye = true 47 | update() 48 | -------------------------------------------------------------------------------- /Generator/CellDrawer.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://Generator/CellDrawer.gd" type="Script" id=1] 4 | 5 | [node name="CellDrawer" type="Node2D"] 6 | script = ExtResource( 1 ) 7 | -------------------------------------------------------------------------------- /Generator/CellularAutomata.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var birth_limit = 5 4 | var death_limit = 4 5 | var n_steps = 4 6 | 7 | func do_steps(map): 8 | for i in n_steps: 9 | map = _step(map) 10 | return map 11 | 12 | func _step(map): 13 | var dup = map.duplicate(true) 14 | for x in range(0, map.size()): 15 | for y in range(0, map[x].size()): 16 | var cell = dup[x][y] 17 | var n = _get_neighbours(map, Vector2(x,y)) 18 | if cell && n < death_limit: 19 | dup[x][y] = false 20 | elif !cell && n > birth_limit: 21 | dup[x][y] = true 22 | return dup 23 | 24 | func _get_neighbours(map, pos): 25 | var count = 0 26 | 27 | for i in range(-1,2): 28 | for j in range(-1,2): 29 | if !(i == 0 && j ==0): 30 | if _get_at_pos(map, pos + Vector2(i,j)): 31 | count += 1 32 | 33 | return count 34 | 35 | func _get_at_pos(map, pos): 36 | if pos.x < 0 || pos.x >= map.size() || pos.y < 0 || pos.y >= map[pos.x].size(): 37 | return null 38 | 39 | return map[pos.x][pos.y] 40 | -------------------------------------------------------------------------------- /Generator/ColorFiller.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var noise 4 | var noise2 5 | 6 | func _init(): 7 | noise = OpenSimplexNoise.new() 8 | noise2 = OpenSimplexNoise.new() 9 | noise2.octaves = 3 10 | noise2.period = 40.0 11 | noise2.persistence = 0.4 12 | noise2.lacunarity = 3.0 13 | 14 | noise.octaves = 5 15 | noise.period = 30.0 16 | noise.persistence = 0.4 17 | noise.lacunarity = 3.0 18 | 19 | func fill_colors(map, colorscheme, eye_colorscheme, n_colors, outline = true): 20 | noise.seed = randi() 21 | noise2.seed = randi() 22 | var groups = [] 23 | var negative_groups = [] 24 | 25 | groups = _flood_fill(map, groups, colorscheme, eye_colorscheme, n_colors, false, outline) 26 | negative_groups = _flood_fill_negative(map, negative_groups, colorscheme, eye_colorscheme, n_colors, outline) 27 | 28 | return { 29 | "groups": groups, 30 | "negative_groups": negative_groups 31 | } 32 | 33 | func _flood_fill_negative(map, groups, colorscheme, eye_colorscheme, n_colors, outline): 34 | var negative_map = [] 35 | for x in range(0, map.size()): 36 | var arr = [] 37 | for y in range(0, map[x].size()): 38 | arr.append(!_get_at_pos(map, Vector2(x,y))) 39 | negative_map.append(arr) 40 | 41 | return _flood_fill(negative_map, groups, colorscheme, eye_colorscheme, n_colors, true, outline) 42 | 43 | # myd overcomplicated way of a flood fill algorithm 44 | func _flood_fill(map, groups, colorscheme, eye_colorscheme, n_colors, is_negative = false, outline = true): 45 | # checked_map holds a 2d map of all the cells and if they have been checked in the flood fill yet 46 | var checked_map = [] 47 | for x in range(0, map.size()): 48 | var arr = [] 49 | for _y in range(0, map[x].size()): 50 | arr.append(false) 51 | checked_map.append(arr) 52 | 53 | # bucket is all the cells that have been found through flood filling and whose neighbours will be checked next 54 | var bucket = [] 55 | for x in range(0, map.size()): 56 | for y in range(0, map[x].size()): 57 | if !checked_map[x][y]: # haven't checked this cell yet 58 | checked_map[x][y] = true 59 | 60 | if map[x][y]: # if this cell is actually filled in the map 61 | bucket.append(Vector2(x,y)) 62 | 63 | var group = { 64 | "arr": [], 65 | "valid": true 66 | } 67 | 68 | while (bucket.size() > 0): # go through remaining cells in bucket 69 | var pos = bucket.pop_back() 70 | 71 | # get neighbours 72 | var right = _get_at_pos(map, pos + Vector2(1, 0)) 73 | var left = _get_at_pos(map, pos + Vector2(-1, 0)) 74 | var down = _get_at_pos(map, pos + Vector2(0, 1)) 75 | var up = _get_at_pos(map, pos + Vector2(0, -1)) 76 | 77 | if is_negative: # dont want negative groups that touch the edge of the sprite 78 | if left == null || up == null || down == null || right == null: 79 | group.valid = false 80 | 81 | # I also do a coloring step in this flood fill, speeds up processing a bit instead of doing it seperately 82 | var col = _get_color(map, pos, is_negative, right, left, down, up, colorscheme, eye_colorscheme, n_colors, outline, group) 83 | 84 | group.arr.append({ 85 | "position": pos, 86 | "color": col 87 | }) 88 | 89 | # add neighbours to bucket to check 90 | if right && !checked_map[pos.x + 1][pos.y]: 91 | bucket.append(pos + Vector2(1, 0)) 92 | checked_map[pos.x + 1][pos.y] = true 93 | if left && !checked_map[pos.x - 1][pos.y]: 94 | bucket.append(pos + Vector2(-1, 0)) 95 | checked_map[pos.x - 1][pos.y] = true 96 | if down && !checked_map[pos.x][pos.y + 1]: 97 | bucket.append(pos + Vector2(0, 1)) 98 | checked_map[pos.x][pos.y+1] = true 99 | if up && !checked_map[pos.x][pos.y - 1]: 100 | bucket.append(pos + Vector2(0, -1)) 101 | checked_map[pos.x][pos.y-1] = true 102 | groups.append(group) 103 | return groups 104 | 105 | # excuse the gigantic amount of parameters here please 106 | func _get_color(map, pos, is_negative, right, left, down, up, colorscheme, eye_colorscheme, n_colors, outline, group): 107 | var col_x = ceil(abs(pos.x - (map.size()-1)*0.5)) 108 | var n = pow(abs((noise.get_noise_2d(col_x, pos.y))), 1.5) * 3.0 109 | var n2 = pow(abs((noise2.get_noise_2d(col_x, pos.y))), 1.5) * 3.0 110 | 111 | # highlight colors based on amount of neighbours 112 | if !down: 113 | if is_negative: 114 | n2 -= 0.1 115 | else: 116 | n -= 0.45 117 | n*= 0.8 118 | if outline: 119 | group.arr.append({ 120 | "position": pos + Vector2(0, 1), 121 | "color": Color(0,0,0,1) 122 | }) 123 | if !right: 124 | if is_negative: 125 | n2 += 0.1 126 | else: 127 | n += 0.2 128 | n*=1.1 129 | if outline: 130 | group.arr.append({ 131 | "position": pos + Vector2(1, 0), 132 | "color": Color(0,0,0,1) 133 | }) 134 | if !up: 135 | if is_negative: 136 | n2 +=0.15 137 | else: 138 | n += 0.45 139 | n*=1.2 140 | if outline: 141 | group.arr.append({ 142 | "position": pos + Vector2(0, -1), 143 | "color": Color(0,0,0,1) 144 | }) 145 | if !left: 146 | if is_negative: 147 | n2 += 0.1 148 | else: 149 | n += 0.2 150 | n*=1.1 151 | if outline: 152 | group.arr.append({ 153 | "position": pos + Vector2(-1, 0), 154 | "color": Color(0,0,0,1) 155 | }) 156 | 157 | # highlight colors if the difference in colors between neighbours is big 158 | var c_0 = colorscheme[floor(noise.get_noise_2d(col_x, pos.y) * (n_colors-1))] 159 | var c_1 = colorscheme[floor(noise.get_noise_2d(col_x, pos.y - 1) * (n_colors-1))] 160 | var c_2 = colorscheme[floor(noise.get_noise_2d(col_x, pos.y + 1) * (n_colors-1))] 161 | var c_3 = colorscheme[floor(noise.get_noise_2d(col_x - 1, pos.y) * (n_colors-1))] 162 | var c_4 = colorscheme[floor(noise.get_noise_2d(col_x + 1, pos.y) * (n_colors-1))] 163 | var diff = ((abs(c_0.r - c_1.r) + abs(c_0.g - c_1.g) + abs(c_0.b - c_1.b)) + 164 | (abs(c_0.r - c_2.r) + abs(c_0.g - c_2.g) + abs(c_0.b - c_2.b)) + 165 | (abs(c_0.r - c_3.r) + abs(c_0.g - c_3.g) + abs(c_0.b - c_3.b)) + 166 | (abs(c_0.r - c_4.r) + abs(c_0.g - c_4.g) + abs(c_0.b - c_4.b))) 167 | if diff > 2.0: 168 | n+= 0.3 169 | n*= 1.5 170 | n2+= 0.3 171 | n2*= 1.5 172 | 173 | # actually choose a color 174 | n = clamp(n, 0.0, 1.0) 175 | n = floor(n * (n_colors-1)) 176 | n2 = clamp(n2, 0.0, 1.0) 177 | n2 = floor(n2 * (n_colors-1)) 178 | var col = colorscheme[n] 179 | 180 | if is_negative: 181 | col = eye_colorscheme[n2] 182 | return col 183 | 184 | func _get_at_pos(map, pos): 185 | if pos.x < 0 || pos.x >= map.size() || pos.y < 0 || pos.y >= map[pos.x].size(): 186 | return null 187 | 188 | return map[pos.x][pos.y] 189 | -------------------------------------------------------------------------------- /Generator/ColorSchemeGenerator.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | # Using ideas from https://www.iquilezles.org/www/articles/palettes/palettes.htm 4 | func generate_new_colorscheme(n_colors): 5 | var a = Vector3(rand_range(0.0, 0.5), rand_range(0.0, 0.5), rand_range(0.0, 0.5)) 6 | var b = Vector3(rand_range(0.1, 0.6), rand_range(0.1, 0.6), rand_range(0.1, 0.6)) 7 | var c = Vector3(rand_range(0.15, 0.8), rand_range(0.15, 0.8), rand_range(0.15, 0.8)) 8 | var d = Vector3(rand_range(0.0, 1.0), rand_range(0.0, 1.0), rand_range(0.0, 1.0)) 9 | 10 | var cols = PoolColorArray() 11 | var n = float(n_colors - 1.0) 12 | for i in range(0, n_colors, 1): 13 | var vec3 = Vector3() 14 | vec3.x = (a.x + b.x *cos(6.28318 * (c.x*float(i/n) + d.x))) + (i/n)*0.8 15 | vec3.y = (a.y + b.y *cos(6.28318 * (c.y*float(i/n) + d.y))) + (i/n)*0.8 16 | vec3.z = (a.z + b.z *cos(6.28318 * (c.z*float(i/n) + d.z))) + (i/n)*0.8 17 | 18 | cols.append(Color(vec3.x, vec3.y, vec3.z)) 19 | 20 | return cols 21 | -------------------------------------------------------------------------------- /Generator/GroupDrawer.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | var groups = [] 4 | var negative_groups = [] 5 | var draw_size = 10 6 | var movement = true 7 | onready var cell_drawer = preload("res://Generator/CellDrawer.tscn") 8 | 9 | func _ready(): 10 | var largest = 0 11 | for g in groups: 12 | largest = max(largest, g.arr.size()) 13 | 14 | for i in range(groups.size() - 1, -1, -1): 15 | var g = groups[i].arr 16 | groups[i]["start_time"] = g.size() + groups.size() 17 | if g.size() >= largest * 0.25: 18 | var cell = cell_drawer.instance() 19 | cell.set_cells(g) 20 | cell.lifetime = groups[i].start_time 21 | cell.movement = movement 22 | 23 | add_child(cell) 24 | else: 25 | groups.erase(g) 26 | 27 | for g in negative_groups: 28 | if g.valid: 29 | var touching = false 30 | for g2 in groups: 31 | if group_is_touching_group(g.arr,g2.arr): 32 | touching = true 33 | if g.has("start_time"): 34 | g2["start_time"] = g["start_time"] 35 | else: 36 | g["start_time"] = g2["start_time"] 37 | 38 | if touching: 39 | var cell = cell_drawer.instance() 40 | cell.set_cells(g.arr) 41 | 42 | cell.lifetime = g.start_time 43 | cell.movement = movement 44 | add_child(cell) 45 | 46 | if (g.arr.size() + negative_groups.size()) % 5 >= 3: 47 | cell.set_eye() 48 | 49 | for c in get_children(): 50 | c.draw_size = draw_size 51 | 52 | func disable_movement(): 53 | movement = false 54 | 55 | func group_is_touching_group(g1, g2): 56 | for c in g1: 57 | for c2 in g2: 58 | if c.position.x == c2.position.x: 59 | if c.position.y == c2.position.y + 1 || c.position.y == c2.position.y - 1: 60 | return true 61 | elif c.position.y == c2.position.y: 62 | if c.position.x == c2.position.x + 1 || c.position.x == c2.position.x - 1: 63 | return true 64 | 65 | 66 | return false 67 | -------------------------------------------------------------------------------- /Generator/GroupDrawer.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://Generator/GroupDrawer.gd" type="Script" id=1] 4 | 5 | [node name="GroupDrawer" type="Node2D"] 6 | script = ExtResource( 1 ) 7 | -------------------------------------------------------------------------------- /Generator/MapGenerator.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | func generate_new(size): 4 | var map = _get_random_map(size) 5 | for i in 2: 6 | _random_walk(size, map) 7 | return map 8 | 9 | func _random_walk(size, map): 10 | var pos = Vector2(randi() % int(size.x), randi() % int(size.y)) 11 | for i in 100: 12 | _set_at_pos(map, pos, true) 13 | 14 | _set_at_pos(map, Vector2(size.x - pos.x - 1, pos.y), true) 15 | pos += Vector2(randi()%3-1,randi()%3-1) 16 | 17 | func _get_random_map(size): 18 | var map = [] 19 | for x in size.x: 20 | map.append([]) 21 | 22 | for x in range(0, ceil(size.x * 0.5)): 23 | var arr = [] 24 | for y in range(0, size.y): 25 | arr.append(rand_bool(0.48)) 26 | 27 | # When close to center increase the cances to fill the map, so it's more likely to end up with a sprite that's connected in the middle 28 | var to_center = (abs(y - size.y * 0.5) * 2.0) / size.y 29 | if x == floor(size.x*0.5) - 1 || x == floor(size.x*0.5) - 2: 30 | if rand_range(0.0, 0.4) > to_center: 31 | arr[y] = true 32 | 33 | map[x] = (arr.duplicate(true)) 34 | map[size.x - x - 1] = (arr.duplicate(true)) 35 | 36 | 37 | # for x in range(0, map.size()): 38 | # for y in range(0, map[x].size()): 39 | # if rand_range(0.0, 1.0) > 0.99: 40 | # map[x][y] = true 41 | 42 | return map 43 | 44 | func _set_at_pos(map, pos, val): 45 | if pos.x < 0 || pos.x >= map.size() || pos.y < 0 || pos.y >= map[pos.x].size(): 46 | return false 47 | 48 | map[pos.x][pos.y] = val 49 | 50 | return true 51 | 52 | func rand_bool(chance): 53 | return rand_range(0.0, 1.0) > chance 54 | -------------------------------------------------------------------------------- /Generator/NameGenerator.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var materials = ["Bone", "Snow", "Boxwood", "Graphite", "Stone", "Wooden", "Water", "Ice", "Birch", "Air", "Crystal", "Magma", "Steel", "Metal", "Plastic", "Concrete", "Glass", "Paper", "Aluminium", "Titanium", "Leather", "Quartz", "Mineral"] 4 | var foods = ["Beet", "Broccoli", "Celery", "Fish", "Cabbage", "Corn", "Dandelion", "Vanilla", "Chocolate", "Lemon", "Coconut", "Strawberry", "Fiddlehead", "Grape", "Cheese", "Cake", "Zucchini", "Lettuce", "Spinach", "Salt", "Turnip", "Banana", "Cucumber", "Pumpkin", "Squash", "Tomato", "Pepper", "Artichoke", "Sunflower", "Asparagus", "Onion", "Shallot", "Meat", "Herb", "Tofu", "Bread", "Rice", "Carrot", "Mushroom", "Bun", "Milk", "Cereal", "Dumpling", "Sushi", "Spaghetti", "Meatball", "Apple Pie", "Dave"] 5 | var flavors = ["Sweet", "Sour", "Spicy", "Hot", "Salty", "Bitter", "Disgusting", "Cheesy"] 6 | var sizes = ["Big", "Small", "Tiny", "Huge", "Massive", "Short", "Grand"] 7 | var properties = ["Colorful", "Destructive", "Mysterious", "Healing", "Chaotic", "Floating", "Heavy", "Deep", "Hateful", "Unique", "Heavenly", "Radioactive", "Toxic", "Burning", "Freezing", "Beautiful", "Ugly", "Cold", "Great", "Terrible", "Light", "Dark"] 8 | var colors = ["Red", "Green", "Blue", "Yellow", "Orange", "Pink", "Purple", "Cyan", "Magenta", "Black", "White", "Gray"] 9 | var ages = ["Old", "Fresh", "Crusty", "New", "Ancient", "Broken", "Fossilized", "Crisp", "Aged", "Fermented"] 10 | var things = ["Feather", "Sandals", "Gem", "Orb", "Dust", "Book", "Amulet", "Heart", "Finger", "Pencil", "Weapon", "Vitamins", "Calculator", "Cloud", "Overlord", "Bottle", "Branch", "Bag", "Alien", "Fire", "Fork", "Sculpture", "Soul", "Toothbrush" ] 11 | var other = ["Destruction", "Chaos", "Equality", "Electricity", "Speed", "Mutations", "Agression", "Worship", "Silence", "Illusion", "Purifying", "Growing", "Breaking", "Secrets"] 12 | # size - age - shape - colour - origin - material - purpose 13 | enum text_add {materials, foods, flavors, sizes, properties, colors, ages} 14 | var can_add = [text_add.materials,text_add.foods,text_add.flavors,text_add.sizes,text_add.properties,text_add.ages] 15 | 16 | func get_name(): 17 | var name_string = "" 18 | 19 | var has_added = [] 20 | for i in 2: 21 | var add = _rand_from_array(can_add) 22 | if has_added.size() == 0 && _rand_chance(0.9): 23 | has_added.append(add) 24 | elif !has_added.has(add) && _rand_chance(0.4): 25 | has_added.append(add) 26 | 27 | if has_added.has(text_add.sizes): 28 | name_string += _rand_from_array(sizes) + " " 29 | if has_added.has(text_add.ages): 30 | name_string += _rand_from_array(ages) + " " 31 | if has_added.has(text_add.colors): 32 | name_string += _rand_from_array(colors) + " " 33 | if has_added.has(text_add.properties): 34 | name_string += _rand_from_array(properties) + " " 35 | if has_added.has(text_add.flavors): 36 | name_string += _rand_from_array(flavors) + " " 37 | if has_added.has(text_add.materials): 38 | name_string += _rand_from_array(materials) + " " 39 | 40 | 41 | 42 | # if _rand_chance(0.2 ): 43 | # name_string += _rand_from_array(sizes) + " " 44 | # if _rand_chance(0.2): 45 | # name_string += _rand_from_array(ages) + " " 46 | # if _rand_chance(0.2): 47 | # name_string += _rand_from_array(colors) + " " 48 | # if _rand_chance(0.2): 49 | # name_string += _rand_from_array(properties) + " " 50 | # if _rand_chance(0.2): 51 | # name_string += _rand_from_array(materials) + " " 52 | # if _rand_chance(0.2): 53 | # name_string += _rand_from_array(flavors) + " " 54 | 55 | if _rand_chance(0.2): 56 | name_string += _rand_from_array(things) + " " 57 | name_string += "of " 58 | name_string += _rand_from_array(other) 59 | else: 60 | if _rand_chance(0.5): 61 | name_string += _rand_from_array(foods) 62 | else: 63 | name_string += _rand_from_array(things) 64 | 65 | return name_string 66 | 67 | func _rand_chance(chance): 68 | return rand_range(0.0, 1.0) < chance 69 | 70 | func _rand_from_array(arr): 71 | return arr[randi() % arr.size()] 72 | -------------------------------------------------------------------------------- /Generator/SpriteGenerator.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var map_generator 4 | var cellular_automata 5 | var colorscheme_generator 6 | var color_filler 7 | var group_drawer 8 | 9 | func _init(): 10 | map_generator = load("res://Generator/MapGenerator.gd").new() 11 | cellular_automata = load("res://Generator/CellularAutomata.gd").new() 12 | colorscheme_generator = load("res://Generator/ColorSchemeGenerator.gd").new() 13 | color_filler = load("res://Generator/ColorFiller.gd").new() 14 | group_drawer = load("res://Generator/GroupDrawer.tscn") 15 | 16 | func get_sprite(sd, size, n_colors, outline = true): 17 | seed(sd) 18 | var map = map_generator.generate_new(size) 19 | 20 | map = cellular_automata.do_steps(map) 21 | var scheme = colorscheme_generator.generate_new_colorscheme(n_colors) 22 | var eye_scheme = colorscheme_generator.generate_new_colorscheme(n_colors) 23 | var all_groups = color_filler.fill_colors(map, scheme, eye_scheme, n_colors, outline) 24 | var g_draw = group_drawer.instance() 25 | g_draw.groups = all_groups.groups 26 | g_draw.negative_groups = all_groups.negative_groups 27 | return g_draw 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Deep-Fold 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/HTML5FileExchange/HTML5FileExchange.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | signal InFocus 4 | 5 | func _ready(): 6 | if OS.get_name() == "HTML5" and OS.has_feature('JavaScript'): 7 | _define_js() 8 | 9 | 10 | func _notification(notification:int) -> void: 11 | if notification == MainLoop.NOTIFICATION_WM_FOCUS_IN: 12 | emit_signal("InFocus") 13 | 14 | func _define_js()->void: 15 | #Define JS script 16 | JavaScript.eval(""" 17 | var fileData; 18 | var fileType; 19 | var fileName; 20 | var canceled; 21 | function upload() { 22 | canceled = true; 23 | var input = document.createElement('INPUT'); 24 | input.setAttribute("type", "file"); 25 | input.setAttribute("accept", "image/png, image/jpeg, image/webp"); 26 | input.click(); 27 | input.addEventListener('change', event => { 28 | if (event.target.files.length > 0){ 29 | canceled = false;} 30 | var file = event.target.files[0]; 31 | var reader = new FileReader(); 32 | fileType = file.type; 33 | fileName = file.name; 34 | reader.readAsArrayBuffer(file); 35 | reader.onloadend = function (evt) { 36 | if (evt.target.readyState == FileReader.DONE) { 37 | fileData = evt.target.result; 38 | } 39 | } 40 | }); 41 | } 42 | function download(fileName, byte) { 43 | var buffer = Uint8Array.from(byte); 44 | var blob = new Blob([buffer], { type: 'image/png'}); 45 | var link = document.createElement('a'); 46 | link.href = window.URL.createObjectURL(blob); 47 | link.download = fileName; 48 | link.click(); 49 | }; 50 | """, true) 51 | 52 | 53 | func load_image()->Image: 54 | if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'): 55 | return 56 | 57 | #Execute js function 58 | JavaScript.eval("upload();", true) #opens promt for choosing file 59 | 60 | #label.text = "Wait for focus" 61 | yield(self, "InFocus") #wait until js promt is closed 62 | 63 | #label.text = "Timer on for loading" 64 | yield(get_tree().create_timer(0.1), "timeout") #give some time for async js data load 65 | 66 | if JavaScript.eval("canceled;", true): # if File Dialog closed w/o file 67 | #label.text = "Canceled prompt" 68 | return 69 | 70 | # use data from png data 71 | #label.text = "Load image" 72 | var imageData 73 | while true: 74 | imageData = JavaScript.eval("fileData;", true) 75 | if imageData != null: 76 | break 77 | #label.text = "No image yet" 78 | yield(get_tree().create_timer(1.0), "timeout") #need more time to load data 79 | 80 | var imageType = JavaScript.eval("fileType;", true) 81 | var imageName = JavaScript.eval("fileName;", true) 82 | 83 | var image = Image.new() 84 | var image_error 85 | match imageType: 86 | "image/png": 87 | image_error = image.load_png_from_buffer(imageData) 88 | "image/jpeg": 89 | image_error = image.load_jpg_from_buffer(imageData) 90 | "image/webp": 91 | image_error = image.load_webp_from_buffer(imageData) 92 | var invalidType: 93 | #label.text = "Unsupported file format - %s." % invalidType 94 | return 95 | if image_error: 96 | #label.text = "An error occurred while trying to display the image." 97 | return 98 | else: 99 | return image 100 | # Display texture 101 | var tex = ImageTexture.new() 102 | tex.create_from_image(image, 0) # Flag = 0 or else export is fucked! 103 | Sprite.texture = tex 104 | #loadedImage = image # Keep Image for later, just in case... 105 | #loadedImageName = imageName 106 | #label.text = "Image %s loaded as %s." % [imageName, imageType] 107 | return 108 | #label.text = "Something went wrong" 109 | 110 | 111 | func save_image(image:Image, fileName:String = "export")->void: 112 | if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'): 113 | return 114 | 115 | image.clear_mipmaps() 116 | if image.save_png("user://export_temp.png"): 117 | #label.text = "Error saving temp file" 118 | return 119 | var file:File = File.new() 120 | if file.open("user://export_temp.png", File.READ): 121 | #label.text = "Error opening file" 122 | return 123 | var pngData = Array(file.get_buffer(file.get_len())) #read data as PoolByteArray and convert it to Array for JS 124 | file.close() 125 | var dir = Directory.new() 126 | dir.remove("user://export_temp.png") 127 | JavaScript.eval("download('%s', %s);" % [fileName, str(pngData)], true) 128 | #label.text = "Saving DONE" 129 | -------------------------------------------------------------------------------- /addons/HTML5FileExchange/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="HTML5 File Exchange" 4 | description="Open/Import and Save/Export files in HTML5 environment. 5 | Supported input - *.png, *.jpeg, *.webp 6 | Supported output - *.png 7 | 8 | Usage: 9 | Get file from User - 10 | var image:Image = yield(HTML5File.get_image(), \"completed\") 11 | Send file to User - 12 | HTML5File.save_image(image:Image, fileName:String = \"export\")" 13 | author="laame" 14 | version="0.1.1" 15 | script="plugin.gd" 16 | -------------------------------------------------------------------------------- /addons/HTML5FileExchange/plugin.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends EditorPlugin 3 | 4 | 5 | func _enter_tree(): 6 | add_autoload_singleton("HTML5File", "res://addons/HTML5FileExchange/HTML5FileExchange.gd") 7 | 8 | 9 | func _exit_tree(): 10 | remove_autoload_singleton("HTML5File") 11 | -------------------------------------------------------------------------------- /default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deep-Fold/SpriteGenerator/841d76d4a91cf7c345d0c33fe01d5cb06259736f/icon.png -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=4 10 | 11 | _global_script_classes=[ ] 12 | _global_script_class_icons={ 13 | 14 | } 15 | 16 | [application] 17 | 18 | config/name="SpriteGenerator" 19 | run/main_scene="res://GUI/GUI.tscn" 20 | config/icon="res://icon.png" 21 | 22 | [autoload] 23 | 24 | HTML5File="*res://addons/HTML5FileExchange/HTML5FileExchange.gd" 25 | 26 | [display] 27 | 28 | window/stretch/mode="2d" 29 | window/stretch/aspect="keep" 30 | 31 | [editor_plugins] 32 | 33 | enabled=PoolStringArray( ) 34 | 35 | [importer_defaults] 36 | 37 | texture={ 38 | "compress/bptc_ldr": 0, 39 | "compress/hdr_mode": 0, 40 | "compress/lossy_quality": 0.7, 41 | "compress/mode": 0, 42 | "compress/normal_map": 0, 43 | "detect_3d": true, 44 | "flags/anisotropic": false, 45 | "flags/filter": false, 46 | "flags/mipmaps": false, 47 | "flags/repeat": 0, 48 | "flags/srgb": 2, 49 | "process/HDR_as_SRGB": false, 50 | "process/fix_alpha_border": true, 51 | "process/invert_color": false, 52 | "process/premult_alpha": false, 53 | "size_limit": 0, 54 | "stream": false, 55 | "svg/scale": 1.0 56 | } 57 | 58 | [physics] 59 | 60 | common/enable_pause_aware_picking=true 61 | 62 | [rendering] 63 | 64 | environment/default_environment="res://default_env.tres" 65 | --------------------------------------------------------------------------------