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/assets/Textures/ObjectTextures/Blunderbus.png b/assets/Textures/ObjectTextures/Blunderbus.png
new file mode 100644
index 0000000..96f9371
Binary files /dev/null and b/assets/Textures/ObjectTextures/Blunderbus.png differ
diff --git a/assets/Textures/ObjectTextures/Blunderbus.png.import b/assets/Textures/ObjectTextures/Blunderbus.png.import
new file mode 100644
index 0000000..b867fd6
--- /dev/null
+++ b/assets/Textures/ObjectTextures/Blunderbus.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://74id2drhfp2s"
+path="res://.godot/imported/Blunderbus.png-655ba7bc5de64378bbd28fc0db8c9b24.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/Textures/ObjectTextures/Blunderbus.png"
+dest_files=["res://.godot/imported/Blunderbus.png-655ba7bc5de64378bbd28fc0db8c9b24.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
diff --git a/assets/Textures/ObjectTextures/Pistol.png b/assets/Textures/ObjectTextures/Pistol.png
new file mode 100644
index 0000000..b5f45f8
Binary files /dev/null and b/assets/Textures/ObjectTextures/Pistol.png differ
diff --git a/assets/Textures/ObjectTextures/Pistol.png.import b/assets/Textures/ObjectTextures/Pistol.png.import
new file mode 100644
index 0000000..765337d
--- /dev/null
+++ b/assets/Textures/ObjectTextures/Pistol.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d2fic5txnwu3u"
+path="res://.godot/imported/Pistol.png-088d61fee53cc956a8ad21cb8c0cb860.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/Textures/ObjectTextures/Pistol.png"
+dest_files=["res://.godot/imported/Pistol.png-088d61fee53cc956a8ad21cb8c0cb860.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
diff --git a/assets/Textures/ObjectTextures/RocketLauncher.png b/assets/Textures/ObjectTextures/RocketLauncher.png
new file mode 100644
index 0000000..6c4ac5b
Binary files /dev/null and b/assets/Textures/ObjectTextures/RocketLauncher.png differ
diff --git a/assets/Textures/ObjectTextures/RocketLauncher.png.import b/assets/Textures/ObjectTextures/RocketLauncher.png.import
new file mode 100644
index 0000000..c10e4f8
--- /dev/null
+++ b/assets/Textures/ObjectTextures/RocketLauncher.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dyvcljaoux8h"
+path="res://.godot/imported/RocketLauncher.png-d6d9f4dd5ce1e4a1f9b18f5bb63728dd.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/Textures/ObjectTextures/RocketLauncher.png"
+dest_files=["res://.godot/imported/RocketLauncher.png-d6d9f4dd5ce1e4a1f9b18f5bb63728dd.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
diff --git a/assets/Textures/ObjectTextures/mac10.png b/assets/Textures/ObjectTextures/mac10.png
new file mode 100644
index 0000000..fc7ace0
Binary files /dev/null and b/assets/Textures/ObjectTextures/mac10.png differ
diff --git a/assets/Textures/ObjectTextures/mac10.png.import b/assets/Textures/ObjectTextures/mac10.png.import
new file mode 100644
index 0000000..686101a
--- /dev/null
+++ b/assets/Textures/ObjectTextures/mac10.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://gx3iw54iemho"
+path="res://.godot/imported/mac10.png-01420520bd1372fc6a82121c21a6d814.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/Textures/ObjectTextures/mac10.png"
+dest_files=["res://.godot/imported/mac10.png-01420520bd1372fc6a82121c21a6d814.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
diff --git a/assets/Textures/ObjectTextures/revolver1.png b/assets/Textures/ObjectTextures/revolver1.png
new file mode 100644
index 0000000..b910e7d
Binary files /dev/null and b/assets/Textures/ObjectTextures/revolver1.png differ
diff --git a/assets/Textures/ObjectTextures/revolver1.png.import b/assets/Textures/ObjectTextures/revolver1.png.import
new file mode 100644
index 0000000..416aeda
--- /dev/null
+++ b/assets/Textures/ObjectTextures/revolver1.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bho7c8s2yno12"
+path="res://.godot/imported/revolver1.png-2275678462fdf0863e5d71c28157a203.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/Textures/ObjectTextures/revolver1.png"
+dest_files=["res://.godot/imported/revolver1.png-2275678462fdf0863e5d71c28157a203.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
diff --git a/assets/blockout_2.tscn b/assets/blockout_2.tscn
index 0903a9f..1eed322 100644
--- a/assets/blockout_2.tscn
+++ b/assets/blockout_2.tscn
@@ -897,6 +897,7 @@ _data = {
[node name="BLOCKOUT2Test" type="Node3D" node_paths=PackedStringArray("player")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.016016, 0.0225029, -0.0192337)
script = ExtResource("1_sbpvn")
+load_save = false
player = NodePath("Player")
money = 50
start_health = 5
diff --git a/assets/mac_10.tscn b/assets/mac_10.tscn
index cd17fe3..ec5a80a 100644
--- a/assets/mac_10.tscn
+++ b/assets/mac_10.tscn
@@ -1,8 +1,9 @@
-[gd_scene load_steps=33 format=4 uid="uid://brl0bsqjl5dg3"]
+[gd_scene load_steps=34 format=4 uid="uid://brl0bsqjl5dg3"]
[ext_resource type="PackedScene" uid="uid://c33b6ldlxxfro" path="res://assets/Models/mac10.blend" id="1_nb4p5"]
[ext_resource type="Script" path="res://scripts/gun.gd" id="2_tskiy"]
[ext_resource type="PackedScene" uid="uid://nq5nq2hjc4ec" path="res://assets/bullet_fake.tscn" id="3_heo3y"]
+[ext_resource type="Texture2D" uid="uid://gx3iw54iemho" path="res://assets/Textures/ObjectTextures/mac10.png" id="3_p1hxc"]
[ext_resource type="PackedScene" uid="uid://dqhltdnqyg8ni" path="res://assets/bullet.tscn" id="3_w1kko"]
[ext_resource type="PackedScene" uid="uid://crvohhc6kgshn" path="res://assets/bullet_hole.tscn" id="4_eleuq"]
[ext_resource type="PackedScene" uid="uid://cp8563f0oxvff" path="res://assets/mag1.tscn" id="4_ji2hu"]
@@ -489,6 +490,7 @@ _data = {
[node name="mac10" node_paths=PackedStringArray("r_hand_location", "l_hand_location", "flare_light", "anim_player", "barrel_raycast", "casing_ejector", "mag_ejector", "audio_fire", "audio_empty", "audio_reload") instance=ExtResource("1_nb4p5")]
script = ExtResource("2_tskiy")
gun_name = "Mac 10"
+gun_icon = ExtResource("3_p1hxc")
fov_zoom_amt = 0.998
recoil_amount = Vector3(0.02, 0.05, 0.05)
max_ammo = 20
diff --git a/assets/pistol1.tscn b/assets/pistol1.tscn
index 1101088..2cae13d 100644
--- a/assets/pistol1.tscn
+++ b/assets/pistol1.tscn
@@ -1,7 +1,8 @@
-[gd_scene load_steps=23 format=3 uid="uid://dslxb3psx30vp"]
+[gd_scene load_steps=24 format=3 uid="uid://dslxb3psx30vp"]
[ext_resource type="PackedScene" uid="uid://svrb3n31mkag" path="res://assets/Models/gun.blend" id="1_frekg"]
[ext_resource type="Script" path="res://scripts/trackerGun.gd" id="2_ieev5"]
+[ext_resource type="Texture2D" uid="uid://d2fic5txnwu3u" path="res://assets/Textures/ObjectTextures/Pistol.png" id="3_gkvbi"]
[ext_resource type="PackedScene" uid="uid://crvohhc6kgshn" path="res://assets/bullet_hole.tscn" id="4_bu1g0"]
[ext_resource type="PackedScene" uid="uid://c1gdehrsytlkk" path="res://assets/casing.tscn" id="5_gls8p"]
[ext_resource type="PackedScene" uid="uid://cp8563f0oxvff" path="res://assets/mag1.tscn" id="6_ysonb"]
@@ -431,6 +432,7 @@ _data = {
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.0145504, -0.0460228)
script = ExtResource("2_ieev5")
gun_name = "Tracker Pistol"
+gun_icon = ExtResource("3_gkvbi")
recoil_amount = Vector3(0, 0.05, 0.05)
bullet_speed = 50
tracker_indicator = NodePath("gun/slide/TrackerIndicator")
diff --git a/assets/player.tscn b/assets/player.tscn
index 8058763..de53746 100644
--- a/assets/player.tscn
+++ b/assets/player.tscn
@@ -1,8 +1,9 @@
-[gd_scene load_steps=31 format=3 uid="uid://drwae3loscbw7"]
+[gd_scene load_steps=32 format=3 uid="uid://drwae3loscbw7"]
[ext_resource type="Script" path="res://scripts/player.gd" id="1_x7wms"]
[ext_resource type="Script" path="res://scripts/recoil.gd" id="3_405jc"]
[ext_resource type="Texture2D" uid="uid://cknftvqq8rbrm" path="res://assets/Textures/Cookie_tutorial_texture_flashlight.png" id="4_x670l"]
+[ext_resource type="PackedScene" uid="uid://dqgtnykkbngem" path="res://assets/weapon_select.tscn" id="5_bvbcl"]
[ext_resource type="PackedScene" uid="uid://br882tlh3cfwu" path="res://hud.tscn" id="5_yenaw"]
[ext_resource type="AudioStream" uid="uid://bki17g7j4kqn4" path="res://assets/Audio/PickupSound Mixdown 3.wav" id="8_dwqsx"]
[ext_resource type="Script" path="res://scripts/bullet_ray.gd" id="10_ektr6"]
@@ -142,6 +143,10 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.437954, -1)
stream = ExtResource("14_pnsbm")
volume_db = 2.0
+[node name="WeaponSelect" parent="Head/Recoil/Camera3D" instance=ExtResource("5_bvbcl")]
+visible = false
+inner_radius = 256
+
[node name="HUD" parent="Head/Recoil/Camera3D" instance=ExtResource("5_yenaw")]
[node name="GunRay" type="RayCast3D" parent="Head/Recoil/Camera3D" groups=["gun_ray"]]
diff --git a/assets/revolver_1.tscn b/assets/revolver_1.tscn
index a28b28d..78ad342 100644
--- a/assets/revolver_1.tscn
+++ b/assets/revolver_1.tscn
@@ -1,8 +1,9 @@
-[gd_scene load_steps=67 format=4 uid="uid://b5eclfg0cmmal"]
+[gd_scene load_steps=68 format=4 uid="uid://b5eclfg0cmmal"]
[ext_resource type="PackedScene" uid="uid://dgogd08c0ubt6" path="res://assets/Models/revolver1.blend" id="1_i5f84"]
[ext_resource type="Script" path="res://scripts/revolver_1.gd" id="2_7rsti"]
[ext_resource type="PackedScene" uid="uid://dqhltdnqyg8ni" path="res://assets/bullet.tscn" id="3_32prk"]
+[ext_resource type="Texture2D" uid="uid://bho7c8s2yno12" path="res://assets/Textures/ObjectTextures/revolver1.png" id="3_nl201"]
[ext_resource type="Texture2D" uid="uid://dtg3nb2ew72c3" path="res://assets/star_05.png" id="3_q41fl"]
[ext_resource type="PackedScene" uid="uid://crvohhc6kgshn" path="res://assets/bullet_hole.tscn" id="4_ubqgq"]
[ext_resource type="PackedScene" uid="uid://c1gdehrsytlkk" path="res://assets/casing.tscn" id="5_m3vsl"]
@@ -1066,6 +1067,7 @@ _data = {
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0718293, 0)
script = ExtResource("2_7rsti")
gun_name = ".44 Galore"
+gun_icon = ExtResource("3_nl201")
fire_mode = 1
fov_zoom_amt = 0.99
recoil_amount = Vector3(0.25, 0.1, 0.1)
diff --git a/assets/rocket_launcher.tscn b/assets/rocket_launcher.tscn
index a3c8960..54cd5f7 100644
--- a/assets/rocket_launcher.tscn
+++ b/assets/rocket_launcher.tscn
@@ -1,7 +1,8 @@
-[gd_scene load_steps=32 format=4 uid="uid://2o2wcc5i1al4"]
+[gd_scene load_steps=33 format=4 uid="uid://2o2wcc5i1al4"]
[ext_resource type="Texture2D" uid="uid://b82yqx4tml6hn" path="res://assets/Models/rocketlauncher1.rough.png" id="1_b5b37"]
[ext_resource type="Script" path="res://scripts/rocket_launcher.gd" id="1_ra4f1"]
+[ext_resource type="Texture2D" uid="uid://dyvcljaoux8h" path="res://assets/Textures/ObjectTextures/RocketLauncher.png" id="2_41bg8"]
[ext_resource type="PackedScene" uid="uid://hptaoyu5jeqk" path="res://assets/rocket.tscn" id="2_dmokb"]
[ext_resource type="Texture2D" uid="uid://c2hi7c5hl2yk2" path="res://assets/Models/rocketlauncher1.normal.png" id="2_y5wa3"]
[ext_resource type="AudioStream" uid="uid://dp71xac3vbsob" path="res://assets/Audio/Weapons/rocket-launcher-firing-SBA-300109793.wav" id="5_i3755"]
@@ -457,6 +458,7 @@ _data = {
[node name="RocketLauncher" type="Node3D" node_paths=PackedStringArray("anim_player", "barrel_raycast", "mag_ejector", "audio_fire", "audio_empty", "audio_reload")]
script = ExtResource("1_ra4f1")
gun_name = "Badooka"
+gun_icon = ExtResource("2_41bg8")
fire_mode = 1
max_ammo = 1
bullet_speed = 60.0
diff --git a/assets/spider2.tscn b/assets/spider2.tscn
index 3b5a73c..5943538 100644
--- a/assets/spider2.tscn
+++ b/assets/spider2.tscn
@@ -992,13 +992,13 @@ mesh = SubResource("ArrayMesh_3dwuk")
visible = false
mesh = SubResource("ArrayMesh_d210d")
-[node name="Area3D" type="Area3D" parent="body" groups=["enemy_target"]]
+[node name="backTarget" type="Area3D" parent="body" groups=["enemy_target"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1.03709, 0)
collision_layer = 128
collision_mask = 128
script = ExtResource("7_8vkma")
-[node name="TargetShape" type="CollisionShape3D" parent="body/Area3D" groups=["enemy"]]
+[node name="TargetShape" type="CollisionShape3D" parent="body/backTarget" groups=["enemy"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.00258863, 1.30966, 0.442835)
shape = SubResource("SphereShape3D_n7n6o")
@@ -1037,8 +1037,8 @@ one_shot = true
stream = ExtResource("10_2qmhc")
volume_db = 10.0
-[connection signal="body_entered" from="body/Area3D" to="body/Area3D" method="_on_body_entered"]
-[connection signal="body_part_hit" from="body/Area3D" to="." method="_on_area_3d_body_part_hit"]
+[connection signal="body_entered" from="body/backTarget" to="body/backTarget" method="_on_body_entered"]
+[connection signal="body_part_hit" from="body/backTarget" to="." method="_on_area_3d_body_part_hit"]
[connection signal="timeout" from="Timers/prefire_timer" to="." method="_on_prefire_timer_timeout"]
[connection signal="timeout" from="Timers/postfire_timer" to="." method="_on_postfire_timer_timeout"]
[connection signal="timeout" from="Timers/knocked_timer" to="." method="_on_knocked_timer_timeout"]
diff --git a/assets/texture_catcher.tscn b/assets/texture_catcher.tscn
new file mode 100644
index 0000000..f0a321f
--- /dev/null
+++ b/assets/texture_catcher.tscn
@@ -0,0 +1,87 @@
+[gd_scene load_steps=15 format=3 uid="uid://t0w0bolkucwl"]
+
+[ext_resource type="Script" path="res://scripts/texture_catcher.gd" id="1_x3mk2"]
+[ext_resource type="PackedScene" uid="uid://2o2wcc5i1al4" path="res://assets/rocket_launcher.tscn" id="2_i6mk3"]
+[ext_resource type="PackedScene" uid="uid://brl0bsqjl5dg3" path="res://assets/mac_10.tscn" id="2_u0ggi"]
+[ext_resource type="PackedScene" uid="uid://cwutm86yp0rk6" path="res://assets/crown.tscn" id="3_gog5n"]
+[ext_resource type="PackedScene" uid="uid://b5eclfg0cmmal" path="res://assets/revolver_1.tscn" id="3_yqo0y"]
+[ext_resource type="PackedScene" uid="uid://dslxb3psx30vp" path="res://assets/pistol1.tscn" id="5_v3dls"]
+[ext_resource type="PackedScene" uid="uid://dqwkal3t4gf2p" path="res://blunderbus.tscn" id="7_do5lr"]
+[ext_resource type="PackedScene" uid="uid://djr7vnr1hcx82" path="res://assets/spider2.tscn" id="8_6tvxc"]
+[ext_resource type="PackedScene" uid="uid://bcmd7elfjhppe" path="res://assets/tree_1.tscn" id="9_0lt7q"]
+[ext_resource type="PackedScene" uid="uid://bycbdb5u5ewgl" path="res://assets/tree_2.tscn" id="10_g2f5o"]
+
+[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_n016m"]
+sky_top_color = Color(1, 1, 1, 1)
+sky_horizon_color = Color(0.81452, 0.81452, 0.81452, 1)
+ground_bottom_color = Color(0.258082, 0.258082, 0.258082, 1)
+ground_horizon_color = Color(0.81452, 0.81452, 0.81452, 1)
+
+[sub_resource type="Sky" id="Sky_qjshd"]
+sky_material = SubResource("ProceduralSkyMaterial_n016m")
+
+[sub_resource type="Environment" id="Environment_w2vw8"]
+background_mode = 2
+sky = SubResource("Sky_qjshd")
+tonemap_mode = 2
+ssao_enabled = true
+sdfgi_enabled = true
+glow_enabled = true
+
+[sub_resource type="ViewportTexture" id="ViewportTexture_xiqw1"]
+viewport_path = NodePath("CAPTURE/SubViewport")
+
+[node name="TextureCatcher" type="Node3D"]
+script = ExtResource("1_x3mk2")
+
+[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
+environment = SubResource("Environment_w2vw8")
+
+[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
+transform = Transform3D(-0.736097, 0.553106, -0.390173, 0, 0.576432, 0.817145, 0.676876, 0.601498, -0.42431, 0, 0, 0)
+shadow_enabled = true
+
+[node name="CAPTURE" type="Node3D" parent="."]
+
+[node name="SubViewport" type="SubViewport" parent="CAPTURE"]
+transparent_bg = true
+render_target_update_mode = 4
+
+[node name="Camera3D" type="Camera3D" parent="CAPTURE/SubViewport"]
+transform = Transform3D(-0.707107, 0, -0.707107, 0, 1, 0, 0.707107, 0, -0.707107, -0.697249, -2.38419e-07, -0.649448)
+
+[node name="snapshotModel" type="Node3D" parent="CAPTURE/SubViewport"]
+
+[node name="RocketLauncher" parent="CAPTURE/SubViewport/snapshotModel" instance=ExtResource("2_i6mk3")]
+transform = Transform3D(0.992614, 0, 0.121315, 0, 1, 0, -0.121315, 0, 0.992614, 0.457345, -0.229299, 0.266961)
+
+[node name="Sprite2D" type="Sprite2D" parent="."]
+texture = SubResource("ViewportTexture_xiqw1")
+
+[node name="PHOTOGRAPHED" type="Node3D" parent="."]
+transform = Transform3D(-0.707107, 0, -0.707107, 0, 1, 0, 0.707107, 0, -0.707107, -0.697249, -2.38419e-07, -0.649448)
+visible = false
+
+[node name="Crown" parent="PHOTOGRAPHED" instance=ExtResource("3_gog5n")]
+transform = Transform3D(-0.896591, 0.345323, -0.277267, 0.104303, 0.773128, 0.625615, 0.430402, 0.532001, -0.729197, -0.297585, -0.374549, -1.68743)
+
+[node name="spider" parent="PHOTOGRAPHED" instance=ExtResource("8_6tvxc")]
+transform = Transform3D(-0.707107, 0, 0.707107, 0, 1, 0, -0.707107, 0, -0.707107, -0.0527083, -0.717475, -2.38857)
+
+[node name="Tree1" parent="PHOTOGRAPHED" instance=ExtResource("9_0lt7q")]
+transform = Transform3D(-0.292272, 0, 0.956335, 0, 1, 0, -0.956335, 0, -0.292272, -0.523788, -2.28829, -3.94497)
+
+[node name="Tree2" parent="PHOTOGRAPHED" instance=ExtResource("10_g2f5o")]
+transform = Transform3D(0.576019, 0, -0.817437, 0, 1, 0, 0.817437, 0, 0.576019, -1.21889, -5.23911, -11.0324)
+
+[node name="mac10" parent="PHOTOGRAPHED" instance=ExtResource("2_u0ggi")]
+transform = Transform3D(-0.707107, 0, 0.707107, 0, 1, 0, -0.707107, 0, -0.707107, -0.0338004, 0.0101067, -0.952258)
+
+[node name="revolver1" parent="PHOTOGRAPHED" instance=ExtResource("3_yqo0y")]
+transform = Transform3D(-0.707107, 0, 0.707107, 0, 1, 0, -0.707107, 0, -0.707107, 0.116568, 0.0718296, -1.10969)
+
+[node name="Pistol" parent="PHOTOGRAPHED" instance=ExtResource("5_v3dls")]
+transform = Transform3D(-0.707107, 0, 0.707107, 0, 1, 0, -0.707107, 0, -0.707107, 0.139735, -0.0145502, -1.10556)
+
+[node name="Blunderbus" parent="PHOTOGRAPHED" instance=ExtResource("7_do5lr")]
+transform = Transform3D(-0.671708, 0, 0.740816, 0, 1, 0, -0.740816, 0, -0.671708, 0.160119, 2.38419e-07, -1.5088)
diff --git a/assets/wea1AE4.tmp b/assets/wea1AE4.tmp
new file mode 100644
index 0000000..b537c68
--- /dev/null
+++ b/assets/wea1AE4.tmp
@@ -0,0 +1,21 @@
+[gd_scene load_steps=2 format=3 uid="uid://dqgtnykkbngem"]
+
+[ext_resource type="Script" path="res://scripts/weapon_select.gd" id="1_qygnb"]
+
+[node name="WeaponSelect" type="Control"]
+layout_mode = 3
+anchors_preset = 8
+anchor_left = 0.5
+anchor_top = 0.5
+anchor_right = 0.5
+anchor_bottom = 0.5
+offset_left = -2.0
+offset_right = -2.0
+grow_horizontal = 2
+grow_vertical = 2
+size_flags_horizontal = 4
+size_flags_vertical = 4
+script = ExtResource("1_qygnb")
+outer_radius = 500
+inner_radius = 100
+line_width = 2
diff --git a/assets/weaD540.tmp b/assets/weaD540.tmp
new file mode 100644
index 0000000..362e28c
--- /dev/null
+++ b/assets/weaD540.tmp
@@ -0,0 +1,32 @@
+[gd_scene load_steps=8 format=3 uid="uid://dqgtnykkbngem"]
+
+[ext_resource type="Script" path="res://scripts/weapon_select.gd" id="1_qygnb"]
+[ext_resource type="Texture2D" uid="uid://bho7c8s2yno12" path="res://assets/Textures/ObjectTextures/revolver1.png" id="2_wxfhl"]
+[ext_resource type="Texture2D" uid="uid://gx3iw54iemho" path="res://assets/Textures/ObjectTextures/mac10.png" id="3_ofoph"]
+[ext_resource type="Texture2D" uid="uid://dyvcljaoux8h" path="res://assets/Textures/ObjectTextures/RocketLauncher.png" id="5_72tmk"]
+[ext_resource type="Texture2D" uid="uid://74id2drhfp2s" path="res://assets/Textures/ObjectTextures/Blunderbus.png" id="6_ows8e"]
+
+[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_uql6w"]
+load_path = "res://.godot/imported/mac10.png-01420520bd1372fc6a82121c21a6d814.ctex"
+
+[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_jfua6"]
+load_path = "res://.godot/imported/Pistol.png-088d61fee53cc956a8ad21cb8c0cb860.ctex"
+
+[node name="WeaponSelect" type="Control"]
+layout_mode = 3
+anchors_preset = 8
+anchor_left = 0.5
+anchor_top = 0.5
+anchor_right = 0.5
+anchor_bottom = 0.5
+offset_left = -2.0
+offset_right = -2.0
+grow_horizontal = 2
+grow_vertical = 2
+size_flags_horizontal = 4
+size_flags_vertical = 4
+script = ExtResource("1_qygnb")
+outer_radius = 500
+inner_radius = 100
+line_width = 2
+options = [ExtResource("3_ofoph"), ExtResource("2_wxfhl"), SubResource("CompressedTexture2D_uql6w"), SubResource("CompressedTexture2D_jfua6"), ExtResource("5_72tmk"), ExtResource("6_ows8e")]
diff --git a/assets/weapon_select.tscn b/assets/weapon_select.tscn
new file mode 100644
index 0000000..e260627
--- /dev/null
+++ b/assets/weapon_select.tscn
@@ -0,0 +1,21 @@
+[gd_scene load_steps=2 format=3 uid="uid://dqgtnykkbngem"]
+
+[ext_resource type="Script" path="res://scripts/weapon_select.gd" id="1_qygnb"]
+
+[node name="WeaponSelect" type="Control"]
+layout_mode = 3
+anchors_preset = 8
+anchor_left = 0.5
+anchor_top = 0.5
+anchor_right = 0.5
+anchor_bottom = 0.5
+offset_left = -2.0
+offset_right = -2.0
+grow_horizontal = 2
+grow_vertical = 2
+size_flags_horizontal = 4
+size_flags_vertical = 4
+script = ExtResource("1_qygnb")
+outer_radius = 500
+inner_radius = 25
+line_width = 2
diff --git a/blunderbus.tscn b/blunderbus.tscn
index d607307..9b86cb0 100644
--- a/blunderbus.tscn
+++ b/blunderbus.tscn
@@ -1,7 +1,8 @@
-[gd_scene load_steps=45 format=4 uid="uid://dqwkal3t4gf2p"]
+[gd_scene load_steps=46 format=4 uid="uid://dqwkal3t4gf2p"]
[ext_resource type="Script" path="res://scripts/blunderbus.gd" id="1_w46uw"]
[ext_resource type="PackedScene" uid="uid://717hhehp83k8" path="res://assets/shotgun_pellet.tscn" id="2_544x3"]
+[ext_resource type="Texture2D" uid="uid://74id2drhfp2s" path="res://assets/Textures/ObjectTextures/Blunderbus.png" id="2_ejm2n"]
[ext_resource type="Texture2D" uid="uid://dkn4i5834sc62" path="res://assets/Models/blunderbus.stock.albedo2.png" id="3_4fcbv"]
[ext_resource type="Texture2D" uid="uid://cx4wjw3x8pa6q" path="res://assets/Models/blunderbus.metal.albedo.png" id="3_5l523"]
[ext_resource type="Texture2D" uid="uid://cgpmyiyweyvl0" path="res://assets/Models/blunderbus.stock.normal2.png" id="4_3js22"]
@@ -619,6 +620,7 @@ _data = {
[node name="Blunderbus" type="Node3D" node_paths=PackedStringArray("anim_player", "barrel_raycast", "audio_fire", "audio_empty", "audio_reload")]
script = ExtResource("1_w46uw")
gun_name = "Blunderbuss"
+gun_icon = ExtResource("2_ejm2n")
fire_mode = 1
fov_zoom_amt = 0.998
recoil_amount = Vector3(0.5, 0.2, 0.2)
diff --git a/gun_resource.tres b/gun_resource.tres
new file mode 100644
index 0000000..cff9f76
--- /dev/null
+++ b/gun_resource.tres
@@ -0,0 +1,7 @@
+[gd_resource type="Resource" load_steps=2 format=3 uid="uid://b6ilgwsha6j4e"]
+
+[ext_resource type="Texture2D" uid="uid://dlswdxm5aqfex" path="res://assets/Textures/ObjectTextures/revolver1.png" id="1_7ct5r"]
+
+[resource]
+metadata/name = "Revolver"
+metadata/texture = ExtResource("1_7ct5r")
diff --git a/project.godot b/project.godot
index c6b7b50..ceee5c4 100644
--- a/project.godot
+++ b/project.godot
@@ -35,7 +35,7 @@ version_control/autoload_on_startup=true
[editor_plugins]
-enabled=PackedStringArray("res://addons/proton_scatter/plugin.cfg")
+enabled=PackedStringArray("res://addons/anthonyec.camera_preview/plugin.cfg", "res://addons/proton_scatter/plugin.cfg")
[global_group]
@@ -192,11 +192,6 @@ numb_9={
"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":57,"key_label":0,"unicode":57,"location":0,"echo":false,"script":null)
]
}
-numb_0={
-"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":48,"key_label":0,"unicode":48,"location":0,"echo":false,"script":null)
-]
-}
crouch={
"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":4194326,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
@@ -209,6 +204,12 @@ kill_self={
"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":75,"key_label":0,"unicode":107,"location":0,"echo":false,"script":null)
]
}
+weapon_select={
+"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":4194306,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
+, 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":3,"canceled":false,"pressed":false,"double_click":false,"script":null)
+]
+}
[layer_names]
diff --git a/scripts/EnemyTarget.gd b/scripts/EnemyTarget.gd
index c1bb573..d4bc26e 100644
--- a/scripts/EnemyTarget.gd
+++ b/scripts/EnemyTarget.gd
@@ -1,6 +1,6 @@
extends Area3D
-@export var damage :=1
+@export var damage : float = 1
var damage_number = preload("res://assets/damage_number.tscn")
diff --git a/scripts/blunderbus.gd b/scripts/blunderbus.gd
index a15bfc0..533a985 100644
--- a/scripts/blunderbus.gd
+++ b/scripts/blunderbus.gd
@@ -8,6 +8,7 @@ var cycle_count
@export_group("Gun Feel")
@export var gun_name : String
+@export var gun_icon : Texture2D
@export_enum("Auto", "Single", "Burst") var fire_mode: int
@export var fov_zoom_amt = .98
@export var ads : bool = false
diff --git a/scripts/gun.gd b/scripts/gun.gd
index c68b33d..2ca4021 100644
--- a/scripts/gun.gd
+++ b/scripts/gun.gd
@@ -3,6 +3,7 @@ extends Node3D
@export_group("Gun Feel")
@export var gun_name : String
+@export var gun_icon : Texture2D
@export_enum("Auto", "Single", "Burst") var fire_mode: int
@export var hitscan_enabled : bool = false
@export var fov_zoom_amt = .98
diff --git a/scripts/player.gd b/scripts/player.gd
index c65475b..1a2cf19 100644
--- a/scripts/player.gd
+++ b/scripts/player.gd
@@ -141,6 +141,7 @@ var controlled_elsewhere = false
@onready var crouch_audio: AudioStreamPlayer3D = $Audio/Crouch
@onready var hud: Control = $Head/Recoil/Camera3D/HUD
@onready var clock_sound: AudioStreamPlayer = $Audio/ClockSound
+@onready var weapon_select_menu: Control = $Head/Recoil/Camera3D/WeaponSelect
func _ready():
@@ -383,7 +384,24 @@ func _physics_process(delta):
for i in range(10):
if Input.is_action_just_pressed("numb_" + str(i)):
- weapon_select((i - 1))
+ if gun != null:
+ if !gun.anim_player.is_playing():
+ if i-1 != level_control.current_gun_index and i <= level_control.held_guns.size():
+ weapon_select((i - 1))
+
+ if Input.is_action_just_pressed("weapon_select"):
+ weapon_select_menu.show()
+ Input.set_mouse_mode(Input.MOUSE_MODE_CONFINED_HIDDEN)
+ controlled_elsewhere = true
+ gamespeed_controlled = true
+ Engine.time_scale = .01
+ elif Input.is_action_just_released("weapon_select"):
+ var selection = weapon_select_menu.close()
+ if selection != null:
+ weapon_select(selection)
+ Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
+ controlled_elsewhere = false
+ gamespeed_controlled = false
#interact button
if Input.is_action_just_pressed("interact"):
@@ -539,13 +557,8 @@ func weapon_bob(vel : float, delta):
weapon_holder.position.x = lerp(weapon_holder.position.x, def_weapon_holder_pos.x, .1 * delta)
func weapon_select(gun_id):
- if gun != null:
- if !gun.anim_player.is_playing():
- if level_control.held_guns.size() >= (gun_id + 1) and level_control.current_gun_index != gun_id:
- gun.anim_player.play("swap_out")
- level_control.gun_spawn(gun_id)
- else:
- level_control.gun_spawn(gun_id)
+ gun.anim_player.play("swap_out")
+ level_control.gun_spawn(gun_id)
func enemy_hit():
var hitmarker_spawn = hitmarker.instantiate()
diff --git a/scripts/revolver_1.gd b/scripts/revolver_1.gd
index 7b04f8f..74455dd 100644
--- a/scripts/revolver_1.gd
+++ b/scripts/revolver_1.gd
@@ -8,6 +8,7 @@ var cycle_count
@export_group("Gun Feel")
@export var gun_name : String
+@export var gun_icon : Texture2D
@export_enum("Auto", "Single", "Burst") var fire_mode: int
@export var fov_zoom_amt = .98
@export var ads : bool = false
diff --git a/scripts/rocket_launcher.gd b/scripts/rocket_launcher.gd
index b748cdd..73cad5b 100644
--- a/scripts/rocket_launcher.gd
+++ b/scripts/rocket_launcher.gd
@@ -9,6 +9,7 @@ var ads = false
@export_group("Gun Feel")
@export var gun_name : String
+@export var gun_icon : Texture2D
@export_enum("Auto", "Single", "Burst") var fire_mode: int
@export var fov_zoom_amt = .98
@export var recoil_amount : Vector3 = Vector3(.2,0,0)
diff --git a/scripts/signal_bus.gd b/scripts/signal_bus.gd
index e9de7dd..1cf2de1 100644
--- a/scripts/signal_bus.gd
+++ b/scripts/signal_bus.gd
@@ -8,3 +8,4 @@ signal king_killed()
signal enemy_count_changed()
signal game_loaded()
signal player_hit()
+signal weapon_list_changed()
diff --git a/scripts/texture_catcher.gd b/scripts/texture_catcher.gd
new file mode 100644
index 0000000..b5bf03a
--- /dev/null
+++ b/scripts/texture_catcher.gd
@@ -0,0 +1,22 @@
+@tool
+extends Node3D
+
+@onready var sub_viewport: SubViewport = $CAPTURE/SubViewport
+@onready var snapshot_model: Node3D = $CAPTURE/SubViewport/snapshotModel
+
+@export var take_snapshot : bool = false
+
+# 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:
+ if take_snapshot:
+ var snapshot_name = snapshot_model.get_child(0).get_name()
+ await get_tree().create_timer(.5).timeout
+ var img = sub_viewport.get_viewport().get_texture().get_image()
+ var image_path = "assets/Textures/ObjectTextures/%s.png" % snapshot_name
+ img.save_png(image_path)
+ take_snapshot = false
diff --git a/scripts/trackerGun.gd b/scripts/trackerGun.gd
index f1e6581..04d6376 100644
--- a/scripts/trackerGun.gd
+++ b/scripts/trackerGun.gd
@@ -3,6 +3,7 @@ extends Node3D
@export_group("Gun Feel")
@export var gun_name : String
+@export var gun_icon : Texture2D
@export_enum("Auto", "Single", "Burst") var fire_mode: int
@export var hitscan_enabled : bool = false
@export var ads : bool = false
diff --git a/scripts/weapon_pickup.gd b/scripts/weapon_pickup.gd
index 1ebc78d..93ab988 100644
--- a/scripts/weapon_pickup.gd
+++ b/scripts/weapon_pickup.gd
@@ -32,6 +32,7 @@ func picked_up():
if level_control.player.gun != null:
level_control.player.gun.anim_player.play("swap_out")
level_control.gun_spawn(weapon_id)
+ SignalBus.emit_signal("weapon_list_changed")
queue_free()
func save():
diff --git a/scripts/weapon_select.gd b/scripts/weapon_select.gd
new file mode 100644
index 0000000..42ff4d3
--- /dev/null
+++ b/scripts/weapon_select.gd
@@ -0,0 +1,103 @@
+#@tool
+extends Control
+
+@export var outer_radius : int = 256
+@export var inner_radius : int = 256
+@export var line_width : int = 4
+
+var options = []
+
+@onready var level_control = get_tree().current_scene
+
+const bkg_color : Color = Color(1, 1, 1, .5)
+const line_color : Color = Color(1,1,1)
+const select_color = Color(1, 1, 1, .5)
+const IMAGE_SIZE = Vector2(512,512)
+
+
+var selection
+
+func close():
+ hide()
+ return selection
+
+# Called when the node enters the scene tree for the first time.
+func _ready() -> void:
+ update_weapon_list()
+ SignalBus.weapon_list_changed.connect(update_weapon_list)
+
+
+# Called every frame. 'delta' is the elapsed time since the previous frame.
+func _process(delta: float) -> void:
+
+ var mouse_pos = get_local_mouse_position()
+ var mouse_radius = mouse_pos.length()
+
+ if mouse_radius < inner_radius:
+ selection = null
+ else:
+ var mouse_rads = fposmod(mouse_pos.angle() * -1, TAU)
+ selection = ceil((mouse_rads / TAU) * (len(options)+1))
+
+
+
+ queue_redraw()
+
+func _draw():
+ var offset = IMAGE_SIZE/ -2
+
+ if selection == null:
+ draw_circle(Vector2.ZERO,inner_radius,select_color)
+
+
+ draw_circle(Vector2.ZERO, outer_radius, bkg_color)
+ draw_arc(Vector2.ZERO,inner_radius,0, TAU, 128,line_color,line_width,true)
+
+ if len(options) >= 1:
+
+ # draw separator lines
+ for i in range(len(options)):
+ var rads = TAU * i / (len(options))
+ var point = Vector2.from_angle(rads)
+ draw_line(
+ point * inner_radius,
+ point * outer_radius,
+ line_color,
+ line_width,
+ true
+ )
+
+ for i in range(0,len(options)):
+ var start_rads = (TAU * (i-1)) / (len(options))
+ var end_rads = (TAU * i) / (len(options))
+ var mid_rads = (start_rads + end_rads)/2.0 * -1
+ var radius_mid = (inner_radius + outer_radius) / 2
+
+ var draw_pos = radius_mid * Vector2.from_angle(mid_rads) + offset
+
+ var object = options[i].instantiate()
+ var texture = object.gun_icon
+
+ if selection == i:
+ var points_per_arc = 32
+ var points_inner = PackedVector2Array()
+ var points_outer = PackedVector2Array()
+
+ for j in range(points_per_arc + 1):
+ var angle = start_rads + j * (end_rads - start_rads) / points_per_arc
+ points_inner.append(inner_radius * Vector2.from_angle(TAU-angle))
+ points_outer.append(outer_radius * Vector2.from_angle(TAU-angle))
+
+ points_outer.reverse()
+ draw_polygon(
+ points_inner + points_outer,
+ PackedColorArray([select_color])
+ )
+
+ draw_texture(
+ texture,
+ Vector2(draw_pos)
+ )
+
+func update_weapon_list():
+ options = level_control.held_guns