From 4c0c57d5baa467d72721c8b0f39195ddc7b5ad12 Mon Sep 17 00:00:00 2001 From: Douwe Ravers Date: Sat, 5 Oct 2024 13:12:42 +0200 Subject: [PATCH] created fog of war --- .../GuiResizerTopLeft.svg | 1 + .../GuiResizerTopLeft.svg.import | 37 + .../GuiResizerTopRight.svg | 1 + .../GuiResizerTopRight.svg.import | 37 + addons/anthonyec.camera_preview/Pin.svg | 1 + .../anthonyec.camera_preview/Pin.svg.import | 37 + addons/anthonyec.camera_preview/plugin.cfg | 7 + addons/anthonyec.camera_preview/plugin.gd | 87 + addons/anthonyec.camera_preview/preview.gd | 404 +++++ addons/anthonyec.camera_preview/preview.tscn | 200 +++ addons/godot_debug_environments/LICENSE | 21 + addons/godot_debug_environments/README.md | 2 + .../debug_environment_editor_inspector.gd | 37 + .../debug_environment_lib.gd | 6 + .../debug_environment_watcher.gd | 10 + addons/godot_debug_environments/flask.svg | 1 + .../godot_debug_environments/flask.svg.import | 37 + .../launchSettingsBase.json | 19 + addons/godot_debug_environments/plugin.cfg | 10 + addons/godot_debug_environments/plugin.gd | 13 + .../scene-library/icons/thumb_placeholder.svg | 5 + .../icons/thumb_placeholder.svg.import | 37 + addons/scene-library/plugin.cfg | 7 + addons/scene-library/plugin.gd | 65 + addons/scene-library/scene_library.cfg | 4 + addons/scene-library/scripts/scene_library.gd | 1469 +++++++++++++++++ .../debug_environments/debug_env_player.tscn | 39 + entities/faction/enemy/enemy.gd | 11 + entities/faction/faction.gd | 13 + entities/faction/fog_of_war.gd | 47 + entities/faction/fog_of_war.tscn | 18 + entities/faction/player/player.gd | 11 + entities/faction/player/player_camera.gd | 33 + project.godot | 67 + resources/meshlib.tres | 28 + resources/meshlib.tscn | 13 + scenes/gam5CAE.tmp | 159 ++ scenes/gam77C8.tmp | 159 ++ scenes/game.gd | 9 + scenes/game.tscn | 234 +++ 40 files changed, 3396 insertions(+) create mode 100644 addons/anthonyec.camera_preview/GuiResizerTopLeft.svg create mode 100644 addons/anthonyec.camera_preview/GuiResizerTopLeft.svg.import create mode 100644 addons/anthonyec.camera_preview/GuiResizerTopRight.svg create mode 100644 addons/anthonyec.camera_preview/GuiResizerTopRight.svg.import create mode 100644 addons/anthonyec.camera_preview/Pin.svg create mode 100644 addons/anthonyec.camera_preview/Pin.svg.import create mode 100644 addons/anthonyec.camera_preview/plugin.cfg create mode 100644 addons/anthonyec.camera_preview/plugin.gd create mode 100644 addons/anthonyec.camera_preview/preview.gd create mode 100644 addons/anthonyec.camera_preview/preview.tscn create mode 100644 addons/godot_debug_environments/LICENSE create mode 100644 addons/godot_debug_environments/README.md create mode 100644 addons/godot_debug_environments/debug_environment_editor_inspector.gd create mode 100644 addons/godot_debug_environments/debug_environment_lib.gd create mode 100644 addons/godot_debug_environments/debug_environment_watcher.gd create mode 100644 addons/godot_debug_environments/flask.svg create mode 100644 addons/godot_debug_environments/flask.svg.import create mode 100644 addons/godot_debug_environments/launchSettingsBase.json create mode 100644 addons/godot_debug_environments/plugin.cfg create mode 100644 addons/godot_debug_environments/plugin.gd create mode 100644 addons/scene-library/icons/thumb_placeholder.svg create mode 100644 addons/scene-library/icons/thumb_placeholder.svg.import create mode 100644 addons/scene-library/plugin.cfg create mode 100644 addons/scene-library/plugin.gd create mode 100644 addons/scene-library/scene_library.cfg create mode 100644 addons/scene-library/scripts/scene_library.gd create mode 100644 editor/debug_environments/debug_env_player.tscn create mode 100644 entities/faction/enemy/enemy.gd create mode 100644 entities/faction/faction.gd create mode 100644 entities/faction/fog_of_war.gd create mode 100644 entities/faction/fog_of_war.tscn create mode 100644 entities/faction/player/player.gd create mode 100644 entities/faction/player/player_camera.gd create mode 100644 resources/meshlib.tres create mode 100644 resources/meshlib.tscn create mode 100644 scenes/gam5CAE.tmp create mode 100644 scenes/gam77C8.tmp create mode 100644 scenes/game.gd create mode 100644 scenes/game.tscn diff --git a/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg b/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg new file mode 100644 index 0000000..fe4dbf5 --- /dev/null +++ b/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg @@ -0,0 +1 @@ + diff --git a/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg.import b/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg.import new file mode 100644 index 0000000..9584d3b --- /dev/null +++ b/addons/anthonyec.camera_preview/GuiResizerTopLeft.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://btc01wc11tiid" +path="res://.godot/imported/GuiResizerTopLeft.svg-eb563f557424c74239e878a1213a5bf4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/anthonyec.camera_preview/GuiResizerTopLeft.svg" +dest_files=["res://.godot/imported/GuiResizerTopLeft.svg-eb563f557424c74239e878a1213a5bf4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/anthonyec.camera_preview/GuiResizerTopRight.svg b/addons/anthonyec.camera_preview/GuiResizerTopRight.svg new file mode 100644 index 0000000..dd00953 --- /dev/null +++ b/addons/anthonyec.camera_preview/GuiResizerTopRight.svg @@ -0,0 +1 @@ + diff --git a/addons/anthonyec.camera_preview/GuiResizerTopRight.svg.import b/addons/anthonyec.camera_preview/GuiResizerTopRight.svg.import new file mode 100644 index 0000000..4a1fa5d --- /dev/null +++ b/addons/anthonyec.camera_preview/GuiResizerTopRight.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://04l05jxuyt7k" +path="res://.godot/imported/GuiResizerTopRight.svg-cc1dc8556d51357c5eb0b01d09d8f049.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/anthonyec.camera_preview/GuiResizerTopRight.svg" +dest_files=["res://.godot/imported/GuiResizerTopRight.svg-cc1dc8556d51357c5eb0b01d09d8f049.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/anthonyec.camera_preview/Pin.svg b/addons/anthonyec.camera_preview/Pin.svg new file mode 100644 index 0000000..8e5935c --- /dev/null +++ b/addons/anthonyec.camera_preview/Pin.svg @@ -0,0 +1 @@ + diff --git a/addons/anthonyec.camera_preview/Pin.svg.import b/addons/anthonyec.camera_preview/Pin.svg.import new file mode 100644 index 0000000..27d274f --- /dev/null +++ b/addons/anthonyec.camera_preview/Pin.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://do6d60od41vmg" +path="res://.godot/imported/Pin.svg-83b09f5c00a829c5d8b136bf5bae65bc.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/anthonyec.camera_preview/Pin.svg" +dest_files=["res://.godot/imported/Pin.svg-83b09f5c00a829c5d8b136bf5bae65bc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/anthonyec.camera_preview/plugin.cfg b/addons/anthonyec.camera_preview/plugin.cfg new file mode 100644 index 0000000..4ad0d4c --- /dev/null +++ b/addons/anthonyec.camera_preview/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Little Camera Preview" +description="Shows a picture-in-picture preview of the selected 2D or 3D camera" +author="Anthony Cossins" +version="1.3" +script="plugin.gd" diff --git a/addons/anthonyec.camera_preview/plugin.gd b/addons/anthonyec.camera_preview/plugin.gd new file mode 100644 index 0000000..4e74dd8 --- /dev/null +++ b/addons/anthonyec.camera_preview/plugin.gd @@ -0,0 +1,87 @@ +@tool +extends EditorPlugin + +const preview_scene = preload("res://addons/anthonyec.camera_preview/preview.tscn") + +var preview: CameraPreview +var current_main_screen_name: String + +func _enter_tree() -> void: + main_screen_changed.connect(_on_main_screen_changed) + EditorInterface.get_selection().selection_changed.connect(_on_editor_selection_changed) + + # Initialise preview panel and add to main screen. + preview = preview_scene.instantiate() as CameraPreview + preview.request_hide() + + var main_screen = EditorInterface.get_editor_main_screen() + main_screen.add_child(preview) + +func _exit_tree() -> void: + if preview: + preview.queue_free() + +func _ready() -> void: + # TODO: Currently there is no API to get the main screen name without + # listening to the `EditorPlugin.main_screen_changed` signal: + # https://github.com/godotengine/godot-proposals/issues/2081 + EditorInterface.set_main_screen_editor("Script") + EditorInterface.set_main_screen_editor("3D") + +func _on_main_screen_changed(screen_name: String) -> void: + current_main_screen_name = screen_name + + # TODO: Bit of a hack to prevent pinned staying between view changes on the same scene. + preview.unlink_camera() + _on_editor_selection_changed() + +func _on_editor_selection_changed() -> void: + if not is_main_screen_viewport(): + # This hides the preview "container" and not the preview itself, allowing + # any locked previews to remain visible once switching back to 3D tab. + preview.visible = false + return + + preview.visible = true + + var selected_nodes = EditorInterface.get_selection().get_selected_nodes() + + var selected_camera_3d: Camera3D = find_camera_3d_or_null(selected_nodes) + var selected_camera_2d: Camera2D = find_camera_2d_or_null(selected_nodes) + + if selected_camera_3d and current_main_screen_name == "3D": + preview.link_with_camera_3d(selected_camera_3d) + preview.request_show() + + elif selected_camera_2d and current_main_screen_name == "2D": + preview.link_with_camera_2d(selected_camera_2d) + preview.request_show() + + else: + preview.request_hide() + +func is_main_screen_viewport() -> bool: + return current_main_screen_name == "3D" or current_main_screen_name == "2D" + +func find_camera_3d_or_null(nodes: Array[Node]) -> Camera3D: + var camera: Camera3D + + for node in nodes: + if node is Camera3D: + camera = node as Camera3D + break + + return camera + +func find_camera_2d_or_null(nodes: Array[Node]) -> Camera2D: + var camera: Camera2D + + for node in nodes: + if node is Camera2D: + camera = node as Camera2D + break + + return camera + +func _on_selected_camera_3d_tree_exiting() -> void: + preview.unlink_camera() diff --git a/addons/anthonyec.camera_preview/preview.gd b/addons/anthonyec.camera_preview/preview.gd new file mode 100644 index 0000000..3c07d04 --- /dev/null +++ b/addons/anthonyec.camera_preview/preview.gd @@ -0,0 +1,404 @@ +@tool + +class_name CameraPreview +extends Control + +enum CameraType { + CAMERA_2D, + CAMERA_3D +} + +enum PinnedPosition { + LEFT, + RIGHT, +} + +enum InteractionState { + NONE, + RESIZE, + DRAG, + + # Animation is split into 2 seperate states so that the tween is only + # invoked once in the "start" state. + START_ANIMATE_INTO_PLACE, + ANIMATE_INTO_PLACE, +} + +const margin_3d: Vector2 = Vector2(10, 10) +const margin_2d: Vector2 = Vector2(20, 15) +const panel_margin: float = 2 +const min_panel_size: float = 250 + +@onready var panel: Panel = %Panel +@onready var placeholder: Panel = %Placeholder +@onready var preview_camera_3d: Camera3D = %Camera3D +@onready var preview_camera_2d: Camera2D = %Camera2D +@onready var sub_viewport: SubViewport = %SubViewport +@onready var sub_viewport_text_rect: TextureRect = %TextureRect +@onready var resize_left_handle: Button = %ResizeLeftHandle +@onready var resize_right_handle: Button = %ResizeRightHandle +@onready var lock_button: Button = %LockButton +@onready var gradient: TextureRect = %Gradient +@onready var viewport_margin_container: MarginContainer = %ViewportMarginContainer +@onready var overlay_margin_container: MarginContainer = %OverlayMarginContainer +@onready var overlay_container: Control = %OverlayContainer + +var camera_type: CameraType = CameraType.CAMERA_3D +var pinned_position: PinnedPosition = PinnedPosition.RIGHT +var viewport_ratio: float = 1 +var editor_scale: float = EditorInterface.get_editor_scale() +var is_locked: bool +var show_controls: bool +var selected_camera_3d: Camera3D +var selected_camera_2d: Camera2D + +var state: InteractionState = InteractionState.NONE +var initial_mouse_position: Vector2 +var initial_panel_size: Vector2 +var initial_panel_position: Vector2 + +func _ready() -> void: + # Set initial width. + panel.size.x = min_panel_size * editor_scale + + # Setting texture to viewport in code instead of directly in the editor + # because otherwise an error "Path to node is invalid: Panel/SubViewport" + # on first load. This is harmless but doesn't look great. + # + # This is a known issue: + # https://github.com/godotengine/godot/issues/27790#issuecomment-499740220 + sub_viewport_text_rect.texture = sub_viewport.get_texture() + + # From what I can tell there's something wrong with how an editor theme + # scales when used within a plugin. It seems to ignore the screen scale. + # For instance, a 30x30px button will appear tiny on a retina display. + # + # Someone else had the issue with no luck: + # https://forum.godotengine.org/t/how-to-scale-plugin-controls-to-look-the-same-in-4k-as-1080p/36151 + # + # And seems Dialogic also scales buttons manually: + # https://github.com/dialogic-godot/dialogic/blob/master/addons/dialogic/Editor/Common/sidebar.gd#L25C6-L38 + # + # Maybe I don't know the correct way to do it, so for now the workaround is + # to set the correct size in code using screen scale. + var button_size = Vector2(30, 30) * editor_scale + var margin_size: float = panel_margin * editor_scale + + resize_left_handle.size = button_size + resize_left_handle.pivot_offset = Vector2(0, 0) * editor_scale + + resize_right_handle.size = button_size + resize_right_handle.pivot_offset = Vector2(30, 30) * editor_scale + + lock_button.size = button_size + lock_button.pivot_offset = Vector2(0, 30) * editor_scale + + viewport_margin_container.add_theme_constant_override("margin_left", margin_size) + viewport_margin_container.add_theme_constant_override("margin_top", margin_size) + viewport_margin_container.add_theme_constant_override("margin_right", margin_size) + viewport_margin_container.add_theme_constant_override("margin_bottom", margin_size) + + overlay_margin_container.add_theme_constant_override("margin_left", margin_size) + overlay_margin_container.add_theme_constant_override("margin_top", margin_size) + overlay_margin_container.add_theme_constant_override("margin_right", margin_size) + overlay_margin_container.add_theme_constant_override("margin_bottom", margin_size) + + # Parent node overlay size is not available on first ready, need to wait a + # frame for it to be drawn. + await get_tree().process_frame + + # Anchors are set in code because setting them in the editor UI doesn't take + # editor scale into account. + resize_left_handle.position = Vector2(0, 0) + resize_right_handle.set_anchors_preset(Control.PRESET_TOP_LEFT) + + resize_right_handle.position = Vector2(overlay_container.size.x - button_size.x, 0) + resize_right_handle.set_anchors_preset(Control.PRESET_TOP_RIGHT) + + lock_button.position = Vector2(0, overlay_container.size.y - button_size.y) + lock_button.set_anchors_preset(Control.PRESET_BOTTOM_LEFT) + +func _process(_delta: float) -> void: + if not visible: return + + match state: + InteractionState.NONE: + panel.size = get_clamped_size(panel.size) + panel.position = get_pinned_position(pinned_position) + + InteractionState.RESIZE: + var delta_mouse_position = initial_mouse_position - get_global_mouse_position() + var resized_size = panel.size + + if pinned_position == PinnedPosition.LEFT: + resized_size = initial_panel_size - delta_mouse_position + + if pinned_position == PinnedPosition.RIGHT: + resized_size = initial_panel_size + delta_mouse_position + + panel.size = get_clamped_size(resized_size) + panel.position = get_pinned_position(pinned_position) + + InteractionState.DRAG: + placeholder.size = panel.size + + var global_mouse_position = get_global_mouse_position() + var offset = initial_mouse_position - initial_panel_position + + panel.global_position = global_mouse_position - offset + + if global_mouse_position.x < global_position.x + size.x / 2: + pinned_position = PinnedPosition.LEFT + else: + pinned_position = PinnedPosition.RIGHT + + placeholder.position = get_pinned_position(pinned_position) + + InteractionState.START_ANIMATE_INTO_PLACE: + var final_position: Vector2 = get_pinned_position(pinned_position) + var tween = get_tree().create_tween() + + tween.set_ease(Tween.EASE_OUT) + tween.set_trans(Tween.TRANS_CUBIC) + tween.tween_property(panel, "position", final_position, 0.3) + + tween.finished.connect(func(): + panel.position = final_position + state = InteractionState.NONE + ) + + state = InteractionState.ANIMATE_INTO_PLACE + + # I couldn't get `mouse_entered` and `mouse_exited` events to work + # nicely, so I use rect method instead. Plus using this method it's easy to + # grow the hit area size. + var panel_hover_rect = Rect2(panel.global_position, panel.size) + panel_hover_rect = panel_hover_rect.grow(40) + + var mouse_position = get_global_mouse_position() + + show_controls = state != InteractionState.NONE or panel_hover_rect.has_point(mouse_position) + + # UI visibility. + resize_left_handle.visible = show_controls and pinned_position == PinnedPosition.RIGHT + resize_right_handle.visible = show_controls and pinned_position == PinnedPosition.LEFT + lock_button.visible = show_controls or is_locked + placeholder.visible = state == InteractionState.DRAG or state == InteractionState.ANIMATE_INTO_PLACE + gradient.visible = show_controls + + # Sync camera settings. + if camera_type == CameraType.CAMERA_3D and selected_camera_3d: + sub_viewport.size = panel.size + + # Sync position and rotation without using a `RemoteTransform` node + # because if you save a camera as a scene, the remote transform node will + # be stored within the scene. Also it's harder to keep the remote + # transform `remote_path` up-to-date with scene changes, which causes + # many errors. + preview_camera_3d.global_position = selected_camera_3d.global_position + preview_camera_3d.global_rotation = selected_camera_3d.global_rotation + + preview_camera_3d.fov = selected_camera_3d.fov + preview_camera_3d.projection = selected_camera_3d.projection + preview_camera_3d.size = selected_camera_3d.size + preview_camera_3d.cull_mask = selected_camera_3d.cull_mask + preview_camera_3d.keep_aspect = selected_camera_3d.keep_aspect + preview_camera_3d.near = selected_camera_3d.near + preview_camera_3d.far = selected_camera_3d.far + preview_camera_3d.h_offset = selected_camera_3d.h_offset + preview_camera_3d.v_offset = selected_camera_3d.v_offset + preview_camera_3d.attributes = selected_camera_3d.attributes + preview_camera_3d.environment = selected_camera_3d.environment + + if camera_type == CameraType.CAMERA_2D and selected_camera_2d: + var project_window_size = get_project_window_size() + var ratio = project_window_size.x / panel.size.x + + # TODO: Is there a better way to fix this? + # The camera border is visible sometimes due to pixel rounding. + # Subtract 1px from right and bottom to hide this. + var hide_camera_border_fix = Vector2(1, 1) + + sub_viewport.size = panel.size + sub_viewport.size_2d_override = (panel.size - hide_camera_border_fix) * ratio + sub_viewport.size_2d_override_stretch = true + + preview_camera_2d.global_position = selected_camera_2d.global_position + preview_camera_2d.global_rotation = selected_camera_2d.global_rotation + + preview_camera_2d.offset = selected_camera_2d.offset + preview_camera_2d.zoom = selected_camera_2d.zoom + preview_camera_2d.ignore_rotation = selected_camera_2d.ignore_rotation + preview_camera_2d.anchor_mode = selected_camera_2d.anchor_mode + preview_camera_2d.limit_left = selected_camera_2d.limit_left + preview_camera_2d.limit_right = selected_camera_2d.limit_right + preview_camera_2d.limit_top = selected_camera_2d.limit_top + preview_camera_2d.limit_bottom = selected_camera_2d.limit_bottom + +func link_with_camera_3d(camera_3d: Camera3D) -> void: + # TODO: Camera may not be ready since this method is called in `_enter_tree` + # in the plugin because of a workaround for: + # https://github.com/godotengine/godot-proposals/issues/2081 + if not preview_camera_3d: + return request_hide() + + var is_different_camera = camera_3d != preview_camera_3d + + # TODO: A bit messy. + if is_different_camera: + if preview_camera_3d.tree_exiting.is_connected(unlink_camera): + preview_camera_3d.tree_exiting.disconnect(unlink_camera) + + if not camera_3d.tree_exiting.is_connected(unlink_camera): + camera_3d.tree_exiting.connect(unlink_camera) + + sub_viewport.disable_3d = false + sub_viewport.world_3d = camera_3d.get_world_3d() + + selected_camera_3d = camera_3d + camera_type = CameraType.CAMERA_3D + +func link_with_camera_2d(camera_2d: Camera2D) -> void: + if not preview_camera_2d: + return request_hide() + + var is_different_camera = camera_2d != preview_camera_2d + + # TODO: A bit messy. + if is_different_camera: + if preview_camera_2d.tree_exiting.is_connected(unlink_camera): + preview_camera_2d.tree_exiting.disconnect(unlink_camera) + + if not camera_2d.tree_exiting.is_connected(unlink_camera): + camera_2d.tree_exiting.connect(unlink_camera) + + sub_viewport.disable_3d = true + sub_viewport.world_2d = camera_2d.get_world_2d() + + selected_camera_2d = camera_2d + camera_type = CameraType.CAMERA_2D + +func unlink_camera() -> void: + if selected_camera_3d: + selected_camera_3d = null + + if selected_camera_2d: + selected_camera_2d = null + + is_locked = false + lock_button.button_pressed = false + +func request_hide() -> void: + if is_locked: return + visible = false + +func request_show() -> void: + visible = true + +func get_pinned_position(pinned_position: PinnedPosition) -> Vector2: + var margin: Vector2 = margin_3d * editor_scale + + if camera_type == CameraType.CAMERA_2D: + margin = margin_2d * editor_scale + + match pinned_position: + PinnedPosition.LEFT: + return Vector2.ZERO - Vector2(0, panel.size.y) - Vector2(-margin.x, margin.y) + PinnedPosition.RIGHT: + return size - panel.size - margin + _: + assert(false, "Unknown pinned position %s" % str(pinned_position)) + + return Vector2.ZERO + +func get_clamped_size(desired_size: Vector2) -> Vector2: + var viewport_ratio = get_project_window_ratio() + var editor_viewport_size = get_editor_viewport_size() + + var max_bounds = Vector2( + editor_viewport_size.x * 0.6, + editor_viewport_size.y * 0.8 + ) + + var clamped_size = desired_size + + # Apply aspect ratio. + clamped_size = Vector2(clamped_size.x, clamped_size.x * viewport_ratio) + + # Clamp the max size while respecting the aspect ratio. + if clamped_size.y >= max_bounds.y: + clamped_size.x = max_bounds.y / viewport_ratio + clamped_size.y = max_bounds.y + + if clamped_size.x >= max_bounds.x: + clamped_size.x = max_bounds.x + clamped_size.y = max_bounds.x * viewport_ratio + + # Clamp the min size based on if it's portrait or landscape. Portrait min + # size should be based on it's height. Landscape min size is based on it's + # width instead. Applying min width to a portrait size would make it too big. + var is_portrait = viewport_ratio > 1 + + if is_portrait and clamped_size.y <= min_panel_size * editor_scale: + clamped_size.x = min_panel_size / viewport_ratio + clamped_size.y = min_panel_size + clamped_size = clamped_size * editor_scale + + if not is_portrait and clamped_size.x <= min_panel_size * editor_scale: + clamped_size.x = min_panel_size + clamped_size.y = min_panel_size * viewport_ratio + clamped_size = clamped_size * editor_scale + + # Round down to avoid sub-pixel artifacts, mainly seen around the margins. + return clamped_size.floor() + +func get_project_window_size() -> Vector2: + var window_width = float(ProjectSettings.get_setting("display/window/size/viewport_width")) + var window_height = float(ProjectSettings.get_setting("display/window/size/viewport_height")) + + return Vector2(window_width, window_height) + +func get_project_window_ratio() -> float: + var project_window_size = get_project_window_size() + + return project_window_size.y / project_window_size.x + +func get_editor_viewport_size() -> Vector2: + var fallback_size = EditorInterface.get_editor_main_screen().size + + # There isn't an API for getting the viewport node. Instead it has to be + # found by checking the parent's parent of the subviewport and find + # the correct node based on name and class. + var editor_sub_viewport_3d = EditorInterface.get_editor_viewport_3d(0) + var editor_viewport_container = editor_sub_viewport_3d.get_parent().get_parent().get_parent() + + # Early return incase editor tree structure has changed. + if editor_viewport_container.get_class() != "Node3DEditorViewportContainer": + return fallback_size + + return editor_viewport_container.size + +func _on_resize_handle_button_down() -> void: + if state != InteractionState.NONE: return + + state = InteractionState.RESIZE + initial_mouse_position = get_global_mouse_position() + initial_panel_size = panel.size + +func _on_resize_handle_button_up() -> void: + state = InteractionState.NONE + +func _on_drag_handle_button_down() -> void: + if state != InteractionState.NONE: return + + state = InteractionState.DRAG + initial_mouse_position = get_global_mouse_position() + initial_panel_position = panel.global_position + +func _on_drag_handle_button_up() -> void: + if state != InteractionState.DRAG: return + + state = InteractionState.START_ANIMATE_INTO_PLACE + +func _on_lock_button_pressed() -> void: + is_locked = !is_locked diff --git a/addons/anthonyec.camera_preview/preview.tscn b/addons/anthonyec.camera_preview/preview.tscn new file mode 100644 index 0000000..77b3ced --- /dev/null +++ b/addons/anthonyec.camera_preview/preview.tscn @@ -0,0 +1,200 @@ +[gd_scene load_steps=8 format=3 uid="uid://xybmfvufjuv"] + +[ext_resource type="Script" path="res://addons/anthonyec.camera_preview/preview.gd" id="1_6b32r"] +[ext_resource type="Texture2D" uid="uid://do6d60od41vmg" path="res://addons/anthonyec.camera_preview/Pin.svg" id="2_p0pa8"] +[ext_resource type="Texture2D" uid="uid://btc01wc11tiid" path="res://addons/anthonyec.camera_preview/GuiResizerTopLeft.svg" id="2_t64ej"] +[ext_resource type="Texture2D" uid="uid://04l05jxuyt7k" path="res://addons/anthonyec.camera_preview/GuiResizerTopRight.svg" id="3_6yuab"] + +[sub_resource type="ViewportTexture" id="ViewportTexture_hchdq"] +viewport_path = NodePath("Panel/SubViewport") + +[sub_resource type="Gradient" id="Gradient_11p6r"] +offsets = PackedFloat32Array(0, 0.3, 0.6, 1) +colors = PackedColorArray(0, 0, 0, 0.235294, 0, 0, 0, 0.0784314, 0, 0, 0, 0.0784314, 0, 0, 0, 0.235294) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_4dkve"] +gradient = SubResource("Gradient_11p6r") +width = 256 +height = 256 +fill_to = Vector2(2.08165e-12, 1) + +[node name="Preview" type="Control"] +z_index = 999 +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_6b32r") + +[node name="Placeholder" type="Panel" parent="."] +unique_name_in_owner = true +visible = false +modulate = Color(1, 1, 1, 0.705882) +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -40.0 +offset_top = -40.0 +offset_right = 410.0 +offset_bottom = 410.0 +grow_horizontal = 0 +grow_vertical = 0 + +[node name="Panel" type="Panel" parent="."] +unique_name_in_owner = true +clip_contents = true +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -520.0 +offset_top = -908.889 +offset_right = -20.0 +offset_bottom = -20.0 +grow_horizontal = 0 +grow_vertical = 0 +pivot_offset = Vector2(450, 300) + +[node name="SubViewport" type="SubViewport" parent="Panel"] +unique_name_in_owner = true +handle_input_locally = false +gui_disable_input = true +size_2d_override_stretch = true + +[node name="Camera3D" type="Camera3D" parent="Panel/SubViewport"] +unique_name_in_owner = true +current = true + +[node name="Camera2D" type="Camera2D" parent="Panel/SubViewport"] +unique_name_in_owner = true +ignore_rotation = false + +[node name="ViewportMarginContainer" type="MarginContainer" parent="Panel"] +unique_name_in_owner = true +clip_contents = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="TextureRect" type="TextureRect" parent="Panel/ViewportMarginContainer"] +unique_name_in_owner = true +layout_mode = 2 +texture = SubResource("ViewportTexture_hchdq") +expand_mode = 1 + +[node name="Gradient" type="TextureRect" parent="Panel"] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +texture = SubResource("GradientTexture2D_4dkve") + +[node name="OverlayMarginContainer" type="MarginContainer" parent="Panel"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="OverlayContainer" type="Control" parent="Panel/OverlayMarginContainer"] +unique_name_in_owner = true +clip_contents = true +layout_mode = 2 +mouse_filter = 2 + +[node name="DragHandle" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +focus_mode = 0 +flat = true + +[node name="ResizeLeftHandle" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"] +unique_name_in_owner = true +visible = false +layout_mode = 1 +offset_right = 60.0 +offset_bottom = 60.0 +size_flags_horizontal = 0 +size_flags_vertical = 0 +mouse_default_cursor_shape = 12 +icon = ExtResource("2_t64ej") +flat = true +icon_alignment = 1 +expand_icon = true + +[node name="ResizeRightHandle" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -60.0 +offset_bottom = 60.0 +pivot_offset = Vector2(60, 60) +size_flags_horizontal = 8 +size_flags_vertical = 0 +mouse_default_cursor_shape = 11 +icon = ExtResource("3_6yuab") +flat = true +icon_alignment = 1 +expand_icon = true + +[node name="LockButton" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_top = -60.0 +offset_right = 60.0 +pivot_offset = Vector2(0, 60) +size_flags_horizontal = 0 +size_flags_vertical = 8 +tooltip_text = "Always Show Preview" +toggle_mode = true +icon = ExtResource("2_p0pa8") +flat = true +icon_alignment = 1 +expand_icon = true + +[connection signal="button_down" from="Panel/OverlayMarginContainer/OverlayContainer/DragHandle" to="." method="_on_drag_handle_button_down"] +[connection signal="button_up" from="Panel/OverlayMarginContainer/OverlayContainer/DragHandle" to="." method="_on_drag_handle_button_up"] +[connection signal="renamed" from="Panel/OverlayMarginContainer/OverlayContainer/DragHandle" to="." method="_on_drag_handle_renamed"] +[connection signal="button_down" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeLeftHandle" to="." method="_on_resize_handle_button_down"] +[connection signal="button_up" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeLeftHandle" to="." method="_on_resize_handle_button_up"] +[connection signal="button_down" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeRightHandle" to="." method="_on_resize_handle_button_down"] +[connection signal="button_up" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeRightHandle" to="." method="_on_resize_handle_button_up"] +[connection signal="pressed" from="Panel/OverlayMarginContainer/OverlayContainer/LockButton" to="." method="_on_lock_button_pressed"] diff --git a/addons/godot_debug_environments/LICENSE b/addons/godot_debug_environments/LICENSE new file mode 100644 index 0000000..7fa92cd --- /dev/null +++ b/addons/godot_debug_environments/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Vladi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/addons/godot_debug_environments/README.md b/addons/godot_debug_environments/README.md new file mode 100644 index 0000000..d27b3a0 --- /dev/null +++ b/addons/godot_debug_environments/README.md @@ -0,0 +1,2 @@ +# Debug_environments +A simple Godot addon that allows to add context (extra scene) to scenes in "Run Current Scene" mode. diff --git a/addons/godot_debug_environments/debug_environment_editor_inspector.gd b/addons/godot_debug_environments/debug_environment_editor_inspector.gd new file mode 100644 index 0000000..359c75b --- /dev/null +++ b/addons/godot_debug_environments/debug_environment_editor_inspector.gd @@ -0,0 +1,37 @@ +@tool +extends EditorInspectorPlugin + +func _can_handle(object): + return object is Node and object == EditorInterface.get_edited_scene_root() + +func _parse_begin(object): + add_custom_control(create_ui(object)) + +func create_ui(node: Node) -> Control: + var path = DebugEnvironmentLib.get_debug_path(node) + var button = Button.new() + button.icon = load("res://addons/godot_debug_environments/flask.svg") + if(FileAccess.file_exists(path)): + button.text = "Edit testing environment" + button.pressed.connect(func(): edit_env(path), CONNECT_DEFERRED) + else: + button.text = "Create testing environment" + button.pressed.connect(func(): create_env(path), CONNECT_DEFERRED) + return button + +func edit_env(path: String): + EditorInterface.open_scene_from_path(path) + +func create_env(path: String): + if !DirAccess.dir_exists_absolute(path.get_base_dir()): + DirAccess.make_dir_recursive_absolute(path.get_base_dir()) + var env = Node.new() + env.name = path.get_file().replace(".tscn", "") + var target = Node.new() + target.name = "$DEBUG_TARGET$" + env.add_child(target) + target.owner = env + var scene = PackedScene.new() + scene.pack(env) + ResourceSaver.save(scene, path) + edit_env(path) diff --git a/addons/godot_debug_environments/debug_environment_lib.gd b/addons/godot_debug_environments/debug_environment_lib.gd new file mode 100644 index 0000000..4b3b2c4 --- /dev/null +++ b/addons/godot_debug_environments/debug_environment_lib.gd @@ -0,0 +1,6 @@ +class_name DebugEnvironmentLib extends Object + +const debug_environment_path = "res://editor/debug_environments/debug_env_$SCENE$" + +static func get_debug_path(node:Node) -> String: + return debug_environment_path.replace("$SCENE$", node.scene_file_path.get_file()) diff --git a/addons/godot_debug_environments/debug_environment_watcher.gd b/addons/godot_debug_environments/debug_environment_watcher.gd new file mode 100644 index 0000000..a3fcf23 --- /dev/null +++ b/addons/godot_debug_environments/debug_environment_watcher.gd @@ -0,0 +1,10 @@ +extends Node + +func _ready() -> void: + # Get loaded scene + var node = get_tree().current_scene + # Check if debug environment is stored + var path = DebugEnvironmentLib.get_debug_path(node) + if not FileAccess.file_exists(path): return + # Replace instance with debug environment + get_tree().call_deferred("change_scene_to_file",path) diff --git a/addons/godot_debug_environments/flask.svg b/addons/godot_debug_environments/flask.svg new file mode 100644 index 0000000..1c11b3d --- /dev/null +++ b/addons/godot_debug_environments/flask.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/godot_debug_environments/flask.svg.import b/addons/godot_debug_environments/flask.svg.import new file mode 100644 index 0000000..8e8ae87 --- /dev/null +++ b/addons/godot_debug_environments/flask.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://clfknatufch1h" +path="res://.godot/imported/flask.svg-2fc53ef5b1742277dd2e351a1ac292cf.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/godot_debug_environments/flask.svg" +dest_files=["res://.godot/imported/flask.svg-2fc53ef5b1742277dd2e351a1ac292cf.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/godot_debug_environments/launchSettingsBase.json b/addons/godot_debug_environments/launchSettingsBase.json new file mode 100644 index 0000000..fef48a0 --- /dev/null +++ b/addons/godot_debug_environments/launchSettingsBase.json @@ -0,0 +1,19 @@ + +{ + "profiles": { + "Godot Debug": { + "commandName": "Executable", + "executablePath": "$EDITOR$", + "commandLineArgs": "--path . --verbose", + "workingDirectory": ".", + "nativeDebugging": true + }, + "Godot Debug $NAME$": { + "commandName": "Executable", + "executablePath": "$EDITOR$", + "commandLineArgs": "--upwards $PATH$ --verbose", + "workingDirectory": ".", + "nativeDebugging": true + } + } +} diff --git a/addons/godot_debug_environments/plugin.cfg b/addons/godot_debug_environments/plugin.cfg new file mode 100644 index 0000000..8aba179 --- /dev/null +++ b/addons/godot_debug_environments/plugin.cfg @@ -0,0 +1,10 @@ +[plugin] + +name="Debug environments" +description=" + A simple script that loads a given scene in a specified environment. + Good to test scenes in a certain context without having to manually select the right test scene. +" +author="Douwe Ravers" +version="1.0" +script="plugin.gd" diff --git a/addons/godot_debug_environments/plugin.gd b/addons/godot_debug_environments/plugin.gd new file mode 100644 index 0000000..20d128c --- /dev/null +++ b/addons/godot_debug_environments/plugin.gd @@ -0,0 +1,13 @@ +@tool +extends EditorPlugin + +var plugin + +func _enter_tree(): + plugin = preload("res://addons/godot_debug_environments/debug_environment_editor_inspector.gd").new() + add_autoload_singleton("DebugEnvironmentWatcher", "res://addons/godot_debug_environments/debug_environment_watcher.gd") + add_inspector_plugin(plugin) + +func _exit_tree(): + remove_autoload_singleton("DebugEnvironmentWatcher") + remove_inspector_plugin(plugin) diff --git a/addons/scene-library/icons/thumb_placeholder.svg b/addons/scene-library/icons/thumb_placeholder.svg new file mode 100644 index 0000000..d02722b --- /dev/null +++ b/addons/scene-library/icons/thumb_placeholder.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/addons/scene-library/icons/thumb_placeholder.svg.import b/addons/scene-library/icons/thumb_placeholder.svg.import new file mode 100644 index 0000000..c1f4e58 --- /dev/null +++ b/addons/scene-library/icons/thumb_placeholder.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cqln7j6oifypf" +path="res://.godot/imported/thumb_placeholder.svg-81a80a6d7c59edb9d7ce28b01f213c5c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/scene-library/icons/thumb_placeholder.svg" +dest_files=["res://.godot/imported/thumb_placeholder.svg-81a80a6d7c59edb9d7ce28b01f213c5c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/scene-library/plugin.cfg b/addons/scene-library/plugin.cfg new file mode 100644 index 0000000..137aa95 --- /dev/null +++ b/addons/scene-library/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Scene Library" +description="A tool for easy work with your scenes" +author="Mansur Isaev and contributors" +version="0.7.0-dev" +script="plugin.gd" diff --git a/addons/scene-library/plugin.gd b/addons/scene-library/plugin.gd new file mode 100644 index 0000000..3dacdb5 --- /dev/null +++ b/addons/scene-library/plugin.gd @@ -0,0 +1,65 @@ +# Copyright (c) 2023-2024 Mansur Isaev and contributors - MIT License +# See `LICENSE.md` included in the source distribution for details. + +@tool +extends EditorPlugin + + +const SceneLibrary = preload("res://addons/scene-library/scripts/scene_library.gd") + +const BUTTON_NAME: String = "Scene Library" + + +var _editor: SceneLibrary = null +var _button: Button = null + + +func _enter_tree() -> void: + _editor = SceneLibrary.new() + _button = add_control_to_bottom_panel(_editor, BUTTON_NAME) + _editor.open_asset_request.connect(_on_open_asset_request) + _editor.show_in_file_system_request.connect(_on_show_in_file_system_request) + _editor.show_in_file_manager_request.connect(_on_show_in_file_manager_request) + _editor.library_saved.connect(_on_library_saved) + _editor.library_unsaved.connect(_on_library_unsaved) + + get_parent().connect(&"scene_saved", _editor.handle_scene_saved) + EditorInterface.get_file_system_dock().files_moved.connect(_editor.handle_file_moved) + EditorInterface.get_file_system_dock().file_removed.connect(_editor.handle_file_removed) + + +func _exit_tree() -> void: + remove_control_from_bottom_panel(_editor) + _editor.queue_free() + + +func _save_external_data() -> void: + if _editor.is_saved(): + return + + var library_path: String = _editor.get_current_library_path() + if library_path.is_empty(): + return + + _editor.save_library(library_path) + + +func _on_library_saved() -> void: + _button.set_text(BUTTON_NAME) + +func _on_library_unsaved() -> void: + _button.set_text(BUTTON_NAME + "(*)") + + +func _on_open_asset_request(path: String) -> void: + EditorInterface.open_scene_from_path(path) + + +func _on_show_in_file_system_request(path: String) -> void: + EditorInterface.get_file_system_dock().navigate_to_path(path) + + +func _on_show_in_file_manager_request(path: String) -> void: + var error := OS.shell_show_in_file_manager(ProjectSettings.globalize_path(path), true) + if error: + push_warning(error_string(error)) diff --git a/addons/scene-library/scene_library.cfg b/addons/scene-library/scene_library.cfg new file mode 100644 index 0000000..80dbda1 --- /dev/null +++ b/addons/scene-library/scene_library.cfg @@ -0,0 +1,4 @@ +library=Array[Dictionary]([{ +"assets": Array[Dictionary]([]), +"name": "Props" +}]) diff --git a/addons/scene-library/scripts/scene_library.gd b/addons/scene-library/scripts/scene_library.gd new file mode 100644 index 0000000..1b61730 --- /dev/null +++ b/addons/scene-library/scripts/scene_library.gd @@ -0,0 +1,1469 @@ +# Copyright (c) 2023-2024 Mansur Isaev and contributors - MIT License +# See `LICENSE.md` included in the source distribution for details. + +extends MarginContainer + + +signal library_changed + +signal library_unsaved +signal library_saved + +signal collection_changed + +signal open_asset_request(path: String) +signal show_in_file_system_request(path: String) +signal show_in_file_manager_request(path: String) +signal asset_display_mode_changed(display_mode: DisplayMode) + + +enum CollectionTabMenu { + NEW, + RENAME, + DELETE, +} +enum LibraryMenu { + NEW, + OPEN, + SAVE, + SAVE_AS, +} +enum DisplayMode{ + THUMBNAILS, + LIST, +} +enum SortMode { + NAME, + NAME_REVERSE, +} +enum AssetContextMenu { + OPEN_ASSET, + COPY_PATH, + COPY_UID, + DELETE_ASSET, + SHOW_IN_FILE_SYSTEM, + SHOW_IN_FILE_MANAGER, + REFRESH, + MAX, +} + + +const NULL_LIBRARY: Array[Dictionary] = [] +const NULL_COLLECTION: Dictionary = {} + +const THUMB_GRID_SIZE: int = 192 +const THUMB_LIST_SIZE: int = 48 + + +var _main_vbox: VBoxContainer = null + +var _collec_hbox: HBoxContainer = null +var _collec_tab_bar: TabBar = null +var _collec_tab_add: Button = null +var _all_tabs_list: MenuButton = null +var _collec_option: MenuButton = null + +var _main_container: PanelContainer = null +var _content_vbox: VBoxContainer = null + +var _top_hbox: HBoxContainer = null +var _asset_filter_line: LineEdit = null +var _asset_sort_mode_btn: Button = null + +var _mode_thumb_btn: Button = null +var _mode_list_btn: Button = null + +var _item_list: ItemList = null + +var _open_dialog: ConfirmationDialog = null +var _save_dialog: ConfirmationDialog = null + +var _save_timer: Timer = null + +var _thumb_grid_icon_size: int = 64 +var _thumb_list_icon_size: int = 16 + +# INFO: May be required for debugging. +var _cache_enabled: bool = true +var _cache_path: String = "res://.godot/thumb_cache" + +# Create thumbnail scene: +var _viewport: SubViewport = null + +var _camera_2d: Camera2D = null + +var _camera_3d: Camera3D = null +var _light_3d: DirectionalLight3D = null + +var _asset_display_mode: DisplayMode = DisplayMode.THUMBNAILS +var _sort_mode: SortMode = SortMode.NAME + +var _thumbnails: Dictionary = {} + +var _mutex: Mutex = null +var _thread: Thread = null +var _thread_queue: Array[Dictionary] = [] +var _thread_sem: Semaphore = null +var _thread_work: bool = true + +var _saved: bool = true +# INFO: Use key-value pairs to store collections. +var _curr_lib: Array[Dictionary] = NULL_LIBRARY # Array[Dictionary[StringName, ImageTexture]] +var _curr_lib_path: String = "" + +var _curr_collec: Dictionary = NULL_COLLECTION + + +func _update_position_new_collection_btn() -> void: + var tab_bar_total_width := float(_collec_tab_bar.get_theme_constant(&"h_separation")) + for i: int in _collec_tab_bar.get_tab_count(): + tab_bar_total_width += _collec_tab_bar.get_tab_rect(i).size.x + + _collec_tab_bar.size = Vector2(minf(_collec_tab_bar.size.x, tab_bar_total_width), 0.0) + _collec_tab_add.position.x = _collec_tab_bar.size.x + + _all_tabs_list.set_visible(_collec_tab_bar.get_offset_buttons_visible()) + + +static func _def_setting(name: String, value: Variant) -> Variant: + if not ProjectSettings.has_setting(name): + ProjectSettings.set_setting(name, value) + + ProjectSettings.set_initial_value(name, value) + return ProjectSettings.get_setting_with_override(name) + +@warning_ignore("narrowing_conversion", "unsafe_method_access") +func _enter_tree() -> void: + _cache_enabled = _def_setting("addons/scene_library/cache/enabled", true) + _cache_path = _def_setting("addons/scene_library/cache/path", "res://.godot/thumb_cache") + + _thumb_grid_icon_size = _def_setting("addons/scene_library/thumbnail/grid_size", 64) + _thumb_list_icon_size = _def_setting("addons/scene_library/thumbnail/list_size", 16) + + self.add_theme_constant_override(&"margin_left", -get_theme_stylebox(&"BottomPanel", &"EditorStyles").get_margin(SIDE_LEFT)) + self.add_theme_constant_override(&"margin_right", -get_theme_stylebox(&"BottomPanel", &"EditorStyles").get_margin(SIDE_RIGHT)) + self.add_theme_constant_override(&"margin_top", -get_theme_stylebox(&"BottomPanel", &"EditorStyles").get_margin(SIDE_TOP)) + + self.set_custom_minimum_size(Vector2(0.0, 180.0)) + + # INFO: Required to create a tab pseudo-container background. + var tabbar_background := Panel.new() + tabbar_background.add_theme_stylebox_override(&"panel", get_theme_stylebox(&"tabbar_background", &"TabContainer")) + self.add_child(tabbar_background) + + _main_vbox = VBoxContainer.new() + _main_vbox.add_theme_constant_override(&"separation", 0) + self.add_child(_main_vbox) + + _collec_hbox = HBoxContainer.new() + _collec_hbox.add_theme_constant_override(&"separation", 0) + # INFO: Required to calculate the position of the "new" button. + _collec_hbox.sort_children.connect(_update_position_new_collection_btn) + _main_vbox.add_child(_collec_hbox) + + _collec_tab_bar = TabBar.new() + _collec_tab_bar.set_auto_translate(false) + _collec_tab_bar.set_drag_to_rearrange_enabled(true) + _collec_tab_bar.set_h_size_flags(Control.SIZE_EXPAND_FILL) + _collec_tab_bar.set_max_tab_width(256) # TODO: Make this parameter receive global editor settings. + _collec_tab_bar.set_theme_type_variation(&"TabContainer") + _collec_tab_bar.add_theme_stylebox_override(&"panel", get_theme_stylebox(&"DebuggerPanel", &"EditorStyles")) + _collec_tab_bar.set_select_with_rmb(true) + _collec_tab_bar.add_tab("[null]") + _collec_tab_bar.set_tab_disabled(0, true) + _collec_tab_bar.set_tab_close_display_policy(TabBar.CLOSE_BUTTON_SHOW_NEVER) + _collec_tab_bar.tab_selected.connect(_on_collection_tab_changed) + _collec_tab_bar.tab_close_pressed.connect(_on_collection_tab_close_pressed) + _collec_tab_bar.tab_rmb_clicked.connect(_on_collection_tab_rmb_clicked) + _collec_tab_bar.active_tab_rearranged.connect(_on_collection_tab_rearranged) + _collec_hbox.add_child(_collec_tab_bar) + + _collec_tab_add = Button.new() + _collec_tab_add.set_flat(true) + _collec_tab_add.set_disabled(true) + _collec_tab_add.set_tooltip_text("Add a new Collection.") + _collec_tab_add.set_button_icon(get_theme_icon(&"Add", &"EditorIcons")) + _collec_tab_add.add_theme_color_override(&"icon_normal_color", Color(0.6, 0.6, 0.6, 0.8)) + _collec_tab_add.set_h_size_flags(Control.SIZE_SHRINK_END) + _collec_tab_add.pressed.connect(show_create_collection_dialog) + _collec_hbox.add_child(_collec_tab_add) + + _all_tabs_list = MenuButton.new() + _all_tabs_list.hide() + _all_tabs_list.set_tooltip_text("List all tabs.") + _all_tabs_list.set_button_icon(get_theme_icon(&"GuiOptionArrow", &"EditorIcons")) + _all_tabs_list.add_theme_color_override(&"icon_normal_color", Color(0.6, 0.6, 0.6, 0.8)) + _collec_hbox.add_child(_all_tabs_list) + + var popup: PopupMenu = _all_tabs_list.get_popup() + popup.index_pressed.connect(_collec_tab_bar.set_current_tab) + + _collec_option = MenuButton.new() + _collec_option.set_flat(true) + _collec_option.set_button_icon(get_theme_icon(&"GuiTabMenuHl", &"EditorIcons")) + _collec_option.add_theme_color_override(&"icon_normal_color", Color(0.6, 0.6, 0.6, 0.8)) + _collec_hbox.add_child(_collec_option) + + popup = _collec_option.get_popup() + popup.add_item("New Library", LibraryMenu.NEW) + popup.add_item("Open Library", LibraryMenu.OPEN) + popup.add_separator() + popup.add_item("Save Library", LibraryMenu.SAVE) + popup.add_item("Save Library As...", LibraryMenu.SAVE_AS) + popup.id_pressed.connect(_on_collection_option_id_pressed) + + _main_container = PanelContainer.new() + _main_container.set_mouse_filter(Control.MOUSE_FILTER_IGNORE) + _main_container.set_v_size_flags(Control.SIZE_EXPAND_FILL) + _main_container.add_theme_stylebox_override(&"panel", get_theme_stylebox(&"DebuggerPanel", &"EditorStyles")) + _main_vbox.add_child(_main_container) + + _content_vbox = VBoxContainer.new() + _main_container.add_child(_content_vbox) + + _top_hbox = HBoxContainer.new() + _content_vbox.add_child(_top_hbox) + + _asset_filter_line = LineEdit.new() + _asset_filter_line.set_placeholder("Filter assets") + _asset_filter_line.set_clear_button_enabled(true) + _asset_filter_line.set_right_icon(get_theme_icon(&"Search", &"EditorIcons")) + _asset_filter_line.set_editable(false) # The value will be changed when the collection is changed. + _asset_filter_line.set_h_size_flags(Control.SIZE_EXPAND_FILL) + _asset_filter_line.text_changed.connect(_on_filter_assets_text_changed) + _top_hbox.add_child(_asset_filter_line) + + _asset_sort_mode_btn = Button.new() + _asset_sort_mode_btn.set_disabled(true) + _asset_sort_mode_btn.set_tooltip_text("Toggle alphabetical sorting of assets") + _asset_sort_mode_btn.set_flat(true) + _asset_sort_mode_btn.set_toggle_mode(true) + _asset_sort_mode_btn.set_button_icon(get_theme_icon(&"Sort", &"EditorIcons")) + _asset_sort_mode_btn.toggled.connect(_sort_assets_button_toggled) + _top_hbox.add_child(_asset_sort_mode_btn) + + _top_hbox.add_child(VSeparator.new()) + + var button_group := ButtonGroup.new() + + _mode_thumb_btn = Button.new() + _mode_thumb_btn.set_flat(true) + _mode_thumb_btn.set_disabled(true) + _mode_thumb_btn.set_tooltip_text("View items as a grid of thumbnails.") + _mode_thumb_btn.set_toggle_mode(true) + _mode_thumb_btn.set_button_icon(get_theme_icon(&"FileThumbnail", &"EditorIcons")) + _mode_thumb_btn.set_button_group(button_group) + _mode_thumb_btn.pressed.connect(set_asset_display_mode.bind(DisplayMode.THUMBNAILS)) + _top_hbox.add_child(_mode_thumb_btn) + + _mode_list_btn = Button.new() + _mode_list_btn.set_flat(true) + _mode_list_btn.set_disabled(true) + _mode_list_btn.set_tooltip_text("View items as a list.") + _mode_list_btn.set_toggle_mode(true) + _mode_list_btn.set_button_icon(get_theme_icon(&"FileList", &"EditorIcons")) + _mode_list_btn.set_button_group(button_group) + _mode_list_btn.pressed.connect(set_asset_display_mode.bind(DisplayMode.LIST)) + _top_hbox.add_child(_mode_list_btn) + + _item_list = AssetItemList.new() + _item_list.set_focus_mode(Control.FOCUS_CLICK) + _item_list.set_max_columns(0) + _item_list.set_mouse_filter(Control.MOUSE_FILTER_PASS) + _item_list.set_same_column_width(true) + _item_list.set_select_mode(ItemList.SELECT_MULTI) + _item_list.set_texture_filter(CanvasItem.TEXTURE_FILTER_LINEAR) + _item_list.set_v_size_flags(Control.SIZE_EXPAND_FILL) + _item_list.gui_input.connect(_on_item_list_gui_input) + _item_list.item_clicked.connect(_on_item_list_item_clicked) + _item_list.item_activated.connect(_on_item_list_item_activated) + _content_vbox.add_child(_item_list) + + _asset_display_mode = _def_setting("addons/scene_library/thumbnail/mode", DisplayMode.THUMBNAILS) + _update_asset_display_mode(_asset_display_mode) + + _open_dialog = _create_file_dialog(true) + _open_dialog.set_title("Open Asset Library") + _open_dialog.connect(&"file_selected", load_library) + self.add_child(_open_dialog) + + _save_dialog = _create_file_dialog(false) + _save_dialog.set_title("Save Asset Library As...") + _save_dialog.connect(&"file_selected", save_library) + self.add_child(_save_dialog) + + _save_timer = Timer.new() + _save_timer.set_one_shot(true) + _save_timer.set_wait_time(10.0) # Save unsaved data every 10 seconds. + _save_timer.timeout.connect(_on_save_timer_timeout) + library_unsaved.connect(_save_timer.start) + self.add_child(_save_timer) + + var world_2d := World2D.new() + + var world_3d := World3D.new() + # TODO: Add a feature to change Environment. + world_3d.set_environment(get_viewport().get_world_3d().get_environment()) + + _viewport = SubViewport.new() + _viewport.set_world_2d(world_2d) + _viewport.set_world_3d(world_3d) + _viewport.set_update_mode(SubViewport.UPDATE_DISABLED) # We'll update the frame manually. + _viewport.set_debug_draw(Viewport.DEBUG_DRAW_DISABLE_LOD) # This is necessary to avoid visual glitches. + _viewport.set_process_mode(Node.PROCESS_MODE_DISABLED) # Needs to disable animations. + _viewport.set_size(Vector2i(THUMB_GRID_SIZE, THUMB_GRID_SIZE)) + _viewport.set_disable_input(true) + _viewport.set_transparent_background(true) + _viewport.set_physics_object_picking(false) + _viewport.set_default_canvas_item_texture_filter(ProjectSettings.get_setting("rendering/textures/canvas_textures/default_texture_filter")) + _viewport.set_default_canvas_item_texture_repeat(ProjectSettings.get_setting("rendering/textures/canvas_textures/default_texture_repeat")) + _viewport.set_fsr_sharpness(ProjectSettings.get_setting("rendering/scaling_3d/fsr_sharpness")) + _viewport.set_msaa_2d(ProjectSettings.get_setting("rendering/anti_aliasing/quality/msaa_2d")) + _viewport.set_msaa_3d(ProjectSettings.get_setting("rendering/anti_aliasing/quality/msaa_3d")) + _viewport.set_positional_shadow_atlas_16_bits(ProjectSettings.get_setting("rendering/lights_and_shadows/positional_shadow/atlas_16_bits")) + _viewport.set_positional_shadow_atlas_quadrant_subdiv(0, ProjectSettings.get_setting("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv")) + _viewport.set_positional_shadow_atlas_quadrant_subdiv(1, ProjectSettings.get_setting("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv")) + _viewport.set_positional_shadow_atlas_quadrant_subdiv(2, ProjectSettings.get_setting("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv")) + _viewport.set_positional_shadow_atlas_quadrant_subdiv(3, ProjectSettings.get_setting("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv")) + _viewport.set_positional_shadow_atlas_size(ProjectSettings.get_setting("rendering/lights_and_shadows/positional_shadow/atlas_size")) + _viewport.set_scaling_3d_mode(ProjectSettings.get_setting("rendering/scaling_3d/mode")) + _viewport.set_scaling_3d_scale(ProjectSettings.get_setting("rendering/scaling_3d/scale")) + _viewport.set_screen_space_aa(ProjectSettings.get_setting("rendering/anti_aliasing/quality/screen_space_aa")) + _viewport.set_texture_mipmap_bias(ProjectSettings.get_setting("rendering/textures/default_filters/texture_mipmap_bias")) + self.add_child(_viewport) + + _camera_2d = Camera2D.new() + _camera_2d.set_enabled(false) + _viewport.add_child(_camera_2d) + + # TODO: Add a feature to set lighting. + _light_3d = DirectionalLight3D.new() + _light_3d.set_shadow_mode(DirectionalLight3D.SHADOW_PARALLEL_4_SPLITS) + _light_3d.set_bake_mode(Light3D.BAKE_STATIC) + _light_3d.set_shadow(true) + _light_3d.basis *= Basis(Vector3.UP, deg_to_rad(45.0)) + _light_3d.basis *= Basis(Vector3.LEFT, deg_to_rad(65.0)) + _viewport.add_child(_light_3d) + + _camera_3d = Camera3D.new() + _camera_3d.set_current(false) + _camera_3d.set_fov(22.5) + _viewport.add_child(_camera_3d) + + # Multithreading starts here. + _mutex = Mutex.new() + _thread_sem = Semaphore.new() + _thread = Thread.new() + _thread.start(_thread_process) + + library_changed.connect(update_tabs) + + collection_changed.connect(update_item_list) + asset_display_mode_changed.connect(_update_asset_display_mode) + + _curr_lib_path = _def_setting("addons/scene_library/library/current_library_path", "res://addons/scene-library/scene_library.cfg") + load_library(_curr_lib_path) + + collection_changed.connect(_collec_tab_bar.size_flags_changed.emit) + + +func _exit_tree() -> void: + _mutex.lock() + _thread_work = false + _mutex.unlock() + + _thread_sem.post() + if _thread.is_started(): + _thread.wait_to_finish() + +@warning_ignore("unsafe_method_access") +func _can_drop_data(at_position: Vector2, data: Variant) -> bool: + if not _item_list.get_rect().has_point(at_position): + return false + + if not data is Dictionary or data.get("type") != "files": + return false + + if _curr_lib.is_read_only() or _curr_collec.is_read_only(): + return false + + var files: PackedStringArray = data["files"] + var rec_ext: PackedStringArray = ResourceLoader.get_recognized_extensions_for_type("PackedScene") + + for file: String in files: + var extension: String = file.get_extension().to_lower() + if not rec_ext.has(extension): + return false + + if has_asset_path(file) or not is_valid_scene_file(file): + return false + + return true + + +func _drop_data(_at_position: Vector2, data: Variant) -> void: + if data is Dictionary: + var files: PackedStringArray = data["files"] + + for path: String in files: + create_asset(path) + + +func mark_saved() -> void: + library_saved.emit() + _saved = true + +func mark_unsaved() -> void: + library_unsaved.emit() + _saved = false + +func is_saved() -> bool: + return _saved + + +func set_current_library(library: Array[Dictionary]) -> void: + if is_same(_curr_lib, library): + return + + _curr_lib = library + library_changed.emit() + # Switch to the first tab. + _collec_tab_bar.set_current_tab(0) + +func get_current_library() -> Array[Dictionary]: + return _curr_lib + + +func set_current_library_path(path: String) -> void: + if is_same(_curr_lib_path, path): + return + + ProjectSettings.set_setting("addons/scene_library/library/current_library_path", path) + _curr_lib_path = path + +func get_current_library_path() -> String: + return _curr_lib_path + + +func has_collection(collection_name: String) -> bool: + for collection: Dictionary in get_current_library(): + if collection.name == collection_name: + return true + + return false + +func create_collection(collection_name: String) -> void: + assert(not has_collection(collection_name), "Collection with this name already exists.") + + var assets: Array[Dictionary] = [] + var new_collection: Dictionary = { + &"name": collection_name, + &"assets": assets, + } + + _curr_lib.push_back(new_collection) + + library_changed.emit() + mark_unsaved() + + # Switch to the last tab. + _collec_tab_bar.set_current_tab(_collec_tab_bar.get_tab_count() - 1) + + +func remove_collection(index: int) -> void: + _curr_lib.remove_at(index) + + library_changed.emit() + mark_unsaved() + + # Swith to the prev tab. + _collec_tab_bar.set_current_tab(_collec_tab_bar.get_current_tab()) + +func show_remove_collection_dialog(index: int) -> void: + var assets: Array[Dictionary] = _curr_lib[index].assets + if assets.is_empty(): + return remove_collection(index) + + var window := ConfirmationDialog.new() + window.set_size(Vector2i.ZERO) + window.set_flag(Window.FLAG_RESIZE_DISABLED, true) + window.focus_exited.connect(window.queue_free) + window.confirmed.connect(remove_collection.bind(index)) + + window.set_ok_button_text("Remove") + + var label := Label.new() + label.set_text("Are you sure you want to delete this collection? (Cannot be undone.)") + window.add_child(label) + + self.add_child(window) + window.popup_centered(Vector2i(300, 0)) + + +func _queue_has_id(id: int) -> bool: + _mutex.lock() + + for item: Dictionary in _thread_queue: + if item.id == id: + _mutex.unlock() + return true + + _mutex.unlock() + return false + +func _queue_update_thumbnail(id: int) -> void: + if not _thumbnails.has(id) or _queue_has_id(id): + return + + _mutex.lock() + var queue_item: Dictionary = {&"id": id, &"thumb": _thumbnails[id]} + _thread_queue.push_back(queue_item) + _mutex.unlock() + + _thread_sem.post() + +func _get_or_create_thumbnail(id: int, path: String) -> ImageTexture: + var thumb: ImageTexture = _thumbnails.get(id, null) + if is_instance_valid(thumb): + return thumb + + var cache_path: String = _get_thumb_cache_path(path) + if _cache_enabled and FileAccess.file_exists(cache_path): + thumb = ImageTexture.create_from_image(Image.load_from_file(cache_path)) + _thumbnails[id] = thumb + else: + thumb = ImageTexture.create_from_image(Image.load_from_file(ProjectSettings.globalize_path("res://addons/scene-library/icons/thumb_placeholder.svg"))) + _thumbnails[id] = thumb + + _queue_update_thumbnail(id) + + return thumb + +func _create_asset(id: int, uid: String, path: String) -> Dictionary: + var asset: Dictionary = { + &"id": id, + &"uid": uid, + &"path": path, + &"thumb": _get_or_create_thumbnail(id, path), + } + return asset + + +static func is_valid_scene_file(path: String) -> bool: + return ResourceLoader.exists(path, "PackedScene") and ResourceLoader.get_recognized_extensions_for_type("PackedScene").has(path.get_extension().to_lower()) + + +static func get_or_create_valid_uid(path: String) -> int: + var id: int = ResourceLoader.get_resource_uid(path) + if id == ResourceUID.INVALID_ID: + id = ResourceUID.create_id() + ResourceUID.add_id(id, path) + + return id + + +func create_asset(path: String) -> void: + assert(is_valid_scene_file(path), "PackedScene file was not found or has an invalid extension.") + + var id: int = get_or_create_valid_uid(path) + var new_asset: Dictionary = _create_asset(id, ResourceUID.id_to_text(id), path) + + var assets: Array[Dictionary] = _curr_collec.assets + assets.push_back(new_asset) + + collection_changed.emit() + mark_unsaved() + + +func remove_asset(id: int) -> bool: + var assets: Array[Dictionary] = _curr_collec.assets + + for i: int in assets.size(): + if assets[i].id != id: + continue + + assets.remove_at(i) + + collection_changed.emit() + mark_unsaved() + + return true + + return false + + +func set_current_collection(collection: Dictionary) -> void: + if is_same(_curr_collec, collection): + return + + _curr_collec = collection + + _item_list.deselect_all() + collection_changed.emit() + +func get_current_collection() -> Dictionary: + return _curr_collec + + +func has_asset_path(path: String) -> bool: + for asset: Dictionary in _curr_collec.assets: + if asset.path == path: + return true + + return false + + +func update_tabs() -> void: + var is_valid: bool = not _curr_lib.is_read_only() and not _curr_lib.is_empty() + + _asset_filter_line.set_editable(is_valid) + _collec_tab_add.set_disabled(_curr_lib.is_read_only()) + _asset_sort_mode_btn.set_disabled(not is_valid) + _mode_thumb_btn.set_disabled(not is_valid) + _mode_list_btn.set_disabled(not is_valid) + + if _curr_lib.size(): + _collec_tab_bar.set_tab_count(_curr_lib.size()) + _collec_tab_bar.set_tab_close_display_policy(TabBar.CLOSE_BUTTON_SHOW_ACTIVE_ONLY) + + var popup: PopupMenu = _all_tabs_list.get_popup() + popup.set_item_count(_curr_lib.size()) + + for i: int in _curr_lib.size(): + _collec_tab_bar.set_tab_title(i, _curr_lib[i].name) + _collec_tab_bar.set_tab_disabled(i, false) + _collec_tab_bar.set_tab_metadata(i, _curr_lib[i]) + + popup.set_item_text(i, _curr_lib[i].name) + else: + _collec_tab_bar.set_tab_count(1) + _collec_tab_bar.set_tab_close_display_policy(TabBar.CLOSE_BUTTON_SHOW_NEVER) + _collec_tab_bar.set_tab_title(0, "[null]") + _collec_tab_bar.set_tab_disabled(0, true) + _collec_tab_bar.set_tab_metadata(0, NULL_COLLECTION) + + # INFO: Required to recalculate position of the "new collection" button. + _collec_tab_bar.size_flags_changed.emit() + +@warning_ignore("unsafe_call_argument") +func update_item_list() -> void: + var assets: Array[Dictionary] = _curr_collec.assets + _item_list.set_item_count(assets.size()) + + var is_list_mode: bool = _asset_display_mode == DisplayMode.LIST + var filter: String = _asset_filter_line.get_text() + + var index: int = 0 + for asset: Dictionary in assets: + var path: String = asset.path + if not filter.is_subsequence_ofn(path.get_file()): + continue + + _item_list.set_item_text(index, path.get_file().get_basename()) + _item_list.set_item_icon(index, asset.thumb) + # NOTE: This tooltip will be hidden because used the custom tooltip. + _item_list.set_item_tooltip(index, path) + _item_list.set_item_metadata(index, asset) + + index += 1 + + _item_list.set_item_count(index) + + +func set_asset_display_mode(display_mode: DisplayMode) -> void: + if is_same(_asset_display_mode, display_mode): + return + + ProjectSettings.set_setting("addons/scene_library/thumbnail/mode", display_mode) + _asset_display_mode = display_mode + + asset_display_mode_changed.emit(display_mode) + +func get_asset_display_mode() -> DisplayMode: + return _asset_display_mode + +static func sort_asset_ascending(a: Dictionary, b: Dictionary) -> bool: + @warning_ignore("unsafe_method_access") + return a.path.get_file() < b.path.get_file() +static func sort_asset_descending(a: Dictionary, b: Dictionary) -> bool: + @warning_ignore("unsafe_method_access") + return a.path.get_file() > b.path.get_file() +static func sort_assets(assets: Array[Dictionary], sort_mode: SortMode) -> void: + if sort_mode == SortMode.NAME: + assets.sort_custom(sort_asset_ascending) + else: + assets.sort_custom(sort_asset_descending) + +func set_sort_mode(sort_mode: SortMode) -> void: + if is_same(_sort_mode, sort_mode): + return + + _sort_mode = sort_mode + sort_assets(_curr_collec.assets, sort_mode) + + collection_changed.emit() + +func get_sort_mode() -> SortMode: + return _sort_mode + + +func show_create_collection_dialog() -> AcceptDialog: + var window := AcceptDialog.new() + window.set_size(Vector2i.ZERO) + window.set_title("Create New Collection") + window.add_cancel_button("Cancel") + window.set_flag(Window.FLAG_RESIZE_DISABLED, true) + window.focus_exited.connect(window.queue_free) + self.add_child(window) + + var ok_button: Button = window.get_ok_button() + ok_button.set_text("Create") + ok_button.set_disabled(true) + + var vbox := VBoxContainer.new() + window.add_child(vbox) + + var label := Label.new() + label.set_text("New Collection Name:") + vbox.add_child(label) + + var line_edit := LineEdit.new() + window.register_text_enter(line_edit) + line_edit.set_text("new_collection") + line_edit.select_all() + + # INFO: Disables the ability to create a collection and set a tooltip. + line_edit.text_changed.connect(func(c_name: String) -> void: + if c_name.is_empty(): + line_edit.set_tooltip_text("Collection name is empty.") + elif has_collection(c_name): + line_edit.set_tooltip_text("Collection with this name already exists.") + else: + line_edit.set_tooltip_text("") + + ok_button.set_disabled(c_name.is_empty() or has_collection(c_name)) + line_edit.set_right_icon(get_theme_icon(&"StatusError", &"EditorIcons") if ok_button.is_disabled() else null) + ) + line_edit.text_changed.emit(line_edit.get_text()) # Required for status updates. + vbox.add_child(line_edit) + + window.confirmed.connect(func() -> void: + var new_collec_name: String = line_edit.get_text() + create_collection(new_collec_name) + ) + window.popup_centered(Vector2i(300, 0)) + line_edit.grab_focus() + + return window + + + +func _serialize_asset(asset: Dictionary) -> Dictionary: + return {"uid": asset.uid, "path": asset.path} + +func _serialize_assets(assets: Array[Dictionary]) -> Array[Dictionary]: + var serialized: Array[Dictionary] = [] + serialized.resize(assets.size()) + + for i: int in assets.size(): + serialized[i] = _serialize_asset(assets[i]) + + return serialized + +func _serialize_collection(collection: Dictionary) -> Dictionary: + return { + "name": collection.name, + "assets": _serialize_assets(collection.assets), + } + +func _serialize_library(library: Array[Dictionary]) -> Array[Dictionary]: + var serialized: Array[Dictionary] = [] + serialized.resize(library.size()) + + for i: int in library.size(): + serialized[i] = _serialize_collection(library[i]) + + return serialized + + +func _cfg_save_library(library: Array[Dictionary], path: String) -> void: + var serialized: Array[Dictionary] = _serialize_library(library) + + var config := ConfigFile.new() + config.set_value("", "library", serialized) + + var error := config.save(path) + assert(error == OK, error_string(error)) + +func _json_save_library(library: Array[Dictionary], path: String) -> void: + var serialized: Array[Dictionary] = _serialize_library(library) + + var file := FileAccess.open(path, FileAccess.WRITE) + assert(FileAccess.get_open_error() == OK, error_string(FileAccess.get_open_error())) + + file.store_string(JSON.stringify(serialized, "\t")) + file.close() + +func save_library(path: String) -> void: + var extension: String = path.get_extension() + assert(extension == "cfg" or extension == "json", "Invalid extension.") + + if extension == "cfg": + _cfg_save_library(_curr_lib, path) + elif extension == "json": + _json_save_library(_curr_lib, path) + else: + return + + mark_saved() + + +func _deserialize_asset(asset: Dictionary) -> Dictionary: + var uid: String = asset.get("uid", "") + var path: String = asset.get("path", "") + + var id: int = ResourceUID.text_to_id(uid) + + # TODO: Add error handling. + if id != ResourceUID.INVALID_ID and ResourceUID.has_id(id): # If the UID is valid. + path = ResourceUID.get_id_path(id) + # If the UID is wrong, try to load the asset by the path. + # It also checks whether the file extension is valid. + elif is_valid_scene_file(path): + id = ResourceLoader.get_resource_uid(path) + uid = ResourceUID.id_to_text(id) + + if not ResourceUID.has_id(id): + ResourceUID.add_id(id, path) + + # Invalid assset. + else: + return {} + + return _create_asset(id, uid, path) + +func _deserialize_assets(assets: Array) -> Array[Dictionary]: + var deserialized: Array[Dictionary] = [] + + for asset: Dictionary in assets: + asset = _deserialize_asset(asset) + if asset.is_empty(): + continue + + deserialized.push_back(asset) + + return deserialized + +func _deserialize_collection(collection: Dictionary) -> Dictionary: + var deserialized: Dictionary = { + &"name": collection[&"name"], + &"assets": _deserialize_assets(collection["assets"]) + } + + return deserialized + +func _deserialize_library(library: Array) -> Array[Dictionary]: + var deserialized: Array[Dictionary] = [] + deserialized.resize(library.size()) + + for i: int in library.size(): + deserialized[i] = _deserialize_collection(library[i]) + + return deserialized + + +func _load_cfg(path: String) -> Array[Dictionary]: + var config := ConfigFile.new() + + var error := config.load(path) + assert(error == OK, error_string(error)) + + var data: Variant = config.get_value("", "library") + if data is Array: + return _deserialize_library(data) + + return NULL_LIBRARY + +func _load_json(path: String) -> Array[Dictionary]: + var json := JSON.new() + + var error := json.parse(FileAccess.get_file_as_string(path)) + assert(error == OK, error_string(error)) + + var data: Variant = json.get_data() + if data is Array: + return _deserialize_library(data) + + return NULL_LIBRARY + +func load_library(path: String) -> void: + var library: Array[Dictionary] = [] + + if FileAccess.file_exists(path): + var extension: String = path.get_extension() + assert(extension == "cfg" or extension == "json", "Invalid extension.") + + if extension == "cfg": + library = _load_cfg(path) + elif extension == "json": + library = _load_json(path) + + # Check for “null” value. + if library.is_read_only(): + return + + set_current_library(library) + set_current_library_path(path) + + +@warning_ignore("unsafe_method_access") +func _calculate_node_rect(node: Node) -> Rect2: + var rect := Rect2() + if node is Node2D and node.is_visible(): + # HACK: This works only in editor. + rect = node.get_global_transform() * node.call(&"_edit_get_rect") + + for i: int in node.get_child_count(): + rect = rect.merge(_calculate_node_rect(node.get_child(i))) + + return rect + +@warning_ignore("unsafe_method_access") +func _calculate_node_aabb(node: Node) -> AABB: + var aabb := AABB() + + if node is Node3D and not node.is_visible(): + return aabb + # NOTE: If the node is not MeshInstance3D, the AABB is not calculated correctly. + # The camera may have incorrect distances to objects in the scene. + elif node is MeshInstance3D: + aabb = node.get_global_transform() * node.get_aabb() + + for i: int in node.get_child_count(): + aabb = aabb.merge(_calculate_node_aabb(node.get_child(i))) + + return aabb + + +func _focus_camera_on_node_2d(node: Node) -> void: + var rect: Rect2 = _calculate_node_rect(node) + _camera_2d.set_position(rect.get_center()) + + var zoom_ratio: float = THUMB_GRID_SIZE / maxf(rect.size.x, rect.size.y) + _camera_2d.set_zoom(Vector2(zoom_ratio, zoom_ratio)) + +func _focus_camera_on_node_3d(node: Node) -> void: + var transform := Transform3D.IDENTITY + # TODO: Add a feature to configure the rotation of the camera. + transform.basis *= Basis(Vector3.UP, deg_to_rad(40.0)) + transform.basis *= Basis(Vector3.LEFT, deg_to_rad(22.5)) + + var aabb: AABB = _calculate_node_aabb(node) + var distance: float = aabb.get_longest_axis_size() / tan(deg_to_rad(_camera_3d.get_fov()) * 0.5) + + transform.origin = transform * (Vector3.BACK * distance) + aabb.get_center() + + _camera_3d.set_global_transform(transform.orthonormalized()) + + +func _get_thumb_cache_dir() -> String: + return ProjectSettings.globalize_path(_cache_path) + +func _get_thumb_cache_path(path: String) -> String: + return _get_thumb_cache_dir().path_join(path.md5_text()) + ".png" + +func _save_thumb_to_disk(id: int, image: Image) -> void: + if not DirAccess.dir_exists_absolute(_get_thumb_cache_dir()): + var error := DirAccess.make_dir_absolute(_get_thumb_cache_dir()) + assert(error == OK, error_string(error)) + + var error := image.save_png(_get_thumb_cache_path(ResourceUID.get_id_path(id))) + assert(error == OK, error_string(error)) + +func _create_thumb(item: Dictionary, callback: Callable) -> void: + var path: String = ResourceUID.get_id_path(item.id) + if not is_valid_scene_file(path): + return callback.call() + + var packed_scene := ResourceLoader.load(path, "PackedScene") as PackedScene + # INFO: Could be null if, for example, the dependencies are broken. + if not is_instance_valid(packed_scene) or not packed_scene.can_instantiate(): + return callback.call() + + var instance: Node = packed_scene.instantiate() + + _viewport.call_deferred(&"add_child", instance) + await instance.ready + + if instance is Node2D: + _camera_3d.set_current(false) + _camera_2d.set_enabled(true) + _focus_camera_on_node_2d(instance) + else: + _camera_2d.set_enabled(false) + _camera_3d.set_current(true) + _focus_camera_on_node_3d(instance) + + await RenderingServer.frame_pre_draw + _viewport.set_update_mode(SubViewport.UPDATE_ONCE) + + await RenderingServer.frame_post_draw + + var image: Image = _viewport.get_texture().get_image() + image.resize(THUMB_GRID_SIZE, THUMB_GRID_SIZE, Image.INTERPOLATE_LANCZOS) + + var thumb: ImageTexture = item.thumb + thumb.update(image) + + if _cache_enabled: + _save_thumb_to_disk(item.id, image) + + instance.call_deferred(&"free") + await instance.tree_exited + + callback.call() + +func _thread_process() -> void: + var semaphore := Semaphore.new() + + while _thread_work: + if _thread_queue.is_empty(): + _thread_sem.wait() + else: + _mutex.lock() + var item: Dictionary = _thread_queue.pop_front() + _mutex.unlock() + + # This ensures that this method will be executed in the main thread. + call_deferred_thread_group(&"_create_thumb", item, semaphore.post) + semaphore.wait() + + + + +func handle_scene_saved(path: String) -> void: + # INFO: When we save a scene, we try to update the asset thumbnail. + # The "_queue_update_thumbnail" method will not create new thumbnails if they have not been previously created. + _queue_update_thumbnail(ResourceLoader.get_resource_uid(path)) + + +func handle_file_moved(old_file: String, new_file: String) -> void: + if not _thumbnails.has(ResourceLoader.get_resource_uid(new_file)): + return + + for collection: Dictionary in _curr_lib: + for asset: Dictionary in collection.assets: + if asset.path == old_file: + asset.path = new_file + break + + collection_changed.emit() + + +func handle_file_removed(file: String) -> void: + # TODO: Need to add Dictionary for asset path. + # Because we can't use UID for deleted files. + # And we have to go through all collections and assets. + var removed: int = 0 + for collection: Dictionary in _curr_lib: + var assets: Array[Dictionary] = collection.assets + + for i: int in assets.size(): + if assets[i].path != file: + continue + + assets.remove_at(i) + removed += 1 + break + + if removed: + collection_changed.emit() + + + + +func _on_collection_tab_changed(tab: int) -> void: + set_current_collection(_collec_tab_bar.get_tab_metadata(tab)) + + +func _on_collection_tab_close_pressed(tab: int) -> void: + show_remove_collection_dialog(tab) + + +func _on_collection_tab_rmb_clicked(tab: int) -> void: + var collection: Dictionary = _collec_tab_bar.get_tab_metadata(tab) + + var popup := PopupMenu.new() + popup.id_pressed.connect(func(option: CollectionTabMenu) -> void: + match option: + CollectionTabMenu.NEW: + show_create_collection_dialog() + + CollectionTabMenu.RENAME: + var old_name: String = collection.name + + var rename_collec_window := AcceptDialog.new() + rename_collec_window.set_size(Vector2i.ZERO) + rename_collec_window.set_title("Rename Collection") + rename_collec_window.add_cancel_button("Cancel") + rename_collec_window.set_flag(Window.FLAG_RESIZE_DISABLED, true) + rename_collec_window.focus_exited.connect(rename_collec_window.queue_free) + + var ok_button: Button = rename_collec_window.get_ok_button() + ok_button.set_text("OK") + ok_button.set_disabled(true) + + var vbox := VBoxContainer.new() + rename_collec_window.add_child(vbox) + + var label := Label.new() + label.set_text("Change Collection Name:") + vbox.add_child(label) + + var line_edit := LineEdit.new() + line_edit.set_select_all_on_focus(true) + line_edit.set_text(old_name) + rename_collec_window.register_text_enter(line_edit) + + # INFO: Disables the ability to create a collection and set a tooltip. + line_edit.text_changed.connect(func(new_name: String) -> void: + var is_valid := false + + if new_name.is_empty(): + line_edit.set_tooltip_text("Collection name is empty.") + elif has_collection(new_name): + line_edit.set_tooltip_text("Collection with this name already exists.") + else: + line_edit.set_tooltip_text("") + is_valid = true + + ok_button.set_disabled(not is_valid) + line_edit.set_right_icon(null if is_valid else get_theme_icon(&"StatusError", &"EditorIcons")) + ) + + line_edit.text_changed.emit(line_edit.get_text()) # Required for update status. + vbox.add_child(line_edit) + + rename_collec_window.confirmed.connect(func() -> void: + collection.name = line_edit.get_text() + _collec_tab_bar.set_tab_title(tab, line_edit.get_text()) + mark_unsaved() + ) + + self.add_child(rename_collec_window) + rename_collec_window.popup_centered(Vector2i(300, 0)) + line_edit.grab_focus() + + CollectionTabMenu.DELETE: + show_remove_collection_dialog(tab) + ) + popup.focus_exited.connect(popup.queue_free) + self.add_child(popup) + + if collection.is_read_only(): # If "null" collection. + # BUG: You can't see it because the tab is disabled. + popup.add_item("New Collection", CollectionTabMenu.NEW) + else: + popup.add_item("New Collection", CollectionTabMenu.NEW) + popup.add_separator() + popup.add_item("Rename Collection", CollectionTabMenu.RENAME) + popup.add_item("Delete Collection", CollectionTabMenu.DELETE) + + popup.popup(Rect2i(get_screen_position() + get_local_mouse_position(), Vector2i.ZERO)) + + +func _on_collection_tab_rearranged(_to_idx: int) -> void: + for i: int in _collec_tab_bar.get_tab_count(): + _curr_lib[i] = _collec_tab_bar.get_tab_metadata(i) + + +func _create_file_dialog(open: bool) -> ConfirmationDialog: + var dialog: ConfirmationDialog = null + + if Engine.is_editor_hint(): # Works only in the editor. + var editor_file_dialog: EditorFileDialog = ClassDB.instantiate(&"EditorFileDialog") + editor_file_dialog.set_access(EditorFileDialog.ACCESS_FILESYSTEM) + editor_file_dialog.set_file_mode(EditorFileDialog.FILE_MODE_OPEN_FILE if open else EditorFileDialog.FILE_MODE_SAVE_FILE) + editor_file_dialog.add_filter("*.cfg", "Config File") + editor_file_dialog.add_filter("*.json", "JSON File") + dialog = editor_file_dialog + else: + var file_dialog := FileDialog.new() + file_dialog.set_access(FileDialog.ACCESS_FILESYSTEM) + file_dialog.set_file_mode(FileDialog.FILE_MODE_OPEN_FILE if open else FileDialog.FILE_MODE_SAVE_FILE) + file_dialog.add_filter("*.cfg", "Config File") + file_dialog.add_filter("*.json", "JSON File") + dialog = file_dialog + + dialog.set_exclusive(true) + + return dialog + +func _popup_file_dialog(window: Window) -> void: + window.popup_centered_clamped(Vector2(1050, 700) * DisplayServer.screen_get_scale(), 0.8) + +func _on_collection_option_id_pressed(option: LibraryMenu) -> void: + match option: + # TODO: Add a feature to check if the current library is saved. + LibraryMenu.NEW: + var new_library: Array[Dictionary] = [] + set_current_library(new_library) + + _curr_lib_path = "" + + LibraryMenu.OPEN: + _popup_file_dialog(_open_dialog) + + LibraryMenu.SAVE when not _curr_lib_path.is_empty(): + save_library(_curr_lib_path) + + LibraryMenu.SAVE, LibraryMenu.SAVE_AS: + _popup_file_dialog(_save_dialog) + + +func _on_filter_assets_text_changed(_filter: String) -> void: + update_item_list() + + +func _sort_assets_button_toggled(reverse: bool) -> void: + set_sort_mode(SortMode.NAME_REVERSE if reverse else SortMode.NAME) + + +func _update_thumb_icon_size(display_mode: DisplayMode) -> void: + if display_mode == DisplayMode.THUMBNAILS: + _item_list.set_fixed_column_width(_thumb_grid_icon_size * 1.5) + _item_list.set_fixed_icon_size(Vector2i(_thumb_grid_icon_size, _thumb_grid_icon_size)) + else: + _item_list.set_fixed_column_width(0) + _item_list.set_fixed_icon_size(Vector2i(_thumb_list_icon_size, _thumb_list_icon_size)) + +func _update_asset_display_mode(display_mode: DisplayMode) -> void: + if display_mode == DisplayMode.THUMBNAILS: + _item_list.set_max_columns(0) + _item_list.set_icon_mode(ItemList.ICON_MODE_TOP) + _item_list.set_max_text_lines(2) + + _mode_thumb_btn.set_pressed_no_signal(true) + else: + _item_list.set_max_columns(0) + _item_list.set_icon_mode(ItemList.ICON_MODE_LEFT) + _item_list.set_max_text_lines(1) + + _mode_list_btn.set_pressed_no_signal(true) + + for i: int in _item_list.get_item_count(): + var asset: Dictionary = _item_list.get_item_metadata(i) + _item_list.set_item_icon(i, asset.thumb) + + _update_thumb_icon_size(display_mode) + + +func _set_thumb_grid_icon_size(icon_size: int) -> void: + icon_size = clampi(icon_size, THUMB_LIST_SIZE, THUMB_GRID_SIZE) + if _thumb_grid_icon_size == icon_size: + return + + ProjectSettings.set_setting("addons/scene_library/thumbnail/grid_size", icon_size) + _thumb_grid_icon_size = icon_size + + _update_thumb_icon_size(_asset_display_mode) + +func _set_thumb_list_icon_size(icon_size: int) -> void: + const ICON_MIN_SIZE = 16 + + icon_size = clampi(icon_size, ICON_MIN_SIZE, THUMB_LIST_SIZE) + if _thumb_list_icon_size == icon_size: + return + + ProjectSettings.set_setting("addons/scene_library/thumbnail/list_size", icon_size) + _thumb_list_icon_size = icon_size + + _update_thumb_icon_size(_asset_display_mode) + + +func _on_item_list_gui_input(event: InputEvent) -> void: + if event is InputEventMouseButton and event.is_pressed() and event.is_command_or_control_pressed(): + const ICON_GRID_STEP = 8 + const ICON_LIST_STEP = 4 + + match event.get_button_index(): + MOUSE_BUTTON_WHEEL_UP: + if _asset_display_mode == DisplayMode.THUMBNAILS: + _set_thumb_grid_icon_size(_thumb_grid_icon_size + ICON_GRID_STEP) + else: + _set_thumb_list_icon_size(_thumb_list_icon_size + ICON_LIST_STEP) + + MOUSE_BUTTON_WHEEL_DOWN: + if _asset_display_mode == DisplayMode.THUMBNAILS: + _set_thumb_grid_icon_size(_thumb_grid_icon_size - ICON_GRID_STEP) + else: + _set_thumb_list_icon_size(_thumb_list_icon_size - ICON_LIST_STEP) + + _: + return + + accept_event() + +func _on_item_list_item_clicked(index: int, at_position: Vector2, mouse_button_index: int) -> void: + if mouse_button_index != MOUSE_BUTTON_RIGHT: + return + + _item_list.select(index, false) + var selected_assets: PackedInt32Array = _item_list.get_selected_items() + + var popup := PopupMenu.new() + popup.connect(&"focus_exited", popup.queue_free) + popup.connect(&"id_pressed", func(option: AssetContextMenu) -> void: + var asset: Dictionary = _item_list.get_item_metadata(selected_assets[0]) + + match option: + AssetContextMenu.OPEN_ASSET: + open_asset_request.emit(asset.path) + + AssetContextMenu.COPY_PATH: + DisplayServer.clipboard_set(asset.path) + + AssetContextMenu.COPY_UID: + DisplayServer.clipboard_set(asset.uid) + + AssetContextMenu.DELETE_ASSET: + var assets: Array[Dictionary] = _curr_collec.assets + + if selected_assets.size() == 1: + assets.remove_at(selected_assets[0]) + else: + selected_assets.reverse() + + for i: int in selected_assets: + assets.remove_at(i) + + collection_changed.emit() + mark_unsaved() + + AssetContextMenu.SHOW_IN_FILE_SYSTEM: + show_in_file_system_request.emit(asset.path) + + AssetContextMenu.SHOW_IN_FILE_MANAGER: + show_in_file_manager_request.emit(asset.path) + + AssetContextMenu.REFRESH: + for i: int in selected_assets: + asset = _item_list.get_item_metadata(i) + _queue_update_thumbnail(asset.id) + ) + self.add_child(popup) + + if selected_assets.size() == 1: # If only one asset is selected. + popup.add_item("Open", AssetContextMenu.OPEN_ASSET) + popup.set_item_icon(-1, get_theme_icon(&"Load", &"EditorIcons")) + popup.add_separator() + popup.add_item("Copy Path", AssetContextMenu.COPY_PATH) + popup.set_item_icon(-1, get_theme_icon(&"ActionCopy", &"EditorIcons")) + popup.add_item("Copy UID", AssetContextMenu.COPY_UID) + popup.set_item_icon(-1, get_theme_icon(&"Instance", &"EditorIcons")) + popup.add_item("Delete", AssetContextMenu.DELETE_ASSET) + popup.set_item_icon(-1, get_theme_icon(&"Remove", &"EditorIcons")) + popup.add_separator() + popup.add_item("Show in FileSystem", AssetContextMenu.SHOW_IN_FILE_SYSTEM) + popup.set_item_icon(-1, get_theme_icon(&"Filesystem", &"EditorIcons")) + popup.add_item("Show in File Manager", AssetContextMenu.SHOW_IN_FILE_MANAGER) + popup.set_item_icon(-1, get_theme_icon(&"Folder", &"EditorIcons")) + popup.add_separator() + popup.add_item("Refresh", AssetContextMenu.REFRESH) + popup.set_item_icon(-1, get_theme_icon(&"Reload", &"EditorIcons")) + else: # If many assets are selected. + popup.add_item("Delete", AssetContextMenu.DELETE_ASSET) + popup.set_item_icon(popup.get_item_index(AssetContextMenu.DELETE_ASSET), get_theme_icon(&"Remove", &"EditorIcons")) + popup.add_item("Refresh", AssetContextMenu.REFRESH) + popup.set_item_icon(-1, get_theme_icon(&"Reload", &"EditorIcons")) + + popup.popup(Rect2i(_item_list.get_screen_position() + at_position, Vector2i.ZERO)) + + +func _on_item_list_item_activated(index: int) -> void: + var asset: Dictionary = _item_list.get_item_metadata(index) + open_asset_request.emit(asset.path) + + +func _on_save_timer_timeout() -> void: + if _curr_lib_path.is_empty(): + return + + save_library(_curr_lib_path) + + + + +class AssetItemList extends ItemList: + func _gui_input(event: InputEvent) -> void: + if event.is_action_pressed(&"ui_text_select_all"): + for i: int in get_item_count(): + select(i, false) + + accept_event() + + func _create_drag_preview(files: PackedStringArray) -> Control: + const MAX_ROWS = 6 + + var vbox := VBoxContainer.new() + var num_rows := mini(files.size(), MAX_ROWS) + + for i: int in num_rows: + var hbox := HBoxContainer.new() + vbox.add_child(hbox) + + var icon := TextureRect.new() + icon.set_texture(get_theme_icon(&"File", &"EditorIcons")) + icon.set_stretch_mode(TextureRect.STRETCH_KEEP_CENTERED) + icon.set_size(Vector2(16.0, 16.0)) + hbox.add_child(icon) + + var label := Label.new() + label.set_text(files[i].get_file().get_basename()) + hbox.add_child(label) + + if files.size() > num_rows: + var label := Label.new() + label.set_text("%d more files" % int(files.size() - num_rows)) + vbox.add_child(label) + + return vbox + + func _get_drag_data(at_position: Vector2) -> Variant: + var item: int = get_item_at_position(at_position) + if item < 0: + return null + + var files := PackedStringArray() + for i: int in get_selected_items(): + var asset: Dictionary = get_item_metadata(i) + files.push_back(asset.path) + + set_drag_preview(_create_drag_preview(files)) + + return {"type": "files", "files": files} + + func _make_custom_tooltip(_for_text: String) -> Object: + var item: int = get_item_at_position(get_local_mouse_position()) + if item < 0: + return null + + var asset: Dictionary = get_item_metadata(item) + if asset.is_empty(): + return null + + var vbox := VBoxContainer.new() + + var thumb_rect := TextureRect.new() + thumb_rect.set_expand_mode(TextureRect.EXPAND_IGNORE_SIZE) + thumb_rect.set_h_size_flags(Control.SIZE_SHRINK_CENTER) + thumb_rect.set_v_size_flags(Control.SIZE_SHRINK_CENTER) + thumb_rect.set_custom_minimum_size(Vector2(THUMB_GRID_SIZE, THUMB_GRID_SIZE)) + thumb_rect.set_texture(asset.thumb) + vbox.add_child(thumb_rect) + + var label := Label.new() + label.set_text(asset.path) + vbox.add_child(label) + + return vbox diff --git a/editor/debug_environments/debug_env_player.tscn b/editor/debug_environments/debug_env_player.tscn new file mode 100644 index 0000000..71e1f66 --- /dev/null +++ b/editor/debug_environments/debug_env_player.tscn @@ -0,0 +1,39 @@ +[gd_scene load_steps=8 format=3 uid="uid://y6j3eke6bhws"] + +[ext_resource type="Script" path="res://scenes/game.gd" id="1_xginw"] +[ext_resource type="PackedScene" path="res://player/player.tscn" id="2_qkysy"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_tx3qi"] +sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) +ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) + +[sub_resource type="Sky" id="Sky_mmhs2"] +sky_material = SubResource("ProceduralSkyMaterial_tx3qi") + +[sub_resource type="Environment" id="Environment_r8f2r"] +background_mode = 2 +sky = SubResource("Sky_mmhs2") +tonemap_mode = 2 +glow_enabled = true + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ob58c"] +albedo_color = Color(0, 0.46, 0.145667, 1) + +[sub_resource type="PlaneMesh" id="PlaneMesh_alxwk"] +material = SubResource("StandardMaterial3D_ob58c") +size = Vector2(20, 20) + +[node name="debug_env_player" type="Node3D"] +script = ExtResource("1_xginw") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(-0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, 0.75, -0.433013, 0, 0, 0) +shadow_enabled = true + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_r8f2r") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +mesh = SubResource("PlaneMesh_alxwk") + +[node name="Player" parent="." instance=ExtResource("2_qkysy")] diff --git a/entities/faction/enemy/enemy.gd b/entities/faction/enemy/enemy.gd new file mode 100644 index 0000000..3cfaa07 --- /dev/null +++ b/entities/faction/enemy/enemy.gd @@ -0,0 +1,11 @@ +extends Faction + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass diff --git a/entities/faction/faction.gd b/entities/faction/faction.gd new file mode 100644 index 0000000..b168206 --- /dev/null +++ b/entities/faction/faction.gd @@ -0,0 +1,13 @@ +class_name Faction extends Node + +var buildings:Array[Node3D]: + get: + var buildings:Array[Node3D] + buildings.assign($Buildings.get_children()) + return buildings + +var units:Array[Node3D]: + get: + var buildings:Array[Node3D] + buildings.assign($Units.get_children()) + return buildings diff --git a/entities/faction/fog_of_war.gd b/entities/faction/fog_of_war.gd new file mode 100644 index 0000000..fe4fe6d --- /dev/null +++ b/entities/faction/fog_of_war.gd @@ -0,0 +1,47 @@ +class_name FogOfWar extends Node + +@export var visual:bool = true +@export var width:int = 20 +@export var height:int = 20 + +var size:int: + get: return width*height + +var faction:Faction: + get: return get_parent() as Faction + +var grid:Array[bool] = [] + +func _ready() -> void: + if not visual: $FogOfWarVisual.queue_free() + generate_new_grid() + visualize_grid() + +func _on_tick() -> void: + update_fog_of_war() + visualize_grid() + +func generate_new_grid()->void: + grid.resize(size) + for i in range(size): + grid[i] = true + +func visualize_grid()->void: + if not has_node("FogOfWarVisual"): return + var fog_of_war_visual := $FogOfWarVisual as GridMap + for i in range(size): + var item := 0 if grid[i] else -1 + fog_of_war_visual.set_cell_item(c2p(i2c(i)), item) + +func update_fog_of_war()->void: + var entities := faction.buildings + faction.units + for i in range(size): + grid[i] = grid[i] and not entities.map( + func(entity:Node3D)->bool: + return entity.position.distance_squared_to(c2p(i2c(i))) < 10 + ).has(true) + +# Converters from coordinates to index values +func c2i(coor:Vector2i)->int: return coor.x+coor.y*width +func i2c(index:int)->Vector2i: return Vector2i(index%width, index/width) +func c2p(coor:Vector2i)->Vector3i: return Vector3i(coor.x-width/2, 0, coor.y-height/2) diff --git a/entities/faction/fog_of_war.tscn b/entities/faction/fog_of_war.tscn new file mode 100644 index 0000000..8169b03 --- /dev/null +++ b/entities/faction/fog_of_war.tscn @@ -0,0 +1,18 @@ +[gd_scene load_steps=3 format=3 uid="uid://ch7ug2prgwyyn"] + +[ext_resource type="Script" path="res://entities/faction/fog_of_war.gd" id="1_bm7vn"] +[ext_resource type="MeshLibrary" uid="uid://cesuajklj4ds4" path="res://resources/meshlib.tres" id="2_5docv"] + +[node name="FogOfWar" type="Node"] +script = ExtResource("1_bm7vn") + +[node name="TickTimer" type="Timer" parent="."] +wait_time = 0.5 +autostart = true + +[node name="FogOfWarVisual" type="GridMap" parent="."] +mesh_library = ExtResource("2_5docv") +cell_size = Vector3(1, 1, 1) +metadata/_editor_floor_ = Vector3(0, 0, 0) + +[connection signal="timeout" from="TickTimer" to="." method="_on_tick"] diff --git a/entities/faction/player/player.gd b/entities/faction/player/player.gd new file mode 100644 index 0000000..3cfaa07 --- /dev/null +++ b/entities/faction/player/player.gd @@ -0,0 +1,11 @@ +extends Faction + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass diff --git a/entities/faction/player/player_camera.gd b/entities/faction/player/player_camera.gd new file mode 100644 index 0000000..84ff26d --- /dev/null +++ b/entities/faction/player/player_camera.gd @@ -0,0 +1,33 @@ +extends Camera3D + +var move_speed : float = 5 +var tilt_speed : float = 2 + +func _process(delta: float) -> void: + process_movement_speed() + process_movement(delta) + process_camera_tilt(delta) + +func process_movement(delta:float) -> void: + var input_vector := Vector3.ZERO; + input_vector += Vector3.UP * Input.get_axis("move_down", "move_up") + input_vector += Vector3.FORWARD * Input.get_axis("move_back", "move_forward") + input_vector += Vector3.RIGHT * Input.get_axis("move_left", "move_right") + translate(input_vector * move_speed * delta) + position.y = clampf(position.y, 2, 20) + +func process_movement_speed() -> void: + if Input.is_action_just_released("move_increase"): + move_speed = clampf(move_speed + 1, 1, 10) + if Input.is_action_just_released("move_decrease"): + move_speed = clampf(move_speed-1, 1, 10) + +func process_camera_tilt(delta: float) -> void: + if Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT): + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + var mouse_move := -Input.get_last_mouse_velocity().normalized() + mouse_move *= tilt_speed * delta + rotation.y = rotation.y + mouse_move.x + rotation.x =clampf(rotation.x + mouse_move.y, -PI/2, 0) + else: + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE diff --git a/project.godot b/project.godot index 9c9e575..7c7916f 100644 --- a/project.godot +++ b/project.godot @@ -11,5 +11,72 @@ config_version=5 [application] config/name="RomAnts" +run/main_scene="res://scenes/game.tscn" config/features=PackedStringArray("4.3") config/icon="res://icon.svg" + +[autoload] + +DebugEnvironmentWatcher="*res://addons/godot_debug_environments/debug_environment_watcher.gd" + +[debug] + +gdscript/warnings/untyped_declaration=2 +gdscript/warnings/unsafe_property_access=1 +gdscript/warnings/unsafe_method_access=1 +gdscript/warnings/unsafe_cast=1 +gdscript/warnings/unsafe_call_argument=1 +gdscript/warnings/return_value_discarded=1 + +[editor] + +movie_writer/movie_file="D:/Projects/movie.avi" + +[editor_plugins] + +enabled=PackedStringArray("res://addons/anthonyec.camera_preview/plugin.cfg", "res://addons/godot_debug_environments/plugin.cfg", "res://addons/scene-library/plugin.cfg") + +[input] + +move_up={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +] +} +move_down={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null) +] +} +move_forward={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +] +} +move_back={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +] +} +move_left={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +] +} +move_right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +] +} +move_increase={ +"deadzone": 0.5, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":4,"canceled":false,"pressed":false,"double_click":false,"script":null) +] +} +move_decrease={ +"deadzone": 0.5, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":5,"canceled":false,"pressed":false,"double_click":false,"script":null) +] +} diff --git a/resources/meshlib.tres b/resources/meshlib.tres new file mode 100644 index 0000000..38e5441 --- /dev/null +++ b/resources/meshlib.tres @@ -0,0 +1,28 @@ +[gd_resource type="MeshLibrary" load_steps=5 format=3 uid="uid://cesuajklj4ds4"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_1o0cn"] +albedo_color = Color(0.22, 0.22, 0.22, 1) + +[sub_resource type="BoxMesh" id="BoxMesh_solw3"] +material = SubResource("StandardMaterial3D_1o0cn") + +[sub_resource type="Image" id="Image_lmm1c"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 255, 67, 67, 67, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 67, 67, 67, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 66, 66, 66, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 65, 65, 65, 255, 65, 65, 65, 255, 65, 65, 65, 255, 65, 65, 65, 255, 65, 65, 65, 255, 65, 65, 65, 255, 65, 65, 65, 255, 65, 65, 65, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 65, 65, 65, 255, 65, 65, 65, 255, 65, 65, 65, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 33, 33, 33, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 61, 61, 61, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 61, 61, 61, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 61, 61, 61, 255, 62, 62, 62, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 62, 62, 62, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 34, 34, 34, 255, 62, 62, 62, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 255, 34, 34, 34, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 64, +"mipmaps": false, +"width": 64 +} + +[sub_resource type="ImageTexture" id="ImageTexture_aro7y"] +image = SubResource("Image_lmm1c") + +[resource] +item/0/name = "MeshInstance3D" +item/0/mesh = SubResource("BoxMesh_solw3") +item/0/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/0/shapes = [] +item/0/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/0/navigation_layers = 1 +item/0/preview = SubResource("ImageTexture_aro7y") diff --git a/resources/meshlib.tscn b/resources/meshlib.tscn new file mode 100644 index 0000000..87d1e8a --- /dev/null +++ b/resources/meshlib.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=3 format=3 uid="uid://bucaq3d3kowwa"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_1o0cn"] +albedo_color = Color(0.22, 0.22, 0.22, 1) + +[sub_resource type="BoxMesh" id="BoxMesh_dj03r"] +material = SubResource("StandardMaterial3D_1o0cn") + +[node name="Node3D" type="Node3D"] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0) +mesh = SubResource("BoxMesh_dj03r") diff --git a/scenes/gam5CAE.tmp b/scenes/gam5CAE.tmp new file mode 100644 index 0000000..6f3fefc --- /dev/null +++ b/scenes/gam5CAE.tmp @@ -0,0 +1,159 @@ +[gd_scene load_steps=17 format=3 uid="uid://rnmcx0o0hdgb"] + +[ext_resource type="Script" path="res://scenes/game.gd" id="1_3n5gp"] +[ext_resource type="Script" path="res://entities/faction/player/player_camera.gd" id="2_1etxd"] +[ext_resource type="Script" path="res://entities/faction/player/player.gd" id="2_ba5k2"] +[ext_resource type="MeshLibrary" uid="uid://cesuajklj4ds4" path="res://resources/meshlib.tres" id="2_wyom5"] +[ext_resource type="Script" path="res://entities/faction/fog_of_war.gd" id="4_8hagq"] +[ext_resource type="Script" path="res://entities/faction/enemy/enemy.gd" id="6_y7ter"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_tx3qi"] +sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) +ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) + +[sub_resource type="Sky" id="Sky_mmhs2"] +sky_material = SubResource("ProceduralSkyMaterial_tx3qi") + +[sub_resource type="Environment" id="Environment_r8f2r"] +background_mode = 2 +sky = SubResource("Sky_mmhs2") +tonemap_mode = 2 +glow_enabled = true + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_e001c"] +width = 16 +height = 16 + +[sub_resource type="PrismMesh" id="PrismMesh_dw2jx"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_nljwt"] +albedo_color = Color(1, 0, 0, 1) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_xv1v4"] +albedo_color = Color(0, 0.766667, 1, 1) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ob58c"] +albedo_color = Color(0, 0.46, 0.145667, 1) + +[sub_resource type="PlaneMesh" id="PlaneMesh_alxwk"] +material = SubResource("StandardMaterial3D_ob58c") +size = Vector2(20, 20) + +[sub_resource type="SphereMesh" id="SphereMesh_3yo4a"] + +[node name="Game" type="Node3D"] +script = ExtResource("1_3n5gp") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(-0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, 0.75, -0.433013, 0, 0, 0) +shadow_enabled = true + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_r8f2r") + +[node name="Player" type="Node" parent="."] +script = ExtResource("2_ba5k2") + +[node name="PlayerCamera" type="Camera3D" parent="Player"] +transform = Transform3D(1, 0, 0, 0, 0.965926, 0.258819, 0, -0.258819, 0.965926, 2.38419e-07, 1.98981, 3.94427) +script = ExtResource("2_1etxd") + +[node name="Sprite3D" type="Sprite3D" parent="Player/PlayerCamera"] +transform = Transform3D(10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 1, 0) +cast_shadow = 0 +axis = 1 +billboard = 2 +texture = SubResource("NoiseTexture2D_e001c") + +[node name="PlayerUI" type="Control" parent="Player"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="SubViewportContainer" type="SubViewportContainer" parent="Player/PlayerUI"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -206.0 +offset_top = -206.0 +grow_horizontal = 0 +grow_vertical = 0 +stretch = true + +[node name="SubViewport" type="SubViewport" parent="Player/PlayerUI/SubViewportContainer"] +handle_input_locally = false +size = Vector2i(206, 206) +render_target_update_mode = 4 + +[node name="Camera3D" type="Camera3D" parent="Player/PlayerUI/SubViewportContainer/SubViewport"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 20, 0) +projection = 1 +size = 20.0 + +[node name="FogOfWar" type="Node" parent="Player"] +script = ExtResource("4_8hagq") + +[node name="FogOfWarVisual" type="GridMap" parent="Player/FogOfWar"] +mesh_library = ExtResource("2_wyom5") +cell_size = Vector3(1, 1, 1) +metadata/_editor_floor_ = Vector3(0, 0, 0) + +[node name="Buildings" type="Node" parent="Player"] + +[node name="PlayerCamp" type="MeshInstance3D" parent="Player/Buildings"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 0.40872, 7) +mesh = SubResource("PrismMesh_dw2jx") +skeleton = NodePath("../../../Environment/Terrain") +surface_material_override/0 = SubResource("StandardMaterial3D_nljwt") + +[node name="Units" type="Node" parent="Player"] + +[node name="Enemy" type="Node" parent="."] +script = ExtResource("6_y7ter") + +[node name="FogOfWar" type="Node" parent="Enemy"] +script = ExtResource("4_8hagq") + +[node name="Buildings" type="Node" parent="Enemy"] + +[node name="MeshInstance3D3" type="MeshInstance3D" parent="Enemy/Buildings"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 0.40872, -7) +mesh = SubResource("PrismMesh_dw2jx") +skeleton = NodePath("../../../Environment/Terrain") +surface_material_override/0 = SubResource("StandardMaterial3D_xv1v4") + +[node name="Units" type="Node" parent="Enemy"] + +[node name="Environment" type="Node" parent="."] + +[node name="Terrain" type="MeshInstance3D" parent="Environment"] +mesh = SubResource("PlaneMesh_alxwk") +skeleton = NodePath("../..") + +[node name="Food" type="Node" parent="Environment"] + +[node name="MeshInstance3D2" type="MeshInstance3D" parent="Environment/Food"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.41598, 0.40872, -3.16507) +mesh = SubResource("SphereMesh_3yo4a") +skeleton = NodePath("../../Terrain") + +[node name="MeshInstance3D4" type="MeshInstance3D" parent="Environment/Food"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6.57951, 0.40872, 2.67711) +mesh = SubResource("SphereMesh_3yo4a") +skeleton = NodePath("../../Terrain") + +[node name="MeshInstance3D5" type="MeshInstance3D" parent="Environment/Food"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.39705, 0.408719, -3.50521) +mesh = SubResource("SphereMesh_3yo4a") +skeleton = NodePath("../../Terrain") + +[node name="MeshInstance3D6" type="MeshInstance3D" parent="Environment/Food"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0892634, 0.408719, 5.57122) +mesh = SubResource("SphereMesh_3yo4a") +skeleton = NodePath("../../Terrain") diff --git a/scenes/gam77C8.tmp b/scenes/gam77C8.tmp new file mode 100644 index 0000000..6f3fefc --- /dev/null +++ b/scenes/gam77C8.tmp @@ -0,0 +1,159 @@ +[gd_scene load_steps=17 format=3 uid="uid://rnmcx0o0hdgb"] + +[ext_resource type="Script" path="res://scenes/game.gd" id="1_3n5gp"] +[ext_resource type="Script" path="res://entities/faction/player/player_camera.gd" id="2_1etxd"] +[ext_resource type="Script" path="res://entities/faction/player/player.gd" id="2_ba5k2"] +[ext_resource type="MeshLibrary" uid="uid://cesuajklj4ds4" path="res://resources/meshlib.tres" id="2_wyom5"] +[ext_resource type="Script" path="res://entities/faction/fog_of_war.gd" id="4_8hagq"] +[ext_resource type="Script" path="res://entities/faction/enemy/enemy.gd" id="6_y7ter"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_tx3qi"] +sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) +ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) + +[sub_resource type="Sky" id="Sky_mmhs2"] +sky_material = SubResource("ProceduralSkyMaterial_tx3qi") + +[sub_resource type="Environment" id="Environment_r8f2r"] +background_mode = 2 +sky = SubResource("Sky_mmhs2") +tonemap_mode = 2 +glow_enabled = true + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_e001c"] +width = 16 +height = 16 + +[sub_resource type="PrismMesh" id="PrismMesh_dw2jx"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_nljwt"] +albedo_color = Color(1, 0, 0, 1) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_xv1v4"] +albedo_color = Color(0, 0.766667, 1, 1) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ob58c"] +albedo_color = Color(0, 0.46, 0.145667, 1) + +[sub_resource type="PlaneMesh" id="PlaneMesh_alxwk"] +material = SubResource("StandardMaterial3D_ob58c") +size = Vector2(20, 20) + +[sub_resource type="SphereMesh" id="SphereMesh_3yo4a"] + +[node name="Game" type="Node3D"] +script = ExtResource("1_3n5gp") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(-0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, 0.75, -0.433013, 0, 0, 0) +shadow_enabled = true + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_r8f2r") + +[node name="Player" type="Node" parent="."] +script = ExtResource("2_ba5k2") + +[node name="PlayerCamera" type="Camera3D" parent="Player"] +transform = Transform3D(1, 0, 0, 0, 0.965926, 0.258819, 0, -0.258819, 0.965926, 2.38419e-07, 1.98981, 3.94427) +script = ExtResource("2_1etxd") + +[node name="Sprite3D" type="Sprite3D" parent="Player/PlayerCamera"] +transform = Transform3D(10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 1, 0) +cast_shadow = 0 +axis = 1 +billboard = 2 +texture = SubResource("NoiseTexture2D_e001c") + +[node name="PlayerUI" type="Control" parent="Player"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="SubViewportContainer" type="SubViewportContainer" parent="Player/PlayerUI"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -206.0 +offset_top = -206.0 +grow_horizontal = 0 +grow_vertical = 0 +stretch = true + +[node name="SubViewport" type="SubViewport" parent="Player/PlayerUI/SubViewportContainer"] +handle_input_locally = false +size = Vector2i(206, 206) +render_target_update_mode = 4 + +[node name="Camera3D" type="Camera3D" parent="Player/PlayerUI/SubViewportContainer/SubViewport"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 20, 0) +projection = 1 +size = 20.0 + +[node name="FogOfWar" type="Node" parent="Player"] +script = ExtResource("4_8hagq") + +[node name="FogOfWarVisual" type="GridMap" parent="Player/FogOfWar"] +mesh_library = ExtResource("2_wyom5") +cell_size = Vector3(1, 1, 1) +metadata/_editor_floor_ = Vector3(0, 0, 0) + +[node name="Buildings" type="Node" parent="Player"] + +[node name="PlayerCamp" type="MeshInstance3D" parent="Player/Buildings"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 0.40872, 7) +mesh = SubResource("PrismMesh_dw2jx") +skeleton = NodePath("../../../Environment/Terrain") +surface_material_override/0 = SubResource("StandardMaterial3D_nljwt") + +[node name="Units" type="Node" parent="Player"] + +[node name="Enemy" type="Node" parent="."] +script = ExtResource("6_y7ter") + +[node name="FogOfWar" type="Node" parent="Enemy"] +script = ExtResource("4_8hagq") + +[node name="Buildings" type="Node" parent="Enemy"] + +[node name="MeshInstance3D3" type="MeshInstance3D" parent="Enemy/Buildings"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 0.40872, -7) +mesh = SubResource("PrismMesh_dw2jx") +skeleton = NodePath("../../../Environment/Terrain") +surface_material_override/0 = SubResource("StandardMaterial3D_xv1v4") + +[node name="Units" type="Node" parent="Enemy"] + +[node name="Environment" type="Node" parent="."] + +[node name="Terrain" type="MeshInstance3D" parent="Environment"] +mesh = SubResource("PlaneMesh_alxwk") +skeleton = NodePath("../..") + +[node name="Food" type="Node" parent="Environment"] + +[node name="MeshInstance3D2" type="MeshInstance3D" parent="Environment/Food"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.41598, 0.40872, -3.16507) +mesh = SubResource("SphereMesh_3yo4a") +skeleton = NodePath("../../Terrain") + +[node name="MeshInstance3D4" type="MeshInstance3D" parent="Environment/Food"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6.57951, 0.40872, 2.67711) +mesh = SubResource("SphereMesh_3yo4a") +skeleton = NodePath("../../Terrain") + +[node name="MeshInstance3D5" type="MeshInstance3D" parent="Environment/Food"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.39705, 0.408719, -3.50521) +mesh = SubResource("SphereMesh_3yo4a") +skeleton = NodePath("../../Terrain") + +[node name="MeshInstance3D6" type="MeshInstance3D" parent="Environment/Food"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0892634, 0.408719, 5.57122) +mesh = SubResource("SphereMesh_3yo4a") +skeleton = NodePath("../../Terrain") diff --git a/scenes/game.gd b/scenes/game.gd new file mode 100644 index 0000000..4dbb900 --- /dev/null +++ b/scenes/game.gd @@ -0,0 +1,9 @@ +class_name Game extends Node3D + + +func _ready() -> void: + pass # Replace with function body. + + +func _process(delta: float) -> void: + pass diff --git a/scenes/game.tscn b/scenes/game.tscn new file mode 100644 index 0000000..8524aec --- /dev/null +++ b/scenes/game.tscn @@ -0,0 +1,234 @@ +[gd_scene load_steps=21 format=3 uid="uid://rnmcx0o0hdgb"] + +[ext_resource type="Script" path="res://scenes/game.gd" id="1_3n5gp"] +[ext_resource type="Script" path="res://entities/faction/player/player_camera.gd" id="2_1etxd"] +[ext_resource type="Script" path="res://entities/faction/player/player.gd" id="2_ba5k2"] +[ext_resource type="PackedScene" uid="uid://ch7ug2prgwyyn" path="res://entities/faction/fog_of_war.tscn" id="5_8a5ny"] +[ext_resource type="Script" path="res://entities/faction/enemy/enemy.gd" id="6_y7ter"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_tx3qi"] +sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) +ground_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) + +[sub_resource type="Sky" id="Sky_mmhs2"] +sky_material = SubResource("ProceduralSkyMaterial_tx3qi") + +[sub_resource type="Environment" id="Environment_r8f2r"] +background_mode = 2 +sky = SubResource("Sky_mmhs2") +tonemap_mode = 2 +glow_enabled = true + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_e001c"] +width = 16 +height = 16 + +[sub_resource type="PrismMesh" id="PrismMesh_dw2jx"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_nljwt"] +albedo_color = Color(1, 0, 0, 1) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_api38"] +albedo_color = Color(0.53, 0, 0, 1) + +[sub_resource type="CapsuleMesh" id="CapsuleMesh_cpeim"] +lightmap_size_hint = Vector2i(17, 14) +material = SubResource("StandardMaterial3D_api38") +radius = 0.25 +height = 1.0 + +[sub_resource type="Animation" id="Animation_qp8oi"] +resource_name = "db_anim" +length = 12.0 +step = 0.5 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("MeshInstance3D:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 2, 2.5, 4, 4.5, 6, 7, 9, 9.5, 12), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector3(-5.16842, 0.25, 6.861), Vector3(-5.16842, 0.25, -0.139), Vector3(-4.16842, 0.25, -0.139), Vector3(4.83158, 0.249999, -0.139), Vector3(5.83158, 0.249999, -1.139), Vector3(5.83158, 0.249999, -7.139), Vector3(4.83158, 0.249999, -7.139), Vector3(-5.16842, 0.249999, -7.139), Vector3(-6.16842, 0.249999, -6.139), Vector3(-6.16841, 0.249998, 4.861)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("MeshInstance3D:rotation") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 2, 2.5, 4, 4.5, 6, 7, 9, 9.5, 12), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Vector3(-1.5708, 0, 0), Vector3(-1.5708, 0, 0), Vector3(-1.5708, -1.5708, 0), Vector3(-1.5708, -1.5708, 0), Vector3(-1.5708, -7.17753e-23, 0), Vector3(-1.5708, -7.17753e-23, 0), Vector3(-1.5708, 1.5708, 0), Vector3(-1.5708, 1.5708, 0), Vector3(-1.5708, -3.14159, 0), Vector3(-1.5708, -3.14159, 0)] +} + +[sub_resource type="Animation" id="Animation_27gua"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("MeshInstance3D:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector3(-5.16842, 0.25, 6.861)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("MeshInstance3D:rotation") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector3(-1.5708, 0, 0)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_ae667"] +_data = { +"RESET": SubResource("Animation_27gua"), +"db_anim": SubResource("Animation_qp8oi") +} + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_xv1v4"] +albedo_color = Color(0, 0.766667, 1, 1) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ob58c"] +albedo_color = Color(0, 0.46, 0.145667, 1) + +[sub_resource type="PlaneMesh" id="PlaneMesh_alxwk"] +material = SubResource("StandardMaterial3D_ob58c") +size = Vector2(20, 20) + +[sub_resource type="SphereMesh" id="SphereMesh_3yo4a"] + +[node name="Game" type="Node3D"] +script = ExtResource("1_3n5gp") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(-0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, 0.75, -0.433013, 0, 0, 0) +shadow_enabled = true + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_r8f2r") + +[node name="Player" type="Node" parent="."] +script = ExtResource("2_ba5k2") + +[node name="PlayerCamera" type="Camera3D" parent="Player"] +transform = Transform3D(1, 0, 0, 0, 0.965926, 0.258819, 0, -0.258819, 0.965926, 2.38419e-07, 1.98981, 3.94427) +script = ExtResource("2_1etxd") + +[node name="Sprite3D" type="Sprite3D" parent="Player/PlayerCamera"] +transform = Transform3D(10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 1, 0) +cast_shadow = 0 +axis = 1 +billboard = 2 +texture = SubResource("NoiseTexture2D_e001c") + +[node name="PlayerUI" type="Control" parent="Player"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="SubViewportContainer" type="SubViewportContainer" parent="Player/PlayerUI"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -206.0 +offset_top = -206.0 +grow_horizontal = 0 +grow_vertical = 0 +stretch = true + +[node name="SubViewport" type="SubViewport" parent="Player/PlayerUI/SubViewportContainer"] +handle_input_locally = false +size = Vector2i(206, 206) +render_target_update_mode = 4 + +[node name="Camera3D" type="Camera3D" parent="Player/PlayerUI/SubViewportContainer/SubViewport"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 20, 0) +projection = 1 +size = 20.0 + +[node name="FogOfWar" parent="Player" instance=ExtResource("5_8a5ny")] + +[node name="Buildings" type="Node" parent="Player"] + +[node name="PlayerCamp" type="MeshInstance3D" parent="Player/Buildings"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 0.40872, 7) +mesh = SubResource("PrismMesh_dw2jx") +skeleton = NodePath("../../../Environment/Terrain") +surface_material_override/0 = SubResource("StandardMaterial3D_nljwt") + +[node name="Units" type="Node" parent="Player"] + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Player/Units"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -5.16842, 0.25, 6.861) +mesh = SubResource("CapsuleMesh_cpeim") + +[node name="AnimationPlayer" type="AnimationPlayer" parent="Player"] +root_node = NodePath("../Units") +libraries = { +"": SubResource("AnimationLibrary_ae667") +} +autoplay = "db_anim" + +[node name="Enemy" type="Node" parent="."] +script = ExtResource("6_y7ter") + +[node name="FogOfWar" parent="Enemy" instance=ExtResource("5_8a5ny")] +visual = false + +[node name="Buildings" type="Node" parent="Enemy"] + +[node name="MeshInstance3D3" type="MeshInstance3D" parent="Enemy/Buildings"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 0.40872, -7) +mesh = SubResource("PrismMesh_dw2jx") +skeleton = NodePath("../../../Environment/Terrain") +surface_material_override/0 = SubResource("StandardMaterial3D_xv1v4") + +[node name="Units" type="Node" parent="Enemy"] + +[node name="Environment" type="Node" parent="."] + +[node name="Terrain" type="MeshInstance3D" parent="Environment"] +mesh = SubResource("PlaneMesh_alxwk") +skeleton = NodePath("../..") + +[node name="Food" type="Node" parent="Environment"] + +[node name="MeshInstance3D2" type="MeshInstance3D" parent="Environment/Food"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.41598, 0.40872, -3.16507) +mesh = SubResource("SphereMesh_3yo4a") +skeleton = NodePath("../../Terrain") + +[node name="MeshInstance3D4" type="MeshInstance3D" parent="Environment/Food"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6.57951, 0.40872, 2.67711) +mesh = SubResource("SphereMesh_3yo4a") +skeleton = NodePath("../../Terrain") + +[node name="MeshInstance3D5" type="MeshInstance3D" parent="Environment/Food"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.39705, 0.408719, -3.50521) +mesh = SubResource("SphereMesh_3yo4a") +skeleton = NodePath("../../Terrain") + +[node name="MeshInstance3D6" type="MeshInstance3D" parent="Environment/Food"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0892634, 0.408719, 5.57122) +mesh = SubResource("SphereMesh_3yo4a") +skeleton = NodePath("../../Terrain")