From 575a1c49a3b8756981ccdb1103b89ffa67ee590c Mon Sep 17 00:00:00 2001 From: Reed Date: Mon, 12 Jan 2026 11:46:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4phantom=20camera=20=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=8F=8A=E5=85=B6=E9=99=84=E5=B1=9E=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _props/_prefabs/camera/camera_tool.gd | 25 - _props/_prefabs/camera/camera_tool.gd.uid | 1 - _props/_prefabs/camera/change_room_tween.tres | 9 - .../_prefabs/camera/scene_static_camera.tscn | 18 - _shared/camera/CameraSystem.gd | 1 - _shared/camera/PlayerStaticCamera.tscn | 119 - _shared/l_test.tscn | 8 - .../assets/PhantomCameraBtnPrimaryDefault.png | Bin 10296 -> 0 bytes .../PhantomCameraBtnPrimaryDefault.png.import | 40 - .../assets/PhantomCameraBtnPrimaryHover.png | Bin 9931 -> 0 bytes .../PhantomCameraBtnPrimaryHover.png.import | 40 - addons/phantom_camera/fonts/Nunito-Black.ttf | Bin 131568 -> 0 bytes .../fonts/Nunito-Black.ttf.import | 36 - .../phantom_camera/fonts/Nunito-Regular.ttf | Bin 131736 -> 0 bytes .../fonts/Nunito-Regular.ttf.import | 36 - .../icons/misc/PriorityOverride.svg | 14 - .../icons/misc/PriorityOverride.svg.import | 43 - .../icons/phantom_camera_2d.svg | 1 - .../icons/phantom_camera_2d.svg.import | 43 - .../icons/phantom_camera_3d.svg | 1 - .../icons/phantom_camera_3d.svg.import | 43 - .../phantom_camera_camera_3d_resource.svg | 3 - ...antom_camera_camera_3d_resource.svg.import | 43 - .../icons/phantom_camera_gizmo.svg | 1 - .../icons/phantom_camera_gizmo.svg.import | 44 - .../icons/phantom_camera_glow_logo.png | Bin 25499 -> 0 bytes .../icons/phantom_camera_glow_logo.png.import | 40 - .../icons/phantom_camera_host.svg | 1 - .../icons/phantom_camera_host.svg.import | 44 - .../icons/phantom_camera_logo.png | Bin 70905 -> 0 bytes .../icons/phantom_camera_logo.png.import | 40 - .../icons/phantom_camera_noise_emitter_2d.svg | 4 - ...phantom_camera_noise_emitter_2d.svg.import | 43 - .../icons/phantom_camera_noise_emitter_3d.svg | 4 - ...phantom_camera_noise_emitter_3d.svg.import | 44 - .../phantom_camera_noise_emitter_gizmo.svg | 4 - ...ntom_camera_noise_emitter_gizmo.svg.import | 44 - .../icons/phantom_camera_noise_resource.svg | 5 - .../phantom_camera_noise_resource.svg.import | 43 - .../icons/phantom_camera_tween.svg | 1 - .../icons/phantom_camera_tween.svg.import | 44 - .../phantom_camera_updater_panel_icon.svg | 3 - ...antom_camera_updater_panel_icon.svg.import | 43 - .../icons/viewfinder/Camera2DIcon.svg | 3 - .../icons/viewfinder/Camera2DIcon.svg.import | 43 - .../icons/viewfinder/Camera3DIcon.svg | 3 - .../icons/viewfinder/Camera3DIcon.svg.import | 43 - .../icons/viewfinder/SceneTypesIcon.svg | 4 - .../viewfinder/SceneTypesIcon.svg.import | 43 - .../icons/viewfinder/Select.svg | 3 - .../icons/viewfinder/Select.svg.import | 43 - addons/phantom_camera/icons/warning.svg | 4 - .../phantom_camera/icons/warning.svg.import | 43 - .../phantom_camera_inspector_plugin.gd | 46 - .../phantom_camera_inspector_plugin.gd.uid | 1 - addons/phantom_camera/panel/editor.gd.uid | 1 - addons/phantom_camera/panel/editor.tscn | 23 - .../panel/updater/download_update_panel.tscn | 253 -- .../panel/updater/update_button.tscn | 101 - .../panel/viewfinder/deadzone_style_box.tres | 14 - .../panel/viewfinder/host_list/host_list.tscn | 87 - .../viewfinder/host_list/host_list_item.tscn | 68 - .../host_list/host_list_item_group.tres | 3 - .../panel/viewfinder/viewfinder_panel.tscn | 563 ---- addons/phantom_camera/plugin.cfg | 7 - addons/phantom_camera/plugin.gd | 181 -- addons/phantom_camera/plugin.gd.uid | 1 - .../scripts/gizmos/phantom_camera_3d_gizmo.gd | 85 - .../gizmos/phantom_camera_3d_gizmo.gd.uid | 1 - .../gizmos/phantom_camera_3d_gizmo_plugin.gd | 37 - .../phantom_camera_3d_gizmo_plugin.gd.uid | 1 - ...om_camera_noise_emitter_gizmo_plugin_3d.gd | 29 - ...amera_noise_emitter_gizmo_plugin_3d.gd.uid | 1 - .../scripts/managers/PhantomCameraManager.cs | 36 - .../managers/PhantomCameraManager.cs.uid | 1 - .../managers/phantom_camera_manager.gd | 149 - .../managers/phantom_camera_manager.gd.uid | 1 - addons/phantom_camera/scripts/panel/editor.gd | 23 - .../scripts/panel/editor.gd.uid | 1 - .../panel/updater/download_update_panel.gd | 162 -- .../updater/download_update_panel.gd.uid | 1 - .../scripts/panel/updater/update_button.gd | 177 -- .../panel/updater/update_button.gd.uid | 1 - .../panel/updater/updater_constants.gd | 8 - .../panel/updater/updater_constants.gd.uid | 1 - .../scripts/panel/viewfinder/host_list.gd | 112 - .../scripts/panel/viewfinder/host_list.gd.uid | 1 - .../panel/viewfinder/host_list_item.gd | 58 - .../panel/viewfinder/host_list_item.gd.uid | 1 - .../scripts/panel/viewfinder/viewfinder.gd | 605 ---- .../panel/viewfinder/viewfinder.gd.uid | 1 - .../scripts/phantom_camera/PhantomCamera.cs | 234 -- .../phantom_camera/PhantomCamera.cs.uid | 1 - .../scripts/phantom_camera/PhantomCamera2D.cs | 326 --- .../phantom_camera/PhantomCamera2D.cs.uid | 1 - .../scripts/phantom_camera/PhantomCamera3D.cs | 482 ---- .../phantom_camera/PhantomCamera3D.cs.uid | 1 - .../PhantomCameraNoiseEmitter2D.cs | 83 - .../PhantomCameraNoiseEmitter2D.cs.uid | 1 - .../PhantomCameraNoiseEmitter3D.cs | 83 - .../PhantomCameraNoiseEmitter3D.cs.uid | 1 - .../phantom_camera/phantom_camera_2d.gd | 1718 ------------ .../phantom_camera/phantom_camera_2d.gd.uid | 1 - .../phantom_camera/phantom_camera_3d.gd | 2471 ----------------- .../phantom_camera/phantom_camera_3d.gd.uid | 1 - .../phantom_camera_constants.gd | 29 - .../phantom_camera_constants.gd.uid | 1 - .../phantom_camera_noise_emitter_2d.gd | 264 -- .../phantom_camera_noise_emitter_2d.gd.uid | 1 - .../phantom_camera_noise_emitter_3d.gd | 265 -- .../phantom_camera_noise_emitter_3d.gd.uid | 1 - .../phantom_camera_host/PhantomCameraHost.cs | 113 - .../PhantomCameraHost.cs.uid | 1 - .../phantom_camera_host.gd | 1418 ---------- .../phantom_camera_host.gd.uid | 1 - .../scripts/resources/Camera3DResource.cs | 116 - .../scripts/resources/Camera3DResource.cs.uid | 1 - .../scripts/resources/PhantomCameraNoise2D.cs | 103 - .../resources/PhantomCameraNoise2D.cs.uid | 1 - .../scripts/resources/PhantomCameraNoise3D.cs | 130 - .../resources/PhantomCameraNoise3D.cs.uid | 1 - .../scripts/resources/PhantomCameraTween.cs | 75 - .../resources/PhantomCameraTween.cs.uid | 1 - .../scripts/resources/camera_3d_resource.gd | 110 - .../resources/camera_3d_resource.gd.uid | 1 - .../resources/phantom_camera_noise_2d.gd | 228 -- .../resources/phantom_camera_noise_2d.gd.uid | 1 - .../resources/phantom_camera_noise_3d.gd | 301 -- .../resources/phantom_camera_noise_3d.gd.uid | 1 - .../scripts/resources/tween_resource.gd | 41 - .../scripts/resources/tween_resource.gd.uid | 1 - .../phantom_camera/themes/button_focus.tres | 17 - .../phantom_camera/themes/button_hover.tres | 13 - .../phantom_camera/themes/button_normal.tres | 17 - addons/phantom_camera/themes/theme.tres | 102 - project.godot | 3 +- 136 files changed, 1 insertion(+), 12811 deletions(-) delete mode 100644 _props/_prefabs/camera/camera_tool.gd delete mode 100644 _props/_prefabs/camera/camera_tool.gd.uid delete mode 100644 _props/_prefabs/camera/change_room_tween.tres delete mode 100644 _props/_prefabs/camera/scene_static_camera.tscn delete mode 100644 _shared/camera/PlayerStaticCamera.tscn delete mode 100644 _shared/l_test.tscn delete mode 100644 addons/phantom_camera/assets/PhantomCameraBtnPrimaryDefault.png delete mode 100644 addons/phantom_camera/assets/PhantomCameraBtnPrimaryDefault.png.import delete mode 100644 addons/phantom_camera/assets/PhantomCameraBtnPrimaryHover.png delete mode 100644 addons/phantom_camera/assets/PhantomCameraBtnPrimaryHover.png.import delete mode 100644 addons/phantom_camera/fonts/Nunito-Black.ttf delete mode 100644 addons/phantom_camera/fonts/Nunito-Black.ttf.import delete mode 100644 addons/phantom_camera/fonts/Nunito-Regular.ttf delete mode 100644 addons/phantom_camera/fonts/Nunito-Regular.ttf.import delete mode 100644 addons/phantom_camera/icons/misc/PriorityOverride.svg delete mode 100644 addons/phantom_camera/icons/misc/PriorityOverride.svg.import delete mode 100644 addons/phantom_camera/icons/phantom_camera_2d.svg delete mode 100644 addons/phantom_camera/icons/phantom_camera_2d.svg.import delete mode 100644 addons/phantom_camera/icons/phantom_camera_3d.svg delete mode 100644 addons/phantom_camera/icons/phantom_camera_3d.svg.import delete mode 100644 addons/phantom_camera/icons/phantom_camera_camera_3d_resource.svg delete mode 100644 addons/phantom_camera/icons/phantom_camera_camera_3d_resource.svg.import delete mode 100644 addons/phantom_camera/icons/phantom_camera_gizmo.svg delete mode 100644 addons/phantom_camera/icons/phantom_camera_gizmo.svg.import delete mode 100644 addons/phantom_camera/icons/phantom_camera_glow_logo.png delete mode 100644 addons/phantom_camera/icons/phantom_camera_glow_logo.png.import delete mode 100644 addons/phantom_camera/icons/phantom_camera_host.svg delete mode 100644 addons/phantom_camera/icons/phantom_camera_host.svg.import delete mode 100644 addons/phantom_camera/icons/phantom_camera_logo.png delete mode 100644 addons/phantom_camera/icons/phantom_camera_logo.png.import delete mode 100644 addons/phantom_camera/icons/phantom_camera_noise_emitter_2d.svg delete mode 100644 addons/phantom_camera/icons/phantom_camera_noise_emitter_2d.svg.import delete mode 100644 addons/phantom_camera/icons/phantom_camera_noise_emitter_3d.svg delete mode 100644 addons/phantom_camera/icons/phantom_camera_noise_emitter_3d.svg.import delete mode 100644 addons/phantom_camera/icons/phantom_camera_noise_emitter_gizmo.svg delete mode 100644 addons/phantom_camera/icons/phantom_camera_noise_emitter_gizmo.svg.import delete mode 100644 addons/phantom_camera/icons/phantom_camera_noise_resource.svg delete mode 100644 addons/phantom_camera/icons/phantom_camera_noise_resource.svg.import delete mode 100644 addons/phantom_camera/icons/phantom_camera_tween.svg delete mode 100644 addons/phantom_camera/icons/phantom_camera_tween.svg.import delete mode 100644 addons/phantom_camera/icons/phantom_camera_updater_panel_icon.svg delete mode 100644 addons/phantom_camera/icons/phantom_camera_updater_panel_icon.svg.import delete mode 100644 addons/phantom_camera/icons/viewfinder/Camera2DIcon.svg delete mode 100644 addons/phantom_camera/icons/viewfinder/Camera2DIcon.svg.import delete mode 100644 addons/phantom_camera/icons/viewfinder/Camera3DIcon.svg delete mode 100644 addons/phantom_camera/icons/viewfinder/Camera3DIcon.svg.import delete mode 100644 addons/phantom_camera/icons/viewfinder/SceneTypesIcon.svg delete mode 100644 addons/phantom_camera/icons/viewfinder/SceneTypesIcon.svg.import delete mode 100644 addons/phantom_camera/icons/viewfinder/Select.svg delete mode 100644 addons/phantom_camera/icons/viewfinder/Select.svg.import delete mode 100644 addons/phantom_camera/icons/warning.svg delete mode 100644 addons/phantom_camera/icons/warning.svg.import delete mode 100644 addons/phantom_camera/inspector/phantom_camera_inspector_plugin.gd delete mode 100644 addons/phantom_camera/inspector/phantom_camera_inspector_plugin.gd.uid delete mode 100644 addons/phantom_camera/panel/editor.gd.uid delete mode 100644 addons/phantom_camera/panel/editor.tscn delete mode 100644 addons/phantom_camera/panel/updater/download_update_panel.tscn delete mode 100644 addons/phantom_camera/panel/updater/update_button.tscn delete mode 100644 addons/phantom_camera/panel/viewfinder/deadzone_style_box.tres delete mode 100644 addons/phantom_camera/panel/viewfinder/host_list/host_list.tscn delete mode 100644 addons/phantom_camera/panel/viewfinder/host_list/host_list_item.tscn delete mode 100644 addons/phantom_camera/panel/viewfinder/host_list/host_list_item_group.tres delete mode 100644 addons/phantom_camera/panel/viewfinder/viewfinder_panel.tscn delete mode 100644 addons/phantom_camera/plugin.cfg delete mode 100644 addons/phantom_camera/plugin.gd delete mode 100644 addons/phantom_camera/plugin.gd.uid delete mode 100644 addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo.gd delete mode 100644 addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo.gd.uid delete mode 100644 addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo_plugin.gd delete mode 100644 addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo_plugin.gd.uid delete mode 100644 addons/phantom_camera/scripts/gizmos/phantom_camera_noise_emitter_gizmo_plugin_3d.gd delete mode 100644 addons/phantom_camera/scripts/gizmos/phantom_camera_noise_emitter_gizmo_plugin_3d.gd.uid delete mode 100644 addons/phantom_camera/scripts/managers/PhantomCameraManager.cs delete mode 100644 addons/phantom_camera/scripts/managers/PhantomCameraManager.cs.uid delete mode 100644 addons/phantom_camera/scripts/managers/phantom_camera_manager.gd delete mode 100644 addons/phantom_camera/scripts/managers/phantom_camera_manager.gd.uid delete mode 100644 addons/phantom_camera/scripts/panel/editor.gd delete mode 100644 addons/phantom_camera/scripts/panel/editor.gd.uid delete mode 100644 addons/phantom_camera/scripts/panel/updater/download_update_panel.gd delete mode 100644 addons/phantom_camera/scripts/panel/updater/download_update_panel.gd.uid delete mode 100644 addons/phantom_camera/scripts/panel/updater/update_button.gd delete mode 100644 addons/phantom_camera/scripts/panel/updater/update_button.gd.uid delete mode 100644 addons/phantom_camera/scripts/panel/updater/updater_constants.gd delete mode 100644 addons/phantom_camera/scripts/panel/updater/updater_constants.gd.uid delete mode 100644 addons/phantom_camera/scripts/panel/viewfinder/host_list.gd delete mode 100644 addons/phantom_camera/scripts/panel/viewfinder/host_list.gd.uid delete mode 100644 addons/phantom_camera/scripts/panel/viewfinder/host_list_item.gd delete mode 100644 addons/phantom_camera/scripts/panel/viewfinder/host_list_item.gd.uid delete mode 100644 addons/phantom_camera/scripts/panel/viewfinder/viewfinder.gd delete mode 100644 addons/phantom_camera/scripts/panel/viewfinder/viewfinder.gd.uid delete mode 100644 addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs delete mode 100644 addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs.uid delete mode 100644 addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs delete mode 100644 addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs.uid delete mode 100644 addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs delete mode 100644 addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs.uid delete mode 100644 addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter2D.cs delete mode 100644 addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter2D.cs.uid delete mode 100644 addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter3D.cs delete mode 100644 addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter3D.cs.uid delete mode 100644 addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd delete mode 100644 addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd.uid delete mode 100644 addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd delete mode 100644 addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd.uid delete mode 100644 addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd delete mode 100644 addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd.uid delete mode 100644 addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_2d.gd delete mode 100644 addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_2d.gd.uid delete mode 100644 addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_3d.gd delete mode 100644 addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_3d.gd.uid delete mode 100644 addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs delete mode 100644 addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs.uid delete mode 100644 addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd delete mode 100644 addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd.uid delete mode 100644 addons/phantom_camera/scripts/resources/Camera3DResource.cs delete mode 100644 addons/phantom_camera/scripts/resources/Camera3DResource.cs.uid delete mode 100644 addons/phantom_camera/scripts/resources/PhantomCameraNoise2D.cs delete mode 100644 addons/phantom_camera/scripts/resources/PhantomCameraNoise2D.cs.uid delete mode 100644 addons/phantom_camera/scripts/resources/PhantomCameraNoise3D.cs delete mode 100644 addons/phantom_camera/scripts/resources/PhantomCameraNoise3D.cs.uid delete mode 100644 addons/phantom_camera/scripts/resources/PhantomCameraTween.cs delete mode 100644 addons/phantom_camera/scripts/resources/PhantomCameraTween.cs.uid delete mode 100644 addons/phantom_camera/scripts/resources/camera_3d_resource.gd delete mode 100644 addons/phantom_camera/scripts/resources/camera_3d_resource.gd.uid delete mode 100644 addons/phantom_camera/scripts/resources/phantom_camera_noise_2d.gd delete mode 100644 addons/phantom_camera/scripts/resources/phantom_camera_noise_2d.gd.uid delete mode 100644 addons/phantom_camera/scripts/resources/phantom_camera_noise_3d.gd delete mode 100644 addons/phantom_camera/scripts/resources/phantom_camera_noise_3d.gd.uid delete mode 100644 addons/phantom_camera/scripts/resources/tween_resource.gd delete mode 100644 addons/phantom_camera/scripts/resources/tween_resource.gd.uid delete mode 100644 addons/phantom_camera/themes/button_focus.tres delete mode 100644 addons/phantom_camera/themes/button_hover.tres delete mode 100644 addons/phantom_camera/themes/button_normal.tres delete mode 100644 addons/phantom_camera/themes/theme.tres diff --git a/_props/_prefabs/camera/camera_tool.gd b/_props/_prefabs/camera/camera_tool.gd deleted file mode 100644 index d14cf69..0000000 --- a/_props/_prefabs/camera/camera_tool.gd +++ /dev/null @@ -1,25 +0,0 @@ -extends Node - -@export var binded_camera: PhantomCamera2D - -var _binded_cam: PhantomCamera2D - -func _ready() -> void: - _binded_cam = binded_camera - if not _binded_cam: - _binded_cam = get_parent() as PhantomCamera2D - - if not _binded_cam: - printerr("[CameraTool]: No Vaild Camera Founded") - -## 将自己的priority设置为最高 -func hold_self_priority() -> void: - var pcs = PhantomCameraManager.get_phantom_camera_2ds() - for pc in pcs: - pc.priority = -1 - - _binded_cam.priority = 100 - -## 将自己的priority设置为不可用 -func release_self_priority() -> void: - _binded_cam.priority = -1 diff --git a/_props/_prefabs/camera/camera_tool.gd.uid b/_props/_prefabs/camera/camera_tool.gd.uid deleted file mode 100644 index 1e4dd4c..0000000 --- a/_props/_prefabs/camera/camera_tool.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ce7d2tpunfaxe diff --git a/_props/_prefabs/camera/change_room_tween.tres b/_props/_prefabs/camera/change_room_tween.tres deleted file mode 100644 index 4eaee96..0000000 --- a/_props/_prefabs/camera/change_room_tween.tres +++ /dev/null @@ -1,9 +0,0 @@ -[gd_resource type="Resource" script_class="PhantomCameraTween" load_steps=2 format=3 uid="uid://bm0iyvgqfrqoj"] - -[ext_resource type="Script" uid="uid://8umksf8e80fw" path="res://addons/phantom_camera/scripts/resources/tween_resource.gd" id="1_j0gyv"] - -[resource] -script = ExtResource("1_j0gyv") -duration = 0.6 -transition = 7 -metadata/_custom_type_script = "uid://8umksf8e80fw" diff --git a/_props/_prefabs/camera/scene_static_camera.tscn b/_props/_prefabs/camera/scene_static_camera.tscn deleted file mode 100644 index 90062c3..0000000 --- a/_props/_prefabs/camera/scene_static_camera.tscn +++ /dev/null @@ -1,18 +0,0 @@ -[gd_scene load_steps=5 format=3 uid="uid://b0xmcb5i4jey"] - -[ext_resource type="Script" uid="uid://bhexx6mj1xv3q" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd" id="1_p2s6f"] -[ext_resource type="Script" uid="uid://8umksf8e80fw" path="res://addons/phantom_camera/scripts/resources/tween_resource.gd" id="2_77rrp"] -[ext_resource type="Script" uid="uid://ce7d2tpunfaxe" path="res://_props/_prefabs/camera/camera_tool.gd" id="3_77rrp"] - -[sub_resource type="Resource" id="Resource_gofl0"] -script = ExtResource("2_77rrp") - -[node name="SceneStaticCamera" type="Node2D"] -script = ExtResource("1_p2s6f") -tween_resource = SubResource("Resource_gofl0") -draw_limits = true -metadata/_custom_type_script = "uid://bhexx6mj1xv3q" - -[node name="Tool" type="Node" parent="." node_paths=PackedStringArray("binded_camera")] -script = ExtResource("3_77rrp") -binded_camera = NodePath("..") diff --git a/_shared/camera/CameraSystem.gd b/_shared/camera/CameraSystem.gd index 6652f9e..def8f48 100644 --- a/_shared/camera/CameraSystem.gd +++ b/_shared/camera/CameraSystem.gd @@ -19,7 +19,6 @@ var _switch_scheduled := false var _dirty := false ## 玩家关卡内静态相机 -const PLAYER_CAMERA_SCENE:= preload("res://_shared/camera/PlayerStaticCamera.tscn") const CAMERA_FOLLOWER:= preload("res://_shared/camera/camera_follower.tscn") func _ready() -> void: diff --git a/_shared/camera/PlayerStaticCamera.tscn b/_shared/camera/PlayerStaticCamera.tscn deleted file mode 100644 index 6973820..0000000 --- a/_shared/camera/PlayerStaticCamera.tscn +++ /dev/null @@ -1,119 +0,0 @@ -[gd_scene load_steps=11 format=3 uid="uid://d1w8ftfhxycfy"] - -[ext_resource type="Script" uid="uid://bhexx6mj1xv3q" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd" id="1_llsih"] -[ext_resource type="Script" uid="uid://bhd4nuiu23e7l" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_2d.gd" id="3_0bl5s"] -[ext_resource type="Script" uid="uid://8umksf8e80fw" path="res://addons/phantom_camera/scripts/resources/tween_resource.gd" id="3_fixiw"] -[ext_resource type="Script" uid="uid://dimvdouy8g0sv" path="res://addons/phantom_camera/scripts/resources/phantom_camera_noise_2d.gd" id="6_ctkin"] - -[sub_resource type="GDScript" id="GDScript_fixiw"] -script/source = "class_name GlobalCamera extends Node2D - -@onready var phantom_camera_2d: PhantomCamera2D = %PhantomCamera2D - -@onready var _0_hook_touch_noise: PhantomCameraNoiseEmitter2D = %\"0_Hook_Touch_Noise\" -@onready var _45_hook_touch_noise: PhantomCameraNoiseEmitter2D = %\"45_Hook_Touch_Noise\" -@onready var _90_hook_touch_noise: PhantomCameraNoiseEmitter2D = %\"90_Hook_Touch_Noise\" -@onready var _135_hook_touch_noise: PhantomCameraNoiseEmitter2D = %\"135_Hook_Touch_Noise\" - - -func emit_camera_shock(noise: PhantomCameraNoise2D) -> void: - if not noise: - return - -func emit_hook_touch_shock(dir : int) -> void: - match dir: - 0: - _0_hook_touch_noise.emit() - 1: - _45_hook_touch_noise.emit() - 2: - _90_hook_touch_noise.emit() - 3: - _135_hook_touch_noise.emit() -" - -[sub_resource type="Resource" id="Resource_pvk7k"] -script = ExtResource("3_fixiw") - -[sub_resource type="Resource" id="Resource_jjeqj"] -script = ExtResource("6_ctkin") -amplitude = 5.0 -frequency = 18.0 -randomize_noise_seed = 1 -noise_seed = 1000 -positional_multiplier_x = 0.0 -positional_multiplier_y = 2.0 -metadata/_custom_type_script = "uid://dimvdouy8g0sv" - -[sub_resource type="Resource" id="Resource_ctkin"] -script = ExtResource("6_ctkin") -amplitude = 5.0 -frequency = 18.0 -randomize_noise_seed = 1 -metadata/_custom_type_script = "uid://dimvdouy8g0sv" - -[sub_resource type="Resource" id="Resource_wjs5j"] -script = ExtResource("6_ctkin") -amplitude = 5.0 -frequency = 18.0 -randomize_noise_seed = 1 -positional_multiplier_x = 2.0 -positional_multiplier_y = 0.0 -metadata/_custom_type_script = "uid://dimvdouy8g0sv" - -[sub_resource type="Resource" id="Resource_ompnq"] -script = ExtResource("6_ctkin") -amplitude = 5.0 -frequency = 18.0 -randomize_noise_seed = 1 -metadata/_custom_type_script = "uid://dimvdouy8g0sv" - -[node name="PlayerStaticCamera" type="Node2D"] -script = SubResource("GDScript_fixiw") - -[node name="PhantomCamera2D" type="Node2D" parent="."] -unique_name_in_owner = true -script = ExtResource("1_llsih") -priority = 1000 -zoom = Vector2(0.8, 0.8) -tween_resource = SubResource("Resource_pvk7k") -tween_on_load = false -draw_limits = true -noise_emitter_layer = 1 -metadata/_custom_type_script = "uid://bhexx6mj1xv3q" - -[node name="0_Hook_Touch_Noise" type="Node2D" parent="PhantomCamera2D"] -unique_name_in_owner = true -script = ExtResource("3_0bl5s") -noise = SubResource("Resource_jjeqj") -growth_time = 0.04095982 -duration = 0.16 -decay_time = 0.10085586 -metadata/_custom_type_script = "uid://bhd4nuiu23e7l" - -[node name="45_Hook_Touch_Noise" type="Node2D" parent="PhantomCamera2D"] -unique_name_in_owner = true -script = ExtResource("3_0bl5s") -noise = SubResource("Resource_ctkin") -growth_time = 0.04096021 -duration = 0.16 -decay_time = 0.10085552 -metadata/_custom_type_script = "uid://bhd4nuiu23e7l" - -[node name="90_Hook_Touch_Noise" type="Node2D" parent="PhantomCamera2D"] -unique_name_in_owner = true -script = ExtResource("3_0bl5s") -noise = SubResource("Resource_wjs5j") -growth_time = 0.0409602 -duration = 0.16 -decay_time = 0.10085564 -metadata/_custom_type_script = "uid://bhd4nuiu23e7l" - -[node name="135_Hook_Touch_Noise" type="Node2D" parent="PhantomCamera2D"] -unique_name_in_owner = true -script = ExtResource("3_0bl5s") -noise = SubResource("Resource_ompnq") -growth_time = 0.040960066 -duration = 0.16 -decay_time = 0.100856625 -metadata/_custom_type_script = "uid://bhd4nuiu23e7l" diff --git a/_shared/l_test.tscn b/_shared/l_test.tscn deleted file mode 100644 index 680bd44..0000000 --- a/_shared/l_test.tscn +++ /dev/null @@ -1,8 +0,0 @@ -[gd_scene load_steps=2 format=4 uid="uid://bomv75fi4uuyi"] - -[ext_resource type="TileSet" uid="uid://doepkfp83k0lb" path="res://_tileset/test.tres" id="1_vei0o"] - -[node name="TileMapLayer" type="TileMapLayer"] -texture_filter = 1 -tile_map_data = PackedByteArray("AAAAAA0AAAAAAAAAAAABAA0AAAAAAAAAAAACAA0AAAAAAAAAAAADAA0AAAAAAAAAAAAEAA0AAAAAAAAAAAAFAA0AAAAAAAAAAAAGAA0AAAAAAAAAAAAHAA0AAAAAAAAAAAAIAA0AAAAAAAAAAAAJAA0AAAAAAAAAAAAKAA0AAAAAAAAAAAALAA0AAAAAAAAAAAAMAA0AAAAAAAAAAAANAA0AAAAAAAAAAAAOAA0AAAAAAAAAAAAPAA0AAAAAAAAAAAAQAA0AAAAAAAAAAAARAA0AAAAAAAAAAAASAA0AAAAAAAAAAAATAA0AAAAAAAAAAAAUAA0AAAAAAAAAAAAVAA0AAAAAAAAAAAAWAA0AAAAAAAAAAAAXAA0AAAAAAAAAAAAYAA0AAAAAAAAAAAAZAA0AAAAAAAAAAAAaAA0AAAAAAAAAAAAbAA0AAAAAAAAAAAAcAA0AAAAAAAAAAAAdAA0AAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAQAAAAAAAAAAAABAAQAAAAAAAAAAAABAAUAAAAAAAAAAAABAAYAAAAAAAAAAAACAAYAAAAAAAAAAAACAAcAAAAAAAAAAAACAAgAAAAAAAAAAAACAAkAAAAAAAAAAAACAAoAAAAAAAAAAAADAAoAAAAAAAAAAAADAAsAAAAAAAAAAAACAAsAAAAAAAAAAAACAAwAAAAAAAAAAAADAAwAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAcAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAkAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAsAAAAAAAAAAAAAAAwAAAAAAAAAAAABAAcAAAAAAAAAAAABAAgAAAAAAAAAAAABAAkAAAAAAAAAAAABAAoAAAAAAAAAAAABAAsAAAAAAAAAAAABAAwAAAAAAAAAAAAPAAoAAAAAAAAAAAAPAAsAAAAAAAAAAAAQAAoAAAAAAAAAAAAQAAsAAAAAAAAAAAATAAgAAAAAAAAAAAATAAkAAAAAAAAAAAAUAAgAAAAAAAAAAAAUAAkAAAAAAAAAAAAVAAgAAAAAAAAAAAAVAAkAAAAAAAAAAAAYAAUAAAAAAAAAAAAYAAYAAAAAAAAAAAAYAAcAAAAAAAAAAAAYAAsAAAAAAAAAAAAYAAwAAAAAAAAAAAAZAAUAAAAAAAAAAAAZAAYAAAAAAAAAAAAZAAcAAAAAAAAAAAAaAAsAAAAAAAAAAAAaAAwAAAAAAAAAAAAZAAwAAAAAAAAAAAAZAAsAAAAAAAAAAAAaAAUAAAAAAAAAAAAaAAYAAAAAAAAAAAAaAAcAAAAAAAAAAAADAAYAAAAAAAAAAAADAAcAAAAAAAAAAAADAAgAAAAAAAAAAAADAAkAAAAAAAAAAAABAAMAAAAAAAAAAAACAAMAAAAAAAAAAAACAAQAAAAAAAAAAAACAAUAAAAAAAAAAAADAAMAAAAAAAAAAAADAAQAAAAAAAAAAAADAAUAAAAAAAAAAAAAAA4AAAAAAAAAAAABAA4AAAAAAAAAAAACAA4AAAAAAAAAAAADAA4AAAAAAAAAAAAEAA4AAAAAAAAAAAAFAA4AAAAAAAAAAAAGAA4AAAAAAAAAAAAHAA4AAAAAAAAAAAAIAA4AAAAAAAAAAAAJAA4AAAAAAAAAAAAKAA4AAAAAAAAAAAALAA4AAAAAAAAAAAAMAA4AAAAAAAAAAAANAA4AAAAAAAAAAAAOAA4AAAAAAAAAAAAPAA4AAAAAAAAAAAAbAA4AAAAAAAAAAAAcAA4AAAAAAAAAAAAdAA4AAAAAAAAAAAAaAA4AAAAAAAAAAAAZAA4AAAAAAAAAAAAYAA4AAAAAAAAAAAAXAA4AAAAAAAAAAAAWAA4AAAAAAAAAAAAVAA4AAAAAAAAAAAAUAA4AAAAAAAAAAAATAA4AAAAAAAAAAAASAA4AAAAAAAAAAAARAA4AAAAAAAAAAAAQAA4AAAAAAAAAAAAPAA8AAAAAAAAAAAAOAA8AAAAAAAAAAAANAA8AAAAAAAAAAAAMAA8AAAAAAAAAAAALAA8AAAAAAAAAAAAKAA8AAAAAAAAAAAAJAA8AAAAAAAAAAAAIAA8AAAAAAAAAAAAHAA8AAAAAAAAAAAAGAA8AAAAAAAAAAAAFAA8AAAAAAAAAAAAEAA8AAAAAAAAAAAAEABAAAAAAAAAAAAADABAAAAAAAAAAAAACABAAAAAAAAAAAAABABAAAAAAAAAAAAABAA8AAAAAAAAAAAAAAA8AAAAAAAAAAAAAABAAAAAAAAAAAAAFABAAAAAAAAAAAAAGABAAAAAAAAAAAAAHABAAAAAAAAAAAAAIABAAAAAAAAAAAAAJABAAAAAAAAAAAAAKABAAAAAAAAAAAAALABAAAAAAAAAAAAAMABAAAAAAAAAAAAANABAAAAAAAAAAAAAOABAAAAAAAAAAAAAPABAAAAAAAAAAAAAQABAAAAAAAAAAAAARABAAAAAAAAAAAAASABAAAAAAAAAAAAATABAAAAAAAAAAAAAUABAAAAAAAAAAAAAVABAAAAAAAAAAAAAWABAAAAAAAAAAAAAXABAAAAAAAAAAAAAYABAAAAAAAAAAAAAZABAAAAAAAAAAAAAaABAAAAAAAAAAAAAbABAAAAAAAAAAAAAcABAAAAAAAAAAAAAdABAAAAAAAAAAAAAeABAAAAAAAAAAAAAeAA8AAAAAAAAAAAAdAA8AAAAAAAAAAAAcAA8AAAAAAAAAAAAbAA8AAAAAAAAAAAAaAA8AAAAAAAAAAAAZAA8AAAAAAAAAAAAYAA8AAAAAAAAAAAAXAA8AAAAAAAAAAAAWAA8AAAAAAAAAAAAVAA8AAAAAAAAAAAAUAA8AAAAAAAAAAAATAA8AAAAAAAAAAAASAA8AAAAAAAAAAAARAA8AAAAAAAAAAAADAA8AAAAAAAAAAAACAA8AAAAAAAAAAAAQAA8AAAAAAAAAAAD//wMAAAAAAAAAAAD//wQAAAAAAAAAAAD//wUAAAAAAAAAAAD//wYAAAAAAAAAAAD//wcAAAAAAAAAAAD//wgAAAAAAAAAAAD//wkAAAAAAAAAAAD//woAAAAAAAAAAAD//wsAAAAAAAAAAAD//wwAAAAAAAAAAAD//w0AAAAAAAAAAAD//w4AAAAAAAAAAAD//w8AAAAAAAAAAAD//xAAAAAAAAAAAAD+/wQAAAAAAAAAAAD+/wUAAAAAAAAAAAD+/wYAAAAAAAAAAAD+/wcAAAAAAAAAAAD+/wgAAAAAAAAAAAD+/wkAAAAAAAAAAAD+/woAAAAAAAAAAAD+/wsAAAAAAAAAAAD+/wwAAAAAAAAAAAD+/w0AAAAAAAAAAAD+/w4AAAAAAAAAAAD+/w8AAAAAAAAAAAD+/xAAAAAAAAAAAAD+/wMAAAAAAAAAAAD+/wIAAAAAAAAAAAD//wIAAAAAAAAAAAAAAAIAAAAAAAAAAAABAAIAAAAAAAAAAAACAAIAAAAAAAAAAAADAAIAAAAAAAAAAAD+/wEAAAAAAAAAAAD//wEAAAAAAAAAAAAAAAEAAAAAAAAAAAABAAEAAAAAAAAAAAACAAEAAAAAAAAAAAADAAEAAAAAAAAAAAD+/wAAAAAAAAAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAACAAAAAAAAAAAAAAADAAAAAAAAAAAAAAD+////AAAAAAAAAAD/////AAAAAAAAAAAAAP//AAAAAAAAAAABAP//AAAAAAAAAAACAP//AAAAAAAAAAADAP//AAAAAAAAAAD+//7/AAAAAAAAAAD///7/AAAAAAAAAAAAAP7/AAAAAAAAAAABAP7/AAAAAAAAAAACAP7/AAAAAAAAAAADAP7/AAAAAAAAAAAeAA0AAAAAAAAAAAAeAA4AAAAAAAAAAAAfAA0AAAAAAAAAAAAfAA4AAAAAAAAAAAAfAA8AAAAAAAAAAAAfABAAAAAAAAAAAAAgAA0AAAAAAAAAAAAgAA4AAAAAAAAAAAAgAA8AAAAAAAAAAAAgABAAAAAAAAAAAAAhAA0AAAAAAAAAAAAhAA4AAAAAAAAAAAAhAA8AAAAAAAAAAAAhABAAAAAAAAAAAAAiAA0AAAAAAAAAAAAiAA4AAAAAAAAAAAAiAA8AAAAAAAAAAAAiABAAAAAAAAAAAAAjAA0AAAAAAAAAAAAjAA4AAAAAAAAAAAAjAA8AAAAAAAAAAAAjABAAAAAAAAAAAAAkAA0AAAAAAAAAAAAkAA4AAAAAAAAAAAAkAA8AAAAAAAAAAAAkABAAAAAAAAAAAAAlAA0AAAAAAAAAAAAlAA4AAAAAAAAAAAAlAA8AAAAAAAAAAAAlABAAAAAAAAAAAAAmAA0AAAAAAAAAAAAmAA4AAAAAAAAAAAAmAA8AAAAAAAAAAAAmABAAAAAAAAAAAAAnAA0AAAAAAAAAAAAnAA4AAAAAAAAAAAAnAA8AAAAAAAAAAAAnABAAAAAAAAAAAAAoAA0AAAAAAAAAAAAoAA4AAAAAAAAAAAAoAA8AAAAAAAAAAAAoABAAAAAAAAAAAAApAA0AAAAAAAAAAAApAA4AAAAAAAAAAAApAA8AAAAAAAAAAAApABAAAAAAAAAAAAAqAA0AAAAAAAAAAAAqAA4AAAAAAAAAAAAqAA8AAAAAAAAAAAAqABAAAAAAAAAAAAArAA0AAAAAAAAAAAArAA4AAAAAAAAAAAArAA8AAAAAAAAAAAArABAAAAAAAAAAAAAsAA0AAAAAAAAAAAAsAA4AAAAAAAAAAAAsAA8AAAAAAAAAAAAsABAAAAAAAAAAAAAtAA0AAAAAAAAAAAAtAA4AAAAAAAAAAAAtAA8AAAAAAAAAAAAtABAAAAAAAAAAAAAuAA0AAAAAAAAAAAAuAA4AAAAAAAAAAAAuAA8AAAAAAAAAAAAuABAAAAAAAAAAAAAvAA0AAAAAAAAAAAAvAA4AAAAAAAAAAAAvAA8AAAAAAAAAAAAvABAAAAAAAAAAAAAwAA0AAAAAAAAAAAAwAA4AAAAAAAAAAAAwAA8AAAAAAAAAAAAwABAAAAAAAAAAAAAxAA0AAAAAAAAAAAAxAA4AAAAAAAAAAAAxAA8AAAAAAAAAAAAxABAAAAAAAAAAAAAyAA0AAAAAAAAAAAAyAA4AAAAAAAAAAAAyAA8AAAAAAAAAAAAyABAAAAAAAAAAAAAzAA0AAAAAAAAAAAAzAA4AAAAAAAAAAAAzAA8AAAAAAAAAAAAzABAAAAAAAAAAAAA0AA0AAAAAAAAAAAA0AA4AAAAAAAAAAAA0AA8AAAAAAAAAAAA0ABAAAAAAAAAAAAA1AA0AAAAAAAAAAAA1AA4AAAAAAAAAAAA1AA8AAAAAAAAAAAA1ABAAAAAAAAAAAAA2AA0AAAAAAAAAAAA2AA4AAAAAAAAAAAA2AA8AAAAAAAAAAAA2ABAAAAAAAAAAAAA3AA0AAAAAAAAAAAA3AA4AAAAAAAAAAAA3AA8AAAAAAAAAAAA3ABAAAAAAAAAAAAA4AA0AAAAAAAAAAAA4AA4AAAAAAAAAAAA4AA8AAAAAAAAAAAA4ABAAAAAAAAAAAAA5AA0AAAAAAAAAAAA5AA4AAAAAAAAAAAA5AA8AAAAAAAAAAAA5ABAAAAAAAAAAAAA6AA0AAAAAAAAAAAA6AA4AAAAAAAAAAAA6AA8AAAAAAAAAAAA6ABAAAAAAAAAAAAA7AA0AAAAAAAAAAAA7AA4AAAAAAAAAAAA7AA8AAAAAAAAAAAA7ABAAAAAAAAAAAAA8AA0AAAAAAAAAAAA8AA4AAAAAAAAAAAA8AA8AAAAAAAAAAAA8ABAAAAAAAAAAAAA9AA0AAAAAAAAAAAA9AA4AAAAAAAAAAAA9AA8AAAAAAAAAAAA9ABAAAAAAAAAAAAA+AA0AAAAAAAAAAAA+AA4AAAAAAAAAAAA+AA8AAAAAAAAAAAA+ABAAAAAAAAAAAAA/AA0AAAAAAAAAAAA/AA4AAAAAAAAAAAA/AA8AAAAAAAAAAAA/ABAAAAAAAAAAAABAAA0AAAAAAAAAAABAAA4AAAAAAAAAAABAAA8AAAAAAAAAAABAABAAAAAAAAAAAABBAA0AAAAAAAAAAABBAA4AAAAAAAAAAABBAA8AAAAAAAAAAABBABAAAAAAAAAAAABCAA0AAAAAAAAAAABCAA4AAAAAAAAAAABCAA8AAAAAAAAAAABCABAAAAAAAAAAAABDAA0AAAAAAAAAAABDAA4AAAAAAAAAAABDAA8AAAAAAAAAAABDABAAAAAAAAAAAABEAA0AAAAAAAAAAABEAA4AAAAAAAAAAABEAA8AAAAAAAAAAABEABAAAAAAAAAAAABFAA0AAAAAAAAAAABFAA4AAAAAAAAAAABFAA8AAAAAAAAAAABFABAAAAAAAAAAAABGAA0AAAAAAAAAAABGAA4AAAAAAAAAAABGAA8AAAAAAAAAAABGABAAAAAAAAAAAABHAA0AAAAAAAAAAABHAA4AAAAAAAAAAABHAA8AAAAAAAAAAABHABAAAAAAAAAAAABIAA0AAAAAAAAAAABIAA4AAAAAAAAAAABIAA8AAAAAAAAAAABIABAAAAAAAAAAAABJAA0AAAAAAAAAAABJAA4AAAAAAAAAAABJAA8AAAAAAAAAAABJABAAAAAAAAAAAABKAA0AAAAAAAAAAABKAA4AAAAAAAAAAABKAA8AAAAAAAAAAABKABAAAAAAAAAAAABLAA0AAAAAAAAAAABLAA4AAAAAAAAAAABLAA8AAAAAAAAAAABLABAAAAAAAAAAAABMAA0AAAAAAAAAAABMAA4AAAAAAAAAAABMAA8AAAAAAAAAAABMABAAAAAAAAAAAABNAA0AAAAAAAAAAABNAA4AAAAAAAAAAABNAA8AAAAAAAAAAABNABAAAAAAAAAAAABOAA0AAAAAAAAAAABOAA4AAAAAAAAAAABOAA8AAAAAAAAAAABOABAAAAAAAAAAAABPAA0AAAAAAAAAAABPAA4AAAAAAAAAAABPAA8AAAAAAAAAAABPABAAAAAAAAAAAABQAA0AAAAAAAAAAABQAA4AAAAAAAAAAABQAA8AAAAAAAAAAABQABAAAAAAAAAAAABRAA0AAAAAAAAAAABRAA4AAAAAAAAAAABRAA8AAAAAAAAAAABRABAAAAAAAAAAAABSAA0AAAAAAAAAAABSAA4AAAAAAAAAAABSAA8AAAAAAAAAAABSABAAAAAAAAAAAABTAA0AAAAAAAAAAABTAA4AAAAAAAAAAABTAA8AAAAAAAAAAABTABAAAAAAAAAAAAAhAAUAAAAAAAAAAAAhAAYAAAAAAAAAAAAiAAUAAAAAAAAAAAAiAAYAAAAAAAAAAAAjAAUAAAAAAAAAAAAjAAYAAAAAAAAAAAAkAAUAAAAAAAAAAAAkAAYAAAAAAAAAAAAlAAUAAAAAAAAAAAAlAAYAAAAAAAAAAAAmAAUAAAAAAAAAAAAmAAYAAAAAAAAAAAAnAAUAAAAAAAAAAAAnAAYAAAAAAAAAAAAoAAUAAAAAAAAAAAAoAAYAAAAAAAAAAAApAAUAAAAAAAAAAAApAAYAAAAAAAAAAAAqAAUAAAAAAAAAAAAqAAYAAAAAAAAAAAArAAUAAAAAAAAAAAArAAYAAAAAAAAAAAAlAAsAAAAAAAAAAAAlAAwAAAAAAAAAAAAmAAsAAAAAAAAAAAAmAAwAAAAAAAAAAAAnAAsAAAAAAAAAAAAnAAwAAAAAAAAAAAA0AAsAAAAAAAAAAAA0AAwAAAAAAAAAAAA1AAsAAAAAAAAAAAA1AAwAAAAAAAAAAAA2AAsAAAAAAAAAAAA2AAwAAAAAAAAAAAA3AAsAAAAAAAAAAAA3AAwAAAAAAAAAAAA0AAAAAAAAAAAAAAA0AAEAAAAAAAAAAAA0AAIAAAAAAAAAAAA0AAMAAAAAAAAAAAA0AAQAAAAAAAAAAAA0AAUAAAAAAAAAAAA1AAAAAAAAAAAAAAA1AAEAAAAAAAAAAAA1AAIAAAAAAAAAAAA1AAMAAAAAAAAAAAA1AAQAAAAAAAAAAAA1AAUAAAAAAAAAAAA2AAAAAAAAAAAAAAA2AAEAAAAAAAAAAAA2AAIAAAAAAAAAAAA2AAMAAAAAAAAAAAA2AAQAAAAAAAAAAAA2AAUAAAAAAAAAAAA3AAAAAAAAAAAAAAA3AAEAAAAAAAAAAAA3AAIAAAAAAAAAAAA3AAMAAAAAAAAAAAA3AAQAAAAAAAAAAAA3AAUAAAAAAAAAAAAsAP7/AAAAAAAAAAAsAP//AAAAAAAAAAAtAP7/AAAAAAAAAAAtAP//AAAAAAAAAAAuAP7/AAAAAAAAAAAuAP//AAAAAAAAAAAvAP7/AAAAAAAAAAAvAP//AAAAAAAAAAAwAP7/AAAAAAAAAAAwAP//AAAAAAAAAAAxAP7/AAAAAAAAAAAxAP//AAAAAAAAAAAiAPv/AAAAAAAAAAAiAPz/AAAAAAAAAAAjAPv/AAAAAAAAAAAjAPz/AAAAAAAAAAAkAPv/AAAAAAAAAAAkAPz/AAAAAAAAAAAlAPv/AAAAAAAAAAAlAPz/AAAAAAAAAAAmAPv/AAAAAAAAAAAmAPz/AAAAAAAAAAAnAPv/AAAAAAAAAAAnAPz/AAAAAAAAAAAoAPv/AAAAAAAAAAAoAPz/AAAAAAAAAAApAPv/AAAAAAAAAAApAPz/AAAAAAAAAAAZAPv/AAAAAAAAAAAZAPz/AAAAAAAAAAAZAP3/AAAAAAAAAAAZAP7/AAAAAAAAAAAaAPv/AAAAAAAAAAAaAPz/AAAAAAAAAAAaAP3/AAAAAAAAAAAaAP7/AAAAAAAAAAAbAPv/AAAAAAAAAAAbAPz/AAAAAAAAAAAbAP3/AAAAAAAAAAAbAP7/AAAAAAAAAAAcAPv/AAAAAAAAAAAcAPz/AAAAAAAAAAAcAP3/AAAAAAAAAAAcAP7/AAAAAAAAAAAdAPv/AAAAAAAAAAAdAPz/AAAAAAAAAAAdAP3/AAAAAAAAAAAdAP7/AAAAAAAAAAAeAPv/AAAAAAAAAAAeAPz/AAAAAAAAAAAeAP3/AAAAAAAAAAAeAP7/AAAAAAAAAAAOAPz/AAAAAAAAAAAOAP3/AAAAAAAAAAAOAP7/AAAAAAAAAAAOAP//AAAAAAAAAAAPAPz/AAAAAAAAAAAPAP3/AAAAAAAAAAAPAP7/AAAAAAAAAAAPAP//AAAAAAAAAAAQAPz/AAAAAAAAAAAQAP3/AAAAAAAAAAAQAP7/AAAAAAAAAAAQAP//AAAAAAAAAAARAPz/AAAAAAAAAAARAP3/AAAAAAAAAAARAP7/AAAAAAAAAAARAP//AAAAAAAAAAASAPz/AAAAAAAAAAASAP3/AAAAAAAAAAASAP7/AAAAAAAAAAASAP//AAAAAAAAAAATAPz/AAAAAAAAAAATAP3/AAAAAAAAAAATAP7/AAAAAAAAAAATAP//AAAAAAAAAAAUAPz/AAAAAAAAAAAUAP3/AAAAAAAAAAAUAP7/AAAAAAAAAAAUAP//AAAAAAAAAAAVAPz/AAAAAAAAAAAVAP3/AAAAAAAAAAAVAP7/AAAAAAAAAAAVAP//AAAAAAAAAAD+//j/AAAAAAAAAAD+//n/AAAAAAAAAAD+//r/AAAAAAAAAAD+//v/AAAAAAAAAAD+//z/AAAAAAAAAAD+//3/AAAAAAAAAAD///j/AAAAAAAAAAD///n/AAAAAAAAAAD///r/AAAAAAAAAAD///v/AAAAAAAAAAD///z/AAAAAAAAAAD///3/AAAAAAAAAAAAAPj/AAAAAAAAAAAAAPn/AAAAAAAAAAAAAPr/AAAAAAAAAAAAAPv/AAAAAAAAAAAAAPz/AAAAAAAAAAAAAP3/AAAAAAAAAAABAPj/AAAAAAAAAAABAPn/AAAAAAAAAAABAPr/AAAAAAAAAAABAPv/AAAAAAAAAAABAPz/AAAAAAAAAAABAP3/AAAAAAAAAAACAPj/AAAAAAAAAAACAPn/AAAAAAAAAAACAPr/AAAAAAAAAAACAPv/AAAAAAAAAAACAPz/AAAAAAAAAAACAP3/AAAAAAAAAAADAPj/AAAAAAAAAAADAPn/AAAAAAAAAAADAPr/AAAAAAAAAAADAPv/AAAAAAAAAAADAPz/AAAAAAAAAAADAP3/AAAAAAAAAAAPAAwAAAABAAAAAAAQAAwAAAABAAAAAAAOAAwAAAABAAAAAAARAAwAAAABAAAAAAA=") -tile_set = ExtResource("1_vei0o") diff --git a/addons/phantom_camera/assets/PhantomCameraBtnPrimaryDefault.png b/addons/phantom_camera/assets/PhantomCameraBtnPrimaryDefault.png deleted file mode 100644 index 30827891816579e19fbc47b913ae2ff2b5937615..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10296 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 KMNUMnLSTYWH#*1w diff --git a/addons/phantom_camera/assets/PhantomCameraBtnPrimaryDefault.png.import b/addons/phantom_camera/assets/PhantomCameraBtnPrimaryDefault.png.import deleted file mode 100644 index 6582ece..0000000 --- a/addons/phantom_camera/assets/PhantomCameraBtnPrimaryDefault.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://censw3w53gldn" -path="res://.godot/imported/PhantomCameraBtnPrimaryDefault.png-fcf3696b583a82b1078609a5bfd648f5.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/assets/PhantomCameraBtnPrimaryDefault.png" -dest_files=["res://.godot/imported/PhantomCameraBtnPrimaryDefault.png-fcf3696b583a82b1078609a5bfd648f5.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/addons/phantom_camera/assets/PhantomCameraBtnPrimaryHover.png b/addons/phantom_camera/assets/PhantomCameraBtnPrimaryHover.png deleted file mode 100644 index 1e0c31abaa0be755734661b39c89ce65fce62f9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9931 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 diff --git a/addons/phantom_camera/fonts/Nunito-Black.ttf.import b/addons/phantom_camera/fonts/Nunito-Black.ttf.import deleted file mode 100644 index ac0fdb7..0000000 --- a/addons/phantom_camera/fonts/Nunito-Black.ttf.import +++ /dev/null @@ -1,36 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://c4mm3of2mc8o5" -path="res://.godot/imported/Nunito-Black.ttf-2a374efbc207a97a99b8c70bdc4b7cbb.fontdata" - -[deps] - -source_file="res://addons/phantom_camera/fonts/Nunito-Black.ttf" -dest_files=["res://.godot/imported/Nunito-Black.ttf-2a374efbc207a97a99b8c70bdc4b7cbb.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -modulate_color_glyphs=false -hinting=1 -subpixel_positioning=1 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/addons/phantom_camera/fonts/Nunito-Regular.ttf b/addons/phantom_camera/fonts/Nunito-Regular.ttf deleted file mode 100644 index dfd0fcb40ec761ccced488d783fc77a4276548db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131736 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

Cg|u*xfjw`p@N!J?^468a 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 - - - - - - - - - - - - - diff --git a/addons/phantom_camera/icons/misc/PriorityOverride.svg.import b/addons/phantom_camera/icons/misc/PriorityOverride.svg.import deleted file mode 100644 index 79ad2a1..0000000 --- a/addons/phantom_camera/icons/misc/PriorityOverride.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dy8eifa6aw2en" -path="res://.godot/imported/PriorityOverride.svg-e76e07f4bbd98169f119e17fe5f2f03f.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/misc/PriorityOverride.svg" -dest_files=["res://.godot/imported/PriorityOverride.svg-e76e07f4bbd98169f119e17fe5f2f03f.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/phantom_camera_2d.svg b/addons/phantom_camera/icons/phantom_camera_2d.svg deleted file mode 100644 index 0c67805..0000000 --- a/addons/phantom_camera/icons/phantom_camera_2d.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/addons/phantom_camera/icons/phantom_camera_2d.svg.import b/addons/phantom_camera/icons/phantom_camera_2d.svg.import deleted file mode 100644 index 9fe97c8..0000000 --- a/addons/phantom_camera/icons/phantom_camera_2d.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dchkkx4v3ikpw" -path="res://.godot/imported/phantom_camera_2d.svg-e5483cbc858fc5f95f7210b1649dff0d.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/phantom_camera_2d.svg" -dest_files=["res://.godot/imported/phantom_camera_2d.svg-e5483cbc858fc5f95f7210b1649dff0d.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/phantom_camera_3d.svg b/addons/phantom_camera/icons/phantom_camera_3d.svg deleted file mode 100644 index db18730..0000000 --- a/addons/phantom_camera/icons/phantom_camera_3d.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/addons/phantom_camera/icons/phantom_camera_3d.svg.import b/addons/phantom_camera/icons/phantom_camera_3d.svg.import deleted file mode 100644 index 8d1154b..0000000 --- a/addons/phantom_camera/icons/phantom_camera_3d.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c71drpb8o4prn" -path="res://.godot/imported/phantom_camera_3d.svg-41ed612e834470377fb56eebffa083fe.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/phantom_camera_3d.svg" -dest_files=["res://.godot/imported/phantom_camera_3d.svg-41ed612e834470377fb56eebffa083fe.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/phantom_camera_camera_3d_resource.svg b/addons/phantom_camera/icons/phantom_camera_camera_3d_resource.svg deleted file mode 100644 index 282adf2..0000000 --- a/addons/phantom_camera/icons/phantom_camera_camera_3d_resource.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/addons/phantom_camera/icons/phantom_camera_camera_3d_resource.svg.import b/addons/phantom_camera/icons/phantom_camera_camera_3d_resource.svg.import deleted file mode 100644 index a2a2fa6..0000000 --- a/addons/phantom_camera/icons/phantom_camera_camera_3d_resource.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dnaykbu6ue5lo" -path="res://.godot/imported/phantom_camera_camera_3d_resource.svg-f8bf8d1a5b7442fd6933bfbed999d57d.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/phantom_camera_camera_3d_resource.svg" -dest_files=["res://.godot/imported/phantom_camera_camera_3d_resource.svg-f8bf8d1a5b7442fd6933bfbed999d57d.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/phantom_camera_gizmo.svg b/addons/phantom_camera/icons/phantom_camera_gizmo.svg deleted file mode 100644 index d791ce0..0000000 --- a/addons/phantom_camera/icons/phantom_camera_gizmo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/addons/phantom_camera/icons/phantom_camera_gizmo.svg.import b/addons/phantom_camera/icons/phantom_camera_gizmo.svg.import deleted file mode 100644 index e46b332..0000000 --- a/addons/phantom_camera/icons/phantom_camera_gizmo.svg.import +++ /dev/null @@ -1,44 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://e36npe2rbxyg" -path.s3tc="res://.godot/imported/phantom_camera_gizmo.svg-ba1aacb9b1c5f4ef401d3bd3697a542b.s3tc.ctex" -metadata={ -"imported_formats": ["s3tc_bptc"], -"vram_texture": true -} - -[deps] - -source_file="res://addons/phantom_camera/icons/phantom_camera_gizmo.svg" -dest_files=["res://.godot/imported/phantom_camera_gizmo.svg-ba1aacb9b1c5f4ef401d3bd3697a542b.s3tc.ctex"] - -[params] - -compress/mode=2 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=true -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=0 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/phantom_camera_glow_logo.png b/addons/phantom_camera/icons/phantom_camera_glow_logo.png deleted file mode 100644 index 41ad8de3d6dac4aeb7771d01adc69f65b7c291df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25499 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 i`=EW$K4@P9?f(PHo+dfTz!fzB0000 \ No newline at end of file diff --git a/addons/phantom_camera/icons/phantom_camera_host.svg.import b/addons/phantom_camera/icons/phantom_camera_host.svg.import deleted file mode 100644 index bd05ffc..0000000 --- a/addons/phantom_camera/icons/phantom_camera_host.svg.import +++ /dev/null @@ -1,44 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://5fatldiu7dd5" -path="res://.godot/imported/phantom_camera_host.svg-3150f8f2d82ca9ecab9a3a415da21c5b.ctex" -metadata={ -"has_editor_variant": true, -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/phantom_camera_host.svg" -dest_files=["res://.godot/imported/phantom_camera_host.svg-3150f8f2d82ca9ecab9a3a415da21c5b.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=true diff --git a/addons/phantom_camera/icons/phantom_camera_logo.png b/addons/phantom_camera/icons/phantom_camera_logo.png deleted file mode 100644 index bc43e567916df70bb8f8b25a7c394b98b6c221e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70905 zcmbq)Ra6{2wDk-yxVyW%Q*;LT@Zy8JyF<}Iic5hO3Pp-jTuXr#_u^2jxV!71bNjFL zKi$`xmt?Q3mF(msC+FlO@md;6IGB`}0000-MOi@y06_XzL;|2A|I7Nyn1=xXWPp~s z-hclfF*O4TEi(dvAf{m?W#cBHXQAL1CFA5H=Mf}fWFuwfq!1D(qNJmefKa}XCLp6) zJ31cS++IFBq7;?fJU$VB=Q^{y*SEGYx3@pMu{F89ySRVYxx6Os@3MP(Cg$V##@FeM zkE58kgP5278(-(c^9uk10fj=Tq~#<6-G%J!|5b?xxG}rAi3hj}yV-_yb%}V|ig`JR zI6DZt+6uebhYjNR#tLCp(394!X9=!PEL}Zwr{-ch1_fuoSns8 ztR>uR1l+8J-5f+b?8Lon#XPK==KqQ~SqOVrin`i}`Z_`@p(1W}5?+pB|58^NBrJ^2 z%aq^IO4`*_BFOz;zW^vy(9Z53F^jM;HWL#!H#bgaT_JlRVHXE67b|KgRKU(o+|yp! z%>;k|=y`jw+vp2=TFJWGOS`)A+ne!w*b3Q0mHyH8bkK~m2K%_`xj6Fr_z2k>@jF>b z``C#%JIi~xOWRvXJ2`P1XbJdP+uPeqSy^#7I}3Wb@Fx0b*x3AMr^{xoQ8zOu;b2m&Zt6;OX zw*O)e4zmCU*t>>?3keBv2#FPU^+MB~8-_>1KV@dsHpuz!PANEVe-q_9^9 zMZ&l>-Ws{OT6y_$R#vhbRtfn>2z~it4$J4#;{xZ0DeD=s85x2tEY!c)3)z36(st*t zw1q;w-F~>~Rd~r-!4#8o!TI?B=syVnWb_RfTingf&3P@n{QQ0jXA0|@l=vnj^9x%d z5CE1^mZ;p1w63g`mH#9U<>ch7L{tI*h<^wHK>_-Yk|GEuIVN5d zc>xp@3~C;NDh*^xOmqS?JP_@FSd5CeKui*DI#vd9JR&+AMjR|+W)5DJejEss5LAiZ1b6`(yvS5i=|`cP`hzP@A4OHtHm!{t zNp(N&vd35JecpQOd}0`H<8Ul^dei2Uzb|tyy^gR!03$yV{^bQHP_-)eimw~3Uw=jW zF?sh_X4&D%HUG3J^#2TG64+Saif@!?l}jgeFGS%Yug4Vu^zb~6XM>i>q1)7FdIeUU zcFB)d-30C8gwIHbHyC*`8Z0aYb&cnxDbbmy*A3G6be}}(u+__N1mYz!)~5CgH4D&h zfWdF*AKa)eE@jkHfmq$@09>;`&?hnJN(SUq%;j)CHJwWyTjmSK;ry_!P??cfGf#8F zb^`~x1}4L&@MnflR?A0P3~9jTABXJp*-sA@91kUACx5zAutX!@kwvU?KSP(ZFSW&c zTzH40@h*^nG$?O-KbKIa-PGTMGoMWv({_e+ccdS(nJ@!kwTu?U^q*2WCntx*HcW(X z^m|{?wH35pZ*o+eevoVwd!tnF7wReuF>A)gn|l*x=4qtt3{Nsle>GKc|DaIpl&&n? zHuL-(k}PcSsp|K_&YtQH{Kra&a@RiaF+M$=cFjPln+jF-kEH^NT73;NT|ARA6&-_@!!nsnkv83b~d0)PGi5Lr+lzjII>r+qoXNR+|L3-6690%cH}_b-V@uu zye;X2nAw7LnAvoLF0c&S=}RyXa-8^{+{^ndVT-pmU#M68g2H!RAgXw@gci|sZhXZaKQ{yz2fUm)T{grCEZi?!Ne+UyQ1d6M!AW= zR!TAbSOf`AtrS;@fn4#yj}hR*|X^fPMSFh*~ZqINo zV4$pMyr3&-6ZdYDvS7@VFC@Ow!_YyE-$55dlRbxbhaYbLfQNC#b_H^WIFL}==a6s^ zn);a<{0Eas3UG(<_Oph*eYA;YRLXuDJfM8_$O{7ro`Or6tS!H5bxV$aq;9f_$RFQA0)X4++xs^J^o}MGEVyYYp~8W9rs<0 zPovzdkvwAEPHd=Tnisk09Gi`o`(xp~HF%m3ERA7IObv27#R{DYE!AC}Kz9ZtkV2X? zA;tiRZ~&Q|sBWfk&uPF2iV0Wavx^ZwBF10HX-!7npd)OCBHll1> ze@STQUo(c@=IrJpnzQ@T)_hyq++5ni`g#8IeC>F1u92}=b#SKfJezSl+|%iIqpzEr zle@dysk~dJ{*$KYGdwyH$)*QVtYb{iqqoOdT4GJ8(kVr!&H5i@!fe>48!(?)u&+Ne z_3T>rE?%OrJ+V=0$8Y$MghM);_8`JEO-_-rPIb|3_id|JX)Lu9@VEw$_SzMV6^{`fFwU?Gw-PDM3RC3Bv=uz&l&YVK#|Ze$`@okF=5 zdOUPa5ff`xTiay(*N7z~R@(?3qoa+Ec8$7)MNS8bL?TBiL>>4*S;c>2qRR40bpzPknehfWdf|qqYvsM2g#g{kEfPbw5vwc@1@ZGd3Qx&pd}3 zEFX%7xw)M>spZTq1un@*JJR^(ikeBgHLsPM4UEQ|L$4K0a$NewHqE;H$32f`E42F# zekm5um$A4AUi=Bu!&T1k7+w$WWe6;3mH!xdmBtL-wdIGd=!XZti&EEAzSrnI({ayK z6_K#LAKp%Dp59e*HK6}xC&kdzQ%A+heO{YSNh=-Nw!VF7>c^2GhFbG&x$JG3Y+}Xr zQ9|f{Ei-53eh1EexT_8RdA0L8%MKOIZ>T>UmuXb_P3=^!I{LT;uu84H>SmhE*VXmJ zC8@XDe99vyCx-zdL4YL` zVKR1%1XCdl{bDy@#3yyl?6Crsj8M_Sq~Im~gbjR1`Gq{t#c8~0XCMQ7D){U1r51{) zY;L-CULQ%Ql{c`Wta1a}&@FGAkmSZUwRn$`3=JBet~3bfDk;U*g-(_9iPaiDa(|Hh zbQ?2g>~bo0-xAP4q_$vOo2;R}X!|3-XN0^E1v#NNY(jR2A>*!{Ra4uMoxRYLAKn{; zzWlJ6M^_k;qbioDA>;gxGTBt)p@UlB3+f@wnGE3@o6MeicLDOx^O^1D>v1j$e;t~Q z4bW)vLUGN4Yij%Yzld@=pPI7m-)}ZI{V^BcqjG69@YmPV&*I|b;^GRC$OMY|K`O-#mJre0`B|G3(2RXwE zc+nNjm>hvpRdvLIQ$T(%3_SnZ)qMG%SU!O?fO@KyTTcGm|4We$-C8;s8B_mF} z6!ZC$Hv#@mS<#pDkNI`Yn?L=!% z=cC-lty`re7Pc-^*v)vW=4_kq{eHaD>)!CWQ>|_E_Wx0Fc;`m+O~(n!Id^(CaOOQ{ zom^=OH8+QyZ4F#z`}}!i4T<6NOyry+nPa@!@YntI+g0$BtfBkdw3oVxdK}4%edclF zg3E<}$L(j*4}c6LawHCp@RIs^0$^cDECcq%xuTMaBRR%m9B!*Hf* zO|Hy##X^1DF<4>bk%WWYAeIC8An2Hq#e;U;&HyoZ+cP1nv~H>NAE?FkhmR@OzDsZ3 z-V&K!W%j^-=pCk|b!|m-UEX%jBT2CKP1Qbi`965B@B5BI*_262{R@q{I;ZT~O}#$` zW2|QJq|Ib#ykDVkGL@3z zF{k-fJa6KW<6CEEC++$5)zR_w)z#p&zI_2}5cHuf=E{98vNF`lYJOp@&1#7sN^fWA zZ)h+iDk`cN;-Im)Zb^RjarH5y+S0NBQrupmuwS5+wqBf8QWT9sV>x%^cFFcG5}%gX z2;-X)iKzID^o`e4QktrEozmB^(HIG{%${@Uj@g-*+|D2KOJb+>-;X~)le}^V)}n+) zy;lG1?uzUNh4zh&sjsQ4tm(a!G?iC(x(=@dit%y``zsE`Tl!p3P zGdEA5^vmlpE9UGnn%~bJyNNNV|B^>bOVvidxa`e;cSS{oj1HkfJ2|n(XL^$VoUXZn zBf{I%pVj1a26kJ;TU(8V>V9RqHTv7ZS695Enxt)stZd&@uP7-gN3KVp+amm+%7nio z(Fv-!D#f+)?%swquT=Ke3ZpYCloIGmL?FQ&oh5dfEG}1Wg?71Q1yH2i_fq7~;U1Lk z$Fe7zOe`{2eM%1j^Ew3ZUA59oZ>>!pz0O*NEfLp-xJct zHd#?O{0FV9s8(>`I#Qp<;Z>0F*sF2+F*WenaE6D-qrZ{M}Ong+g?u0#3I zxwvXNW19ZgWJR*1whvktX<4;WDp~pG&pW;I?%ylg&BZ^-@_+rgq%f~)y2xM%NudfV z)L+>AmXN5Xs+#K_s;xb(_Z5bUTWB9->Sy=Q@0E~j(fHm* zQWHBtffVp0BGNdHokaIIHC44byMYEbE*G++^g{d@46KVMG`G}Y;~Lj6!BQ4%9GwJOwYa2!H^9mPThPV2Wt^Tb z-*63ZpqD;+navl8-38*N1M9H{A3!>(**mYxZ;8n-ZHqdD1RJ6R^M_8ly!w{ftn1wB z-V8$T48BjUDZ1BD8YGJe?~EVhO%xXH+23l}8#VlFZ4qwzPHd19g*k0cGhQwe^!+EL z#|PT+k?eeMw%-jze^5$hOro2%*WmKD*ob1zh#yt!y-qoB1+*gTXAf=o8CdSxSiWX7 z!*9N(ReW@|rw0}+G@V^4^tmx|d0{7h7+d?y(r1E>j$b!|S|X@WJEO>HT5N-oYTe1o zlE0aSB0TEv2VhHmC}lhvN{qY;%~h)}Y<&_uH8NTFbxtuKB13Lmw{x?~ zv2H^TYxPfSY7jbIylL9<>f1BgALcb{+Nuj*x=vFcfaqk5wG+d`!y|)g`u>V{YaLF( zUV?Y_r*B6`x^0Z$&Zr$~qY|Eg$21X(RV*53-qnb8L(`8)M>M;^^HVSKBc8=jX-0qgPd27hRc~Z3{i31|h4zt1 ze|&u8I_7?&Hr>gG4|c5rrPMlpx8HAdBGV$Tw9uOo&t+>bF*yo?6bi-rM2*thBoXEWt*6l z@v9z{CrvBfY%LM}1(Q}ZD4mu_0-HZ~!?3Bexn2RO%;Z34TB0Asn}8yq3=ekY>kDbm zi-atIFI0U%2xGPYE#O3Pm~Iz()YcJ3I=(7Y{9;g76yedfRO{kaLoKQQTf_;Ps60AC zMMeg#%rstNiYSlLUq^IDAR)h-({gfJ)%xeC(#ra2 zvZdX1=C)>g{qWsw@Vqu1`H#1b>oAkDXM+kkaW%qUn5v#KU1~+ppBLQ^_lM(!z~^hb!86 zMkSza%83OQ=6CkHe>Szb$gjuest-LMdT>8n7q_M?)mDc52iDcG@Er)9KCG_f)OFBj zFfTSdhulUWU;7?6dA)1;-TNnbt;u6@Z8ezTY<1`z`0>DfQ|jl_G~4CX!R0nQPC6)8 z8k1+SDV7+u3;@o{>ky+d4qv2aLguxi)dtC_!6Pd<*^c6M%lVFP_5Aj-2RVnH4c^-P z6nbwnFKTcKjfy(<@ca9HmXDI1d*H% zu9Cn3N=JKoUV7f_%pIDa|804i4psjVth}cw9S!#V8bTO``$sY;lo(Gaw3~rvu>%i` zJb+>N{)Jo0a=KrtXmrMThum)%TQw&8_}fuh#DR;RMmmN*W?$;VW?w)Ue5$Zdl5V-}`eJj4b~!MBO)~at39?zh&Gfggj8&c? zVq>UnIAx$0KqTb-ta`)ovWsf_km)$M>VLhgY zsPOQ(5V@eRD_EC@&}cwZ8vui8e{FwWAP_BfNN~hCAMG&OY+@|{;OC7rTX2My8ar*> zIDL{1 z2S9;?qfK~hP_q?<((2wsxT{8fmtVA-L;bekfG>>{o87KUa0DhOvUSs*z4=xqRM72?jkU_QM`geHSOZ;B&@LW-EC|8PCItpE0bAPiA zGWG@yk2b2c>+zze4z|mcG9_;j3a?TJ(X!$cfN#v&X(2ASU`2D}>7L@0Eravrw-3Fw z9qdf>>{6cLpy(a!l`rhlpNVmHGf487e1G5MRI--D(C9JyR^$HoGM?G-xs6vX(_bMG z!Hh%cnzZuWWg;KH;WCwNpS~HBXu4=U zK1mIyLmXw&A_!kLQ!XMdaIX-Af6zksl0d|OFkIS_^=Ns05GE>>JOQdv?)MjNo^qLR z4Z@RdEhmb0S@UUIT1%8&cKyX=2d=hcechVk>zMWPUq@y_)fCT5R@=v*I-BsfpAxU= z?N!-H={3U_094~_x9D~$#GV4$0@X^_loVlyfTKM&Tc&e=BaP-PY4WiHg}M|Wv1G5) z0LtsKtFnZcpVI|=6ABi1yX1zUoHM5*WsAAhuQDhPw)c2x)wIDUT&?$GzpooZPzYgC z%)Ys?v|(F?Lw^Gg;$vxpkSs#c@lZ)N2@WtkNQR{$DgGZh0!{=_h^F|Q-DO=y!?CBKVe7{4d<;?lXCcxw&VoWqvrTP!=tWM2)nxDAzL2V%4 zAj+#jHmfHB`?(B#9Oz^*doVy#e5T{&m)6qqdxdj&@lILViZ=12`kUOq?)k7of{!wY zM|>LP=fc>|JCxUuQx|wrUfpHNOb*+HSqVmp)GInH#tGdVgiCaRn8GeE*Kmn7-# zTy;4=qWr?~_Cv9|FMh1xakkK$-KS3zUt+uh_(&X%+TG7QM8qXrB*&?;O!$*(Cp$_* zsrrUIo(*dmK*MtzcS388ko*mzAh;!2x@-573%>~mUliSJ!FuA~=N49OADgPB@*J#u08&+XdM+>n;pqJwSK6%apLfq$=$c`HBgmYpQy!-I-tXhiA4N;)n(7OF-r%@0!{cx|@!-Qgb`>!f0gDVa!r z;Nq@-(JzxEYmdX{>FA zYGP5R^o&HEmQd`7O4HXl_oD$PAFUkNHJB8g37PN)?TSN`5Hl*jz7Gkq zwQQZmrw+aAMz;J+huqPU^kZ}S^C)ka$+yEKjeU!9z=Vyl#n(&&!7uy%NIz6E!N=Xt zQmZiMzuIH5Iy5SDyX}1`4cuR;Wg_3K{lFR6Z-yN<|e-TU8RK@)cyIUTH-|IXR)>(u+jlXfe@KduwO_?QsjTzvR@Eo9+$z zo`#D4i5bO{K$qTGeLKx3Q&_Ms9~sGxYr|2ZIL&r?kr%eNuZ7Lx&zcV78|Gd4 z$uB1Lb8YKrxpip)7KHuh&D+WWiqO7vq61ew!&$wv(qTSH6NEQ?;EkX{MO4dyR2uwFJ z!y#5NGxPAkz+4F2w*)O!n0#++RU)hGwbI@lU~M z;(kAGAbo89ev`(nhljZrM9;;|jgmy(hZ2aO=$T=ZF#t z!0)z*YlUgCY7uVPS2MRzF4o|xws_?Fn9A32mX0sXFfm>NE;wPh2VT{kbbJ;ir+|&f2YerKyGaTtWCwusI_26R zUx8+9fjGTVcix|dAJK>zvZH?r%(kH!3@(}SmM+xW2Hum-p+-dVGGlw;`eA^O^cs;B zM8Mua;>CM9KdJ_Vj>-d;Vs~d ziaBTl6a7EAXzL?ldOqePL8RU>^>=AP9(yBl?*pw0t(V5;ek@mK3BPOpo_i!{`FX>Q zQTn%=f<|hep*AGG$7V`teYx^b zzmBZU?EE^^`cJ85*mKP}b@2M66v0=>Pqe?EZu;C|Q3nIjyt&|qPm4nVc5!;|`D5`D zu#DOc7S$7eAo~TyzLwLhx?O@^2w8~ zppJR#D;^(w#3}1b8)5oXUWXsFv{bnJz56jtZu3N@(?P8A#rBEgzoR!=0dDn?q@G-J80Rzn&O~or)%<}z387!D&&(It?$`98P z=hUspb+lBEk}%}+<9bc)c@|taI{MC(T!ZlSwDI{Rdwy%=>B_jFJhd9EIY4A%D zu>I4QV+Sj`xp&(bVB;I+g^TOxMWPz)G-v!WP?|(|SkynA>Tog4#7>}nPp-v?Dr`>) z5K2YeAso-P5|qBSrXY-@7!xew1(+A%n&r<;pSPIi@VDO*72ov(D*I;CAj+2Upk<@( zZrw4Q5Nyr3DH=8LtLf*y-7Y7Ts^#Hd#XP)S&EwM!l=8;2xU_gXF!`uz*;PE`98ZK3 zf1&LQ@UVXu{QcA9V|nx_cUaVN3PI;^a!Tqw*~i z!f) z@42AWtu|2ye$)D7N3;!$9d?ZH?A+J{&?3uOi_|CZjV2E7cJY6-Y}%`U zn{N;X-KD*sib@)0{%!1!$i=#!XyUpq+barH9L#8@U~}Y0BXX-A9-^ElQRi}7Eksw} zSn0f9V`(wXUJGve#$c@{OgpqJUb*}EVXuE3ECM#89gy~;9r8nE%CW5n>E+O}2vG=; zbyO>H>$d=bNQ)7Dk`c5KBoJ-@Im6=sq}HYyh2;ZhGu62DZ0x#T^SkU&e_glzqd~>Q zHoNb3C8K^ot%&P#Y(fkUmZ*dc^MsTPHvDvm;Z`JrQ7WtDBBFPA_|Z`fyeCcl^Fsq> zI!s_I(*SIQdX9&LcK;!%8kzX%v~(wn&mbKqfGSIazKgn98~za~9SuDINvOLTa3%L0dlSPjlb&jhffL87 zKXsg+Y_3@J&1%g|M5lMPP2iOA^1C)W_t_b5t1SC?Ox{8(jeewqJWP_pl$7Vc;B+^K z;0>T~tHw+at??qmr?-ngHIgHTo5v-}B?sL+n=0Lt0`_p)1eT33bMJu(FPqQzQ~Z-w zs?QW9DbX;^sa-PfPR~BNkFfL;d2htvb?d=o$3NPzS8&KHxL-2O`C_aoZ4fd{gv1~l zC4~NnG=j-4V5GC?3AVn_}|K1oOF)yR@p?xGgNev5XrF4%|~ z#Alb&jK^Mq`9YVbc1(SqOgwfLI(rA=kw-lN+rb^)@KoH8V{utDq|fxC^l^g!j$ef+y;9a4z%f~oXw(DYk=?1ob~Kvc#!=<0pT=#; zFO&yJ+mYz-kg?x;(&H+(J<{_P6H-qtLTB=4#;a$=?A$$$VK8sjC4@^J`G(KR+y`^= z{fSbUNcDK2N2@7rteF9DN^GU86NTLwGXecNO(bO&Ox2Qq1mnK^)}DXF9a7A9b*B^) z(td&b^)-6moS5td@%bXoxu@l8aD<$^EDj?h5&_MIG%zEU{JXFg1w<91!tmewC!mu( z25_X$Z`@r0X`a(R>8YJkmfb*OMmh`D4 zy+kP4C~=-m1UQ7J%J#RZO>Nl8TRvEcO?erkH8#znL>qEy<~OKX_<-XbjH#Uw7Pf}` zC=Y_LS@HiOCH?<;(_Jt}@;0Phc94UoYI&MlO{?hP683ejM<%A!eEDN8gF*%VO*43^ zVF!;ivG?D~@KVh~PRKT&{wv`lhrMnw{m|roK%^fq>qL^}2bxa-9sl3Xseoj$S+dAo zp|Ini)7WGyu=NAxVw?Ca6PyV7O>RD`OAI-bK^^guB7&aTEB{ zQ%yg#$|awtp%H*}Diyj-a6KKN9wKh(#X(X1g(qTX+-mx-373Nb;4zN_Nq~!xkr}}c zpm4%IQL_Y-K4}l3y{BS-P(*oUSf*=#Wd1jY$)vBODEZ2Gy)Y&uBy+T=a{dEsq(IqW z?2B6lIehK>aBAQjT7?|;5>WUJz!Sxdq!Q@>kJ#p`t6~a8WSOe_=3;~KAH|&YWpC8N zjpe#7-n_m=;c9l-3U*R#-m&L$S{ePqoqUT$u1rp1!N1F@IacIX-;$+iYE12ZRkqvb z#z^PQ9jT|ow4XMAOKm*oqpT!uKGPN^fY;9nx?W_v5K>=WJP+AEK6j<~y#DD*sYCmB zWO<@)`qQ&gZdA$3rDa9LmJ2If$ZnujNaae0-2j9OFmvS#*d}r)_$*u_F}dZDFSw1` zV=TIGn8E|SZIQfZqq0yhP;Tgq`0y9=V%k9_MBVpsEY&&#RE-F2OX{!PW8WHo=%TrI z80EDE+ZCduBJ7(fr@?XvZfrQ?j5p_!Cn7PW77PF8V|3W}3BGRxXBNDn+sQ3h z5IArU?>K8f5Y>DgRMLsFeH(bRwx%vNY{q{Oec*u?6B2%JZKxR;*!We`aEC7R0d%;1 z6kEc_`Y-NCw?MDJ)gxvRB8!e5Kts=hK8o2}1K`4z>QR*+L#Jn;eC`%xy&p{7 z0v>x*T7eOQzdkwS6J)&Z6(OV_uYZUEMOmphu)iT zp0{)tYoZSg>puxp-V^EIOH;*~%y-x$j-1_w6diWIhZeR|*L(O$yS&&EaL}=YZFm-S z(s3eCZg(1F3K#iwo{8^z&J&fv5f|@!*LGarqRBbQX5n5;Ljt(FKgz+{u`?Ki(Un2$ zgy9eJZwt~7#(*SWD8OhX=tq&XdD{|t1$H-~CK1?GN0?N6Rv}3dAft>^CJNMgVtmDf zxL2ndI-FPB=cE2%G@oy&uPNfbw7=;7hP}m6&XYYcie2I(5VhGrPS;2|LtV+?409cR zF9;4mpzqTOzcRl%a2#R9fgg|73wM~l)2reYrPHgUA=%RxAQ#5*SHDrvRZJ(ALqiu= z+z~h+GzsRv7XZnGt2sQBG@91x-5B;cY5&h8TmGUpIh-F1)gFks`adN zqf|gAx~d{=p%1rOuuynzecrUPD7iT|zw>g7XeC6k?zL1PC%}%1eXmEhF@yyq10>|e zBx;k&JrM;kkOTcPd>k92;GRodFVv`LarR?UQeyX=>T3LjYFSjjK^jR`C`gIrGDz9@ zS1LD}?{NTk`y#(t?0SwBOYcHYSAs^Nl+S>R{Dd%(;aA`@)7ShPnu9dtt1bt;G!Oa* z`Q=O~Ea05Z;<+KyLjBQe_wgKdzCJ)5B#a?~--4V@rI;1r$<=)fa3L#J7&8C?+?Y@V z1m(8A0IFD6q86ay*p%||-j`@v>(W=EtOv0cATwy0asOpxLO78HFh0_$c-Ur6Vq1*M?9lQcCXQs|N zpWrQ?DFU*C;0D16TEcub+4$fka}Z&6g*W)6NbO7;LXnVfssFv_Cb#8;{0F4uVr5GG zayG%OyT3b^Azbe4zJB*bR{Y4(=Qh97|Fj0OUh~`(d!8xfyyBB@I!O> z39S~4_0l^)2YWd0xcD>&G{=x*VYElJrj~_({%JarjcKG=I!ia}Y>^$nM2! z@CbFU9Y}OL!Huo8ZtId0F>6}XT3^8_0^^6uzB;}%%z#(JXzwDPG|kr8FVO_M_J6}; z6Ty#HY%EWcufHY}zV0+bX0g#ZFCIKDYiwpBE~@#9xFMdvtvmujIbpQB(;jw;tV3D3 zcBX4<)-s!``jzx;P7@P@rKj2_9$I7u6lG*gAhEn$RsvL*5D5cB>b437;cHbbp8X5^ zzo-qQ$cF1*{Lq)QGRoF6XS)W^uRo}nhK0Up`CtsgRv@nh6mn*Sq5T&e<~kN(dG*|14xUAevy5n9)cZd zKPFvb(cWl%{`J->?mH>7F7FcL0vGjY0}XRm$5>4nE8!vS_-PLf9!gdcW|)U}DGvl# zq;|fd;=H~Fxc%PnPOkZ};Gg7WxHDWaNS0VT#Yu;W&_X~?>8{Yah~{3|pX9H|%AW_! z5oB`l76*5Ust8`@u6R;cXEeD3;Ac`)8rkod{FT6>T&q^u4Mh(8aH?UAs)=s(lAo0= z2bia<#`QLQ8nM5v1s|T()@giTr1=3TWt+mspqdwAW?s%aahdwXUsqVHbvemm{QE&dr+Gu<#5NjCV1yNDs60mxFYf>}%GeJM>Kca-L$99zyf5>Hv7*#i(x|+ZWTX}AL zU*;G%{Q0xsAZ<*$RKE<`YE{^!%C#$dxJKxo_+fxJr@L}z3b%y zoS~f^abv(VZOK8%^pfsJ!DDv(b@X+PYE&W%VdRtC8Pd@CS`>s{x_)z1tZ=1V?;-Q} zZ42-1(`5}E!y{4GtH2gn$MZX0`1lPy2Thz_1epq`w+2!|V0j88#j?J;RKd>pgj&Ga zUm&+Z`V;A=Ha6j;e%5JCIfg8<289g&*Y3S|vHJKVZg))n`+kA3@?DqXadm5=6)tpceNpA_M=_1)t^TrT?yzEa^wE0cHSPq9h*0X{nE&{te#xWY znh5$x(D9FzCi9Er$52xiWkt09E!i@p5F`35LP4f(c616R0SijSSbpSFhKq?vUs6A- zeZA@Q%3R4k6aGWj53jIPm39XXcq;k6fRZ`8k_I{!_7*h4{;`Dw1BDbu5CIIgcwmMt zT`Ptl%+z@L;lV-meIHI?i@jk~E(UpD23*tVb~IfzpSsUR;WQLgY`A?Pki=^}RY4VI@SJAD}1RgtVm?9$uW3_<0X4E`ZRm&}@f!A4Av_8Z!O&gxk4W zf)O8=@0fbD>SgcqsE-5Bq5_NVYnRTL$?jbN`-RU{|19$>PX=oASFow(s|pH2x-FR< z;ZnZ`4Ysnj#f@68HK+-dn9RakrkRYmDMjz|oPCnL1mk2mZasAm(?ug(m&iW7_AFT_ zT;N^jx5Hb!&k-AVGVe2|dYA%K%rQjgfC5VN zm2C~|w4=$na`&8UE%HI?w&R=KHVhv9G$%~}TVzh==%09g;(3okFmW}6Xpd_42Zyx6 zBggdAi_-17{d>5JS>8`s>{q?UHH)>j-<1qwFP1e1q7q%~ukx<|7~#niL2qcZQW(Pn z(G*!}SHZLV*80vB8-B3hzPm3yD>?codeoz*G6+TNV!c8w=@oxFM_vy7(Pc>i!fkVS zI7`!OK8Cuaa8rzVwwN&C>}TdCzAdC~i9_?{}n&8p9>-X~HtHhvN z%`jR{e0tC8I<2VjXXAFU@%g!3A)A^S`r!Qe8s>_b)F);=N?nijU zGe+WTaZ%TPbbM#|F&)z@b1lrfaorS8bS`3CkA4L2LNOc~T`oTikF`Q^6epM-E3}K@ z$#4xouflK``fTFzRQ6W?y1zerKvb$)nDmv&8R_50f`kz6!jZ{+U|`o~^7r<%h(2KK z90JLfCJs9I$S7oteej$uR?S+ivKeA?@N#@z@3m?aGw>4rTJSiUYT@`w(|d~6B82yh zNs1VLi7o0ve^_sTGjYFkXuTg!kR{ZoC&!c`BE^g{Kz%SwJ&IHiRYF#9jdCRu{oE+N zC2_(m?*XX`^08|BcwAqXrA|(ZJnTOniCTDtGtR;hueLY9?oHyOyoZTbmv(%u(`_Z$qpqF=J5y0rB2;o&- z)|3FIZqzu4SR0;84UoJvBvo`FpUOB2XG1+Vw0hq9@a8G=-+!&|KZrn!3gr;hI1Fq> z;rtnSsqHTDesV-&{0$Zz@b<|VS06UU!4H3E$~RiO3L1CI!&O3aha7!RSr82h7^mVLIM1JBcD~qF6Nh_Zn)6sj9>K| z<73YXfD30mSP0UkP5x5YR+l@dHX~OZ+)G<+l``3-9qflZtZ4y4o2hH(5L3=i=(&6Q zw7T(%@fdwX*Y_gyGZS({_T?oF-gUWBww3bk={(|Oy8@C#7>63AErDN*WYuGtg^HdA zIL6WP3GcQ>p+YmQ&;IPuWKn6c%?h+0VzBTRV*@11ukss^VT+zN z>MwYK&2ztoK2;>2#Aa#Vti1%csX)5Q9LJ%WXmt;8mOO2yS>QbNhkoh=Moo5f_{~0z z_ZH!O1fNiHy*8{_1l-V>KRJAY@x_dSd;f=|EAfZw{r>mfS?y!r%~-M(#xAKDOO~h* zQi-t>A{3R(9lH=w5wa(x1>qx0hHOcpMU-Wd$P%(-otdBS>-PuT*K_ZE&NG+A1Rpb42snsB6z=M!qcs-!Q2kFX9;O54a81(JJ)8IhL_&HHRxb)zz^<8)sFZ14o5k1;%h!aLfHN34;&7WR*+(oB;PW zs_|x2#u1N=hilKiVm_3tf1fP5?l*+$a%l5G9E z^g(8vFV($@B)86y-8#H3D#$LcNmmNHbaXhEw}TJekxC{?;~Ej}`#|Vsm9hOCZ?OVg zg1dLeBebkh;^}IUU07qXD^9BP!QM74oi zzA;;Z&c~AL8?fZm?E&wg7`k<71#_5L$tR?4n+Gpd!}M>mhaWsa&x&FT-!fxY($%gS zZ}M%N{4$~ZJCVY&+MS4`eWc!=#|JU9?yw$kkZ3Z27ttNh$PNm)RFwQ&OsYc+exIVN zmlr}uAHM7$R@mNavz+W@sMMxh`qdM0_eE>no5|&`+vn8^9wu|-iFlUP#8`i{w!{*R z2ypk(c?EJS&ojIZ%wYEGs}@1d8-6-{X?xLbs9IZ^awA{~4HtvgU5&S$atjJj3!)OE zLM*$b#EjO!h5a>~lF`i8zcwDLg_gS!%DWST_O0T+$9DGE?^OK+`w_53=^XJR`6K7Ie+Ic@Lg|4v_&-}w|qUHx82{?=-$vCGjcdSp08BCs5?@`D-Zc0rRP_%byl zx8||C?januT2=cc=lv`B%CbweTwFd!dH3>aCF30(zMVUHX#3*lZKZqn%!AG|+&dO! zA0vt20JkU4zGwc;5zoL{b_la=J4^odu`>G+`il=S|Gas*T5&AL+KWYG@b~E(%mG_* zESt8AnUVbdH)*P{gH7!JCsDL>Gm!amzJCh8$dV6Zi~UVGMru3C*K_Dca)99E9Q~dH z;i)3wqZIZ8Z-yjeru)hfjsm;0rgsuTuQ_Qnn$CIrCv&l=yu2yKC-IxwP+KKARbu>) z#Nc?xqRK-wGAq?@8$9|1y+t2Wf}m4s3}3O`d-^f!{9lyrFAVj#q*MlF#Ir@T|6I>i zQIQPgbzj5}oD9JOTW@<OZxmG`g!)&yt%IJ-OWed|TvJj!fWN z?hY=rsl>8WSCWT^v8*K75Drzji{cYBhp(K3&EI>fdU;oQ!Lc_k^M>*rcbS66u$FjK3KHiDH!aRDMbzX{pUjss$ zFpWVri~TISrOdHB0>q{X+GDE;g*+7PS#@IX@@S|fX4HHA=kU?wAF{VT(au#3c^?@I z7L&d$9(3$!nUjb7LFag9F}mX0#plXBl0Xs(CCE|xuM$|tXIEEtUS4->WP83V|6XxY z=MG_4Qf{^IRy8$;s>&+aT^%+a+I=)hDr9!9u_!DRW{M=UyN<;n#U8UfGAgeyMDuz| zj*^GH2%n{r=*a{jFQcqobyX;JaXfwz*P}3Pg|ZcDv`*iwV}#r&IZ}_l zsUYT)qXTy(fda#YXv*=+m#>}oQB>PkaPahN*g2y-Y3J#S#K)!v&g}va!@@ zTmVW>69RdX!Vw1~DM@x{v7>q+zlrbAeus5|(#18c5Ki5%GqZzI2A`$^Po<4(0s=7_etC~KPOwzg}pg>^1})yPU=z#9`}0xRa0Cgl($8T zlpvcERB_k_@T_SZQ9=)hcb6gFl-uuPxw=BsFAAP>8fj}9ADsCuuTLQs9jHK=JhIJA zu{&Oi5vMqZPJQrAgn|eMs(Wu24)j?)Bpv>{=wjVE@y)ROQ`+U2wN|Myw~T1PB~v}8 zmc>W=mauHUe*7qjC%V&t+CIvjAhYt!*jCAIMpG$wcYG4{d932&tqwxBQ;B^%%{?z| zI+2irn22Eq5vqMG!y&8LyUiN>+yXA(-XI6I+p=ndr#^TB zyCFxG*^eFArbfqDST>!VIb)QGvF|fw&){8rNIj5pT4}BF7#>VJ5+{fK9Z#TX6QXR^fuWNG5HF-iS$!?I3R6kbI6~cGsaQX)1ppNjFwrm z-PxHd@i{K)onqeGsn)rc5v^2o_1fhsE1}-&^s$&sTyaOeI<!kcOJc?n zf3TM{l}3e{*Z%Y;q>$EJiF|pg#*;Adl_hxqZcQda{BKMHL*s6z3Jek{a(8&D-cR*l z#vH})@50pYeZ@JYp1W9=djCK#D?p9%yQWw(qw?x;a>|O0K*UF6kr2+2eD(RXA}4G< zz`A~CsO|18&gUv(Qnt?BZ3>gGxq#Pk=pO2#Lpp<*SLpTX#)%ZWYMsv3szS#o?7&KxMF zIIRy#Q||3QRr&5g3XkfSW2fQ^-(Y9g^|AvzK6~U5OZ2fm2mJs!V-C{i7Dq|?HvK8O zqk&zuPJQd?-_-cAZL1+(f= z7US1RlkMAA7!MzH_K^pa0^bXpJtntX654s9=0_~`agIU)K8c${dl0bZQwDcKW#!z> zn<*(oL!DaW96#1En@zXMh<;j zznt=7$o(-H@LO`C{cG(@ez=*zjv9)NUQ6jE1^(bVxw9{U`Qtr%Xq0O5w{_*P@$L@+ zmUgxLMf7Xw3I2?INrN%JI|v-43`;}~e^Mm~jp(#qN0;AqM2ckrdIQCp#sF!N#O`A|8Yv3;pH`5SgWQdIW3JRX@b z9c|=Vamy~gBs1v8zCAzyT_lU)Q;=!e(Ndm6q!s&nix%@u6lHol-!7{R0E@V^9{(tX6(|IfjXb%O+A|jjhuK8=oNjG zV9PdT|0nU8PmF1?mHmh*6iF?~qk;>VG}h>sb-rxsz-D(AYbGYR;|_s>>hPr5MvG-h zcSRrvRorVv$UW$!b~9T6f+7E;NQd09nB=M$I*pssH8wXry}g}~ySO-V#tkJFFZJTu z2~VECC%wA93dg@HVV{9v|5*3sz+a(q8+&Gqzo=l3Zg> zV4w@OBwbNvy)i~!Isa|}t`?~#X2eEJ-Xr2+rw6V#O#=Cw$y<=B$z9*@{f|_*4RqZj z(;K0*Y}a{zq#6{#OD?e`JWe8BR3_x| zDhvRBYY062FL}IDFXXEgnfelf3M!_rr#u)Y4cmt8DL3T4MLX}DygsBx(u`hibf8_9 zH#!l;Wj%vb$IU@M#=h6Yj??}&i2TI(@PG5EKDi;GcepTV^!2DwpaaP zV?MTqY~XWtlloYlH>sD7QGcA=+Pk6gZ0i0!F+AsF0P?YQbNRsG5r0e=dAHFo>1y`;{>;WKDXHx$u=~HW zbLQ}-(On7DJacNv3VXqt|0~I9m+d=c<%N!Eqr~*ux|n99;w>G7Z*n4I%n6w%%$&)n zX65fDn_}mT;%sq!G0V`!Vfb3ajpWy8F6D!Z!r! zK&=5uSdv_|ZSAk~+OsP1#XryWccI=py>bn$w*jB>4WGderx~q&zcjK)tqX^8H{##o z?@W~ROiY!sH^)iGw(V*}Pa*7hHdo%|T_6WOh!iAcl<{L7kdqByF_F86coq?*MK+M- z&R9GaD{2}1y!WZ8CneObwPNz@xxiWPfIuP*B?UrPr?7!?jEgQB>&+y$AE^9oEy;oBI^KyWh{+wmE7_rpeitiGo zmUXb#PO^1w@$MB)$q3hPeLQjP?FOoQFO@WpF5sGuEujIGdwrWLG0wM6qRC3Jvu#g5 zHWrW9$Q=p=zl2tbE7CtMoh{VPH=qf3*dOJ)aBz#$L%`ae)#o-)G!kG5rhPm3<}%-( zXvvr1G0eK+j)R|5upunvq1QzKuqF6czmAPsj@@$?Ekf7JZSRNugM?_4c+iSq;s0_`tPuFgggF0p>B;fnB#>vI$;>?6F9@SHi z&VSV(9*VdeZnY!p4b^(WzfB?S*tZ=3U0{$EoGLAdsPG;OdjmpMA996pFdg{(*nd7^ z2;60MtFXGq@#Z_~*VxIcx)+w`FFt4cZv7_FHa#EWA0jX5GWyxu{;xHo^qmL?g>hkf zc?u(qjMb4j&tK#P^QWCMhP%xDeqf(HXz#jT99A3gG_!KxKHAMO_vMb010994PzItw$8mdK)|0jCR?F^Wa$VN_` z?8DP`@I_H4yjWQ;n97P*>NJ$)Aqbaoxo@a(@(p%C?kv36*!T45g|mCzh8F~C{jysJ3-g?6zu{)I zolf5?pHMA7vMr{P2WdnS?jfk5-FzB>P>v_Vhv8 z@j&`Js}Xp(%+>)_{}cp;lg^2h{m{IFNIE%id!^7WL@4nFB?&eWQDwQ}*p2gmTW zv@>e+r3ug7b6`PwQaR;fKDpqn8B7%2qCib?N2>llrTt@Ho;z{c>D!}6l4M8w8FTs2 z0##UtZGDcKyv;X?58RfYWW}x=G~8{>?$^H`2B4BSK{ofF{kxQSq(Y|{~iLiYxJ4d*YGYJ@i>ANn+?Us2{#HE-e$ zsd|< z+W?$cvcxWolHCJ12Un@xE28{tDyx4pR`4{cM2Fx9)t7;Gr%-<#3C0JMZwe!vlCJRI ztV3_Qz{&}iWDfJzF0)c=5f%|;o)f8Ip`5lq6nD%my2HPJsm%V`c+nqo0zQ*l-W9J* zEJowvbae5DR6=3Iw(tF*hEDB|8ES)Rf4ud)MFMD`k6|`htfK!vGV4Y*tU!`}Z^&SYg$5rW^!SdSA z(!Y!3{i=BtG;k>ufePQcBPc}p4Z3f9cU1rm z{oL8|J$WPH?zFQLb?b%BWchnxDx!6S;(fK^_|z%Oo6VTR$cGL)v|nTIKBF!?WWG4X zfOC7pvtxFpWhfJ`ABjQvzo`YdWp*2@U-~Vncxx{~jQc{Bo5K6={!Aj7aLDPB_qdlA zLv@V@_3TzIQeFm$F{F>rA+G6HMZyPjzORt`m4h@RA_ZXG|4{ZnNnP` zE@1PiS`WUQkYt0FSXO8UE^v8C<@ibv4<0<_`r6j+3}|kZ9dU!{`^MZ`Hp;NEDUNIS z09HYna1%&N>!rQ?ud(@c+PBwNn(k{CHpQSQ%HR?i$T3$Vd}Gn|NHx5KFu;Z-#EySQ zl4G{MT;{vCzNDAknzY~2^mr*viWLfx*w|PdT{Jp* zF1)Hv=x%D|c>d9oMg4n1A=``N5gji=SfVf1Ar!imaZ)D^Xly zMCfRU4hwqpW$DIJHx?rQwW*^YH;n0nET^_s+97#8S^+2;_hI~7S!0?s%m5!hDNqu@ zLNo-j=M=fa0C>VS-v@L2DqP-vx%zk*AWX8ld)eu~89`FQmJDRO5M!SDZiBTnfDRnt z=|FLENG!h>^=JWba$=GQLi!L7e{6?QJN!iU^0x6i)7LHt^ z=}-`Kxp3a7Oc-;Y*(hB3E&wRrLpm=3jY;6M+~I<8bOFJlF)#0cWBM%f>mY8&HA3_uk&`*JpaWo6;R)OnT>Dy*~1^u19Ei%@P=N>qvvd zB=RNN2HSQn@ zN@Em2LiD7a*5qAz9fCb5a29?+-OxVQhb*ZwN&Xx2){mUSN#u!CF9Z9~1a5+yxCsWf z3(>dqsv#3%-u_MTJ~gv8_TPS0v~;CR|FZJt8=)kGc&yiM0+1G4Hmy9BU3RQA=;ExS z11cRRjlz5O*2G_Flj=_Ogg?2DXRt)H}!mD}1tE z`1_0HQH$Lrm`DOBcv(GCmSTmHaa~5poQ&@($ zl88uwX~1MfloL3Y(Q5Ga>!Th(AspD_(sdz;JeD7jql5fkX@_`_9y$aqoPwNeRnJ1% zTlWKE^u}swvC+%fQ-fV^ogNG>-8b-jMc%Z1oF7?ut78>RbrU&`5QCx8ps|2@57DX1 zA;z@qss_=c6ddhOH>dMwpc;xh1`MJJazN4npreb*alO@e%R&rBf@G5|C121Z7RUi4 zxSJ(WmWq-|-|<49VBhvkV_BTnp_!z!xs!Az87n7zKwae}LY&B@I%u3dQ~3N$Eh8s=#37jryx{nYFm~c_D0LrIj(!wh)akwdq+Q1pO56dWp4^@MgYH07xElNz= zoOw3)RF`;8q9_oV$TrYSk~*T0%Eg~eIe#g+$&7ky$1cF4@XJsCGcS9HiVFT_rAn-e zXXX&=Qf;mcw~vI&i!GcE!!LIda6^3vp( zJ^z^RN4$8$L*505CLgI$e5Le5>ChxEZ{SC1flqKnC|$phj}IyCkROCc7b+NdZ7FUPfj5DNZpvC{oV|@jr7gfRT$NBftwy3B%yjcS)?DYI~UR7D0&d z;R2ZK{*z+{?eVeAQj#vj=+rPfw3lM|UljZPXyXq^8;BCglhA0g+$673-11(qCECdk z*D`H+MDRkLqB<&DY4-2oD}Qn~wl7kJcAEq}|W3Z)G5RX>_VzUGX znj6Aii&U3{nUgne-h}hsl5BQyM_fC@86?F&kX$VMqIHe&pqC_|TH8q`sPbYR<4sY; z8E8NzCX+ZJW-dgYl3m3jzL+}TF3uR;xKYWJNqGop%ZUl5eCH|B@Y9*$;<7^r{#7}> z6J8L1^I|xdo1)2)apFAT0-_zjaohMo@mhc^WTOb!^zLe-$oxbaBB_zxywIsjxOj+Q zXPvWmkR%(#)k7lI4x*`&oLzsRAMP+Lm>_7dDNMu|CAYj88Sy{#QB$?7>x%dn6QxIC zPaE$%N|_#jNM;rn=RLPsn zbUWJd!{jK`rn(AU`WL7fVppA;;isH4fJWw%8#sz+n#%MOX5dL#!O_A8C`0$Bk`L>C zatS{>eyjS+C$!`g-!}suwM^U}1O?V{lf|f{4%IJK{?Q5Ayr}VnjLAw4Dso06wW`MG z;719{Ypj?w*x?1_y(7_>N+_HWxeUbd03{vxhX4l;d6KaFJc{gaoXDY%3qZE(5RAAP z+O!No#!q_UV+mxE-#?>X+T3Ky(W}37M|s%8_p75{c6%UI8O_a@wdxXe7~Qj4EzbSY z3qYveM(8kYz7z#t>@u`9$Gi^cFL-pN9YF?7Q~*2}r8x?8gaOqoQo-id-n7=q;ERCS zFMJRnK!7G4p*KSC7JNt>? zxcN6;)H>sI*EQvMLYjxO8b^JSGmmB4MRA2>c8YUx;F|=!#N%I+uP%Q-Wjc(pM#E}| zNj0FA!(2|;M@gFiX!OFxjYua8lLbX5HficL!jHg z2ymPVKfT@<%`=jhc91_XH$0QW_L57`%3%3n7a@Uuw7f0>E5MzOc)XAN$>RVgW!}36<^lpbGQ#*dmtFS{acmxV zgVK7xH5z4`;GS;gbuee>CLJmg%qhLY!}p)EzSN()4@QG-F!y9Og8&-@jZf+Z!}K!> zTThEc!;e6$IsBKK+`OrAj1{%M-qvpx37f5mt^$QSZTJCjGwU{$g=+nCkOS(T)Ea zgJ)yiPFdLAF60crf8zIWgr%(18AVGJ%?8h--Ix9g@R?J3_XI%Z^aFT|J5J1AoCDd!ZMSvJOITe^JhpRc4UuExcgtu0EBeG7xOVQ^xX(TTNuf1*i z7#raYmHcNL_#FJ;!L>4UqI{(Z?tm&Hy&J0n$;V1GrPo0o*&dDd0-&L_CX(T3S~ zsmSX{IbFB{VQJT9W}*af z4B;XmSB7u&)z~6jG*iu5LfR64k z0MUZHicp8#hJj`xL2&B6=aWy>1M3z?Q?yfxZeMh}e3cs=m~-z~n{e^Ln4|X9mw^L# z4Jk3A#EGlVJ~a+;>Okw;a=P~%-urqlDVb38c%&fcuMA*Dyp1N@=O(!Bib76@HyImQ zX@G|i;~~*~a(Aq-Q<|GY49=FPPJaKvx%~CT=ri>4{9`?BD6bP7l-SFG{K!O3!lIb% z+Y*(e^Do7FkYC;(-4_3%k)3_{lN0;*GVzl6uc=)ZZs}WEfJmIMNWnmiNNC-vjs)$e z18VM}9=R*J9+&Z(oPk)m!AQapIWSct>*3?T{_d~v@z6G+AizV2B6J;9;U4ATAzKi*zR)L2uC+W#w!`LGs8yid zO!B+q@DI>s${B=w4XMq-Z|frL1kcnw#|Ht*p}zOl%S*Yri3aDe=C%VAUgdC*mDvvp zitW+OL?7(l)MeX%wwTv~e9IQpwV5A7`6Lo|l3SGC@*z#qa-v3}3jN)N30U%`tw91u z;>Px)wv=IifRERs4mfK>#bSSqIUA(y%W~E@{VM?q?+<-+x_n0?o!5Daw0VKBn%C;j){pal){cz@pj z&HoOE)4}!iVYQx1Vz`FsRzj1NB4Fi~3Q_v2Oys2jk&cgAKO(q2WXgPeI0L|lzF5H= z6Y?@`!OM}Jfl1;9O;sokNHJ;P=32C8h(6zu&6)-Rlp{&AixNuWav3J z*6Kf&1$c08Avz#QP?E7w%c5@WDP24PVhv3iVbhihl>Den#EEwq+qPH z!alX}{D?3J3w`X;*knW?ul4TIha zn3&A0^2>5q@mo@?l(kHtw=rTIeQj+NIy$vY>gFE?qWUMgJF)h4rlSA1t?l#Lr0MAK z2NXA#d@>S3Lixn-Jz9EUfq-P#BvIUy@z;2%(dyJTrD7lrFhc-U^W#idg*jkMJ= zar>BL+jEyUZoi(tp^VUCKp$P%lU$EEDbDmHWj$TJWf}F3;8{rn!pGg|c-xys|7AU)s zuyt@(o3&X3!YL8T992)at#MVd^fpw|)uMX!iY5Sn{Xj;7yK?h|k#A2MdV4uS-h{@; z0t{(Cl+dSxny%=I=VqB0<#$1b=M2B4>rq4%jLlWg6=UOoJ^pI|#E>HdL%(@}^W=FU z`e8&BME?;YDbe;E2&rnF29SUsx`pyz>lCn1__rK{zXzhfvo7A|X9yt=s589xdZ|w*g{(9Xsu8f5JeH}b`tN=GwOh3_8@j((Lf@)q{i{2;?noNj;E5uDa$evCDM`H1 zcd5Y2-}zQ((E|Y^PreMqo$Id$e=8&{00YR7C*XN&a`xB4I@+RRJ?#%a`3pobSI}r@ z$BwMZV0OYV^gk!?LD6062q5wAqX=G5gv3Y%iX|CSkj@vt1Rmlp335LxniwinA9wbr zx#G1HUiT-W5`FkTueM6q6oT&Id(uL6is1Z!LTb9k-Wy2;_p29qgC3s9Mh{YQ3OI=APFcc)2m-EnX;G-fjOu zqYLKz`>7#8*!Ou-Rc#Cegt*bL)eUP?~;5j56fKOex zymXWYxNQ%-!u0LF;sAK8`|_7^W7GS;pA}$~(kW zS+XYli1v*PKgjk}ZC8+g00FhB6q%5UpmS=RZ88!Z-38V&-+qMh0QE&0dgIjSC$EEd zg`A+Uf8jB_x84AI*Xu#z;trqUgmVads(IuS|Gom0&(vG)E3W)NsO0cn&dAPXc8Rt- zy2Xz_?W+{M$p;|5;j^Hta{v)*?$1y1qvbH5wMfxdPzrRh5R*3!M?99!&v86>+d)^8 zMzf7*Xl`zpwfu`gQBZ?NFIgGpbC`Lk?1>KXl`1486YX_a>mdj2>d z^6}eM9K9Jg#@>9{G0d_S5SXs!=hP!MajlCzLtIi#vO^fXI0g!z0vw?|Tij(uyn@KC zC`}Mnuktxwa4}`N*`rk~gw0IPNg9ln7>Z*Q{(XXm^_Lnj15_hPC{CG?i~6ljPX(KW zfh)={5LXURp}FFQg^}ERqfg?^TUIB7j?X?;^UlF`cdrE0+_S!|=}+5;@DVEHPW27G z;EHwf72y{Tly4;4#>c1*pE|h*Ey8%|>g`4WX~6S+aQ8-`4Ne#MU6=v=4t|;8>h~|| z^kq#*1zuR&ubmuYhyXbPxJ}vhwd|z#fKV6@i#_U+@6m@xtEV z3%&1G(ZB` zkwBzvA;L@;lpA5cYs6%jx7}_8)=rM*N6tLE;axLh@o$68+L@@0=N?b&XeA2j0F<(% z2nm4q2>A=IeG{OD7V{^(M^uPg*5d5>9=^$Jx&QUzWUARe@~q@3Z&NH7s7s%UBl-eJ zA(3tEBHrd_{3^gEL)hxA0T1#F(LTXfvUpcL!xq6%jEhBtsDN@?)Wteoz^sY{5E@~w z7(|sE>`kjr(OkNaR2_v*>^mm_Ec%HTk*Eed|{i1NOi8gO}#RTZo7aH=iHp>!42H1Yb(4z|@U< zuXn%6RK)t8u&Es&5Kyu3uR|dcz<;#0Pjny20Z&fK64=mkVwWihG6Jbwki+y;x*$OO zeTs?>1J7#boCZIT{stY1>3Diwq$m(oguCMbWT!B4?S7-|MRFXBLp6W>Dt#!%@$(w6 znAnpouSW-q_@+n=O$J89M%_{AzAc5}?=fM=X?q`2MY@Tz+>AHqn$$J9|)s| zv}@7?1u*J3&KsBpGjJF+?4$Tgri82Ia=wv7qv3JUp0OQpv2EZ=Mh+s7Eq8hK)#nGK z8`UaCAv#TBGK8@52nb*VR;@1>RRjs`5+qUAQ-OPmwq;Vb2v`@DRKx)S6%&p!gTg?h zwLkSR*MOfp64>{AakLbGo4BeJK_Ntg2X{}~LL+gu^C^7M%S5;fd~iO9J?!GDeM5Rf z{tEizsm@RQMHuywj9lKJiqw|i#QA`lyr#-(!2~OY(Jy!)0VSFG*d#|~&*%(^lE}zZ zV7v+5&E^A?L+RKOoKD>(O7IUVS-9x_L^FOVN0(UW4l$nl&?4o0!A7pC4xmMl(go!6 z15kr=$A#yT_dlQe9A8@V==*B0j`ZeiV*H^`4LdV9N?!5ouoSP??$n=<+0mh(u?%#8 zB_x^JLkK4b$-u(2eBKLYKoAahgA@1o_;Vz@b1Vl0j<%g(r?NYv*zdpp=-4_v9f1Vo z5(J>dP+@`z*%Q;FPM78R>_LCI&{M?$d=s(|AV1AeDiZ(ifP0yDne2@1vgM!ff8lQZ zpGoZA2zA9YqruO30`4uh(yk@7(j>V`MC;K4=;`H0%vA7JX=Pkjr_v2G?;$)89HnsN<5; z-lsIQy|Xu0JSH*suSMX%QojtJ{vX!e)@Ay?S`B5M0aM^vf;>{Fh~Fi@@J9VXq5Nl0 z=xIG^!<^j;ep}m#2l2!0=&Wyh=!!804*cWw8>{=D6Xg7{Y}3%j&}$--@t%|yY6q#qLcV(LIU ze|#R(yIT@de>|}v^1g+C6)|-AR#Cw3F8oZ~i$Jf&m3_}NSAx|zYEoi8a}fA4bYo&mw1wMV zQGY*Hljdn{*;#!MKl}aEsk8$)slT6j<~=@fT{$tWm++r=b_D*(pQqMc1UOulj46}K z;BV&vs_qikz|!NOh$v)a1{_95b-uMFu2v3V$=J>3FPW|38bKI;!1i=@#J}WnVaf-D zGFNRlG{7mh?k9q%+GiAVpz2A9#4*{C(!INl)v*svh<6?%{k$0;FT3SiYxKNp`4Sme zTYr^$dA;C?Ko8q~1sNAg_$ReM>I?TW|OkJ$h6(l{|7Ik)^ z-vv;fxMlovM*>hjabW!Uc;V45I6K}6=f+ob6?ZH74_O>GZT-ykv~pNmit{-BJ=OCa z7hB38qoCk|dLsW(gTs0)#Pz;`x7(TywAk}sv;`T6@<5w2Xoh(0z|KX#^Q=UmwUYTmc|SlxIQ@l7(=~DP_P17FRsSu3z_);H3AaAjVL-G8OjU&+q#oapHqh3I zm**dO|Krv3k#V_9^szH2Eo^D*L1FOr2*nA*_@ytq2ATm4=D;F!BsB8W67%CeM^1QV zziKvUFA#U(D6o43N0_D!hC_W?WIQ%{=6eqI=N+-Lv~W{J*~cXwZrGnixe}3p3(orF zaU~3$pWJP7N>YANbcG z5;(p0P{`3=V3OyR!D&DTsPNch@=koKn_rIK|00^bF%6a-pp#)w5x+>_4j8JDQ5{ny zXL-%NDE>!L;L)*yeA|ioXQjRytPX03?}ZD7M({l(Sw81Oe2kz`cx5zq_hf8XAbJX>rCG1L&fes?d*0&u?1PuuqT2=iy{@_+Gf4$?td? zAVA2BJ?c8V8Y)h&p(he~icSp7eOZ+}Co5ri%IiTf~y?m;{b{XU_?VQN`Qc*VJ*f zAVH?n5jTeL!-o9Fetw$A-HHQ|{oG~EXU#IcC=L+Tw0(-uy=X8;ewxg?B$2mOCfz^2Y+RmdBC7cUxV!Te_B5erVzWghYp9*%R zXh;v%GsT$*A>aM-?EH5 zC__c?iD8Dcz#FR{>%Wo3ob+q9`y3#VY4lF{f9IFps_myI5Pt!3Ks`Q{d3@xpe%FO# zI~#nMCPNFK{T4+$NE1RWO>Ey@I;T;?gHr>X_dBCuA@K9RuZ=Xk%9^>E>fZWMZ0HMk z4{sd?x_EUY`^5QX`$bD)OA=xE{TSvtAu=yygH4?@|>ok;@VI3}jo9JmWs zAV46*q)6f&FtGe>bg*q;Xp%PHx_|$&XeMGn5*%gWTiuA^p-*dd4hiMIyZ=B8l%|{! zu`;EI?FuEEiUSQhLBK(zw_DPH0V` z?z~e}7SuR2?3O?mK166a9%pn1)C2q^W0rx5a?g?}P+&GEaIm-}AdD-vuCz0)JF%A& z09-Ql!^kdNql>G)`BIWi)sLg7=T7+AX-%~WoDxt$F;afy^Lq3mRysqt04XQP>qwQ- zP7_3~Z-oI~z69Whcb9#U1TgU>;*InifB<`^+kY%co{4*`^*o0DFl9WiXR(ERSvqM? zeT}-mfR6y!YHYeYVHDdDcZAMYz6<0{4ES!{9h%5_(H zxY7l0>+SYHsDhn%p1Au?)NHlVog&BhH)E`KHfIR{^+mxnr|P|^|}^Xwq}-$cy+QFJE$P`zy!f6nZTW$Zg+DQnr6EE!8w{7~7G zh7eJfkO*h&WKW`~QHo>_DU}hS5Jkv3XhHTZyLaCIVCHk4=bZb#?(cQodMFtQ*ezld z%YT%1ltmGurN;-gFVD9Gd5ta|$#F=N$}W%MK?)+?YQ$fF=+@YzGE%rs!e_YXo}%>W zldcGEqw8#41zb57w1Zb}ta~C$FS*0dx$_rx{@dHSWAyY#Z`ZHyzj#znNiroSIwTX~ zER?bl`(p^e9!Ri}?0K=rz;hY#x_nd;<4l-EDm;&tyFw3ax2s4(+NTXuY-$2>tEL`+tYuRsTL!8_ILtdF6Eiy*h+5A_X#DYSTs1)G z@6J$>zkKQ2@@o>ZY}(OFz=szVF0(`_JPwy}&B>xd)EggQ8PY2wEdko#C%->%5|vn) zyjLpfm*X%rtzh;vHUVM+qU;$BwvdH5?KE>QAc>L>-GsNsGBfSoY_JYoGsJ~V-PrHj zTno%&QoGS?;gj}X*!ab~u#LgG-COO*u1Twwlf1q0A8I`{@Df)+2tdF`ITr-LR~yD_ zvGZqiyA3sZZ{CY~jzwua^3UN%f3G{#y+dH+a6RqfE93gJwp0jY2z>e7R4=yn^D~dU z?NcHuE6be9r(3VCqhE;@2W8~Xi2rtc|9xzW?76YKy+&WFvkJQDE0RgFhP+>&);eX( zDQQ)LD18F%$&ooYNCqky6$*T%9@hFSWU2Ny5~|_xt@lk@{Br4J`!xQw^H0YxuGl z%za!hr}4;OwuAM+hfU(HWARZ=g*Ax8%m>k*vm{^Pm5K$gS?O+(ibui4CFt0}#g?&( z-(Wv)-_kb!4C@1PluS|TjTWRmAQeeYXRmFmTONl@ND@a)v$B)`JtB$?_P?2Cg4F zX@P$jy#UL>$OaJE19!-usXGDX0hc&&BPbE&B9dfT-cF?l@3U*6Nr z74@PiQGp2sHl5Q?;#43%hOWW|g4yYeCQzBABQwdp*SNjF^SY&Bch;q#!u-HsgmXr@ zrO0R0Kyf^dBO(`tiVk+0OXq(%;Tt zJe6TxQt9G&#OiI!w6y>D{)jQzoRd!fOe&4LVgqzxSq_;zye%LVupicldMp)V{0DO5 zqtLp`l#Atd3K>RPDC|3xNxKzZ_fhu9Wg-JP>H=Y zLdNSOg=fgoLFH`UDN$;musC^|BpyIICFQyEd-I>Z%+bHYajS++ufu~_58S0pWqS@Qc*e3ej)YV`|$&BxVG3SUZu)C9W(-tw92C-5TA_Oj;7Tw!`R zI2U$6zScWf)kG+PJ9FP;WY?D}1S@9gGJ0geZKV<9(k1^f$EL4$qHao_yo;$-3eZZ)*_Tg_S1&Y}~BO`r!vy0Ze6* zCxR|~kFGt>Sf*-e!OFwk`ixoIC7GQrzf12`PLC=|>v^rD5FgE*7yHW(18^t@sYw>V zA4GieVo34Ck!L&#A&hh2Dn!!QzIfsbg7H^>i-3zUR(T&x6oKs*OcVU4GpjpdV&udU9>+y1--Cdp`zt@DcCZLlh!t5F~mn{pdB# z_92|>S$`k?ngDez7pA+1PMz2(@zpR=48a|v<@N3b&0?j|TQg3g42OZ-07)Zbo3aZJmVcx{ zD5tudF$@BU@Q|P);WPbu^{}UB69$;)5=#&@4&Wx-QkqepnBkrK9ba4fq^;HBp}>mR zNlQJ7cF#$KB>;;e3OY7vqdoh3gv7u3^9g2T(7C)O3Wt9yA3LHx{TR?>KPPX=C3GByXb-TJF>t~$Sccb2Eo$>h z&uOa<8m+a-HE$7BR>dDv*}ji!M+?J-@EDf*q~L*O!jaLH)}Bc2TbK-@A)JkVXkV@Odup8S z@sqzSZk}DVtMil~g)wlRco95jzp8W-sG>Z9k|ieWE_R8?;q@0ab`%)%k%bfPyubH- zKUJ}DYe+|r5v3c93VCfLIv-UZq@JJ}wYf(s=Sx{+^Ji(Ft?;$Sp4s{3p=2SOtUXmK1Yw^d<%tA6#& zJsD&>DhGkhDlqR+Oyx8!BUycr_!h-yhso)s4FlNqaPb9h)m6V;eO1WHetcy^=ln%1 zMFfgs^btLR_b&r2g}7ostcntlCL9A^C&~-vt*oKDbMTzu_4(au@j0!u3HmdAnyZ2C9S zuW%(%5cB%|=)aDN#wY7G1SJmI1)BSg-kpz&=ys5vh4`^WYQ+LsZVBZ?f373%2J!qO zVsd)6k@VGrO#(tZQ2Pr%Cf}Yja*2Fvtf#E%+x>4t<+FMx3OUq1%#!-?&upo&oGle5 z4Rwg`1BTb+nbaWi%SH-N*W>>eqsC5)L~M88-uU$X?{I~XqP3U! zPqoyz8%pBjToS#1bkT|iRN2X1Xau-nDmUnZa<~lb+AaPB5>k*os_GYg*CjSv(34?l(R}$_Hr+v*%Rj%H(`_Q%b zWd2fHH#eOmJkyeh5FftOdoBa{_KfzMAeWjqFOC)!R%@aSGgv?i-V~R9o>J$PKA_#x zJT-z{nuNGdfzkp6=t5UT>R#%`Jpy?ZYB}=No1lqOxT3e)gnc?sZ!}_~1tYz5a*o8O za;x{*C<>Py={`EIqhy`V;5DpeVrOyoZ`9Ny!mdyvi&+GfYbRP~%fAT6fIQZx{`C7r z1w5>V@K@$!YwR2R36nNlfCYjF5H7*Lf_87e;N9u93>f^rE4Qdgtmp@%w zo8Kz^MzvSqE${2|eqVgf*aZA-O6OG0}=?Q!?2GZYpdy)I++aAnz2pRR(^JPTuEyeV2eSc9YYA$ys%D#pT z?D>@5qbwb?5NO@4rpB-<|IZ_+X`B@VtO52r;f1ey4=-PD`oi>lbG|Ha&=y>?cgc#@H*Z=ny|a{)a+KE%3>C3P@2p04yb(^22VFXI4N# z6!$iL0xQoi3qM3E>^*t9Y<9MrXWLIJTg|nSIM(lAZTCI1PrIVm&`aOGLFkHPoGB~Z zew@((ris#?a?vbePXBXT*po4wXM9#3tj{;dX5y)5tXqU>4@e2$ktCwAAjVZQs}?$` zkn^mG>8=QmO&F1H{AH~N^|GQ3^bNOvR$P}*xm?$xXXNEoog3nT ze6$S?9!IHo#mE63@X+{!^KX#ejn!Z0U#Ilo%sW2|o;rD@c#un1Ek%R0gKtm`-u}|G zUoUV;n9Ufc6+iq{UqqY7M7sDEAuC9VL-=h>zv26> z={xPvC!|~L;U}!=*JZeew4H?`3N{}xhkGI!<>fWgqG!CKn>~yAjfb4H->X{-jh!3I z9r;R#A8wLC`l(_JT>$))r6(E#bb3!1J@`1M*&BJ25J+e&9lrsq;3 z>jV5bf8AZ$!l?S64spJe-l{`f{<5p>h?@dbEs?*sG*iV%UvWzeZ(BZ*2R!aTTs;P7 zQKlZ1%j_Op{W0Dj-$Pi+Fic@qiFPu#1#rAcWeIZ)FO-9Jm8Z+jVa{=Yn+f%W+AE;f zwBrgUxbZ@8&!abb>LyDd0a8w9HRRmvKIfa3EqbIaO3B#pbS9VMIZuO}oC&8xCOt`R zz=|nO=qPO~u=K^&RmY|wC7KD-?S5ptIdNCAETkSHhVGmX`zi=(SgfQZonqRR_&9kC zNrA_x5G?ki<%5?p%L>6s9QUH&(6 zyN9qTr`1@p6jkEoE6C8rd!Rft?|#!sM2vrWIp5WhWWN$*etDbeO{Gb;wWzJH*j1q^ zF&;B#3pCYq=R60kaVv7Kq9)^TFy(!y!97+Eh?s&%k?V1ng^}O{qb~inOm7nn6FdRD zV%)o=lcj7r=YDYc9Di`}+#C0fKl`-5!azXNjuJxL5KmleB1PBMMaattsN7jS_`ri7}N>C06!`F4Y0}kP?glZ z!50nF5er-@q?|3uqbi;5#uwH7Ch`#VXQB>_+adLQs~DhxH)g93{-K+nTK2co3;*lU z)$WS%hMmq_c0A+f+^Q$?vV5=Pe^jy5*^{l5+N!2#{YUtI@!#SYtE^zvN!eDWuNpBy zjzlNgT|30HRp?>eCvIxCpXZ%M0W+2a7T_Z6;}V@=aMTzD|KWu{qj3y{MDNttxD&Uk zF1TpD9(t@lR5{@n{NXIa}&vs$l~x@H8UQq6~Pb9Oqo5u zL7pjA$L9^AnABVESG#?*I=k&BxAUQb-Ev0 z^9QcsiQa7pNcw`lmLtXlNPm&&?$DL%RkXXW(H*VF6-WsH{R39lnJHCWOc z+Xg!M$*VvYVVsY!zr>gbIvtjm{HKYVlmK%a#QO~e?F^anLoJRQsu#5s^ncNB_!C+o zemcQZ!=Iz)94e=&xFjUTLs9mS&e2m=u3Dx7AYM;qJdFAOS=s{$lk9Te$ zpK}o1D1gTP@ZD;k->Ju;Y_zScirmV$pe2mx*=Ezn?0Y-n`=aD1zTcFl}$lqy}>u30ARHT;!EmQ4L_eG_e zUn$%7|10QOlIO9p5xH9?n|KmIhbqP5yg=4tw<;R;|KV;6fdaIB(?vj2v%TLcD-AjF z*M)*!bVxg-J2ak+uD0!Y$5#>Hk;4oOIjBFzU3g2M#SHTr{U4fIgH9dhHNBXQNM8H0 zFw8X;>DD_vDD?4B1Yzk11XGlpXunBgKdmK;CklpQ>jnanPO6HY0*mNbS4yS@3s{Q2 z_e5KCTA9fYklb=*bc9Gq+FsP&u~qM==qD-62Bq#@4Ny>vvMZpQ=B& zAH5S0a5!_u6J$h*MrLNh@x?rDuZ3gz89F2x)3AnkhoF5$h8V*`5S8~{HRYnv#`t@N)0gE{&`jg^htj$cu3hMXFPirI%hSereVm5zy!?ls(EESUVkURdp))9cV0Do-VCs*oHwgppn6L1>KsvB& z4xMKoyaeZ>MiP(1Lr8vQ(gdW-Lz&eebVV6pLz6fV{)(Ovmqv_N{JtK~OU9>sNxAw- z@6o@9i*3t)LuQJ^SehEMC9n)2M4@8NqgUtY4?I2xH=s@x7-9Yz@FEeNqozEP<^-d~$g<8?!vy#XR~q<|pBK|K`6+69eMdN^6LSX{ASn6f`O*_Sk8Q$~6jf zmOk*h3Mp&(uP3!JL4mp!53q^vNPzy-GpC6|4_3w#AlfDPgv!{AXG|@=`01vd`&p$h z7`ziJUOE6gD;lEv^%LX!~>Es$v%Z zgw;lvAO~Dyz$tgiiV9hd3(_AVz~5Xr00 zI=BuW!4z}QQn5Mab+QdRn+twlBh6-Bj}?p9`0>ea z=j+OfWyyx$OL7DM%JH;#B>kX58 zZAM2)J7b}Q9s+HPB6>Kg99NnB_*-f*9t=vIoQ3LjfV}vi6~pG_cNRn5CVTsDsTO1o zk&ZQw>OH6)?$(IVo*AEMo*=8dI>cmbn|{CUwO`S}>hq=N%jfh8$X_15`S~-uJ|$3N za#=l4J0YPU)1wB0?>)K465PxfnMg8K)`QC^d)u`mEBOZvejWK{90uw!Ms%g?h=&A% zmon*{h}&;C={QUWd1pxiz!6o+f=EX^W@m||tyk*L#1tjN?DltW&RgBMcxmg$OFex> z$pTORBb^D*vf1RNZ{HFGlIE!^8xJL$^2)z-{`{Gxg#e+HxxjQkEn`arKX`aM3vKdB zL;}!wAX^rDcgNC=gH`N%IYhS%${c6^4Y-Ta0XQbap#m=HbqW9yBc$!if8d;yqZ4!Q z|GjAvhDstVPIVgMHy;q^h zSW%26{F_MUT&MY!r_OZdJ_CbZ` zz5V%-8`JBQ-jDk#kp@E^nekO2piTt!jP9|9_D^yu=%A6N2+a*So#iK|Q^rv{mF@$2U7b&aaFw?xAz=~2{o z#M^sKEKc{C9S8ra5%RRN$DCbrA|MwGW*NB?`s#z3^V z6qI{{j85^1asK&P+V(6CsM=#;e?9XK#Nvq7NvT=m8GI=!9gmDVG^Z1Kw5btl{NT}!Rz(>%!YoaU4_}uQg#sN>iS{YobuY;eIE=q z3Q_T%%b-+XchM}{Cv~02+~T2NjyLz!V5P*|?OJ}V`kU~|)I==+ z=@u0H0e!MVNCL(3+E#PtVCK>6jkmRr;LB0k7<;>(3zCPw4f|$1^Yj1zAh88ws}A{G zC24abfZtWB%?mrlVXY``Dxb;(zP4a_cl*eHMmP5jmzolgKGz{LR}wzi0c&eD)<;;A zSmi+eU-?PCx z{Ql_v{cQ)@J>Hl3zL@ilO>=2VgcbDJ_-COjrZ2rty?x1-0*d2$N5K#)b?QtjlY=Ug z)jjBy5vwM|co2QG$lHft0?_>_Pj3yhFJmz znQoLr-#O?)g>0>dDFlrDyI{$df)cqF^!y-?Kcg}+B-LFhN zF_V$Pi}H-TK9(#BZ}7b5319put9|yzzDd#WBz?h>(A=P*3#i?3$drCh@K}VVmv2uyW{HCM0VUg zhYR#}k{mNnX*`@IO)^3N2#VG+$Pktuz!;<__k>MXU;V*TwE}Ro= zeMf`Yoj@riH_T=7WKJtI6Z>Z+>H77yF{W7FsL^-;k8x{@pY9^NQKa}hGZHJ+5eqa9 zmxm}vC%_kMJGzuEdSkKs(axeK2Te91)D*^>{&0C#tnKvuvCW3vF$U0HV*o$dlQdzN zudqVtU6lUb(>*1%L}aa$&3~OgYr?CqhI;P@olO{-xt)^sap{ppv=2K& zRP7_<>xj&N9F;+t2KF!EI2%xj04IohSF8o7w~-;v*AVI2#IjiZZ8Z#(%uL|Br%O3! zaI9(J;mpR>@c&-#>-t5wda+3x- z)r;-XY1-8+nDS)VkP_XApElLLeYK{I2V8EcGHFmtV`ouOT4k&ktBrO zerdE*z-#J2{%g|Hk!yiFdUA*zx1!CE#M_Wj9CrXSBn&iAo{Ghi7;eoxoBqq)=O1g| zu2b@9&i&q7e8nHJVTNfSNDKL-0IZ%(Wb2CsBx`r;y1UMiQ z{t&u~&N_8?cqOO5t?hYfccT;_M~}YaOXm8(`GqAtk*Vn0M%~{6+nw{mFfT%Z2|fg5 zglX5SkwgcaG2A8^5vpg=T4A4GU&RA*Xs4}|aw6R-xn;>mzo7Dl^DJIWq*}_w7&bZNJ02XGLv84Fr?n2vI(+<9&8KTz#2A~n`x_0Axm#+MndxIco zIXsH^Ysdcb(sDz&OiBfkw5hBGI_1Arzc}Mct7Wbl$>8hip09uy)95FABB#L-v|0>h zzoN03A7&uXxCmGD6;eWT0aLm|O0C}rOND+e{-#zjBh{qkJb!_!#HOj)H~hJS#bj5R zxPJ40q{EL_YfE?mC?jQ~U5%{sFt{MB~& zQT6)?s8EIk{G#A2fBWwE;kNF{odaA1N{69_pT#XLfQR?mfG47d$bX%xE}wxYy`b~x zTatm5&5195#jkb4)=Vxfw7EaxDJ=TTs*M9Qoe-pb+FGokOUTDE5 zKPXytk+(bgCp3e(fS(cQm0Hh_kVuWL$EI`8h_n-$DHCNb0TV8bD`uT${9>0i->ah- zo})L176UC?m#Z8eJ;J{UahL}Hj3O%sq&PX+XF&oh%g*Z4r8ZG_S)wrM@9xPCObZR!3K#Cs5f+-+b@zOc@;<>5vP`R_S(G@6tP|sf zIlm_HoTdKBzbShjvII#p=li}KYH57S{nP%YR_xXfaRz*s9-(%Z@}@{xoZJj^v6P-F zPKUoutTe+6=O3}XaA3W>mLb_S`rTB%geYK+ocL~yDJ@z(Q>Se;z?A5xNlfwrHqbZ^L=~qe zeJ{T_d?4hM zu1vg73SftYX|I$qp&@!qF;gdT)AS>!D#Xz^-uN$dQ`UnNq&p5;_8P<$TzC0$3^2tC zZ+1qm4qQ$%k6(#CCUPR^&|Nb6TR{8iA&y9ve^cBwq@&!|FMkFPk+}!FW$%c`R_Lk;lbu~ul=2chQb5>ctx*`0THkYt{nlEY~YQR zHA45D2WvkxZEj^~cIwQcLQT_~j@8@UsTy~;x;OEz8f~g6tct>^gp)7gi~iRBndF(-!-5dz;_Aj~6t@a^f+8Mn z*YsD|p7|xkmRrw)H{kjdq?(RpjqAXA&)#TMQnR)qEZ)8#*pP3AdgDf^8lKYESzUeh zaARZlA^E)BnG0vle(B1#-VdS{aJgnfQrz705!6*udMpq^i1v(0Mj-7k`G>oCuZ~3O zFBo4B58j{*y|`R^T=b~$;j78xmkNb=S=xzKA4orL{JxlQ)I@+#FZK378#r-+^Gq;j z`kkhA7VwzhBSPL5t?8@HEJveMcjIqiFKIS6RZJz3zJP?~ zkJyO2_lB>}d0xG~HDY+?REO~g+?RL?Pn~cJ`J9q9f`qE7pAMs-7kD&{} zJL7G~zw9cNBMSfx-GrQeOK7jxF`%U))-Nq;>k*}&#w+Hs!GK~(22qKlPeS&b{5Z03-}Mr`u!39V@-HMimqaTKsiM z@+a7_HmX)H4=E)yN^&B|_Ipn?Lh>9#71cx4z_gN!SP`H}+F5`0_jQ!Bl;&>N^2^h- zn)GL4gJUI?**rHi^%x6X$+-v^pUla4aDN@x!S)h0%G&lnDhelp5V3c7z?H@HYPa9Nt8 z`GMum&(~~dnKky0A_F|`zR2;~ajc#ZV)tnGgc%r1pL z*Xg!QVeQLX7#AyDkh{YF9CajpoRxZ$S}$3CnX-E;=N*q5g2s;x!N9E0{fC^571Z#Y zQDk~iy5dJ~QBvhw2$X;KZ2{M)w<{c@V-e`X!Znfd5o{YykDtY)XE5{EpR}OcJl*>a zG49lQkWwr9u!JEVrcfLwPSvj`GKwd@O`B9$*41SBM9zXdQR!rvnV1B3##HC66oSs?6K}->-!n3W*`V?w zDM^xK+G%!uHt`DZ0xUfX`OmhG6^Zh3roj{)&gn?ahh}`6R;RIr57Vc|e~pDHzW<~L zpTk>;fEZDNFM^ZH^H3=1g#o%bzb7+EAe|EZz;H_)`Crj>>D3kHrTrq8AulN){Qx4R#zYtjFS$O09UAHN5P5_9Q9$hPQ#6wj;e=p|oh-uZ_>I0=t z#C-WH3B)rtBNl4fqQU679_(zMuRNH({a)6M8G{Nu*wtU$o?e>Q6BCd;HBTmD-*U4p z?5&*F1~I6fGDP})nlZ}t5{VNljZKd_H*tz9$Ri2_?pC>nHIuj$4)oydu!-PzAi2)=ELuj6*Y;_Tb2@NK-IDD*Du+s6&vCembCzvxYb$;dfj_X(%|L z{th1<5E!>2L7dckClCR5F&e<+3LF^hPBpY zgRSbfDMp@>!SpRHk~K*ZXaV5o$oYyvUo-F`sLWEr5VQ>xmgfKXTdnu{iR9JL&4I=d zJ!U}WmqL&#kP8Z^wL48w1mV4||NK39A&JAl7{wbzImQaR!-1k6*lv&kNl8mz606i6 z{7;=so%!kJY~oBLuq%iBjKqP3a8Rd{Do0%GU*_8`7aMg9d&gR;@(9Sk#^u{cGUm zE$b(ZWb9!i|E3KcPye-dBNuWw4@wavOgoQw5Aw=?GzyhBz3B|%=Rcn^0U6fD{IIb{ z@#l%kQXJj=2uT2#UH>rS@t*^H^G5rR>xH~~^Wvq8#<6RXnQ*DJpBB81IPoI{0(Jb< zV$=~_&lsu+KdtyU8-*i;tb3omRwNB2&3?(2&w1y*x3zzxu_7|BhIqK-#K^G&#aUWn z+Htg1EqsKBm>6|mk9>ibJBnb1y1DX6M1G+BDR*VH%5&&H(i(WPs`>rrzx>Z1^XtJ( zREuItyh*(oJLxa#+8_{VA{jVbBvzK`)m-&kn0^>n_!EVTD?iLv`S4o8w`*`8b7(e! z48an3D->jQwEfDV`6JvHKr{$*nGf3GsdB*eI@g5@Gj_*ZfcMeLj~Plf#-PA>Y6O|$ zNINjvQQcza+2z_8Uo#g=+jx&RgMZ7NMDa3I<1Fx5kNg{uP)3*~DNhm=ak;iNC2MWt zESFNBi1r1ibB>L_NDJxXdhp-njUnx=!7tu<+;~D~oB52+OU&2(R#S zy=76bm^sWZoX9W+m&*)RqE3H%9{Yzm!gho^L^`rkH}GY{6IFqGMxMegpJCGs^f$Bt z8xTc0^Il@iRHXUHrZ5j;h`jMQ1|(Zs%Yx(SCNEmAJM3sRVCiKyrWnyOU-}aW*iWy# z!}l1Oy8y%5_hUa4y;&N}Z_EM;nf_h72Nr1Zb zr+ASJ>Ha?R($|@WxKGX>PukR#D6@T7Eq6Jv^I9tpr@mc}WpDU%p?QDJUxnI9q~<=F zf)DO&nLc~QeW19@Sgo$SuQ`7^Y5(FFGRPeK0!z9-#yJ6zInmv!ocYa_k0r)$_x)`| zPO++n4fPTEC*I3z@L=WZ-52+cUU*0Q2PqtT(DUY9 z$R=a)!AC|i3E4_-v+ct6`(J-Qv5|5e_7+nzZ1b2rmQ1SJ&AUBn+Y32}20crI(BwpqCB+A7Q0eDEjL~fsFwq2!RJt z;wNNgAwz=ae0AyP`pnEsi8X-zrGS<8HXql3B%2Chj`_C>-QJzgTw(k(1C>^Kekeuf%#fdzO+w$l5TBP&a zUt(9PJ05hrXs8tGIrAeUil20&aDeL&%s7>?R0v@Ch!V7qO#Dh#5+uv#3P18cSN9KO z?)+?LJ7Em>XOoZVE^_4j=k0N!Afm7E*{0ZPcJ`*$(0CcLMTGWJ-)%#a6O^Hij{s}X zf}jw4s@8xnIAL5Vzo#vy@A*~rpc4^teFN|WBT^JXop_bKNdQDID}ODk_nVXsyRn;TOlhY@-+1fUYelVS}Uho&)kd3Kbi30^}vMs(6}WV7CmOqu6{B9g4C6{zSY%WXmLE3HC87e~$} zuM*QSDW{rU{4}s@XVK}%!S`<0XTz`^aret#rFjgnVWM-X=-z&*AHL-rW;2S$fW@J%fWaVgI(GNXhIaO3G$LmZ<^^Rf-Tq zbVG$d$41!HHB@+o2A(U{sjv8Z=W(W%Bb)v0c|r>?VRq@Xl4LF9XGAA(A16(q6bOWd z0LKdq#JlglYP@20n(8Wgt9fC2$9MH$7DfBG?}{kS06D^ay~&t_|0>-(CQan-oIWh5 zefi>ZHjVM=?s5TBcKXLrfrAOlloQN3nAI>dC?`hkJRy8f?|`7zZ1$X>%iS`8Yd3B= z)M+G#JFV;o1)A6{pbOY4Uo*rLH!-Rai30s+lV^h*Y5A>av)sX}<6r)?A>r=$?L84W zPyqZ`KxEe(BhVxt)iU4BN%KA4s?k)*{8oWuL+q5O0bZs^S54&pSl{unv6_D;CZV^Y z38$XmfE>6DGA@973HDKz&8$CO8L)gAQxnl%`{-XpOn&ku(IHB_xRjV3{XX`2?MvCgM0cnKUFqn1J5f~ zr3fir!gU~TYrw@{IGP!ko1}NOd2}LnZtffdyyd%n`(G4pWBd0&Zvs9p-_KGU{tj(w zF2rrBUi+ny`rU1yzn|^TPb={pMvj_~6bsPZ{Vxep!1sJ3CCB7D!I!ubaM0MvsX+0v z7arn-5{nE4wzw*KA8s>S_mUBE2Tx9yU<|pDWoO()LroH2@&M^mV!6;jM3y;f2W@qtc z2zwFWNYFoY?G$n}3Z6rjTF|;!J?CV-9Dgb``A1anusPq;uWp079r+#*$>EDu^T z#yo&bgveZK^5W&yg{cdM3M|3#+b^r|oY#g_P!ePcV|fjD#=_5vm~Nixy>;`Z`|EQ9 zrUe3{A}qf?Ks4MNEn3k$9uBZA) z?TlPaVL(Mnw zfdeB!4g$QW!AaQc6t$3Wa6JFbzqSNBHUYR6Sz3MYa%6w#1kMb-Ip3y)lsG|7Cvg_w zhG83;914CN`#bi(A6v-v^UspvzwBFhzapDO7I6nf~u3pT)*q zQPv9wsQ4BRq@%x~654>#e(wgO#yM(u5CMeB9l$3|Pla+dTm{;93v|Co!Z;DKDipF2 z?}(uCfH-cP1#-2wvYy%K3b})PP@h=xX6(m`UQ<)u%DEp?myvqXmZI&e2mJ@mg2hy6 z`d4w**}c8XL9{oA^fx~4?CHx#RK3?~c=w0P6EkqZTc!#9Iz`r)^(pdf0z_a=uVz5y zPH;4Pr4A&Gvwz2!mPdyCz99gGOoJITc-2^oBzY6f@o}~AjFy(##8hSpmFYt233$b7 ztN-h__e(;G<~-5ZjpN2mexeB%kZXB}B?XWw*&mP3#Hu>u8qZxivXuT3=fHC|N>68yY;QJp#XTcR!8%5zeMN4-MjkL6U;0&EoA}OJS z2nZ-CH8Zq;fTV;10@9_Rgmeo?H^PvD)F@puA3xx(b=SJKwW72oBIlQXs4U@_6&EykfJWc0;-9%bWiGdofqF0!r`Xi zG9ml>H`by6eIh~hj_LVeG4osk7WV*f61onalmPKxIsnvfn@54UeGHNUFz>J zio2ud6>rTH$z{iU#n|2V4@ROPBo1rGTp8#;y zo|${Kzu!BU2GNoMj6e+$?*3;)+pR>XJ>Tudm^wI|Xna{Au%gA&Q{14t00z|&S|nDZ zX?Nl-xUs0DOWE4@j-C$;AeY}>eM!3InZ4(}rEQ?we(Ub|o9S$o<(uTWV8cl)v7UJQ zDrEhU5ew;LBRYur3=&Yt)sKPf7eS7`PlBZWyJ7TBaKfjNXA}Vp{D6**uZBjn7pq^* zQ#ZfMHz%zhR^wYJ#2%A%hRbJ8HDbjd zy+84i#$tA3`5FOwunWoI4#s)DozM5pX9`KZ%B4 zv38@9fKmv+@H=YGV3v@wX+#Lvu5GoGXE?ZS&n2#$>EtTz`y|DQG@YKI0e;gwV8$`& z%N;{?<7&C;jNVVYzD`PrXfW%Cjm1(o0$C)%J8V|++IkQ?Te`X<)S?sVL+!OjezjuX zetrDS7_d>W-y|_I09=T)dEx9Z2syJGQi1&4s4Xd32 zIVI44O5y1?89FJDMj&{;`#js-sfK`h8XZ=~pZ82G{D@v?Qe+j@t>KGNeq0l7F5Y*e z`BO%@7p4=tl2Bi&c#wESqhA`Vr;}d&=0-ZB0|N-}?;@SS@Rw-)+a$0 zwX-V2*Di7J_K>2;+vEQq0?#*5%3z4oJr$5#YVy5$UDgH$&R0Cx z(}LGu#83JJi1}Q(fLS=Skl?mLtd?TE#LPS^XVEIA!1Ud(vIKnUAW8qpgIX9<{K6n- zFNXR*Bf%RV>K`GmXgJn{TK&;h!(jfh6Hws+%z}Y6V0Ldr0r`kDLJD97@r~NdM$OHA z{qvF@NQ&SpIq2Xq`p{N`wRaW04Nkspp#d={s@%E5Cg*tq1LqnnDR6QMJxBanWUpA7 zc$je)qnO7a!c#$C2nV>3&wvBsKhN4{o@eI028tD^^Y38AsINYN>}>!Vl&IEavnWWZ zdJCvk_R4_-?B2eyvD4E_wO2r0K_`BWoAyu=TCB^n+dsiAk~&#fd(21Tc;_V|Ke)#o z0BuA~seNI4$`E|9?Koi(w2l3QzaEWWG@miTNGB`s=Y-{Bf24KaQsazKPnv+-=fe3q zNVyn7Z(6+wlznDER7$$d4yvF|(3rEk5AI`SF2$^gDrmP)f~?{+jN;KLf@L)%?Ccc3 z4QEm*CuiDPoVN6PF+L?7)Ku&+Y+C^_G~wxuqkMt8RBwEoFhv#8xVp%;g1OqQ%)jn> zJ{9dOWu2U2Z7si=%ooEgq9aFbUWlZ8a)jijxl-IAou`IJgRI%TsGuGWoy$Su?aLuF zAmLlodxED=P@I4%JA4}K=!%^L<*|(R(##_6ddmq~D9^J0@a!Wwd-LFd3lOr2l3)l# z#{m6nXLOavcV%-nfL2wzC5b%7>DdSKwruk<%!}WO3qXQ}tX>EnSQ$o?WR&}E4lPtR z!c~Qv>g;VQ87l`!7V7aI)7gkN@q63QNU%Ee(v7lx8Z=G^7$IBFg#aWk(h+R|an~su ztO%-P1=%k=TZG->V$N1X%Tl|8bK_K~qDebP^UpNE_G*E`E|0m?mT|oYmmWk#+S%P zlstd15;wd<_;Z_z1N#nQ;?veq?BU%2m<(q=too2hc@1{C>YWB_yZ zSyOFy;^6BYGC?d6$XtTjNyW?D8)W_Gz!?>Nx-1R}qcD08+=DWn6<9zbs3~3`Ylt*W zys&6kv|9F$OSgoL=x;JDcj^`n`W_>*Ko0zkZE)wd0wVKW9_(=aZj+~gRqZpF?zVUjVpQAB-09tPNiGJ{Pw(Bx2hY4F1!i%kmQ zmzZWOiD-vr!Zqc}&AcX@T8JI(N*=$8puNGh?s8I0qBD9Psy5V?kqE9ZcCO6tj*qW; z1-QC=hIJ)*P*4u5ZQ9^4S;n~vQNJL{EgGJP2a zs0;MaRA}-Whc|~dROe3EC)EmOM-oYH96KGgw-@+pSi7(OdZe`D6l>E&0zB+^YJYj- zn#s|g;!;mha$ZB*6{Jc^#t!#ECtF)NJ4-KwRi{QLI;xRV`#(P2OoNSrG$Ft(`z8v8 zJrZq75Hpbx=Q|=Z>d$DMbqu8V^C8m>dgaehY-T3qWY`O~4l0|L(r-24z*C*THDgtEPd5~BOlkaPmK0N{$BxgE zV@#YaA8ZUrX3g(}``N$rbR#>7NP?l+BTRte!Wg!^Z#V1kUV=E@IdW!d3w^~B3TRa$ zT44T#VA;Kl&vs6s=f~fq&^P~0^)|#YHW5ywz&4=tQvoVMoDmS9a#{BEJ#O zpzFvIzPaTk!o%+bl&T`yj04^ZMbk)XV=DkO{q7Y>oqe1@9+dH-@8;2yRkoF0s^XW| z7Esi$Zz2DI#vV}>T~#-tane6%!0Z!GkBtq=kYugQCQdRVG2$;Z7| z0ytdS^Dxq^TYKuj4=$|c*UNU%_E;m$zkho_UHr`&D>I+c`Q z{aS;Df`|)Zm=laPrqne6GKxMpFjK?f3ZS5STjPwlFhD~J?vbi+0SR|vj=b(EG(Eo1 ziXmiCmkfa}o7)`N;Y}p^>~MF?=zTAC7}v9s|5E98mk`s4tmT;*2ulq283FJEj2pj{ z8$F^}_e%!8@>2MnZOZTv|909{4h{Bf@Co*DJO7I6ex&e)DP)VWB92bQOZF3Z9c&@n zZ}G8&$nt@a7kH8UYA+lSxb1{StF@PqTIaC0Qd+ zw$bcmS)RYW@Q)wc{pe9^1X3~;^D&l-wyeRFM5j!ebbI^A=QJm?RQZebf6eumf%lfH zJ-#}^`M9^Ii5HlYoCMHFt=%G^Gk^0)&&%f3kMZ{lrAvJ^(A{Zb`+&>mckTFL8Axa89UlvE_#%5tJNV%<;7*j zQc*qQEYHPsMrAxHOdxAP;7@+S!k_?4XhX(_K5}hO3m%mz78BN*yByjjfb0R`=p^W% zByugW9K3KhOsgFIwQM8%!|uqt>wQlKl)-s%b=X`M&@aXO40IC(sVAXBZgGN{N*|gU zlDELme%bZ8SRJb;Mgoo;NMBz|ZVn_V5UYM6o zH%1R~4RX1QTs-lRS1^gWL_syvZ zyhw<8ePhz99JnX+rE%WOLwNiTLO41|_uQKjO$sj-5)u*NWvE*SsnV`~hb{nPW z5_T2VEg|hkI_b9@7g z0`q-o__bfx?!Ac9jQJ; zqf}6LFoK7fP$oS>_J^A)miQ8XDMRx?w}o*#-zc_Q==;%8|RQ@4)VnM1>v&J-;s= zjiF81ez?Ol)FeUMxWr;Jz)3?$$Y$1f{@3%*A&=#L8vIIl)HEk`Z`DV_TIKbKJtRSP zhVcN*Bc$;djQQrm{fq(n0y15Vx^#UFxh}*)%hGn7ytNgp4y>}h>t}_}-OYPj75tR5 z&28_8$I;Mjire5jcfgL2A0#B2a6O?U7V>^$;TjuEz<&(=%2rn-909u9H*xjeKkS}| z|BL`6p#X`I@J;;D2L3=f#H%6TqXLq=|K*bo2jzSFC+fu`&3t(2C)b191YKP53c8Jh zf7HF5@}-`zK2gE-KiO(M`09S|T1e=q%2QS(A1o@7{(#+MxrOOgfF-)q^E(d%X#18i z&DklzSQVtq5l~20Xo=ZPu(xjS_Y=m6m4JFSD+RBuKZPdUUtNBAEk8gg_Ix@HJR=1t zktd+Ge`FxEqzac(!xyN)>`!!$!`)|Zs?Su=`h>N@Bh-*&&;w}lkzKlKhzJq#94HmE zH>&|uSx!pFcC6#o<^GUBv#)ADNvEes+hDq6d%F0a$H(oZeXN-UN=ZmeQiq>(hsA1% z8nYRcp>3H>gp+X4h4S__g|O!~9S^pVUj+w~@N%mo{Dg=z)P* z#>S9=j;e_2g500RfZViTXL(4S=X?1!dwb1>2SNKx5)*7!MY{};wtv!@EMrLU-AZ&U z%m`yDirbX;g5DiMn(-UC;@*|DcYQ`^R5j;1UrAS^{LYKP?f2E&tzbqaSW9Bze~1SE z-}%X_fRbc9=){URID;Ad;`}EoVeXMnLB*(9dKT#VgyLq@=lE+E#Y@>fZbbQSxavLC z(yhMf>{T;`La|%L;N!m82gaWtXlkxdNBpNM5Jdt_rLlhpLI7r>l?vadX!x$s=`_J~ z4B9Cj1&>skI310l{6~B0xvvg$)vrj;1&7*_3I9DiwKD{22&gV$pq?_FG4CT-a7H+a zB;k%%`NALnXiMU6E~hLKOf2qGM*n5kWfNjVxo zFY5**rFl&YqoYeVFBkVns@~c5__8KGdlAKWJ^@aJQlGcsE)FGH{;tL~pGv{zZS4k` zw}yxr-Kd1Ts)KlaUf{kAI&5mFS;~ugMFy@A7McjRKT)%)1|$1)SK_6z_v{*`rvqEq z%9m=UVkJ}|n<9#C3-dLguI3Z2?pwPB{|T*w(CV|2!oqp3UCC5@6-Zs!I9d0z+IH*G zKiE$OEAlsm-zfZfVcR*2_iMz&xn~o8VE}O#R)NIEk{p>!f(|^FgSL#G@6~3Bzsnox zmP<(Nmh*^g&5@Z$ur`x{wJ~i@+=~zuX?;cW>;@AL;3J*vss4 zz}dQqWPE%$KD>J;5C*kMUe+ZN71Cxp*nP9#h3#c$^)l?F# z`@jqfAlCeCI%jh|g!>y*Q2mE!7BC1B0h(<=EgM)Nhh^V0R6yuCh2+(}evhz8gwM&Q z`Z1Y+b%)TQXo!pB9yXe}UMV?7h(MyKB1!d0yXd3aagUmu9?2f}ax_-S=Z z^oE}@Co362WxtxF(C$KjrUpooaAlr0)cf1FhY=}YsUs?mB;SL|!i@SyIib9`s}IUp zjcsb(d+-jlq>c-zNGEyM4tc>Cy+% zkTEw!U)|dLSf;xsX!rA{I81*3O<$OYo7)b$Xzd5XW@N&xj7df=)}>c9@3jFUHaLZ2 zM48qQaK{X8$r`Mrg=U+LW&QC>z@nmwB1w*asVa9?B$24CA;y9 zFwrxWzY6_8={^Eh#x@DfvS*wg{wQ-jGCF`%wC+#hSPhyj)Vn(EFMNT>mRc1HJ*PfD*(PQGx72o}H}fPirpiTqszF z+6wOOT9Li^dep3?nWI(d;h$L4WwLU!^a72IOH9oJtBSTU_1B~lVPe03{Ly8?bk_KX z4xc->1GawI`Heo{Xzb>OA)Pcqt8_BJbE&`mlm=C`lPgIWC)}SVbmQHc;VOVJ5JeQ> zi`xw=m-tXZDpNz+-X%=!0#|ygyJndVGPA4Cd1r-`!b(SOO z?vXwLS@kG{RZex>Pr%x25Pj7l`MiTPdmX|n+mF|ui)=eTdD?={UauYj$f@eGK(Fq* zvBsnbCSOf~R#!@4_HfN+0xB=DLJS1ofs$Rh*;F`esy zw#?h?*%V-CwfdeJw<~lz5z$o(!&8E z{rHh>HyIGTuy?()3gUUm^v>b|*Dvn4qP6hYpN)+U!~8Yc_Rtf~2t$49a4=KyXFw-k z0qg{eup+zZOJpUTqs@VQN&m6ye*tGICh3uyl)>kdj7W~pc}IAQavxq<9T+y_2XKhH zU||5LCo_#|pAtusA@`nZxe6xu+GgH;cC-DkXh`akb)UNT&gYxtx*NST0eo%1fSiIr zS&dAMZ0DbMLbVlL>Mp;3iK~h$sOgujQb@e8RwfVKY0Qj$;C(3TU0+|X1;p;oH`>K^ z@kK_2D}>oShHcTVkld*83`S=5&)AVAiunM z;Ymg?y^dCv{7kIgOt5cX0sU%e<6uS8;ut9(hb0_0{aR|;np70B=UqTYOZF_g&~?k- z15*hTzcTY+{Ue?x9)Zb!+S1| zpVR8gn+8S%0z#eQXDFv%RY0@;W*Oxd$4hK`+OVk6t4&I#Uq+ zPoKxj<@z1sXX4{%1fp-y4}<*yYk?`KSB=r}I9;s_UaydSMN$lov_pI`lzI~Gj2=&l z2(KpplH^79y$lB?lTJ0oxHsG}#kMR8Ckce_?V^`t10Kotm#JoV=i=W_^t26}w^duC zHm+|fUk>05*57Xv1$P2Qgh=Ag@454myN(kW*pcP+&su8vn_AajhWr$!E~tP=2!kum z-)O_zUdahDBD1wJCg?ScbvANl8mIX@X(B4mP)o8K-x zSYt{0a`32j#IJMY`<1l^f$^_kCt@5iK=8RVgczA&_V3;F5bT9q$qhwERPd*g&!Bh% zqZjiN!Z1;=7q=*qhkT0YVo4tz#lS?Q0(#Am=|PeyL&|MGHYd?$@qPPmoqCrPa@RR3 zr*kwjAu1MK;Jq5Y`8Ci|QBgmGqz+J;FkyZ2S4sp+vfAyX zqJryEQmW+A%*?{1E@VPc8d5sjeJ2caA;+DHwjj|2J!2$qnU%16#qk|o1*WCa_4Ru2 zLbAoMWC-cU&|d|aOU07XKi)C-e2W+O>xM9nI^#dlgQ7~e42KZgh&`?<1R%Lyt)1s5 z4IAPCB>kv6!kSO|m?3YbG{Se5bgIWFNdO+N3Ca+TqtkLwnS2B&2@It$*@*l_9mA~u z^^zI03}LZjS3=#RZ(`u^^f@^?uWV6P02>uB*aU>V-ki^xf4$nZhtrQEYL|>*2+&!H zE)s|LsN7O6laG_GY&X+N$_FpJyZPKWq3oo~8#X}>ZRq|&bsR$MY-YHvkU&NdV@JOu z$cJh`kZ;fw2d*L?gzirJeja?exzc4;oZ^e)3RKnWU$$@*7ZHgj(|!s%x80~_0X2ma zM?~5B#bre~dJOq3qsiXw_5RrIP??#h?{e@}4(m8U>=F3Z*SlASCe?b3V$5lKkBkZ=QTXt=0h zZ}Td13j=K!H3jx;Yfta(V6IJ-90FZ8jJ|wIYr*#Gk`eIM zaM0YILy(<5ibag^3^ft;`g53;aoD%W5UB|=r4N)Yfk|pCFGD&UbP!ILUtZf8;u{E8 zOg&dSXLh#<%EYAf*N789W=1}j50rX)v|w$&sVQCWH?c^0JlA^|kq5Pamn_+ls4BXU zv1AjA;n-jzAtBc*&rgL(dq`)O4?VS%-^spWt<$dg*5*+^*7)-b-7HHP9Nasup=9CB zF>3TzQW-29p`X&C?`sqYHKIJg*r>9Jm>towMk!^GxtPJ@1pA*5zZCAE%Q-4u;JF^o zKyZgXJY8nLD&3*FQ1e5#;4XH;2&o{rD<8d{fD2#*R4Kgr`RA!tU;mk_o*v&SYn`3k zqC7)ddV2T(kar&cq}|lg!cEe3hNoL*fbFaK|W>$3P0+o(v`=X+9bqxz@nUI8`UR9EaQTMgcep__=EW<8>9%u zRT@uzNF8Ocx9seZDCwLhW9z@G1D6cc#Gc_7e1qKf^_{Dtbt;$)4q*#eLPaOD>-mSh z6Oc2v2eo&kPTCS|kZ%oVzRw5&(wSWX%y)pu?$u>AD!Bh$WM6a=jqnXWnvCAhJcHC@ zvBssM4yC+vEZ)SU_tt*Uh2yTu+L%CF+kl~&%QFT4SWOyop-i5nddS`-ix5;*)TbcG z=R10nB!=ig3ZG>KvdoYVS~&O6zMN4hlT}IgjAa}}P~JG#xVYP0{SqO2rFPaw#_V)c z3=A(uSgGeAtJRH5J>*ks_gsAwGGKG4V=RacdCM!3}(29DT0YNkJeTJOK5t&oRhKcNcB%TTP9Iv|mm$&i^b zp!jCY>ApPTcs|$w?v0%?DwT{cdCc>WarE6&SqAM9l4NIOBUnQ{hhEb}sWH{w`e8pF zTe$m0k5GKD^9gZN38Yci7jfrITU?vDTXx|Dw;;3zzGAJ+e`wgIbj$ufL;Xr{1QC2S zjFg!IP{x8|aZj$P`b7x4iE?j%w0C^aJXloJ4?sB8gi8)(`Y>e9BloKFzYy=+^fV$~ z&zyRGv3d6~&LZa}rM-1O{fNuYEHLz8J(5=IxnKt+Afw4$4jl{itZy!kN|;ZOgBA$? zwHb$YW*ECGZYw1)%DvN22f=d2AAGZPsfE$2Kop_sL9q6cX~jRk zo0&m@vujHVisK4Dn6bk;U|~P4cgPxn)@?VMhCVAHTR%QLWFOYxhr}NZ8KbT1~g=6rX;mt9Es6nimVRNaifznW1? zRe1R?l|7EIXiCPY-FQ zM|0u{)bvP>N(CSLXe;C5cf$MzMK&)O8_?>I-Y+ib!JVeNvKwzaD}F?=?R<`>$#E$u z!TI|s>MFYFp%WN#1$e2o@-Y3zpxcyXE|O#X#|()vOS{iB#~1pGoVtg`P`$wDsF<;){;db_lTb5gZc~O;5N@*bRd;v^L-^ZpC5y2|-Gs z-@M;L?O%-jC*}aR2lVB2Ro1r!u$FqtzCN~P9K`f;kx&gHhAnz>mmwpI=jC#QL#@v~ zB`>ELNFzUPHv4zI5FG9_E2XLexcy{iMukPI#CoCozsO(4&qNfMB3-l|8k9(`mp#mh zd@4{1ZlWw9KNnY@Y)pecrg~Z0ncn9dYg?FzbLunS{54W{t+OogGj~Vg4cF8;-339p z20?SJclHg%MO5RO~Ssup{hC4scZ2QdA*HHsYjGSmj)pjW}kV|%+BU?MwZ z4|_T#KObU4sdQIG8bszL<|-F!CkN>5*~OxQPQ3~KQzKs@N+*_K z#HmHTldmS{EabJXpZ*0s_gigqb!q=G``eICyN&Rti)w4@YBK8#l=P)Ry(Zl0Yxz`? ze&}O-gu$m@zYi&!ER@FWm>{y_@_#H7EDil09gv~fI!PuWY_zd7?jOr2#(3cd=@&Jc z|CJucrljzdnmzX8&<+T{pN@UO@OUS~<+I@Ggh!}VXKw6;w0N7x6}QgXN%>^r@qsQr z`Rc`W&xPJG6GYoq=^*cFC!}F1=tyWA)%Ns=XN>ElEtw3N=>?(#f(h`XK136xkn$|1 z`QmkHaDN2Ztp2APDAu}r(U|2HRhgC-X-&rdUBiE+y89o^4N{EfBYFC<4+1hmc zdS%}XC!UhxS;y*}{xr&>R*ZAit zs8Q{h%lMDq0k6M(BUDu5jiz2%oi_s_=MGx={|y2Nz`H5g@yME z$so`E+B0&@euV1C23%J9k009~zB(K}T^^wSE^tm!ej~KJE;P&xO*`3n)uw>jzWA|O zJ!AWLA9C(}eFv|K|NP~7!&g4)Ev0c^*wKh;VzI>cdnQZ?OF6CEFJFcDpyP8{%U&1p zb&PlIa(%{4jYVr8>F|tMzb3Z-A~&nA0G4JmkT4HDmZ(Zp6Z>}2RZHW!IM3#$O57eF zLq-xzUk5mWN#J7g_cml_qMFEN~m3Y^&0qp z+(0J#%gYg!a%Ks&wn)73TECq${5 zCbE*J9v)}|h`@b8`EUam&k8{~>h4ckJnpdo~yU#3ne}nyws0 ze6EUcYBT0;x7dlBinZ#!Gz9J0_sm8Q~aH9S`v&qOVB_6L-M>nV8v3i!Hdk2@kh3yi`p}Sc;2G zY+jp&v50^3vB}KM_L?MbL%Z|RM!gO}M>*D56fh-mVoYNg&PzUnKZ33IkbeBy+bUV& z?#j;+bwX`aPoDW9ZeJE2Jse1se5i_O+3hpb|Mhf)tu6T%fhSb#Oz@od_&xmmGVf#8 z=Nk8V5B4_t1kGV?AplWr$|ng22Hl5NbTG2DWY}m4LVQ8!lS)OnqLGE!4^*lCyFRC! z?*F#E8v_egEEE>m5>HR^z~-z59FIvc*pJ(E`>Bzp-=DPQx$-=KK7l1aC<>I!s}|7p zB}=7rbWu_DAy_x@IQ~)H90<1fwB?)IrgLG{qm^>cJeMd+_)>dyL_x_072MW@H^n#L zV+h){NQ4NdSq)f}@{Bk1_O5^SaK|B6vjtS^R!i*eK|Zf={gDd8-=LK`o?v7fd{q;Z5$#4@J=Q$h0{fPC2tQxy%^$p!- z77(Dn5xnoNsIW##3{LXh<))wu0N$;NBKe$dpVcdLXyltg%J9*pF64K;izwR8fEn*2f5~J%t zq!d(S2;bUO$O9Z}63u@-O~#7;s_M{oq$hc?Ho(xfyu1M2w(nygDM?hQqRMEO4(C;9 zv3qfg)J0o1&Pn2Je41+r4=D))Lw%OOXjo?||7aRKACgURa|1oW4Y}oV2UkuN8{x~A zs*^*XoslnEH{vrw|Ber~<$b-sp=E@MF7cm{Gxsse`D3*WoG;hv@1fL_5s6PpDec1u zcckgzrUrce;=eB(@Thnu)WjGGF?rtmhXSHr z#h9gJAH^(QENb)!6(K;wTjXVg_-1$)e4C1FFouWwlV(Ljzf+ESrNGr}qVr86G?&*R(VTxLC1K>lOvfemg4E9PLWdc$EwV~{{LqB! z-PZ1I*cI2m-?Ll2e?hKRJFDmoP1e1oIASQ>EzQ+6Y{?mHUgxS*Vcv+50WxAsa_d-}Rh=$+imiTW9$T zB_48-Uj4mNJ)`(tta}wLm_VNa$0N-n|KTgWQ0)G_>3z?xHF@;h(kAg1gZb0Qcl#ta zX`YT}`7QMSZ>^=L;#yp?V2PG(B(QCgstU5tB_8k5G70DMfbShtwe5 zqi33SzUQeUefxU7)jwJ!s80%#p`m##(0$T{5s90BYpxHXndcQ8;+f?KIj8@L(&JIl zXTR=4YG+$Y?$S0X+@AI92+h1-_(MSl&JcM1Tgw;-{$Ai8yG7%rOMYKXzA4lH^FtOy zEtNszKG}{vieqi#McWjL9*aGWWyw;)tg6T75_lQ5m}kk){ZfwPA5vZaFhi{oT2Q-s$w;JNVav`+Hg zU^nr*gZa(P_TcsdrrBszju|;g#|GiSv*Wx(<9M zJAV)+IhNF(jl~mq-$P}@Dr0*XNL(P^Uf_GWxb_n_X6*d>XAauVUI4IUz595t+kohY zB&2(y`Jq)w?UAJ9@+8VI~9c1_XTKMA^ z-SV?(%GmqBWNWZGH2Mg3`Me|w=;doU`WFTP#Ev<4zN9UF^X~=#F4OFo5(-sc@Y>i^ z`IPfTw?kS5e-6bj%0Q*D*@oOklXrq*qm(yXN%jP1t-ZI8onJ%6^D2vIHdSohrv%ZV zCVN%pl}y8wC2Ydn7zJn=&!5Wh^*Pb!*Hv+o50Yo9S_Fb$3A~!V38mrtH8ByQa;{UJ zTQ2dqjqkeT_n<^UNkb?ZOSDB<)RExk9}fHj29Ee^3DF~~>;}9({@ddB#`V>Is3$Ud zMvE(cJ+Wx=C)*c+#ak!WnTDu_b!aBd|2OFUiW4K?^=rZG{mvT|%% zz|6^7*x>z+L=l4E>xCAq}OnJnyfO4;r#`+H# zojyG-=$l`+rK}Fr`u^aXgn_a}9qM^StEYIA;X&dkmpZ@1Pc9#_5`@THc}fX6-HP_Z zQJ*R2n%b_|(>ME(NJM-{uZ&lB@;yb6!GS(SR+&sSJ41o^#g88ZV0wWo~} z6ikClfh)|wy-zMK8pMvCl~(uRj|zqF8s$K;Sg*8*MtmD%U3$KX9DcYeUoT!oq{56} z+49UhaQn59;bpbuVJD}Q2dtNy-zfJyY zQ`1;;z~2LLJVyl?6qQu1T5r@_HVtK4IPC|YwR|vr(SSuZu16GY_(xI1)3Y*mP&~9@ z%dv`h5&sbx%(v*1bDdg%OY$F*RHxpm$TJ_3q|pR~#H1q^Wu7s9iiMcQM8sKW3q_(0 z@?*LgW3FUKbvw+ezI4ZETZl=F)uZF8GJGDU3TmAPPW^goWS8{bCH-AUS}?8!x_(S# zoC_$+j1U%ECyM8ewYw61QP260hjGgCDW?usV!s)Q0_C$7@tW0_wv%+3HuOtW+^C{M z@_)^fju72QN=XQG60ene>4bQ_{RFJK1p^XprG3G_jM;Cu?0Watn^&&=zmUIABx!x+ zFLJalFY@01=UV@v%GS{C}Y|CuxQHs89DnHkl@(mLi}k87Tu>7OYAuB_j7=g{jgw|GQk z**eSw{ef(kUzW>HIi^cEKmCQlEN7mPEpyU$RB+EPiy82r0{=f9Os!usK!YZiZLTHh zvv$LwF2uyR-$J3+d~gW6JE!}Dx)uiFyW8zf%Nr_o&_(;n?_}uU?q~JAi_@^30g+oa z`1A9-eYKDOJgfIIr2O*0ayE*D7zYz($7&l1&E>mtOvsv8;bsu#<+|&fbS%_t+5vfU z5rwa2;v+7IUumz7yCoUixj?H<^7@{P3r z74v5*FrkWpWLZoZQfo#CoQQ3E;ZRoOq$o4|Ps@Yg`50fz&_NgxgP%7Wc_BAKg@NE+ z)eG^&MbwYZ0AQY@Q}zMh)Z-%X=Z_~ZH|hrtzvRahd-(b)-NV+Myy-<#<(8DZ%k&=_ znrfFcw&tm_4+zNpzP?+P(K40H{^L2+P~4mU^GEe@?@Uo_h60r4qwrk0f8*Th6#p}7 zjNp1DhsvSL+YkTFo*Zw?)8X=MNmmA2ikH5jmhXABj_gIVa}^3IadivN2v!;TD6&)C zzPM#!71SPlbl2SM&xQQGBWSV9Wl(XbCt=8it^Dg4C-FJVR&yK`a&$0qe&Hr)yv%7r z=lKt#uoWdt&MbmxbS6Bq(1dfnneOQ{*rFApFhcHZmA>;g9pXwyC&_^%|E<_GpL~Vz z^)Uh8QihOBMs zUB7-ha~RT^Tl3!mQ7fOnUD5}>>*xHKM%KdR9>sdfIA89iz}{c~Vc8$A@@~DZ&`=GZ zsnYqK2;KVT(;LNnMYEoBf43D$XzS8IBMJ-XZL}r$c8bJ-DQ(&JlJyClTiM5y2Dyog zkI27Pm~c`mR@N?Y#vBS!_+VJJD-d$>j~2{=warq5CfjW-%^MQ`l+T+)1h}x&VC>Ez zFG#G?V`%zbm$%5y?-?MhY$eE$p&6rW&ae2U1sCbcB}7{4YHd|YlW?_*Dzu^Oh)#qI z#dus!u1ez1od$eDLSiM=iuS+i%QXX{))#m(9KuQK%FhhP66@}y@vBHJHDrp2MAnCp z;aw<^XQDd$^f9yK9YVXsZPGhw+?oUZ#6-Lm#g{d02+9`ZqN}g8;)|c-FKB0zP_3`_ z1JRl^rjzQ6?qvm=;&<{t7MU+0gF*fe6Une8(GfyU)TvT2Id9L%fzyo#p@vrlx_@U^ z%AgXbw)_`4Ole0n_a-=KQARt_B$@B(o`*SeO^8MR-uw+QXSXpIeH!>vsjmC`EZ(bO z>?SQz)PR`ap`)Fw825abtWCKk2fT z#o20W#wjU(?I+kOb%Z~14l=N8;U+evX6co%exAZpPOtV?kxNHV!wHKER9hvj)BdRZ z;Z_f=Mdx6%-QxW%m3$P!{b^U9i#hlor-$3UP5YpaZ6CoO`@G4?D?{$ME$(r0{jHpS z9`MQW_ySu$@}_st!NEb}tI3~M)$2FE5qW07Kyoi)aMkc<%8g-`OlZD?HkIRF>^t3O zu_#A-z3uu^x41aHfMI*wQp*efSr(5cgOSjjO3d^J66MIthV_JwPtS`itV;#G>fZAh7n`Xv*mx>f1^_m_f!pZa>3 zbOmFLh)=uRKBpKMVn@X{|8rX?F`t>V^H=z38jPHNYUvWT1^UZH6}{$vc~BqlDZ^RN z2IZMr=lOqYXTjEl!$olv-2RkwcS#JGq|}C>fOJZO2#82aBQY9D36W3`qy}Tu=oXOf z?(SwVn(^`>-sidB;XL`L7O#^o zlZA2My?OS&?h=>%)f2u6ay&ZWtR()iXBDk)e$`XPk0%HRTPKn)sZ#Q$WP&L|z(`0{ ze-Litt6$CIS95bzuCW)JdsADRS(h>jy`_oJAGVl`pdHD{qr*~r{J1#5t3lMsjtIM~ z%&(w4(5-A?F#e`ete(my_Yv%~(v+`)EIv*?eEG5dh@P?g4?k@%dFu)~doUZ*5Z zI4DN#?b8S-oS|eD@2-f$N1NPZjd#B{EG0{49qd9`h*l|yKx%Ki!4TuWijxav{7-_h z_;Oe3^QaUKo4!3oNWgQBg}Le4jmS{b5CMVVrk}CLcd3r7*a#JMHH}?(Y0Jy4-xAlm zZgNoj9P2auzB*bwMR~~x5GlP~15J43u#PhvmUHN2CDCt5zNh?>gK(1K2S|j91@EyK zrc4_cSCw2jCXh<{hzVLrC?arD=N=zP5SUC(k26Z9m%!-JP`3tdg13FSB0PYTYBDRN zma0Klm;BQ?hH6yVn={Jlao$>HR$aMdHKW{`XGVtKe$MN&JR!PV2)OSIXGp);z>Z)o zR9zsI5)es4Nn25vHA;EgVA8)!OVGasZztOP`bt%%{&oUp&cZRK#_pjVwKg8E)}Q#6 z{-EW8xCA1xB)nT_Ux=&71l91bB}q>Zp>}?yq(n3BhSP1w$eoT)P?tKaMF3>VlbM$G)<& zy}jr2`4Jf!QdeLfklR^gWkz(?4)acmIZiAN7eg49Kh*GpnO}oo7S~^Vx}VhUM|v-T zL(w|8|J|39Ejq$gpbL4iTW}~COlw(k8}2fk2x&nS_-Dd z+B1a-O*zg?2IZvRx)pC4x;Y*^H+hlPB+F@{(wgue7q1~XFHIzYOLnqwH@JL1-OWJh z4r!fKOFHRgajXokJGVStLEHSK8NqhAY`OG!bWv$bcB!?~o#$p`4b81xd@1%<%Ge7y z>b~rR4B5KOib1xGf9W){RBpXp(FdOJLRb{8Ud`VZzdL6c9(g4n8*b=Wz{f?+6C6Za z+fnfL_f^3C6|SPPRuJ*Gniklt(^_|+?BUK`V!jYgMcmUqpjp6hhuCS?;|SGoY3?j@ zXOLv0ssJKEh+~o_5d})iw9&v#YTc*nG_z<3tjSFUNx5j<-eMOTzM{QW4&RF~Ju9-c zM>wYhH7CvvZd&oT!twhh8MokP84lYOnY6UqpAImn)eQW5lUNT126Igwa|YV8)xUT8T7AS|!EfN^&gX?c^L?%-MNvHtC5Im+PMv#XJs|>tmBi~3X#8w|FV)DY1I{8 z4s=gcra0NG1w=_4;1O7#l24ujU8MyfYbNatq0R9ovi&?t44S%sGLdz3rXSUxP<_1u z#YL6G#kt|!0;r9D${ zK^|uKc{dhH&nf_IVdkNtd}a$;@RCTDr9@{d+(QvDbUz- z&J&(r>&cvsURhmjkI4P$eR-@=H5MYtwQvvwDhJcY%gF;yUWu zr+WDb9HM6^Wy4R*&5sL#kSvs&BgDMNcQ{tOboVY|!oS`OYu1u+?NgLdWZuiC6`K`c z(pJCRekQtkV0#M$Vq^mS(@!>laKct`0EYFumAvRF_X*(T8znQ{CI*I)fTPRHJVk_a z>%x{(ltPLL@E3?RYG}Z)WLVV2eXy@IJ<5uTbFb_>Nr(`@08fJkCRoETB=~!6aZc!L z+Sd<&>nG;W0-5(~Y6ei*Wg;Y%;F`V#*{UT_Ot(3#*vlsed`LtR6T^QITQeV9t){M! zf0T$;zqUa@h5Hg6v!K<7XyG^5Z49CWXe*m`-b*MurL2JaL>e1mNzFsk&KrV$67KYB zg_w^28NEgGE#lMU*!rSnQqy|}BSy@3>d!=M_49EsfZ-DoBASO*q zs-#rb6xJ_y;_|1i?n>&$Xc$LH1Pxw6kz)^CP`_DA1LD}8wwjK6YD#Jhi1dXlYqqZM zzq?ci9V}?ef6ITAE^~6(G$HrZzrcp0CQ`kTf8g&SjlZ?{5*Qi2Qqw?GN%#}$Non2+ zX#Jh%=s)=GUWwUJ`=R*mD;I6NdRTrNZ1o8htz=q|Wm;;+hlsl@knq6cJ(DJm6R9H{ zA-Sa!DCnnMT(!Ik(braVe6vHl8t^Veu*gHF9KjYLRp!JOVcc?@jkz1I@? zo3s9z3N-KCe8xF6YUOYHZrd@|=+hArMb<}Welj@`t8}DbVywuat4cOCJL|ZEeifnE zNz&Os{}ouxGSBcHIhfp95B(5K{NY0Yuz;h%OUOdcLQaua5(pP806eGOb~1Nafu3d>QOU8timb ztqCxpfNS1(sbJ(^Iq;KU@GI5mCYCSlq#7|vIu~kjL%qGpsE6Z(Quhl=WG`;^|fb zw-L+axwWOj2ix$t^8VDHy-tAzter(V48{;Qso zlWA@*6VSL*d!Oi*HDX}!Aeli?l^=yFDbXcJr2l(aU!#dD!~=d2nuFyBNOImZMX5{# zzgTvPd}~rj`T~}@&dDDnZd5X>7$$xAQrb{o(XQK@mtM(P{L4!s)X7G8{T|lG1=yn}#l$uh0_Nn*sssE;xtPYhZe6;%c%iw3-8g*#?Y5k6w zm^OlO-L3BKB`mjWueBW$#X!O+Pa@ZOhk>xNK%namLplOc1m$>qS|`|G`m$%zjLlkX z4|4{i&5w{Ue-S`VO`ivF`Z(O=WVSe6rf9`$<5tk0@YTjz%sjIGS!Vpc_mCGHl% zsjtgLrzOwGF2Kcn$l>{ReFIby0`VU38o>G%qGMM=;hK8dTO!HCGluy*|FKjUXql%k zbm6W4!@4Vdq}B?Yk)^!Nj!$5xm z@nZ-ongP`$ARu6PG|}0-;k|~ubhWzFHxz7Fx_O8Wi?Z*}MA=I@8_xZo*{r9dS=K5TVnz6}NMDEc~SI?r=_@`)?lDUj~FAH!M zXS;5%Y?S(by7&J|=i|Es?g#a!79l-!J|TZ#@;WR4zl6eqHKB!PW`F=?C%~0P$3{JE zV<>e@oFXu%Cb-Ef2Z87ly0B{$da5+THzPA8!yMfuu5YmS2caeYR52U0e3J0WC%}lFW&UNXQ^eX}cu5GH@?#mP6(1ukS|4yaYP&wVeYRY=z=Z+)&PWd-6WyAo zMfJbcyYd#%TiWSd@2lY!RA5Rj6Laxu9=<^gOB(xc!Vr0HpFMlicyzaM$0NXtqQjgYD7TO8bR> zbJz8tYpykR#kpN~4 z9k)MpYPsT~56D_IW3zTWyV0 z;dw+nsE&8=4oX?2XtEH&qHg48R9IlYD-wXjGJeBW6d}W|{frWhZ7aGAM+M1%H)4JF zg;`|{r1B~N@N}J)z}iLQH?nHFw3j~;9OM`oU>7|fx+Sg0kk`_@0;3JUS^>l$>j4>t zmyev4g@du$DO*B7m!G-KG5h3K_Zt$f0LO3nf7+6Iuq_gS_b3QEab#=dUGig|F;)di z;_QMF@t$KxqbsBK8TKF>i@%@7Q&$Q_mspa9upo%&GFcJ`#SNZi6%+Lt@Dy#p8bq5ietK=pMqYq&TqR(<}Kshuh0(JldnN%@AP zS~ZO4HbVY`V{RVeupwT8yQ0xAVk_;}Yje2p>)3B+B%{tL3Qz1(KFdiy&P{n!!Fu=l zaj0}?QY>*^M3xnPZhakygI1yoGLWEZy)CN`efEA7N( zqeSVt*YV1G9-hjPWm39{M%h=D$LdrHjHJJ5`(d4;YiXcwel~>Vq2rv!WX&j-AV;FR zN7Z=#sDM*-W$My<4G@AGHV7C^l%P=vGLi{Y${U#fs6MSn^&04R77aI3Lw`OQi!_wO zBvR@kD}X9ulo39brOLwaF - - - diff --git a/addons/phantom_camera/icons/phantom_camera_noise_emitter_2d.svg.import b/addons/phantom_camera/icons/phantom_camera_noise_emitter_2d.svg.import deleted file mode 100644 index f25e7c0..0000000 --- a/addons/phantom_camera/icons/phantom_camera_noise_emitter_2d.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://b2r7mhd780y8d" -path="res://.godot/imported/phantom_camera_noise_emitter_2d.svg-1b3d37fe36964dc86a6ea6681d0772bb.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/phantom_camera_noise_emitter_2d.svg" -dest_files=["res://.godot/imported/phantom_camera_noise_emitter_2d.svg-1b3d37fe36964dc86a6ea6681d0772bb.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/phantom_camera_noise_emitter_3d.svg b/addons/phantom_camera/icons/phantom_camera_noise_emitter_3d.svg deleted file mode 100644 index c567f6a..0000000 --- a/addons/phantom_camera/icons/phantom_camera_noise_emitter_3d.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/addons/phantom_camera/icons/phantom_camera_noise_emitter_3d.svg.import b/addons/phantom_camera/icons/phantom_camera_noise_emitter_3d.svg.import deleted file mode 100644 index fd4775c..0000000 --- a/addons/phantom_camera/icons/phantom_camera_noise_emitter_3d.svg.import +++ /dev/null @@ -1,44 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cby76y7m6xn4f" -path.s3tc="res://.godot/imported/phantom_camera_noise_emitter_3d.svg-9b90fe54aa618f65d52ac94515d41ea4.s3tc.ctex" -metadata={ -"imported_formats": ["s3tc_bptc"], -"vram_texture": true -} - -[deps] - -source_file="res://addons/phantom_camera/icons/phantom_camera_noise_emitter_3d.svg" -dest_files=["res://.godot/imported/phantom_camera_noise_emitter_3d.svg-9b90fe54aa618f65d52ac94515d41ea4.s3tc.ctex"] - -[params] - -compress/mode=2 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=true -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=0 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/phantom_camera_noise_emitter_gizmo.svg b/addons/phantom_camera/icons/phantom_camera_noise_emitter_gizmo.svg deleted file mode 100644 index 9b9bcb2..0000000 --- a/addons/phantom_camera/icons/phantom_camera_noise_emitter_gizmo.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/addons/phantom_camera/icons/phantom_camera_noise_emitter_gizmo.svg.import b/addons/phantom_camera/icons/phantom_camera_noise_emitter_gizmo.svg.import deleted file mode 100644 index f94b1f9..0000000 --- a/addons/phantom_camera/icons/phantom_camera_noise_emitter_gizmo.svg.import +++ /dev/null @@ -1,44 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dw4iy855s0atm" -path.s3tc="res://.godot/imported/phantom_camera_noise_emitter_gizmo.svg-9a593802655a8d5038c7f55deab3882d.s3tc.ctex" -metadata={ -"imported_formats": ["s3tc_bptc"], -"vram_texture": true -} - -[deps] - -source_file="res://addons/phantom_camera/icons/phantom_camera_noise_emitter_gizmo.svg" -dest_files=["res://.godot/imported/phantom_camera_noise_emitter_gizmo.svg-9a593802655a8d5038c7f55deab3882d.s3tc.ctex"] - -[params] - -compress/mode=2 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=true -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=0 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/phantom_camera_noise_resource.svg b/addons/phantom_camera/icons/phantom_camera_noise_resource.svg deleted file mode 100644 index d3c6deb..0000000 --- a/addons/phantom_camera/icons/phantom_camera_noise_resource.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/addons/phantom_camera/icons/phantom_camera_noise_resource.svg.import b/addons/phantom_camera/icons/phantom_camera_noise_resource.svg.import deleted file mode 100644 index 92c7e3d..0000000 --- a/addons/phantom_camera/icons/phantom_camera_noise_resource.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://fudwitkewe70" -path="res://.godot/imported/phantom_camera_noise_resource.svg-a81ed223714edd2c0d9cfa00be0c3f58.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/phantom_camera_noise_resource.svg" -dest_files=["res://.godot/imported/phantom_camera_noise_resource.svg-a81ed223714edd2c0d9cfa00be0c3f58.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/phantom_camera_tween.svg b/addons/phantom_camera/icons/phantom_camera_tween.svg deleted file mode 100644 index 6956fb5..0000000 --- a/addons/phantom_camera/icons/phantom_camera_tween.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/addons/phantom_camera/icons/phantom_camera_tween.svg.import b/addons/phantom_camera/icons/phantom_camera_tween.svg.import deleted file mode 100644 index b9e024b..0000000 --- a/addons/phantom_camera/icons/phantom_camera_tween.svg.import +++ /dev/null @@ -1,44 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dphl04mdf3220" -path="res://.godot/imported/phantom_camera_tween.svg-16faced08ef4a5f3458264d894230dbd.ctex" -metadata={ -"has_editor_variant": true, -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/phantom_camera_tween.svg" -dest_files=["res://.godot/imported/phantom_camera_tween.svg-16faced08ef4a5f3458264d894230dbd.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=true diff --git a/addons/phantom_camera/icons/phantom_camera_updater_panel_icon.svg b/addons/phantom_camera/icons/phantom_camera_updater_panel_icon.svg deleted file mode 100644 index 6d3bcd4..0000000 --- a/addons/phantom_camera/icons/phantom_camera_updater_panel_icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/addons/phantom_camera/icons/phantom_camera_updater_panel_icon.svg.import b/addons/phantom_camera/icons/phantom_camera_updater_panel_icon.svg.import deleted file mode 100644 index d1ace76..0000000 --- a/addons/phantom_camera/icons/phantom_camera_updater_panel_icon.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://d4j4hrb7yusyq" -path="res://.godot/imported/phantom_camera_updater_panel_icon.svg-19823e6cbee8115f8b2554d0ee6e79db.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/phantom_camera_updater_panel_icon.svg" -dest_files=["res://.godot/imported/phantom_camera_updater_panel_icon.svg-19823e6cbee8115f8b2554d0ee6e79db.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/viewfinder/Camera2DIcon.svg b/addons/phantom_camera/icons/viewfinder/Camera2DIcon.svg deleted file mode 100644 index 59efad4..0000000 --- a/addons/phantom_camera/icons/viewfinder/Camera2DIcon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/addons/phantom_camera/icons/viewfinder/Camera2DIcon.svg.import b/addons/phantom_camera/icons/viewfinder/Camera2DIcon.svg.import deleted file mode 100644 index 714d685..0000000 --- a/addons/phantom_camera/icons/viewfinder/Camera2DIcon.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://ccnsrg8hq74p2" -path="res://.godot/imported/Camera2DIcon.svg-300e6f57281180711c5ecf391104d4ba.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/viewfinder/Camera2DIcon.svg" -dest_files=["res://.godot/imported/Camera2DIcon.svg-300e6f57281180711c5ecf391104d4ba.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/viewfinder/Camera3DIcon.svg b/addons/phantom_camera/icons/viewfinder/Camera3DIcon.svg deleted file mode 100644 index 2366c3f..0000000 --- a/addons/phantom_camera/icons/viewfinder/Camera3DIcon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/addons/phantom_camera/icons/viewfinder/Camera3DIcon.svg.import b/addons/phantom_camera/icons/viewfinder/Camera3DIcon.svg.import deleted file mode 100644 index be171ac..0000000 --- a/addons/phantom_camera/icons/viewfinder/Camera3DIcon.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dkiefpjsrj37n" -path="res://.godot/imported/Camera3DIcon.svg-4805c46004db1c89cc9443dd740693f5.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/viewfinder/Camera3DIcon.svg" -dest_files=["res://.godot/imported/Camera3DIcon.svg-4805c46004db1c89cc9443dd740693f5.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/viewfinder/SceneTypesIcon.svg b/addons/phantom_camera/icons/viewfinder/SceneTypesIcon.svg deleted file mode 100644 index 87e3f79..0000000 --- a/addons/phantom_camera/icons/viewfinder/SceneTypesIcon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/addons/phantom_camera/icons/viewfinder/SceneTypesIcon.svg.import b/addons/phantom_camera/icons/viewfinder/SceneTypesIcon.svg.import deleted file mode 100644 index e97d921..0000000 --- a/addons/phantom_camera/icons/viewfinder/SceneTypesIcon.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dk7omm0x44suj" -path="res://.godot/imported/SceneTypesIcon.svg-66e2255bd3398007bec03a5cbfa4d0aa.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/viewfinder/SceneTypesIcon.svg" -dest_files=["res://.godot/imported/SceneTypesIcon.svg-66e2255bd3398007bec03a5cbfa4d0aa.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/viewfinder/Select.svg b/addons/phantom_camera/icons/viewfinder/Select.svg deleted file mode 100644 index 34b109b..0000000 --- a/addons/phantom_camera/icons/viewfinder/Select.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/addons/phantom_camera/icons/viewfinder/Select.svg.import b/addons/phantom_camera/icons/viewfinder/Select.svg.import deleted file mode 100644 index b1cb20c..0000000 --- a/addons/phantom_camera/icons/viewfinder/Select.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://rghrkoqrm2ig" -path="res://.godot/imported/Select.svg-cdf90b8b400d3b91a023b70c6a823894.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/viewfinder/Select.svg" -dest_files=["res://.godot/imported/Select.svg-cdf90b8b400d3b91a023b70c6a823894.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/icons/warning.svg b/addons/phantom_camera/icons/warning.svg deleted file mode 100644 index 63dbedf..0000000 --- a/addons/phantom_camera/icons/warning.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/addons/phantom_camera/icons/warning.svg.import b/addons/phantom_camera/icons/warning.svg.import deleted file mode 100644 index 07b7909..0000000 --- a/addons/phantom_camera/icons/warning.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cjlv0bg7byjx0" -path="res://.godot/imported/warning.svg-c1b21c265e0842bbdc9ed10735995366.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/phantom_camera/icons/warning.svg" -dest_files=["res://.godot/imported/warning.svg-c1b21c265e0842bbdc9ed10735995366.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/addons/phantom_camera/inspector/phantom_camera_inspector_plugin.gd b/addons/phantom_camera/inspector/phantom_camera_inspector_plugin.gd deleted file mode 100644 index d5a0d22..0000000 --- a/addons/phantom_camera/inspector/phantom_camera_inspector_plugin.gd +++ /dev/null @@ -1,46 +0,0 @@ -@tool -extends EditorInspectorPlugin - -#var _phantom_camera_script: Script = preload("res://addons/phantom_camera/scripts/phantom_camera.gd") - - -# TODO - Enable again once work is resumed for inspector based tasks - -#func _can_handle(object) -> bool: -# return object is _phantom_camera_script - - -func _parse_category(object: Object, category: String) -> void: - - var _margin_container: MarginContainer = MarginContainer.new() - var _margin_v: float = 20 - _margin_container.add_theme_constant_override("margin_left", 10) - _margin_container.add_theme_constant_override("margin_top", _margin_v) - _margin_container.add_theme_constant_override("margin_right", 10) - _margin_container.add_theme_constant_override("margin_bottom", _margin_v) - add_custom_control(_margin_container) - - var _vbox_container: VBoxContainer = VBoxContainer.new() - _margin_container.add_child(_vbox_container) - - var align_with_view_button = Button.new() - align_with_view_button.connect("pressed", _align_camera_with_view.bind(object)) - align_with_view_button.set_custom_minimum_size(Vector2(0, 60)) - align_with_view_button.set_text("Align with view") - _vbox_container.add_child(align_with_view_button) - - var preview_camera_button = Button.new() - preview_camera_button.connect("pressed", _preview_camera.bind(object)) - preview_camera_button.set_custom_minimum_size(Vector2(0, 60)) - preview_camera_button.set_text("Preview Camera") - _vbox_container.add_child(preview_camera_button) - - - -func _align_camera_with_view(object: Object) -> void: - print("Aligning camera with view") - print(object) - -func _preview_camera(object: Object) -> void: - print("Previewing camera") - print(object) diff --git a/addons/phantom_camera/inspector/phantom_camera_inspector_plugin.gd.uid b/addons/phantom_camera/inspector/phantom_camera_inspector_plugin.gd.uid deleted file mode 100644 index 325b3c0..0000000 --- a/addons/phantom_camera/inspector/phantom_camera_inspector_plugin.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dhwxgngr7sn6d diff --git a/addons/phantom_camera/panel/editor.gd.uid b/addons/phantom_camera/panel/editor.gd.uid deleted file mode 100644 index 52a27c4..0000000 --- a/addons/phantom_camera/panel/editor.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ppret7j0jle7 diff --git a/addons/phantom_camera/panel/editor.tscn b/addons/phantom_camera/panel/editor.tscn deleted file mode 100644 index 0c75c6a..0000000 --- a/addons/phantom_camera/panel/editor.tscn +++ /dev/null @@ -1,23 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://cfdoaceoosi1w"] - -[ext_resource type="Script" uid="uid://cgfwg3paxkj2x" path="res://addons/phantom_camera/scripts/panel/editor.gd" id="1_86hp7"] -[ext_resource type="PackedScene" uid="uid://cuqkqsp3ikv5u" path="res://addons/phantom_camera/panel/updater/update_button.tscn" id="1_oowcd"] -[ext_resource type="PackedScene" uid="uid://dbkr3d716wtx0" path="res://addons/phantom_camera/panel/viewfinder/viewfinder_panel.tscn" id="2_xecnk"] - -[node name="EditorPanel" type="VBoxContainer"] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_86hp7") - -[node name="UpdateButton" parent="." instance=ExtResource("1_oowcd")] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 8 -size_flags_vertical = 1 - -[node name="ViewfinderPanel" parent="." instance=ExtResource("2_xecnk")] -unique_name_in_owner = true -layout_mode = 2 diff --git a/addons/phantom_camera/panel/updater/download_update_panel.tscn b/addons/phantom_camera/panel/updater/download_update_panel.tscn deleted file mode 100644 index 5fa49e3..0000000 --- a/addons/phantom_camera/panel/updater/download_update_panel.tscn +++ /dev/null @@ -1,253 +0,0 @@ -[gd_scene load_steps=15 format=3 uid="uid://b25fl4usw0nlp"] - -[ext_resource type="Script" uid="uid://cjblcocen12r3" path="res://addons/phantom_camera/scripts/panel/updater/download_update_panel.gd" id="1_sx5xq"] -[ext_resource type="Texture2D" uid="uid://cc0wmici0eic8" path="res://addons/phantom_camera/icons/phantom_camera_logo.png" id="2_f3yo7"] -[ext_resource type="FontFile" uid="uid://c4mm3of2mc8o5" path="res://addons/phantom_camera/fonts/Nunito-Black.ttf" id="3_h8uk3"] -[ext_resource type="FontFile" uid="uid://dve7mgsjik4dg" path="res://addons/phantom_camera/fonts/Nunito-Regular.ttf" id="4_gwh4i"] -[ext_resource type="Texture2D" uid="uid://censw3w53gldn" path="res://addons/phantom_camera/assets/PhantomCameraBtnPrimaryDefault.png" id="5_bonti"] - -[sub_resource type="ImageTexture" id="ImageTexture_sjwi2"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_insma"] -bg_color = Color(0.0190018, 0.21903, 0.16997, 1) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.0980392, 0.572549, 0.458824, 1) -border_blend = true -corner_radius_bottom_right = 12 -corner_radius_bottom_left = 12 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8m63d"] -bg_color = Color(0.0784314, 0.109804, 0.129412, 1) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.105882, 0.619608, 0.498039, 1) -corner_radius_bottom_right = 12 -corner_radius_bottom_left = 12 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_yn22d"] -bg_color = Color(0.0117647, 0.164706, 0.12549, 1) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.0980392, 0.572549, 0.458824, 1) -border_blend = true -corner_radius_bottom_right = 12 -corner_radius_bottom_left = 12 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_djsbc"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_xtrn6"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_o12j0"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_buptb"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_g3tf0"] - -[node name="DownloadUpdatePanel" type="Control"] -custom_minimum_size = Vector2(300, 350) -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_bottom = -61.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_sx5xq") - -[node name="DownloadHTTPRequest" type="HTTPRequest" parent="."] -unique_name_in_owner = true - -[node name="Timer" type="Timer" parent="DownloadHTTPRequest"] -one_shot = true - -[node name="VBox" type="VBoxContainer" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_constants/separation = 2 - -[node name="VBoxContainer2" type="VBoxContainer" parent="VBox"] -layout_mode = 2 -theme_override_constants/separation = -20 - -[node name="MarginContainer" type="MarginContainer" parent="VBox/VBoxContainer2"] -layout_mode = 2 -theme_override_constants/margin_top = 12 - -[node name="Logo" type="TextureRect" parent="VBox/VBoxContainer2/MarginContainer"] -unique_name_in_owner = true -clip_contents = true -custom_minimum_size = Vector2(300, 160) -layout_mode = 2 -texture = ExtResource("2_f3yo7") -expand_mode = 3 -stretch_mode = 5 - -[node name="VBoxContainer" type="VBoxContainer" parent="VBox/VBoxContainer2"] -layout_mode = 2 -theme_override_constants/separation = -5 - -[node name="NameLabel" type="Label" parent="VBox/VBoxContainer2/VBoxContainer"] -layout_mode = 2 -theme_override_colors/font_color = Color(0.960784, 0.960784, 0.960784, 1) -theme_override_fonts/font = ExtResource("3_h8uk3") -theme_override_font_sizes/font_size = 32 -text = "Phantom Camera" -horizontal_alignment = 1 - -[node name="DownloadVersionLabel" type="Label" parent="VBox/VBoxContainer2/VBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme_override_colors/font_color = Color(0.960784, 0.960784, 0.960784, 1) -theme_override_fonts/font = ExtResource("4_gwh4i") -theme_override_font_sizes/font_size = 18 -text = "v1.2.3 is available for download." -horizontal_alignment = 1 - -[node name="CurrentVersionLabel" type="Label" parent="VBox"] -unique_name_in_owner = true -visible = false -layout_mode = 2 -theme_override_fonts/font = ExtResource("4_gwh4i") -text = "Current version: 0.0.0" -horizontal_alignment = 1 - -[node name="Center2" type="CenterContainer" parent="VBox"] -layout_mode = 2 - -[node name="NotesButton" type="LinkButton" parent="VBox/Center2"] -layout_mode = 2 -theme_override_colors/font_color = Color(0.917647, 0.631373, 0.368627, 1) -theme_override_colors/font_hover_color = Color(0.721569, 0.454902, 0.192157, 1) -theme_override_fonts/font = ExtResource("3_h8uk3") -theme_override_font_sizes/font_size = 18 -text = "Release Notes" - -[node name="Center" type="CenterContainer" parent="VBox"] -layout_mode = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="VBox/Center"] -layout_mode = 2 - -[node name="BreakingLabel" type="Label" parent="VBox/Center/VBoxContainer"] -unique_name_in_owner = true -visible = false -layout_mode = 2 -theme_override_colors/font_color = Color(0.72549, 0.227451, 0.34902, 1) -theme_override_fonts/font = ExtResource("3_h8uk3") -theme_override_font_sizes/font_size = 18 -text = "Potential Breaking Changes -in new release" -horizontal_alignment = 1 -uppercase = true - -[node name="BreakingMarginContainer" type="MarginContainer" parent="VBox/Center/VBoxContainer"] -unique_name_in_owner = true -visible = false -layout_mode = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="VBox/Center/VBoxContainer/BreakingMarginContainer"] -layout_mode = 2 - -[node name="RichTextLabel2" type="RichTextLabel" parent="VBox/Center/VBoxContainer/BreakingMarginContainer/VBoxContainer"] -layout_mode = 2 -theme_override_fonts/normal_font = ExtResource("4_gwh4i") -theme_override_fonts/bold_font = ExtResource("3_h8uk3") -theme_override_fonts/mono_font = ExtResource("3_h8uk3") -theme_override_font_sizes/normal_font_size = 18 -theme_override_font_sizes/bold_font_size = 14 -theme_override_font_sizes/mono_font_size = 12 -bbcode_enabled = true -text = "[center][b]I am prepared for any breaking -changes that may occur from this update[/b][/center]" -fit_content = true - -[node name="BreakingOptionButton" type="OptionButton" parent="VBox/Center/VBoxContainer/BreakingMarginContainer/VBoxContainer"] -unique_name_in_owner = true -visible = false -layout_mode = 2 -mouse_default_cursor_shape = 2 -theme_override_fonts/font = ExtResource("3_h8uk3") -theme_override_font_sizes/font_size = 18 -theme_override_icons/arrow = SubResource("ImageTexture_sjwi2") -theme_override_styles/normal = SubResource("StyleBoxFlat_insma") -theme_override_styles/hover = SubResource("StyleBoxFlat_8m63d") -theme_override_styles/pressed = SubResource("StyleBoxFlat_yn22d") -alignment = 1 -item_count = 2 -selected = 0 -popup/item_0/text = "Confirm choice" -popup/item_0/id = 0 -popup/item_1/text = "Yes, I am prepared" -popup/item_1/id = 1 - -[node name="DownloadButton" type="Button" parent="VBox/Center/VBoxContainer"] -unique_name_in_owner = true -custom_minimum_size = Vector2(240, 90) -layout_mode = 2 -mouse_default_cursor_shape = 2 -theme_override_styles/normal = SubResource("StyleBoxEmpty_djsbc") -theme_override_styles/hover = SubResource("StyleBoxEmpty_xtrn6") -theme_override_styles/pressed = SubResource("StyleBoxEmpty_o12j0") -theme_override_styles/disabled = SubResource("StyleBoxEmpty_buptb") -theme_override_styles/focus = SubResource("StyleBoxEmpty_g3tf0") - -[node name="DownloadButtonBG" type="NinePatchRect" parent="VBox/Center/VBoxContainer/DownloadButton"] -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 -texture = ExtResource("5_bonti") -patch_margin_left = 38 -patch_margin_top = 37 -patch_margin_right = 38 -patch_margin_bottom = 39 - -[node name="UpdateLabel" type="Label" parent="VBox/Center/VBoxContainer/DownloadButton"] -unique_name_in_owner = true -layout_mode = 1 -anchors_preset = 14 -anchor_top = 0.5 -anchor_right = 1.0 -anchor_bottom = 0.5 -offset_top = -14.5 -offset_bottom = 14.5 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_colors/font_color = Color(0.960784, 0.960784, 0.960784, 1) -theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) -theme_override_fonts/font = ExtResource("3_h8uk3") -theme_override_font_sizes/font_size = 20 -text = "Update" -horizontal_alignment = 1 -uppercase = true - -[node name="MarginContainer" type="MarginContainer" parent="VBox"] -layout_mode = 2 -theme_override_constants/margin_top = 10 - -[node name="RichTextLabel" type="RichTextLabel" parent="VBox/MarginContainer"] -layout_mode = 2 -theme_override_fonts/normal_font = ExtResource("4_gwh4i") -theme_override_fonts/mono_font = ExtResource("3_h8uk3") -theme_override_font_sizes/normal_font_size = 12 -theme_override_font_sizes/mono_font_size = 12 -bbcode_enabled = true -text = "[center]The updater can be disabled within: -[code]Project Settings / Phantom Camera / Updater / Enable Updater[/code][/center]" -fit_content = true - -[connection signal="pressed" from="VBox/Center2/NotesButton" to="." method="_on_notes_button_pressed"] diff --git a/addons/phantom_camera/panel/updater/update_button.tscn b/addons/phantom_camera/panel/updater/update_button.tscn deleted file mode 100644 index a6efba1..0000000 --- a/addons/phantom_camera/panel/updater/update_button.tscn +++ /dev/null @@ -1,101 +0,0 @@ -[gd_scene load_steps=10 format=3 uid="uid://cuqkqsp3ikv5u"] - -[ext_resource type="FontFile" uid="uid://c4mm3of2mc8o5" path="res://addons/phantom_camera/fonts/Nunito-Black.ttf" id="1_5e5k4"] -[ext_resource type="Script" uid="uid://bwc42i46603qn" path="res://addons/phantom_camera/scripts/panel/updater/update_button.gd" id="1_xtaw5"] -[ext_resource type="Texture2D" uid="uid://d4j4hrb7yusyq" path="res://addons/phantom_camera/icons/phantom_camera_updater_panel_icon.svg" id="2_c4d83"] -[ext_resource type="PackedScene" uid="uid://b25fl4usw0nlp" path="res://addons/phantom_camera/panel/updater/download_update_panel.tscn" id="2_vtgcx"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_c7fd1"] -content_margin_left = 10.0 -content_margin_top = 4.0 -content_margin_right = 10.0 -content_margin_bottom = 4.0 -bg_color = Color(0.0980392, 0.415686, 0.341176, 1) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_radius_top_left = 20 -corner_radius_top_right = 20 -corner_radius_bottom_right = 20 -corner_radius_bottom_left = 20 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_y83dj"] -content_margin_left = 10.0 -content_margin_top = 4.0 -content_margin_right = 10.0 -content_margin_bottom = 4.0 -bg_color = Color(0.0784314, 0.109804, 0.129412, 1) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.960784, 0.960784, 0.960784, 1) -corner_radius_top_left = 20 -corner_radius_top_right = 20 -corner_radius_bottom_right = 20 -corner_radius_bottom_left = 20 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_slf6e"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_lekqh"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dr4n4"] -content_margin_bottom = 20.0 -bg_color = Color(0.0784314, 0.109804, 0.129412, 1) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.227451, 0.72549, 0.603922, 1) - -[node name="UpdateButton" type="Button"] -visible = false -offset_left = 1.0 -offset_right = 149.0 -offset_bottom = 28.0 -size_flags_vertical = 3 -theme_override_colors/font_color = Color(0.960784, 0.960784, 0.960784, 1) -theme_override_colors/font_hover_color = Color(0.939288, 0.917743, 0.892615, 1) -theme_override_colors/icon_normal_color = Color(0.960784, 0.960784, 0.960784, 1) -theme_override_fonts/font = ExtResource("1_5e5k4") -theme_override_font_sizes/font_size = 14 -theme_override_styles/normal = SubResource("StyleBoxFlat_c7fd1") -theme_override_styles/hover = SubResource("StyleBoxFlat_y83dj") -theme_override_styles/pressed = SubResource("StyleBoxEmpty_slf6e") -theme_override_styles/focus = SubResource("StyleBoxEmpty_lekqh") -text = "Update available" -icon = ExtResource("2_c4d83") -script = ExtResource("1_xtaw5") - -[node name="HTTPRequest" type="HTTPRequest" parent="."] -unique_name_in_owner = true - -[node name="DownloadDialog" type="AcceptDialog" parent="."] -unique_name_in_owner = true -transparent_bg = true -title = "New Update" -initial_position = 2 -size = Vector2i(450, 480) -transient = false -unresizable = true -borderless = true -keep_title_visible = false -content_scale_mode = 1 -theme_override_constants/buttons_separation = 30 -theme_override_styles/panel = SubResource("StyleBoxFlat_dr4n4") -ok_button_text = "Close" - -[node name="DownloadUpdatePanel" parent="DownloadDialog" instance=ExtResource("2_vtgcx")] -unique_name_in_owner = true -offset_left = 2.0 -offset_top = 2.0 -offset_right = -2.0 -offset_bottom = -80.0 - -[node name="NeedsReloadDialog" type="ConfirmationDialog" parent="."] -unique_name_in_owner = true - -[node name="UpdateFailedDialog" type="AcceptDialog" parent="."] -unique_name_in_owner = true diff --git a/addons/phantom_camera/panel/viewfinder/deadzone_style_box.tres b/addons/phantom_camera/panel/viewfinder/deadzone_style_box.tres deleted file mode 100644 index 7353299..0000000 --- a/addons/phantom_camera/panel/viewfinder/deadzone_style_box.tres +++ /dev/null @@ -1,14 +0,0 @@ -[gd_resource type="StyleBoxFlat" format=3 uid="uid://dpa7yvxlq043a"] - -[resource] -bg_color = Color(0.227451, 0.72549, 0.603922, 0.2) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_detail = 1 -expand_margin_left = 1.0 -expand_margin_top = 1.0 -expand_margin_right = 1.0 -expand_margin_bottom = 1.0 diff --git a/addons/phantom_camera/panel/viewfinder/host_list/host_list.tscn b/addons/phantom_camera/panel/viewfinder/host_list/host_list.tscn deleted file mode 100644 index 332415d..0000000 --- a/addons/phantom_camera/panel/viewfinder/host_list/host_list.tscn +++ /dev/null @@ -1,87 +0,0 @@ -[gd_scene load_steps=8 format=3 uid="uid://mbjdav5oqves"] - -[ext_resource type="Script" uid="uid://c84cxry3t35ny" path="res://addons/phantom_camera/scripts/panel/viewfinder/host_list.gd" id="1_h6ayt"] -[ext_resource type="Texture2D" uid="uid://5fatldiu7dd5" path="res://addons/phantom_camera/icons/phantom_camera_host.svg" id="1_xlgqb"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_w002y"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kq7gm"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.0784314, 0.109804, 0.129412, 1) -border_width_top = 2 -border_width_right = 2 -border_color = Color(0.960784, 0.960784, 0.960784, 1) -corner_radius_top_right = 8 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ynag5"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.960784, 0.960784, 0.960784, 1) -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -corner_radius_top_right = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_q2svd"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.0784314, 0.109804, 0.129412, 1) -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_radius_top_right = 8 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_e0jvt"] -content_margin_left = 0.0 -content_margin_top = 8.0 -content_margin_right = 0.0 -content_margin_bottom = 8.0 -bg_color = Color(0.0784314, 0.109804, 0.129412, 1) -border_width_top = 2 -border_width_right = 2 -border_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_radius_top_right = 10 - -[node name="PCamHostList" type="VBoxContainer"] -anchors_preset = 9 -anchor_bottom = 1.0 -size_flags_horizontal = 0 -size_flags_vertical = 0 -theme_override_constants/separation = -2 -alignment = 2 -script = ExtResource("1_h6ayt") - -[node name="HostListButton" type="Button" parent="."] -unique_name_in_owner = true -custom_minimum_size = Vector2(40, 40) -layout_mode = 2 -size_flags_horizontal = 0 -theme_override_colors/icon_hover_color = Color(0.0784314, 0.109804, 0.129412, 1) -theme_override_colors/icon_hover_pressed_color = Color(0.0784314, 0.109804, 0.129412, 1) -theme_override_styles/focus = SubResource("StyleBoxEmpty_w002y") -theme_override_styles/hover_pressed = SubResource("StyleBoxFlat_kq7gm") -theme_override_styles/hover = SubResource("StyleBoxFlat_ynag5") -theme_override_styles/pressed = SubResource("StyleBoxFlat_kq7gm") -theme_override_styles/normal = SubResource("StyleBoxFlat_q2svd") -icon = ExtResource("1_xlgqb") -expand_icon = true - -[node name="ScrollContainer" type="ScrollContainer" parent="."] -unique_name_in_owner = true -layout_mode = 2 -size_flags_vertical = 3 -theme_override_styles/panel = SubResource("StyleBoxFlat_e0jvt") -horizontal_scroll_mode = 0 - -[node name="HostListContainer" type="VBoxContainer" parent="ScrollContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme_override_constants/separation = 8 diff --git a/addons/phantom_camera/panel/viewfinder/host_list/host_list_item.tscn b/addons/phantom_camera/panel/viewfinder/host_list/host_list_item.tscn deleted file mode 100644 index 9ce67e5..0000000 --- a/addons/phantom_camera/panel/viewfinder/host_list/host_list_item.tscn +++ /dev/null @@ -1,68 +0,0 @@ -[gd_scene load_steps=10 format=3 uid="uid://btn6jgv0vix7"] - -[ext_resource type="FontFile" uid="uid://dve7mgsjik4dg" path="res://addons/phantom_camera/fonts/Nunito-Regular.ttf" id="1_anjxo"] -[ext_resource type="Theme" uid="uid://bhppejri5dbsf" path="res://addons/phantom_camera/themes/theme.tres" id="1_wql5t"] -[ext_resource type="Texture2D" uid="uid://rghrkoqrm2ig" path="res://addons/phantom_camera/icons/viewfinder/Select.svg" id="2_71b6g"] -[ext_resource type="ButtonGroup" uid="uid://dfu0b8jbtyr1n" path="res://addons/phantom_camera/panel/viewfinder/host_list/host_list_item_group.tres" id="3_06a0y"] -[ext_resource type="Script" uid="uid://bv24ubx8mutw7" path="res://addons/phantom_camera/scripts/panel/viewfinder/host_list_item.gd" id="3_a5o8b"] -[ext_resource type="Texture2D" uid="uid://cjlv0bg7byjx0" path="res://addons/phantom_camera/icons/warning.svg" id="3_qgpy7"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_0rxfi"] -content_margin_right = 6.0 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_llqnh"] - -[sub_resource type="Theme" id="Theme_7h15c"] -Button/colors/icon_hover_color = Color(0.960784, 0.960784, 0.960784, 1) -Button/colors/icon_hover_pressed_color = Color(0.227451, 0.72549, 0.603922, 1) -Button/colors/icon_normal_color = Color(0.227451, 0.72549, 0.603922, 1) -Button/colors/icon_pressed_color = Color(0.227451, 0.72549, 0.603922, 1) -Button/constants/icon_max_width = 20 -Button/styles/focus = SubResource("StyleBoxEmpty_llqnh") - -[node name="HostListItem" type="PanelContainer"] -offset_right = 229.0 -offset_bottom = 34.0 -theme_override_styles/panel = SubResource("StyleBoxEmpty_0rxfi") -script = ExtResource("3_a5o8b") - -[node name="HBoxContainer" type="HBoxContainer" parent="."] -layout_mode = 2 -theme_override_constants/separation = 2 - -[node name="SelectPCamHost" type="Button" parent="HBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_vertical = 4 -tooltip_text = "Select the Phantom Camera Host node from the scene hierarchy" -focus_mode = 0 -theme = SubResource("Theme_7h15c") -icon = ExtResource("2_71b6g") -flat = true - -[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer"] -layout_mode = 2 -theme_override_constants/separation = 8 - -[node name="ErrorPCamHost" type="TextureRect" parent="HBoxContainer/HBoxContainer"] -unique_name_in_owner = true -custom_minimum_size = Vector2(18, 18) -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 4 -tooltip_text = "This Phantom Camera Host node will not affect a Camera node. -See the warning in the Scene Tree for more information." -texture = ExtResource("3_qgpy7") -expand_mode = 1 - -[node name="SwitchPCamHost" type="Button" parent="HBoxContainer/HBoxContainer"] -unique_name_in_owner = true -custom_minimum_size = Vector2(40, 0) -layout_mode = 2 -tooltip_text = "Change the viewfinder's camera to the camera of this Phantom Camera Host" -theme = ExtResource("1_wql5t") -theme_override_fonts/font = ExtResource("1_anjxo") -theme_override_font_sizes/font_size = 18 -toggle_mode = true -button_group = ExtResource("3_06a0y") -text = "{ PCamHostName }" diff --git a/addons/phantom_camera/panel/viewfinder/host_list/host_list_item_group.tres b/addons/phantom_camera/panel/viewfinder/host_list/host_list_item_group.tres deleted file mode 100644 index 64c4600..0000000 --- a/addons/phantom_camera/panel/viewfinder/host_list/host_list_item_group.tres +++ /dev/null @@ -1,3 +0,0 @@ -[gd_resource type="ButtonGroup" format=3 uid="uid://dfu0b8jbtyr1n"] - -[resource] diff --git a/addons/phantom_camera/panel/viewfinder/viewfinder_panel.tscn b/addons/phantom_camera/panel/viewfinder/viewfinder_panel.tscn deleted file mode 100644 index b6caa0d..0000000 --- a/addons/phantom_camera/panel/viewfinder/viewfinder_panel.tscn +++ /dev/null @@ -1,563 +0,0 @@ -[gd_scene load_steps=28 format=3 uid="uid://dbkr3d716wtx0"] - -[ext_resource type="Script" uid="uid://drmv3363t8amc" path="res://addons/phantom_camera/scripts/panel/viewfinder/viewfinder.gd" id="1_utvi8"] -[ext_resource type="StyleBox" uid="uid://dpa7yvxlq043a" path="res://addons/phantom_camera/panel/viewfinder/deadzone_style_box.tres" id="2_uabt4"] -[ext_resource type="FontFile" uid="uid://c4mm3of2mc8o5" path="res://addons/phantom_camera/fonts/Nunito-Black.ttf" id="3_li3i3"] -[ext_resource type="Texture2D" uid="uid://5fatldiu7dd5" path="res://addons/phantom_camera/icons/phantom_camera_host.svg" id="4_lcg1p"] -[ext_resource type="FontFile" uid="uid://dve7mgsjik4dg" path="res://addons/phantom_camera/fonts/Nunito-Regular.ttf" id="5_4jhax"] -[ext_resource type="Texture2D" uid="uid://dy8eifa6aw2en" path="res://addons/phantom_camera/icons/misc/PriorityOverride.svg" id="6_ptuth"] -[ext_resource type="Script" uid="uid://c84cxry3t35ny" path="res://addons/phantom_camera/scripts/panel/viewfinder/host_list.gd" id="7_kpij0"] -[ext_resource type="Theme" uid="uid://bhppejri5dbsf" path="res://addons/phantom_camera/themes/theme.tres" id="8_b4akn"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_fle8t"] -bg_color = Color(0.227451, 0.72549, 0.603922, 0.2) -draw_center = false -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_detail = 1 -expand_margin_left = 1.0 -expand_margin_top = 1.0 -expand_margin_right = 1.0 -expand_margin_bottom = 1.0 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xmo1t"] -draw_center = false -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.745098, 0.858824, 0.380392, 1) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_q7vs4"] -bg_color = Color(0.929412, 0.87451, 0.619608, 1) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0, 0, 0, 1) -corner_radius_top_left = 10 -corner_radius_top_right = 10 -corner_radius_bottom_right = 10 -corner_radius_bottom_left = 10 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_iho1a"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_obaj6"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_4b76l"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_yh38y"] -content_margin_left = 10.0 -content_margin_top = 10.0 -content_margin_right = 10.0 -content_margin_bottom = 10.0 -bg_color = Color(0.129412, 0.407843, 0.337255, 1) -border_width_left = 4 -border_width_top = 4 -border_width_right = 4 -border_width_bottom = 4 -border_color = Color(0.988235, 0.498039, 0.498039, 1) -corner_radius_top_left = 10 -corner_radius_top_right = 10 -corner_radius_bottom_right = 10 -corner_radius_bottom_left = 10 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_gci88"] -content_margin_left = 10.0 -content_margin_top = 10.0 -content_margin_right = 10.0 -content_margin_bottom = 10.0 -bg_color = Color(0.180392, 0.576471, 0.482353, 1) -corner_radius_top_left = 10 -corner_radius_top_right = 10 -corner_radius_bottom_right = 10 -corner_radius_bottom_left = 10 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_fsxik"] -content_margin_left = 10.0 -content_margin_top = 10.0 -content_margin_right = 10.0 -content_margin_bottom = 10.0 -bg_color = Color(0.129412, 0.407843, 0.337255, 1) -border_width_left = 4 -border_width_top = 4 -border_width_right = 4 -border_width_bottom = 4 -border_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_radius_top_left = 10 -corner_radius_top_right = 10 -corner_radius_bottom_right = 10 -corner_radius_bottom_left = 10 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_g5wua"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_x4bx8"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_840sd"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ja3vm"] -bg_color = Color(0.53, 0.1643, 0.255725, 1) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_blend = true -corner_radius_top_left = 10 -corner_radius_top_right = 10 -corner_radius_bottom_right = 10 -corner_radius_bottom_left = 10 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mk273"] -bg_color = Color(0.43, 0.1333, 0.207475, 1) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_blend = true -corner_radius_top_left = 10 -corner_radius_top_right = 10 -corner_radius_bottom_right = 10 -corner_radius_bottom_left = 10 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_agqdu"] -bg_color = Color(0.72549, 0.227451, 0.34902, 1) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_blend = true -corner_radius_top_left = 10 -corner_radius_top_right = 10 -corner_radius_bottom_right = 10 -corner_radius_bottom_left = 10 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_w002y"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kq7gm"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.0784314, 0.109804, 0.129412, 1) -border_width_top = 2 -border_width_right = 2 -border_color = Color(0.960784, 0.960784, 0.960784, 1) -corner_radius_top_right = 8 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ynag5"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.960784, 0.960784, 0.960784, 1) -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -corner_radius_top_right = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_q2svd"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.0784314, 0.109804, 0.129412, 1) -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_radius_top_right = 8 - -[node name="ViewfinderPanel" type="Control"] -clip_contents = true -custom_minimum_size = Vector2(0, 300) -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -mouse_filter = 2 -script = ExtResource("1_utvi8") - -[node name="Viewfinder" type="Control" parent="."] -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 -metadata/_edit_lock_ = true - -[node name="SubViewportContainer" type="SubViewportContainer" parent="Viewfinder"] -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 -stretch = true - -[node name="SubViewport" type="SubViewport" parent="Viewfinder/SubViewportContainer"] -unique_name_in_owner = true -handle_input_locally = false -canvas_item_default_texture_filter = 0 -gui_disable_input = true -size = Vector2i(1152, 648) -size_2d_override_stretch = true -render_target_update_mode = 4 - -[node name="Camera2D" type="Camera2D" parent="Viewfinder/SubViewportContainer/SubViewport"] -unique_name_in_owner = true -editor_draw_screen = false - -[node name="DeadZoneHBoxContainer" type="HBoxContainer" parent="Viewfinder"] -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/separation = 0 - -[node name="DeadZoneLeftHBoxContainer" type="VBoxContainer" parent="Viewfinder/DeadZoneHBoxContainer"] -clip_contents = true -layout_mode = 2 -size_flags_horizontal = 3 -mouse_filter = 2 -theme_override_constants/separation = 0 - -[node name="DeadZoneLeftTopPanel" type="Panel" parent="Viewfinder/DeadZoneHBoxContainer/DeadZoneLeftHBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 -mouse_filter = 2 -theme_override_styles/panel = ExtResource("2_uabt4") - -[node name="DeadZoneLeftCenterPanel" type="Panel" parent="Viewfinder/DeadZoneHBoxContainer/DeadZoneLeftHBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -mouse_filter = 2 -theme_override_styles/panel = ExtResource("2_uabt4") - -[node name="DeadZoneLeftBottomPanel" type="Panel" parent="Viewfinder/DeadZoneHBoxContainer/DeadZoneLeftHBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 -mouse_filter = 2 -theme_override_styles/panel = ExtResource("2_uabt4") - -[node name="DeadZoneCenterHBoxContainer" type="VBoxContainer" parent="Viewfinder/DeadZoneHBoxContainer"] -unique_name_in_owner = true -clip_contents = true -layout_mode = 2 -size_flags_horizontal = 4 -mouse_filter = 2 -theme_override_constants/separation = 0 - -[node name="DeadZoneCenterTopPanel" type="Panel" parent="Viewfinder/DeadZoneHBoxContainer/DeadZoneCenterHBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 -mouse_filter = 2 -theme_override_styles/panel = ExtResource("2_uabt4") - -[node name="DeadZoneCenterCenterPanel" type="Panel" parent="Viewfinder/DeadZoneHBoxContainer/DeadZoneCenterHBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_vertical = 4 -mouse_filter = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_fle8t") - -[node name="DeadZoneCenterBottomPanel" type="Panel" parent="Viewfinder/DeadZoneHBoxContainer/DeadZoneCenterHBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 -mouse_filter = 2 -theme_override_styles/panel = ExtResource("2_uabt4") - -[node name="DeadZoneRightHBoxContainer" type="VBoxContainer" parent="Viewfinder/DeadZoneHBoxContainer"] -clip_contents = true -layout_mode = 2 -size_flags_horizontal = 3 -mouse_filter = 2 -theme_override_constants/separation = 0 - -[node name="DeadZoneRightTopPanel" type="Panel" parent="Viewfinder/DeadZoneHBoxContainer/DeadZoneRightHBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 -mouse_filter = 2 -theme_override_styles/panel = ExtResource("2_uabt4") - -[node name="DeadZoneRightCenterPanel" type="Panel" parent="Viewfinder/DeadZoneHBoxContainer/DeadZoneRightHBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -mouse_filter = 2 -theme_override_styles/panel = ExtResource("2_uabt4") - -[node name="DeadZoneRightBottomPanel" type="Panel" parent="Viewfinder/DeadZoneHBoxContainer/DeadZoneRightHBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 -mouse_filter = 2 -theme_override_styles/panel = ExtResource("2_uabt4") - -[node name="AspectRatioContainer" type="AspectRatioContainer" parent="Viewfinder"] -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 -ratio = 1.77778 - -[node name="CameraViewportPanel" type="Panel" parent="Viewfinder/AspectRatioContainer"] -layout_mode = 2 -mouse_filter = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_xmo1t") - -[node name="TargetPoint" type="Panel" parent="Viewfinder/AspectRatioContainer/CameraViewportPanel"] -unique_name_in_owner = true -layout_mode = 1 -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -3.0 -offset_top = -3.0 -offset_right = 3.0 -offset_bottom = 3.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_q7vs4") - -[node name="NoSupportMsg" type="Label" parent="."] -unique_name_in_owner = true -visible = false -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_top = -8.0 -offset_bottom = -8.0 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_fonts/font = ExtResource("3_li3i3") -theme_override_font_sizes/font_size = 24 -theme_override_styles/normal = SubResource("StyleBoxEmpty_iho1a") -text = "Control scenes are not supported" -horizontal_alignment = 1 -vertical_alignment = 1 -metadata/_edit_lock_ = true - -[node name="EmptyStateControl" type="Control" parent="."] -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 -metadata/_edit_use_anchors_ = true -metadata/_edit_lock_ = true - -[node name="BGColorRect" type="ColorRect" parent="EmptyStateControl"] -visible = false -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -color = Color(0, 0, 0, 1) -metadata/_edit_lock_ = true - -[node name="VBoxContainer" type="VBoxContainer" parent="EmptyStateControl"] -layout_mode = 1 -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -200.0 -offset_top = -112.0 -offset_right = 200.0 -offset_bottom = 112.0 -grow_horizontal = 2 -grow_vertical = 2 -alignment = 1 - -[node name="EmptyStateIcon" type="TextureRect" parent="EmptyStateControl/VBoxContainer"] -unique_name_in_owner = true -custom_minimum_size = Vector2(0, 64) -layout_mode = 2 -texture = ExtResource("4_lcg1p") -expand_mode = 1 -stretch_mode = 5 - -[node name="EmptyStateText" type="RichTextLabel" parent="EmptyStateControl/VBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme_override_colors/default_color = Color(1, 1, 1, 1) -theme_override_fonts/normal_font = ExtResource("5_4jhax") -theme_override_fonts/bold_font = ExtResource("3_li3i3") -theme_override_font_sizes/normal_font_size = 24 -theme_override_font_sizes/bold_font_size = 24 -theme_override_styles/focus = SubResource("StyleBoxEmpty_obaj6") -theme_override_styles/normal = SubResource("StyleBoxEmpty_iho1a") -bbcode_enabled = true -text = "[center][b]NodeType[/b] Description [/center]" -fit_content = true - -[node name="AddNodeButton" type="Button" parent="EmptyStateControl/VBoxContainer"] -unique_name_in_owner = true -custom_minimum_size = Vector2(400, 54) -layout_mode = 2 -size_flags_horizontal = 4 -focus_mode = 0 -theme_override_colors/font_color = Color(1, 1, 1, 1) -theme_override_fonts/font = ExtResource("3_li3i3") -theme_override_font_sizes/font_size = 24 -theme_override_styles/focus = SubResource("StyleBoxEmpty_4b76l") -theme_override_styles/hover = SubResource("StyleBoxFlat_yh38y") -theme_override_styles/pressed = SubResource("StyleBoxFlat_gci88") -theme_override_styles/normal = SubResource("StyleBoxFlat_fsxik") - -[node name="AddNodeTypeText" type="RichTextLabel" parent="EmptyStateControl/VBoxContainer/AddNodeButton"] -unique_name_in_owner = true -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_top = 9.0 -offset_bottom = -11.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 2 -theme_override_colors/default_color = Color(1, 1, 1, 1) -theme_override_fonts/normal_font = ExtResource("5_4jhax") -theme_override_fonts/bold_font = ExtResource("3_li3i3") -theme_override_font_sizes/normal_font_size = 24 -theme_override_font_sizes/bold_font_size = 24 -theme_override_styles/focus = SubResource("StyleBoxEmpty_g5wua") -theme_override_styles/normal = SubResource("StyleBoxEmpty_x4bx8") -bbcode_enabled = true -text = "[center]Add [img=32]res://addons/phantom_camera/icons/viewfinder/Camera3DIcon.svg[/img] [b]{NodeType}[/b][/center]" -scroll_active = false - -[node name="PriorityOverrideButton" type="Button" parent="."] -unique_name_in_owner = true -visible = false -layout_mode = 1 -offset_left = 5.0 -offset_top = 5.0 -offset_right = 165.0 -offset_bottom = 57.0 -mouse_default_cursor_shape = 2 -theme_override_styles/focus = SubResource("StyleBoxEmpty_840sd") -theme_override_styles/hover = SubResource("StyleBoxFlat_ja3vm") -theme_override_styles/pressed = SubResource("StyleBoxFlat_mk273") -theme_override_styles/normal = SubResource("StyleBoxFlat_agqdu") -metadata/_edit_lock_ = true - -[node name="PriorityOverrideIcon" type="TextureRect" parent="PriorityOverrideButton"] -layout_mode = 1 -offset_left = 8.0 -offset_top = 4.0 -offset_right = 32.0 -offset_bottom = 28.0 -texture = ExtResource("6_ptuth") -expand_mode = 1 - -[node name="PriorityOverrideByLabel" type="Label" parent="PriorityOverrideButton"] -layout_mode = 0 -offset_left = 30.0 -offset_top = 1.0 -offset_right = 140.0 -offset_bottom = 24.0 -theme_override_fonts/font = ExtResource("3_li3i3") -theme_override_font_sizes/font_size = 14 -text = "OVERRIDDEN BY" -vertical_alignment = 1 - -[node name="PriorityOverrideNameLabel" type="Label" parent="PriorityOverrideButton"] -unique_name_in_owner = true -layout_mode = 0 -offset_left = 6.0 -offset_top = 21.0 -offset_right = 153.0 -offset_bottom = 44.0 -theme_override_fonts/font = ExtResource("5_4jhax") -theme_override_font_sizes/font_size = 14 -text = "PCam_Name -" -vertical_alignment = 1 -text_overrun_behavior = 3 - -[node name="SizeLabel" type="Label" parent="."] -unique_name_in_owner = true -layout_mode = 1 -anchors_preset = 4 -anchor_top = 0.5 -anchor_bottom = 0.5 -offset_top = -11.5 -offset_right = 40.0 -offset_bottom = 11.5 -grow_vertical = 2 - -[node name="PCamHostList" type="VBoxContainer" parent="."] -unique_name_in_owner = true -visible = false -layout_mode = 1 -anchors_preset = -1 -anchor_bottom = 1.0 -offset_top = 588.0 -grow_vertical = 2 -size_flags_horizontal = 0 -size_flags_vertical = 0 -theme_override_constants/separation = -2 -alignment = 2 -script = ExtResource("7_kpij0") - -[node name="HostListButton" type="Button" parent="PCamHostList"] -unique_name_in_owner = true -custom_minimum_size = Vector2(40, 40) -layout_mode = 2 -size_flags_horizontal = 0 -theme_override_colors/icon_hover_pressed_color = Color(0.0784314, 0.109804, 0.129412, 1) -theme_override_colors/icon_hover_color = Color(0.0784314, 0.109804, 0.129412, 1) -theme_override_styles/focus = SubResource("StyleBoxEmpty_w002y") -theme_override_styles/hover_pressed = SubResource("StyleBoxFlat_kq7gm") -theme_override_styles/hover = SubResource("StyleBoxFlat_ynag5") -theme_override_styles/pressed = SubResource("StyleBoxFlat_kq7gm") -theme_override_styles/normal = SubResource("StyleBoxFlat_q2svd") -icon = ExtResource("4_lcg1p") -expand_icon = true - -[node name="PanelContainer" type="PanelContainer" parent="PCamHostList"] -layout_mode = 2 -size_flags_vertical = 3 -theme = ExtResource("8_b4akn") - -[node name="ScrollContainer" type="ScrollContainer" parent="PCamHostList/PanelContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_vertical = 3 -theme = ExtResource("8_b4akn") -horizontal_scroll_mode = 0 - -[node name="HostListContainer" type="VBoxContainer" parent="PCamHostList/PanelContainer/ScrollContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme = ExtResource("8_b4akn") diff --git a/addons/phantom_camera/plugin.cfg b/addons/phantom_camera/plugin.cfg deleted file mode 100644 index 4fcbcfe..0000000 --- a/addons/phantom_camera/plugin.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[plugin] - -name="Phantom Camera" -description="Control the movement and dynamically tween 2D & 3D cameras positions. Built for Godot 4. Inspired by Cinemachine." -author="Marcus Skov" -version="0.9.4.1" -script="plugin.gd" diff --git a/addons/phantom_camera/plugin.gd b/addons/phantom_camera/plugin.gd deleted file mode 100644 index 0421330..0000000 --- a/addons/phantom_camera/plugin.gd +++ /dev/null @@ -1,181 +0,0 @@ -@tool -extends EditorPlugin - -#region Constants - -const PCAM_HOST: String = "PhantomCameraHost" -const PCAM_2D: String = "PhantomCamera2D" -const PCAM_3D: String = "PhantomCamera3D" -const PCAM_NOISE_EMITTER_2D: String = "PhantomCameraNoiseEmitter2D" -const PCAM_NOISE_EMITTER_3D: String = "PhantomCameraNoiseEmitter3D" - -const PCam3DPlugin: Script = preload("res://addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo_plugin.gd") -const PCam3DNoiseEmitterPlugin: Script = preload("res://addons/phantom_camera/scripts/gizmos/phantom_camera_noise_emitter_gizmo_plugin_3d.gd") -const EditorPanel: PackedScene = preload("res://addons/phantom_camera/panel/editor.tscn") -const updater_constants: Script = preload("res://addons/phantom_camera/scripts/panel/updater/updater_constants.gd") -const PHANTOM_CAMERA_MANAGER: StringName = "PhantomCameraManager" - -#endregion - -#region Private Variables - -var _settings_show_jitter_tips: String = "phantom_camera/tips/show_jitter_tips" -var _settings_enable_editor_shortcut: String = "phantom_camera/general/enable_editor_shortcut" -var _settings_editor_shortcut: String = "phantom_camera/general/editor_shortcut" - -# TODO - Pending merge of https://github.com/godotengine/godot/pull/102889 - Should only support Godot version after the release that is featured in -#var _editor_shortcut: Shortcut = Shortcut.new() -#var _editor_shortcut_input: InputEventKey -#endregion - -#region Public Variables - -var pcam_3d_gizmo_plugin = PCam3DPlugin.new() -var pcam_3d_noise_emitter_gizmo_plugin = PCam3DNoiseEmitterPlugin.new() - -var editor_panel_instance: Control -var panel_button: Button -#var viewfinder_panel_instance - - -#endregion - -#region Private Functions - -func _enable_plugin() -> void: - print_rich("Phantom Camera documentation can be found at: [url=https://phantom-camera.dev]https://phantom-camera.dev[/url]") - if not Engine.has_singleton(PHANTOM_CAMERA_MANAGER): - add_autoload_singleton(PHANTOM_CAMERA_MANAGER, "res://addons/phantom_camera/scripts/managers/phantom_camera_manager.gd") - - -func _disable_plugin() -> void: - remove_autoload_singleton(PHANTOM_CAMERA_MANAGER) - - -func _enter_tree() -> void: - add_autoload_singleton(PHANTOM_CAMERA_MANAGER, "res://addons/phantom_camera/scripts/managers/phantom_camera_manager.gd") - - # Phantom Camera Nodes - add_custom_type(PCAM_2D, "Node2D", preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd"), preload("res://addons/phantom_camera/icons/phantom_camera_2d.svg")) - add_custom_type(PCAM_3D, "Node3D", preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd"), preload("res://addons/phantom_camera/icons/phantom_camera_2d.svg")) - add_custom_type(PCAM_HOST, "Node", preload("res://addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd"), preload("res://addons/phantom_camera/icons/phantom_camera_2d.svg")) - add_custom_type(PCAM_NOISE_EMITTER_2D, "Node2D", preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_2d.gd"), preload("res://addons/phantom_camera/icons/phantom_camera_noise_emitter_2d.svg")) - add_custom_type(PCAM_NOISE_EMITTER_3D, "Node3D", preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_3d.gd"), preload("res://addons/phantom_camera/icons/phantom_camera_noise_emitter_3d.svg")) - - # Phantom Camera 3D Gizmo - add_node_3d_gizmo_plugin(pcam_3d_gizmo_plugin) - add_node_3d_gizmo_plugin(pcam_3d_noise_emitter_gizmo_plugin) - - var setting_updater_mode: String - var setting_updater_mode_default: int - if FileAccess.file_exists("res://dev_scenes/3d/dev_scene_3d.tscn"): # For forks - setting_updater_mode = "Off, Console Output" - setting_updater_mode_default = 1 - else: # For end-users - setting_updater_mode = "Off, Console Output, Updater Window" - setting_updater_mode_default = 2 - - if not ProjectSettings.has_setting(updater_constants.setting_updater_mode): - ProjectSettings.set_setting(updater_constants.setting_updater_mode, setting_updater_mode_default) - ProjectSettings.add_property_info({ - "name": updater_constants.setting_updater_mode, - "type": TYPE_INT, - "hint": PROPERTY_HINT_ENUM, - "hint_string": setting_updater_mode, - }) - ProjectSettings.set_initial_value(updater_constants.setting_updater_mode, setting_updater_mode_default) - ProjectSettings.set_as_basic(updater_constants.setting_updater_mode, true) - - - ## Setting for enabling / disabling Jitter tips in the Output - if not ProjectSettings.has_setting(_settings_show_jitter_tips): - ProjectSettings.set_setting(_settings_show_jitter_tips, true) - ProjectSettings.add_property_info({ - "name": _settings_show_jitter_tips, - "type": TYPE_BOOL, - }) - ProjectSettings.set_initial_value(_settings_show_jitter_tips, true) - ProjectSettings.set_as_basic(_settings_show_jitter_tips, true) - - -# TODO - Pending merge of https://github.com/godotengine/godot/pull/102889 - Should only support Godot version after this release -# if not ProjectSettings.has_setting(_settings_enable_editor_shortcut): -# ProjectSettings.set_setting(_settings_enable_editor_shortcut, false) -# ProjectSettings.set_initial_value(_settings_enable_editor_shortcut, false) - -# TODO - Pending merge of https://github.com/godotengine/godot/pull/102889 - Should only support Godot version after this release -# _viewfinder_shortcut_default.events = [editor_shortcut] -# if ProjectSettings.get_setting(_settings_enable_editor_shortcut): -# if not ProjectSettings.has_setting(_settings_editor_shortcut): -# ProjectSettings.set_setting(_settings_editor_shortcut, _editor_shortcut) -# ProjectSettings.set_initial_value(_settings_editor_shortcut, _editor_shortcut) - - - # TODO - Should be disabled unless in editor - # Viewfinder - editor_panel_instance = EditorPanel.instantiate() - editor_panel_instance.editor_plugin = self - panel_button = add_control_to_bottom_panel(editor_panel_instance, "Phantom Camera") - panel_button.toggled.connect(_btn_toggled) - if panel_button.toggle_mode: _btn_toggled(true) - - scene_changed.connect(editor_panel_instance.viewfinder.scene_changed) - scene_changed.connect(_scene_changed) - - -func _exit_tree() -> void: - panel_button.toggled.disconnect(_btn_toggled) - scene_changed.disconnect(editor_panel_instance.viewfinder.scene_changed) - scene_changed.disconnect(_scene_changed) - - remove_control_from_bottom_panel(editor_panel_instance) - editor_panel_instance.queue_free() - - remove_node_3d_gizmo_plugin(pcam_3d_gizmo_plugin) - remove_node_3d_gizmo_plugin(pcam_3d_noise_emitter_gizmo_plugin) - - remove_custom_type(PCAM_2D) - remove_custom_type(PCAM_3D) - remove_custom_type(PCAM_HOST) - remove_custom_type(PCAM_NOISE_EMITTER_2D) - remove_custom_type(PCAM_NOISE_EMITTER_3D) - - remove_autoload_singleton(PHANTOM_CAMERA_MANAGER) -# if get_tree().root.get_node_or_null(String(PHANTOM_CAMERA_MANAGER)): -# remove_autoload_singleton(PHANTOM_CAMERA_MANAGER) - - -func _btn_toggled(toggled_on: bool): - editor_panel_instance.viewfinder.set_visibility(toggled_on) -# if toggled_on: -# editor_panel_instance.viewfinder.viewfinder_visible = true -# editor_panel_instance.viewfinder.visibility_check() -# else: -# editor_panel_instance.viewfinder.viewfinder_visible = false - -func _make_visible(visible): - if editor_panel_instance: - editor_panel_instance.set_visible(visible) - -## TODO - Signal can be added directly to the editor_panel with the changes in Godot 4.5 (https://github.com/godotengine/godot/pull/102986) -func _scene_changed(scene_root: Node) -> void: - editor_panel_instance.viewfinder.scene_changed(scene_root) - -# TODO - Pending merge of https://github.com/godotengine/godot/pull/102889 - Should only support Godot version after this release -#func _set_editor_shortcut() -> InputEventKey: -# var shortcut: InputEventKey = InputEventKey.new() -# shortcut.keycode = 67 # Key = C -# shortcut.alt_pressed = true -# return shortcut - -#endregion - - -#region Public Functions - -func get_version() -> String: - var config: ConfigFile = ConfigFile.new() - config.load(get_script().resource_path.get_base_dir() + "/plugin.cfg") - return config.get_value("plugin", "version") - -#endregion diff --git a/addons/phantom_camera/plugin.gd.uid b/addons/phantom_camera/plugin.gd.uid deleted file mode 100644 index 1c23477..0000000 --- a/addons/phantom_camera/plugin.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c7g41in1osxi6 diff --git a/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo.gd b/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo.gd deleted file mode 100644 index 42e71a5..0000000 --- a/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo.gd +++ /dev/null @@ -1,85 +0,0 @@ -@tool -extends EditorNode3DGizmo - -var pcam_3d: PhantomCamera3D - -func _redraw() -> void: - clear() - - var icon: Material = get_plugin().get_material(get_plugin().get_name(), self) - add_unscaled_billboard(icon, 0.035) - - pcam_3d = get_node_3d() - - if pcam_3d.is_following() and pcam_3d.draw_follow_gizmo_line(): - _draw_target(pcam_3d.get_follow_target_position(), "follow_target") - if pcam_3d.is_looking() and pcam_3d.draw_look_at_gizmo_line(): - _draw_target(pcam_3d.get_look_at_target_position(),"look_at_target") - - if pcam_3d.is_active(): return - - var frustum_lines: PackedVector3Array = PackedVector3Array() - var height: float = 0.25 - var width: float = height * 1.25 - var forward: float = height * -1.5 - - # Trapezoid - frustum_lines.push_back(Vector3.ZERO) - frustum_lines.push_back(Vector3(-width, height, forward)) - - frustum_lines.push_back(Vector3.ZERO) - frustum_lines.push_back(Vector3(width, height, forward)) - - frustum_lines.push_back(Vector3.ZERO) - frustum_lines.push_back(Vector3(-width, -height, forward)) - - frustum_lines.push_back(Vector3.ZERO) - frustum_lines.push_back(Vector3(width, -height, forward)) - - ####### - # Frame - ####### - ## Left - frustum_lines.push_back(Vector3(-width, height, forward)) - frustum_lines.push_back(Vector3(-width, -height, forward)) - - ## Bottom - frustum_lines.push_back(Vector3(-width, -height, forward)) - frustum_lines.push_back(Vector3(width, -height, forward)) - - ## Right - frustum_lines.push_back(Vector3(width, -height, forward)) - frustum_lines.push_back(Vector3(width, height, forward)) - - ## Top - frustum_lines.push_back(Vector3(width, height, forward)) - frustum_lines.push_back(Vector3(-width, height, forward)) - - ############## - # Up Direction - ############## - var up_height: float = height + 0.15 - var up_width: float = width / 3 - - ## Left - frustum_lines.push_back(Vector3(0, up_height, forward)) - frustum_lines.push_back(Vector3(-up_width, height, forward)) - - ## Right - frustum_lines.push_back(Vector3(0, up_height, forward)) - frustum_lines.push_back(Vector3(up_width, height, forward)) - - var frustum_material: StandardMaterial3D = get_plugin().get_material("frustum", self) - add_lines(frustum_lines, frustum_material, false) - - -func _draw_target(target: Vector3, type: StringName) -> void: - var target_lines: PackedVector3Array = PackedVector3Array() - var direction: Vector3 = pcam_3d.global_position - target - var end_position: Vector3 = -direction * pcam_3d.quaternion - - target_lines.push_back(Vector3.ZERO) - target_lines.push_back(end_position) - - var target_material: StandardMaterial3D = get_plugin().get_material(type, self) - add_lines(target_lines, target_material, false) diff --git a/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo.gd.uid b/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo.gd.uid deleted file mode 100644 index 564e0cf..0000000 --- a/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cyr6fgximfw6q diff --git a/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo_plugin.gd b/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo_plugin.gd deleted file mode 100644 index 622e7bf..0000000 --- a/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo_plugin.gd +++ /dev/null @@ -1,37 +0,0 @@ -@tool -extends EditorNode3DGizmoPlugin - -const PhantomCamera3DNode: Script = preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd") -const PhantomCamera3DGizmo: Script = preload("res://addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo.gd") -const _icon_texture: Texture2D = preload("res://addons/phantom_camera/icons/phantom_camera_gizmo.svg") -var _gizmo_name: String = "PhantomCamera3D" - -var gizmo_name: String: set = set_gizmo_name -var _gizmo_icon: Texture2D -var _gizmo_spatial_script: Script = PhantomCamera3DNode - - -func set_gizmo_name(name: String) -> void: - _gizmo_name = name - - -func _get_gizmo_name() -> String: - return _gizmo_name - - -func _has_gizmo(spatial: Node3D) -> bool: - return spatial is PhantomCamera3D - - -func _init() -> void: - create_icon_material(gizmo_name, _icon_texture, false, Color.WHITE) - create_material("frustum", Color8(252, 127, 127, 255)) - create_material("follow_target", Color8(248, 67, 47)) - create_material("look_at_target", Color8(61, 207, 225)) - - -func _create_gizmo(for_node_3d: Node3D) -> EditorNode3DGizmo: - if for_node_3d is PhantomCamera3DNode: - return PhantomCamera3DGizmo.new() - else: - return null diff --git a/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo_plugin.gd.uid b/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo_plugin.gd.uid deleted file mode 100644 index ce528d7..0000000 --- a/addons/phantom_camera/scripts/gizmos/phantom_camera_3d_gizmo_plugin.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bkevga3bx4rfj diff --git a/addons/phantom_camera/scripts/gizmos/phantom_camera_noise_emitter_gizmo_plugin_3d.gd b/addons/phantom_camera/scripts/gizmos/phantom_camera_noise_emitter_gizmo_plugin_3d.gd deleted file mode 100644 index 3dd4d3e..0000000 --- a/addons/phantom_camera/scripts/gizmos/phantom_camera_noise_emitter_gizmo_plugin_3d.gd +++ /dev/null @@ -1,29 +0,0 @@ -extends EditorNode3DGizmoPlugin - -var _spatial_script: Script = preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_3d.gd") -var _gizmo_icon: Texture2D = preload("res://addons/phantom_camera/icons/phantom_camera_noise_emitter_gizmo.svg") - -var _gizmo_name: StringName = "PhantomCameraNoiseEmitter" - -func _init() -> void: - create_material("main", Color8(252, 127, 127, 255)) - create_handle_material("handles") - create_icon_material(_gizmo_name, _gizmo_icon, false, Color.WHITE) - - -func _has_gizmo(node: Node3D): - return node.get_script() == _spatial_script - - -func _get_gizmo_name() -> String: - return _gizmo_name - - -func _redraw(gizmo: EditorNode3DGizmo): - gizmo.clear() - - var icon: Material = get_material(_gizmo_name, gizmo) - gizmo.add_unscaled_billboard(icon, 0.035) - - #var material = get_material("main", gizmo) - #gizmo.add_lines(_draw_frustum(), material) diff --git a/addons/phantom_camera/scripts/gizmos/phantom_camera_noise_emitter_gizmo_plugin_3d.gd.uid b/addons/phantom_camera/scripts/gizmos/phantom_camera_noise_emitter_gizmo_plugin_3d.gd.uid deleted file mode 100644 index 2b93b6c..0000000 --- a/addons/phantom_camera/scripts/gizmos/phantom_camera_noise_emitter_gizmo_plugin_3d.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dddokcd2ug05i diff --git a/addons/phantom_camera/scripts/managers/PhantomCameraManager.cs b/addons/phantom_camera/scripts/managers/PhantomCameraManager.cs deleted file mode 100644 index f0f6b96..0000000 --- a/addons/phantom_camera/scripts/managers/PhantomCameraManager.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Linq; -using Godot; - -#nullable enable - -namespace PhantomCamera.Manager; - -public static class PhantomCameraManager -{ - private static GodotObject? _instance; - - public static GodotObject Instance => _instance ??= Engine.GetSingleton("PhantomCameraManager"); - - public static PhantomCamera2D[] PhantomCamera2Ds => - Instance.Call(MethodName.GetPhantomCamera2Ds).AsGodotArray() - .Select(node => new PhantomCamera2D(node)).ToArray(); - - public static PhantomCamera3D[] PhantomCamera3Ds => - Instance.Call(MethodName.GetPhantomCamera3Ds).AsGodotArray() - .Select(node => new PhantomCamera3D(node)).ToArray(); - - public static PhantomCameraHost[] PhantomCameraHosts => - Instance.Call(MethodName.GetPhantomCameraHosts).AsGodotArray() - .Select(node => new PhantomCameraHost(node)).ToArray(); - - public static PhantomCamera2D[] GetPhantomCamera2Ds() => PhantomCamera2Ds; - public static PhantomCamera3D[] GetPhantomCamera3Ds() => PhantomCamera3Ds; - public static PhantomCameraHost[] GetPhantomCameraHosts() => PhantomCameraHosts; - - public static class MethodName - { - public static readonly StringName GetPhantomCamera2Ds = new("get_phantom_camera_2ds"); - public static readonly StringName GetPhantomCamera3Ds = new("get_phantom_camera_3ds"); - public static readonly StringName GetPhantomCameraHosts = new("get_phantom_camera_hosts"); - } -} diff --git a/addons/phantom_camera/scripts/managers/PhantomCameraManager.cs.uid b/addons/phantom_camera/scripts/managers/PhantomCameraManager.cs.uid deleted file mode 100644 index cdf7f4e..0000000 --- a/addons/phantom_camera/scripts/managers/PhantomCameraManager.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://vtj8iqx4bp43 diff --git a/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd b/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd deleted file mode 100644 index 6e13d9a..0000000 --- a/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd +++ /dev/null @@ -1,149 +0,0 @@ -@tool -extends Node - -const _CONSTANTS = preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd") - -#region Signals - -# Noise -signal noise_2d_emitted(noise_output: Transform2D, emitter_layer: int) -signal noise_3d_emitted(noise_output: Transform3D, emitter_layer: int) - -# PCam Host -signal pcam_host_added_to_scene(pcam_host: PhantomCameraHost) -signal pcam_host_removed_from_scene(pcam_host: PhantomCameraHost) - -# PCam -signal pcam_added_to_scene(pcam: Node) -signal pcam_removed_from_scene(pcam: Node) - -# Priority -signal pcam_priority_changed(pcam: Node) -signal pcam_visibility_changed(pcam: Node) - -signal pcam_teleport(pcam: Node) - -# Limit (2D) -signal limit_2d_changed(side: int, limit: int) -signal draw_limit_2d(enabled: bool) - -# Camera3DResource (3D) -signal camera_3d_resource_changed(property: String, value: Variant) - -# Viewfinder Signals -signal viewfinder_pcam_host_switch(pcam_host: PhantomCameraHost) -signal pcam_priority_override(pcam: Node, shouldOverride: bool) -signal pcam_dead_zone_changed(pcam: Node) -signal pcam_host_layer_changed(pcam: Node) - -#endregion - -#region Private Variables - -var _phantom_camera_host_list: Array[PhantomCameraHost] -var _phantom_camera_2d_list: Array[PhantomCamera2D] -var _phantom_camera_3d_list: Array[Node] ## Note: To support disable_3d export templates for 2D projects, this is purposely not strongly typed. - -#endregion - -#region Public Variables - -var phantom_camera_hosts: Array[PhantomCameraHost]: - get: - return _phantom_camera_host_list - -var phantom_camera_2ds: Array[PhantomCamera2D]: - get: - return _phantom_camera_2d_list - -var phantom_camera_3ds: Array[Node]: ## Note: To support disable_3d export templates for 2D projects, this is purposely not strongly typed. - get: - return _phantom_camera_3d_list - -var screen_size: Vector2i - -#endregion - -#region Private Functions - -func _enter_tree() -> void: - if not Engine.has_singleton(_CONSTANTS.PCAM_MANAGER_NODE_NAME): - Engine.register_singleton(_CONSTANTS.PCAM_MANAGER_NODE_NAME, self) - Engine.physics_jitter_fix = 0 - - -func _ready() -> void: - # Setting default screensize - screen_size = Vector2i( - ProjectSettings.get_setting("display/window/size/viewport_width"), - ProjectSettings.get_setting("display/window/size/viewport_height") - ) - - # For editor - if Engine.is_editor_hint(): - ProjectSettings.settings_changed.connect(func(): - screen_size = Vector2i( - ProjectSettings.get_setting("display/window/size/viewport_width"), - ProjectSettings.get_setting("display/window/size/viewport_height") - ) - ) - # For runtime - else: - get_tree().get_root().size_changed.connect(func(): - screen_size = get_viewport().get_visible_rect().size - ) - -#endregion - -#region Public Functions - -func pcam_host_added(caller: Node) -> void: - if is_instance_of(caller, PhantomCameraHost): - _phantom_camera_host_list.append(caller) - pcam_host_added_to_scene.emit(caller) - else: - printerr("This method can only be called from a PhantomCameraHost node") - -func pcam_host_removed(caller: Node) -> void: - if is_instance_of(caller, PhantomCameraHost): - _phantom_camera_host_list.erase(caller) - pcam_host_removed_from_scene.emit(caller) - else: - printerr("This method can only be called from a PhantomCameraHost node") - - -func pcam_added(caller) -> void: - if is_instance_of(caller, PhantomCamera2D): - _phantom_camera_2d_list.append(caller) - pcam_added_to_scene.emit(caller) - elif caller.is_class("PhantomCamera3D"): ## Note: To support disable_3d export templates for 2D projects, this is purposely not strongly typed. - _phantom_camera_3d_list.append(caller) - pcam_added_to_scene.emit(caller) - -func pcam_removed(caller) -> void: - if is_instance_of(caller, PhantomCamera2D): - _phantom_camera_2d_list.erase(caller) - pcam_removed_from_scene.emit(caller) - elif caller.is_class("PhantomCamera3D"): ## Note: To support disable_3d export templates for 2D projects, this is purposely not strongly typed. - _phantom_camera_3d_list.erase(caller) - pcam_removed_from_scene.emit(caller) - else: - printerr("This method can only be called from a PhantomCamera node") - - -func get_phantom_camera_hosts() -> Array[PhantomCameraHost]: - return _phantom_camera_host_list - -func get_phantom_camera_2ds() -> Array[PhantomCamera2D]: - return _phantom_camera_2d_list - -func get_phantom_camera_3ds() -> Array: ## Note: To support disable_3d export templates for 2D projects, this is purposely not strongly typed. - return _phantom_camera_3d_list - - -func scene_changed() -> void: - _phantom_camera_2d_list.clear() - _phantom_camera_3d_list.clear() - _phantom_camera_host_list.clear() - -#endregion diff --git a/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd.uid b/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd.uid deleted file mode 100644 index ea18023..0000000 --- a/addons/phantom_camera/scripts/managers/phantom_camera_manager.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://duq6jhf6unyis diff --git a/addons/phantom_camera/scripts/panel/editor.gd b/addons/phantom_camera/scripts/panel/editor.gd deleted file mode 100644 index a10018c..0000000 --- a/addons/phantom_camera/scripts/panel/editor.gd +++ /dev/null @@ -1,23 +0,0 @@ -@tool -extends VBoxContainer - -#region Onready - -@onready var updater: Control = %UpdateButton -@onready var viewfinder: Control = %ViewfinderPanel - -#endregion - -#region Public Variables - -var editor_plugin: EditorPlugin - -#endregion - - -#region Private Functions - -func _ready(): - updater.editor_plugin = editor_plugin - -#endregion diff --git a/addons/phantom_camera/scripts/panel/editor.gd.uid b/addons/phantom_camera/scripts/panel/editor.gd.uid deleted file mode 100644 index 6be368c..0000000 --- a/addons/phantom_camera/scripts/panel/editor.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cgfwg3paxkj2x diff --git a/addons/phantom_camera/scripts/panel/updater/download_update_panel.gd b/addons/phantom_camera/scripts/panel/updater/download_update_panel.gd deleted file mode 100644 index b19beb9..0000000 --- a/addons/phantom_camera/scripts/panel/updater/download_update_panel.gd +++ /dev/null @@ -1,162 +0,0 @@ -####################################################################### -# Credit goes to the Dialogue Manager plugin for this script -# Check it out at: https://github.com/nathanhoad/godot_dialogue_manager -####################################################################### - -@tool -extends Control - -#region Constants - -const TEMP_FILE_NAME = "user://temp.zip" - -#endregion - - -#region Signals - -signal failed() -signal updated(updated_to_version: String) - -#endregion - - -#region @onready - -#@onready var logo: TextureRect = %Logo -@onready var _download_verion: Label = %DownloadVersionLabel -@onready var _download_http_request: HTTPRequest = %DownloadHTTPRequest -@onready var _download_button: Button = %DownloadButton -@onready var _download_button_bg: NinePatchRect = %DownloadButtonBG -@onready var _download_label: Label = %UpdateLabel - -@onready var _breaking_label: Label = %BreakingLabel -@onready var _breaking_margin_container: MarginContainer = %BreakingMarginContainer -@onready var _breaking_options_button: OptionButton = %BreakingOptionButton -#@onready var current_version_label: Label = %CurrentVersionLabel - -#endregion - - -#region Variables - -# Todo - For 4.2 upgrade - Shows current version -var _download_dialogue: AcceptDialog -var _button_texture_default: Texture2D = load("res://addons/phantom_camera/assets/PhantomCameraBtnPrimaryDefault.png") -var _button_texture_hover: Texture2D = load("res://addons/phantom_camera/assets/PhantomCameraBtnPrimaryHover.png") - -var next_version_release: Dictionary: - set(value): - next_version_release = value - _download_verion.text = "%s update is available for download" % value.tag_name.substr(1) - # Todo - For 4.2 upgrade - #current_version_label.text = "Current version is " + editor_plugin.get_version() - get: - return next_version_release - -var _breaking_window_height: float = 520 -var _breaking_window_height_update: float = 600 - -#endregion - - -#region Private Functions - -func _ready() -> void: - _download_http_request.request_completed.connect(_on_http_request_request_completed) - _download_button.pressed.connect(_on_download_button_pressed) - _download_button.mouse_entered.connect(_on_mouse_entered) - _download_button.mouse_exited.connect(_on_mouse_exited) - - _breaking_label.hide() - _breaking_margin_container.hide() - _breaking_options_button.hide() - - _breaking_options_button.item_selected.connect(_on_item_selected) - - -func _on_item_selected(index: int) -> void: - if index == 1: - _download_button.show() - _download_dialogue.size = Vector2(_download_dialogue.size.x, _breaking_window_height_update) - else: - _download_button.hide() - _download_dialogue.size = Vector2(_download_dialogue.size.x, _breaking_window_height) - - -func _on_download_button_pressed() -> void: - _download_http_request.request(next_version_release.zipball_url) - _download_button.disabled = true - _download_label.text = "Downloading..." - _download_button_bg.hide() - - -func _on_mouse_entered() -> void: - _download_button_bg.set_texture(_button_texture_hover) - - -func _on_mouse_exited() -> void: - _download_button_bg.set_texture(_button_texture_default) - - -func _on_http_request_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void: - if result != HTTPRequest.RESULT_SUCCESS: - failed.emit() - return - - # Save the downloaded zip - var zip_file: FileAccess = FileAccess.open(TEMP_FILE_NAME, FileAccess.WRITE) - zip_file.store_buffer(body) - zip_file.close() - - OS.move_to_trash(ProjectSettings.globalize_path("res://addons/phantom_camera")) - - var zip_reader: ZIPReader = ZIPReader.new() - zip_reader.open(TEMP_FILE_NAME) - var files: PackedStringArray = zip_reader.get_files() - - var base_path = files[1] - # Remove archive folder - files.remove_at(0) - # Remove assets folder - files.remove_at(0) - - for path in files: - var new_file_path: String = path.replace(base_path, "") - if path.ends_with("/"): - DirAccess.make_dir_recursive_absolute("res://addons/%s" % new_file_path) - else: - var file: FileAccess = FileAccess.open("res://addons/%s" % new_file_path, FileAccess.WRITE) - file.store_buffer(zip_reader.read_file(path)) - - zip_reader.close() - DirAccess.remove_absolute(TEMP_FILE_NAME) - - updated.emit(next_version_release.tag_name.substr(1)) - - -func _on_notes_button_pressed() -> void: - OS.shell_open(next_version_release.html_url) - -#endregion - -#region Public Functions - -func show_updater_warning(next_version_number: Array, current_version_number: Array) -> void: - var current_version_number_0: int = current_version_number[0] as int - var current_version_number_1: int = current_version_number[1] as int - - var next_version_number_0: int = next_version_number[0] as int # Major release number in the new release - var next_version_number_1: int = next_version_number[1] as int # Minor release number in the new release - - if next_version_number_0 > current_version_number_0 or \ - next_version_number_1 > current_version_number_1: - _breaking_label.show() - _breaking_margin_container.show() - _breaking_options_button.show() - _download_button.hide() - - _download_dialogue = get_parent() - _download_dialogue.size = Vector2(_download_dialogue.size.x, _breaking_window_height) - -#endregion diff --git a/addons/phantom_camera/scripts/panel/updater/download_update_panel.gd.uid b/addons/phantom_camera/scripts/panel/updater/download_update_panel.gd.uid deleted file mode 100644 index ff50946..0000000 --- a/addons/phantom_camera/scripts/panel/updater/download_update_panel.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cjblcocen12r3 diff --git a/addons/phantom_camera/scripts/panel/updater/update_button.gd b/addons/phantom_camera/scripts/panel/updater/update_button.gd deleted file mode 100644 index 686535c..0000000 --- a/addons/phantom_camera/scripts/panel/updater/update_button.gd +++ /dev/null @@ -1,177 +0,0 @@ -####################################################################### -# Credit goes to the Dialogue Manager plugin for this script -# Check it out at: https://github.com/nathanhoad/godot_dialogue_manager -####################################################################### - -@tool -extends Button - -#region Constants - -const REMOTE_RELEASE_URL: StringName = "https://api.github.com/repos/ramokz/phantom-camera/releases" -const UPDATER_CONSTANTS := preload("res://addons/phantom_camera/scripts/panel/updater/updater_constants.gd") - -#endregion - - -#region @onready - -@onready var http_request: HTTPRequest = %HTTPRequest -@onready var download_dialog: AcceptDialog = %DownloadDialog -@onready var download_update_panel: Control = %DownloadUpdatePanel -@onready var needs_reload_dialog: AcceptDialog = %NeedsReloadDialog -@onready var update_failed_dialog: AcceptDialog = %UpdateFailedDialog - -#endregion - - -#region Variables - -# The main editor plugin -var editor_plugin: EditorPlugin - -var needs_reload: bool = false - -# A lambda that gets called just before refreshing the plugin. Return false to stop the reload. -var on_before_refresh: Callable = func(): return true - -#endregion - - -#region Private Functions - -func _ready() -> void: - hide() - - # Check for updates on GitHub Releases - check_for_update() - - pressed.connect(_on_update_button_pressed) - http_request.request_completed.connect(_request_request_completed) - download_update_panel.updated.connect(_on_download_update_panel_updated) - needs_reload_dialog.confirmed.connect(_on_needs_reload_dialog_confirmed) - - -func _request_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void: - if result != HTTPRequest.RESULT_SUCCESS: return - - if not editor_plugin: return - var current_version: String = editor_plugin.get_version() - - # Work out the next version from the releases information on GitHub - var response: Array = JSON.parse_string(body.get_string_from_utf8()) - if typeof(response) != TYPE_ARRAY: return - - # GitHub releases are in order of creation, not order of version - var versions: Array = response.filter(func(release): - var version: String = release.tag_name.substr(1) - return version_to_number(version) > version_to_number(current_version) - ) - - if versions.size() > 0: - if ProjectSettings.get_setting(UPDATER_CONSTANTS.setting_updater_mode) == 1: ## For console output mode - - print_rich(" -[color=#3AB99A] ********[/color] -[color=#3AB99A] ************[/color] -[color=#3AB99A]**************[/color] -[color=#3AB99A]****** *** *[/color] -[color=#3AB99A]****** ***[/color] -[color=#3AB99A]********** *****[/color] -[color=#3AB99A]******** ***********[/color] -[color=#3AB99A]******** *********** **[/color] -[color=#3AB99A]********* **************[/color] -[color=#3AB99A]********** *************[/color] -[color=#3AB99A]** ** ** ******* **[/color] -[font_size=18][b]New Phantom Camera version is available[/b][/font_size]") - - if FileAccess.file_exists("res://dev_scenes/3d/dev_scene_3d.tscn"): - print_rich("[font_size=14][color=#EAA15E][b]As you're using a fork of the project, you will need to update it manually[/b][/color][/font_size]") - - print_rich("[font_size=12]If you don't want to see this message, then it can be disabled inside:\n[code]Project Settings/Phantom Camera/Updater/Show New Release Info on Editor Launch in Output[/code]") - - return - - download_update_panel.next_version_release = versions[0] - download_update_panel.show_updater_warning( - versions[0].tag_name.substr(1).split("."), - current_version.split(".") - ) - _set_scale() - editor_plugin.panel_button.add_theme_color_override("font_color", Color("#3AB99A")) - editor_plugin.panel_button.icon = load("res://addons/phantom_camera/icons/phantom_camera_updater_panel_icon.svg") - editor_plugin.panel_button.add_theme_color_override("icon_normal_color", Color("#3AB99A")) - show() - - -func _on_update_button_pressed() -> void: - if needs_reload: - var will_refresh = on_before_refresh.call() - if will_refresh: - EditorInterface.restart_editor(true) - else: - _set_scale() - download_dialog.popup_centered() - - -func _set_scale() -> void: - var scale: float = EditorInterface.get_editor_scale() - download_dialog.min_size = Vector2(300, 250) * scale - - -func _on_download_dialog_close_requested() -> void: - download_dialog.hide() - - -func _on_download_update_panel_updated(updated_to_version: String) -> void: - download_dialog.hide() - - needs_reload_dialog.dialog_text = "Reload to finish update" - needs_reload_dialog.ok_button_text = "Reload" - needs_reload_dialog.cancel_button_text = "Cancel" - needs_reload_dialog.popup_centered() - - needs_reload = true - text = "Reload Project" - - -func _on_download_update_panel_failed() -> void: - download_dialog.hide() - update_failed_dialog.dialog_text = "Updated Failed" - update_failed_dialog.popup_centered() - - -func _on_needs_reload_dialog_confirmed() -> void: - EditorInterface.restart_editor(true) - - -func _on_timer_timeout() -> void: - if not needs_reload: - check_for_update() - -#endregion - - -#region Public Functions - -# Convert a version number to an actually comparable number -func version_to_number(version: String) -> int: - var regex = RegEx.new() - regex.compile("[a-zA-Z]+") - if regex.search(str(version)): return 0 - - var bits = version.split(".") - var version_bit: int - var multiplier: int = 10000 - for i in bits.size(): - version_bit += bits[i].to_int() * multiplier / (10 ** (i)) - - return version_bit - - -func check_for_update() -> void: - if ProjectSettings.get_setting(UPDATER_CONSTANTS.setting_updater_mode) == 0: return - - http_request.request(REMOTE_RELEASE_URL) - -#endregion diff --git a/addons/phantom_camera/scripts/panel/updater/update_button.gd.uid b/addons/phantom_camera/scripts/panel/updater/update_button.gd.uid deleted file mode 100644 index cb88ddc..0000000 --- a/addons/phantom_camera/scripts/panel/updater/update_button.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bwc42i46603qn diff --git a/addons/phantom_camera/scripts/panel/updater/updater_constants.gd b/addons/phantom_camera/scripts/panel/updater/updater_constants.gd deleted file mode 100644 index 94ac2ad..0000000 --- a/addons/phantom_camera/scripts/panel/updater/updater_constants.gd +++ /dev/null @@ -1,8 +0,0 @@ -extends RefCounted - -# Plugin Project Settings Sections -const setting_phantom_camera: StringName = "phantom_camera/" -const setting_updater_name: StringName = setting_phantom_camera + "updater/" - -# Updater Settings -const setting_updater_mode: StringName = setting_updater_name + "updater_mode" diff --git a/addons/phantom_camera/scripts/panel/updater/updater_constants.gd.uid b/addons/phantom_camera/scripts/panel/updater/updater_constants.gd.uid deleted file mode 100644 index 1f86170..0000000 --- a/addons/phantom_camera/scripts/panel/updater/updater_constants.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c8qkbc38waor2 diff --git a/addons/phantom_camera/scripts/panel/viewfinder/host_list.gd b/addons/phantom_camera/scripts/panel/viewfinder/host_list.gd deleted file mode 100644 index 662e598..0000000 --- a/addons/phantom_camera/scripts/panel/viewfinder/host_list.gd +++ /dev/null @@ -1,112 +0,0 @@ -@tool -extends VBoxContainer - -#region Constants - -const _constants := preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd") -const _host_list_item: PackedScene = preload("res://addons/phantom_camera/panel/viewfinder/host_list/host_list_item.tscn") - -#endregion - -signal pcam_host_removed(pcam_host: PhantomCameraHost) - -@onready var _host_list_button: Button = %HostListButton -@onready var _host_list_scroll_container: ScrollContainer = %ScrollContainer -@onready var _host_list_item_container: VBoxContainer = %HostListContainer - -var _host_list_open: bool = false - -var _bottom_offset_value: float - -var _pcam_host_list: Array[PhantomCameraHost] -var _pcam_manager: Node - -var _viewfinder_panel: Control - -#region Private Functions - -func _ready() -> void: - _host_list_button.pressed.connect(_host_list_button_pressed) - if Engine.has_singleton(_constants.PCAM_MANAGER_NODE_NAME): - _pcam_manager = Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME) - _pcam_manager.pcam_host_removed_from_scene.connect(_remove_pcam_host) - - if not get_parent() is Control: return # To prevent errors when opening the scene on its own - _viewfinder_panel = get_parent() - _viewfinder_panel.resized.connect(_set_offset_top) - - _host_list_item_container.resized.connect(_set_offset_top) - - -func _set_offset_top() -> void: - offset_top = _set_host_list_size() - - -func _host_list_button_pressed() -> void: - _host_list_open = !_host_list_open - - var tween: Tween = create_tween() - var max_duration: float = 0.6 - - # 300 being the minimum size of the viewfinder's height - var duration: float = clampf( - max_duration / (300 / _host_list_item_container.size.y), - 0.3, - max_duration) - - tween.tween_property(self, "offset_top", _set_host_list_size(), duration)\ - .set_ease(Tween.EASE_OUT)\ - .set_trans(Tween.TRANS_QUINT) - - -func _set_host_list_size() -> float: - if not _host_list_open: - return clampf( - _viewfinder_panel.size.y - \ - _host_list_item_container.size.y - \ - _host_list_button.size.y - 20, - 0, - INF - ) - else: - return (_viewfinder_panel.size.y - _host_list_button.size.y / 2) - - -func _remove_pcam_host(pcam_host: PhantomCameraHost) -> void: - if _pcam_host_list.has(pcam_host): - _pcam_host_list.erase(pcam_host) - - var freed_pcam_host: Control - for host_list_item_instance in _host_list_item_container.get_children(): - if not host_list_item_instance.pcam_host == pcam_host: continue - freed_pcam_host = host_list_item_instance - host_list_item_instance.queue_free() - -#endregion - -#region Public Functions - -func add_pcam_host(pcam_host: PhantomCameraHost, is_default: bool) -> void: - if _pcam_host_list.has(pcam_host): return - - _pcam_host_list.append(pcam_host) - - var host_list_item_instance: PanelContainer = _host_list_item.instantiate() - var switch_pcam_host_button: Button = host_list_item_instance.get_node("%SwitchPCamHost") - if is_default: switch_pcam_host_button.button_pressed = true - - if not pcam_host.tree_exiting.is_connected(_remove_pcam_host): - pcam_host.tree_exiting.connect(_remove_pcam_host.bind(pcam_host)) - - host_list_item_instance.pcam_host = pcam_host - - _host_list_item_container.add_child(host_list_item_instance) - - -func clear_pcam_host_list() -> void: - _pcam_host_list.clear() - - for host_list_item_instance in _host_list_item_container.get_children(): - host_list_item_instance.queue_free() - -#endregion diff --git a/addons/phantom_camera/scripts/panel/viewfinder/host_list.gd.uid b/addons/phantom_camera/scripts/panel/viewfinder/host_list.gd.uid deleted file mode 100644 index 6923d3e..0000000 --- a/addons/phantom_camera/scripts/panel/viewfinder/host_list.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c84cxry3t35ny diff --git a/addons/phantom_camera/scripts/panel/viewfinder/host_list_item.gd b/addons/phantom_camera/scripts/panel/viewfinder/host_list_item.gd deleted file mode 100644 index 5707974..0000000 --- a/addons/phantom_camera/scripts/panel/viewfinder/host_list_item.gd +++ /dev/null @@ -1,58 +0,0 @@ -@tool -extends Control - -const button_group_resource: ButtonGroup = preload("res://addons/phantom_camera/panel/viewfinder/host_list/host_list_item_group.tres") -const _constants = preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd") - -@onready var select_pcam_host: Button = %SelectPCamHost -@onready var switch_pcam_host: Button = %SwitchPCamHost - -var pcam_host: PhantomCameraHost: - set(value): - pcam_host = value - if not is_instance_valid(value): return - if not pcam_host.renamed.is_connected(_rename_pcam_host): - pcam_host.renamed.connect(_rename_pcam_host) - pcam_host.has_error.connect(_pcam_host_has_error) - get: - return pcam_host - -var _pcam_manager: Node - -#region Private fucntions - -func _ready() -> void: - switch_pcam_host.button_group = button_group_resource - select_pcam_host.pressed.connect(_select_pcam) - switch_pcam_host.pressed.connect(_switch_pcam_host) - - if not is_instance_valid(pcam_host): return - switch_pcam_host.text = pcam_host.name - - _pcam_host_has_error() - - -func _pcam_host_has_error() -> void: - if pcam_host.show_warning: - %ErrorPCamHost.visible = true - else: - %ErrorPCamHost.visible = false - - -func _rename_pcam_host() -> void: - switch_pcam_host.text = pcam_host.name - - -func _select_pcam() -> void: - EditorInterface.get_selection().clear() - EditorInterface.get_selection().add_node(pcam_host) - - -func _switch_pcam_host() -> void: - if not Engine.has_singleton(_constants.PCAM_MANAGER_NODE_NAME): return - if not is_instance_valid(_pcam_manager): - _pcam_manager = Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME) - - _pcam_manager.viewfinder_pcam_host_switch.emit(pcam_host) - -#endregion diff --git a/addons/phantom_camera/scripts/panel/viewfinder/host_list_item.gd.uid b/addons/phantom_camera/scripts/panel/viewfinder/host_list_item.gd.uid deleted file mode 100644 index 9df2919..0000000 --- a/addons/phantom_camera/scripts/panel/viewfinder/host_list_item.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bv24ubx8mutw7 diff --git a/addons/phantom_camera/scripts/panel/viewfinder/viewfinder.gd b/addons/phantom_camera/scripts/panel/viewfinder/viewfinder.gd deleted file mode 100644 index fe163a7..0000000 --- a/addons/phantom_camera/scripts/panel/viewfinder/viewfinder.gd +++ /dev/null @@ -1,605 +0,0 @@ -@tool -extends Control - -#region Constants - -const _constants = preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd") - -# TODO - Should be in a central location -const _camera_2d_icon: CompressedTexture2D = preload("res://addons/phantom_camera/icons/viewfinder/Camera2DIcon.svg") -const _camera_3d_icon: CompressedTexture2D = preload("res://addons/phantom_camera/icons/viewfinder/Camera3DIcon.svg") -const _pcam_host_icon: CompressedTexture2D = preload("res://addons/phantom_camera/icons/phantom_camera_host.svg") -const _pcam_2D_icon: CompressedTexture2D = preload("res://addons/phantom_camera/icons/phantom_camera_2d.svg") -const _pcam_3D_icon: CompressedTexture2D = preload("res://addons/phantom_camera/icons/phantom_camera_3d.svg") - -const _overlay_color_alpha: float = 0.3 - -#endregion - -#region @onready - -@onready var dead_zone_center_hbox: VBoxContainer = %DeadZoneCenterHBoxContainer -@onready var dead_zone_center_center_panel: Panel = %DeadZoneCenterCenterPanel -@onready var dead_zone_left_center_panel: Panel = %DeadZoneLeftCenterPanel -@onready var dead_zone_right_center_panel: Panel = %DeadZoneRightCenterPanel -@onready var target_point: Panel = %TargetPoint - -@onready var aspect_ratio_container: AspectRatioContainer = %AspectRatioContainer -@onready var camera_viewport_panel: Panel = aspect_ratio_container.get_child(0) -@onready var _viewfinder: Control = %Viewfinder -@onready var _dead_zone_h_box_container: Control = %DeadZoneHBoxContainer -@onready var sub_viewport: SubViewport = %SubViewport - -@onready var _empty_state_control: Control = %EmptyStateControl -@onready var _empty_state_icon: TextureRect = %EmptyStateIcon -@onready var _empty_state_text: RichTextLabel = %EmptyStateText -@onready var _add_node_button: Button = %AddNodeButton -@onready var _add_node_button_text: RichTextLabel = %AddNodeTypeText - -@onready var _priority_override_button: Button = %PriorityOverrideButton -@onready var _priority_override_name_label: Label = %PriorityOverrideNameLabel - -@onready var _camera_2d: Camera2D = %Camera2D - -@onready var _pcam_host_list: VBoxContainer = %PCamHostList - -#endregion - -#region Private Variables - -var _no_open_scene_icon: CompressedTexture2D = preload("res://addons/phantom_camera/icons/viewfinder/SceneTypesIcon.svg") -var _no_open_scene_string: String = "[b]2D[/b] or [b]3D[/b] scene open" - -var _selected_camera: Node -var _active_pcam: Node - -var _is_2d: bool - -var _pcam_manager: Node - -var _root_node: Node - -#endregion - -#region Public Variables - -var pcam_host_group: Array[PhantomCameraHost] - -var is_scene: bool - -var viewfinder_visible: bool - -var min_horizontal: float -var max_horizontal: float -var min_vertical: float -var max_vertical: float - -var pcam_host: PhantomCameraHost - -#endregion - - -#region Private Functions - -func _ready() -> void: - if not Engine.is_editor_hint(): - set_process(true) - camera_viewport_panel.self_modulate.a = 0 - - _root_node = get_tree().current_scene - - if _root_node is Node2D || _root_node is Node3D: - %SubViewportContainer.visible = false - if _root_node is Node2D: - _is_2d = true - else: - _is_2d = false - - _set_viewfinder(_root_node, false) - - if not Engine.is_editor_hint(): - _empty_state_control.visible = false - - _priority_override_button.visible = false - - # Triggered when viewport size is changed in Project Settings - ProjectSettings.settings_changed.connect(_settings_changed) - - # PCam Host List - _pcam_host_list.visible = false - _assign_manager() - _visibility_check() - - -func _pcam_host_switch(new_pcam_host: PhantomCameraHost) -> void: - _set_viewfinder_camera(new_pcam_host, true) - - -func _exit_tree() -> void: - if aspect_ratio_container.resized.is_connected(_resized): - aspect_ratio_container.resized.disconnect(_resized) - - if _add_node_button.pressed.is_connected(_visibility_check): - _add_node_button.pressed.disconnect(_visibility_check) - - if is_instance_valid(_active_pcam): - if _active_pcam.dead_zone_changed.is_connected(_on_dead_zone_changed): - _active_pcam.dead_zone_changed.disconnect(_on_dead_zone_changed) - - if _priority_override_button.pressed.is_connected(_select_override_pcam): - _priority_override_button.pressed.disconnect(_select_override_pcam) - - -func _process(_delta: float) -> void: - if Engine.is_editor_hint() and not viewfinder_visible: return - if not is_instance_valid(_active_pcam): return - - var unprojected_position_clamped: Vector2 = Vector2( - clamp(_active_pcam.viewport_position.x, min_horizontal, max_horizontal), - clamp(_active_pcam.viewport_position.y, min_vertical, max_vertical) - ) - - if not Engine.is_editor_hint(): - target_point.position = camera_viewport_panel.size * unprojected_position_clamped - target_point.size / 2 - - if not _is_2d: return - if not is_instance_valid(pcam_host): return - if not is_instance_valid(pcam_host.camera_2d): return - - var window_size_height: float = ProjectSettings.get_setting("display/window/size/viewport_height") - sub_viewport.size_2d_override = sub_viewport.size * (window_size_height / sub_viewport.size.y) - - _camera_2d.global_transform = pcam_host.camera_2d.global_transform - _camera_2d.offset = pcam_host.camera_2d.offset - _camera_2d.zoom = pcam_host.camera_2d.zoom - _camera_2d.ignore_rotation = pcam_host.camera_2d.ignore_rotation - _camera_2d.anchor_mode = pcam_host.camera_2d.anchor_mode - _camera_2d.limit_left = pcam_host.camera_2d.limit_left - _camera_2d.limit_top = pcam_host.camera_2d.limit_top - _camera_2d.limit_right = pcam_host.camera_2d.limit_right - _camera_2d.limit_bottom = pcam_host.camera_2d.limit_bottom - - -func _settings_changed() -> void: - var viewport_width: float = ProjectSettings.get_setting("display/window/size/viewport_width") - var viewport_height: float = ProjectSettings.get_setting("display/window/size/viewport_height") - var ratio: float = viewport_width / viewport_height - aspect_ratio_container.set_ratio(ratio) - camera_viewport_panel.size.x = viewport_width / (viewport_height / sub_viewport.size.y) - - # Applies Project Settings to Viewport - sub_viewport.canvas_item_default_texture_filter = ProjectSettings.get_setting("rendering/textures/canvas_textures/default_texture_filter") - - # TODO - Add resizer for Framed Viewfinder - - -func _visibility_check() -> void: - if not viewfinder_visible: return - - var pcam_host: PhantomCameraHost - var has_camera: bool = false - if not Engine.has_singleton(_constants.PCAM_MANAGER_NODE_NAME): return - - if not Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME).get_phantom_camera_hosts().is_empty(): - has_camera = true - pcam_host = Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME).get_phantom_camera_hosts()[0] - - var root: Node = EditorInterface.get_edited_scene_root() - if root is Node2D: - var camera_2d: Camera2D - - if has_camera: - camera_2d = pcam_host.camera_2d - else: - camera_2d = _get_camera_2d() - - _is_2d = true - is_scene = true - _add_node_button.visible = true - _check_camera(root, camera_2d) - elif root is Node3D: - var camera_3d: Camera3D - if has_camera: - camera_3d = pcam_host.camera_3d - elif root.get_viewport() != null: - if root.get_viewport().get_camera_3d() != null: - camera_3d = root.get_viewport().get_camera_3d() - - _is_2d = false - is_scene = true - _add_node_button.visible = true - _check_camera(root, camera_3d) - else: - # Is not a 2D or 3D scene - is_scene = false - _set_empty_viewfinder_state(_no_open_scene_string, _no_open_scene_icon) - _add_node_button.visible = false - - # Checks if a new scene is created and changes viewfinder accordingly - if not get_tree().node_added.is_connected(_node_added_to_scene): - get_tree().node_added.connect(_node_added_to_scene) - - if not _priority_override_button.pressed.is_connected(_select_override_pcam): - _priority_override_button.pressed.connect(_select_override_pcam) - - -func _node_added_to_scene(node: Node) -> void: - if node is Node2D or node is Node3D: - get_tree().node_added.disconnect(_node_added_to_scene) - _visibility_check() - - -func _get_camera_2d() -> Camera2D: - var edited_scene_root: Node = EditorInterface.get_edited_scene_root() - - if edited_scene_root == null: return null - - var viewport: Viewport = edited_scene_root.get_viewport() - if viewport == null: return null - - var viewport_rid: RID = viewport.get_viewport_rid() - if viewport_rid == null: return null - - var camerasGroupName: String = "__cameras_%d" % viewport_rid.get_id() - var cameras: Array[Node] = get_tree().get_nodes_in_group(camerasGroupName) - - for camera in cameras: - if camera is Camera2D and camera.is_current: - return camera - - return null - - -func _check_camera(root: Node, camera: Node) -> void: - var camera_string: String - var pcam_string: String - var color: Color - var camera_icon: CompressedTexture2D - var pcam_icon: CompressedTexture2D - - if _is_2d: - camera_string = _constants.CAMERA_2D_NODE_NAME - pcam_string = _constants.PCAM_2D_NODE_NAME - color = _constants.COLOR_2D - camera_icon = _camera_2d_icon - pcam_icon = _pcam_2D_icon - else: - camera_string = _constants.CAMERA_3D_NODE_NAME - pcam_string = _constants.PCAM_3D_NODE_NAME - color = _constants.COLOR_3D - camera_icon = _camera_3d_icon - pcam_icon = _pcam_3D_icon - - if camera: -# Has Camera - if camera.get_children().size() > 0: - for cam_child in camera.get_children(): - if cam_child is PhantomCameraHost: - pcam_host = cam_child - - if pcam_host: - if get_tree().root.get_node(_constants.PCAM_MANAGER_NODE_NAME).get_phantom_camera_2ds() or \ - get_tree().root.get_node(_constants.PCAM_MANAGER_NODE_NAME).get_phantom_camera_3ds(): - # Pcam exists in tree - _set_viewfinder(root, true) - _set_viewfinder_state() - %NoSupportMsg.visible = false - else: -# No PCam in scene - _update_button(pcam_string, pcam_icon, color) - _set_empty_viewfinder_state(pcam_string, pcam_icon) - else: -# No PCamHost in scene - _update_button(_constants.PCAM_HOST_NODE_NAME, _pcam_host_icon, _constants.PCAM_HOST_COLOR) - _set_empty_viewfinder_state(_constants.PCAM_HOST_NODE_NAME, _pcam_host_icon) - else: -# No PCamHost in scene - _update_button(_constants.PCAM_HOST_NODE_NAME, _pcam_host_icon, _constants.PCAM_HOST_COLOR) - _set_empty_viewfinder_state(_constants.PCAM_HOST_NODE_NAME, _pcam_host_icon) - else: -# No Camera - _update_button(camera_string, camera_icon, color) - _set_empty_viewfinder_state(camera_string, camera_icon) - - -func _update_button(text: String, icon: CompressedTexture2D, color: Color) -> void: - _add_node_button_text.set_text("[center]Add [img=32]" + icon.resource_path + "[/img] [b]"+ text + "[/b][/center]"); - var button_theme_hover: StyleBoxFlat = _add_node_button.get_theme_stylebox("hover") - button_theme_hover.border_color = color - _add_node_button.add_theme_stylebox_override("hover", button_theme_hover) - - -func _set_viewfinder_state() -> void: - _empty_state_control.visible = false - _viewfinder.visible = true - - if is_instance_valid(_active_pcam): - if _active_pcam.get_follow_mode() == _active_pcam.FollowMode.FRAMED: - _dead_zone_h_box_container.visible = true - target_point.visible = true - else: - _dead_zone_h_box_container.visible = false - target_point.visible = false - - -func _set_empty_viewfinder_state(text: String, icon: CompressedTexture2D) -> void: - _viewfinder.visible = false - _framed_view_visible(false) - - _empty_state_control.visible = true - _empty_state_icon.texture = icon - if icon == _no_open_scene_icon: - _empty_state_text.set_text("[center]No " + text + "[/center]") - else: - _empty_state_text.set_text("[center]No [b]" + text + "[/b] in scene[/center]") - - if _add_node_button.pressed.is_connected(_add_node): - _add_node_button.pressed.disconnect(_add_node) - - _add_node_button.pressed.connect(_add_node.bind(text)) - - -func _add_node(node_type: String) -> void: - var scene_root: Node = EditorInterface.get_edited_scene_root() - - match node_type: - _no_open_scene_string: - pass - _constants.CAMERA_2D_NODE_NAME: - var camera: Camera2D = Camera2D.new() - _instantiate_node(scene_root, camera, _constants.CAMERA_2D_NODE_NAME) - _constants.CAMERA_3D_NODE_NAME: - var camera: Camera3D = Camera3D.new() - _instantiate_node(scene_root, camera, _constants.CAMERA_3D_NODE_NAME) - _constants.PCAM_HOST_NODE_NAME: - var pcam_host: PhantomCameraHost = PhantomCameraHost.new() - var camera_owner: Node - if _is_2d: - camera_owner = _get_camera_2d() - else: - camera_owner = get_tree().get_edited_scene_root().get_viewport().get_camera_3d() - _instantiate_node( - scene_root, - pcam_host, - _constants.PCAM_HOST_NODE_NAME, - camera_owner - ) - _constants.PCAM_2D_NODE_NAME: - var pcam_2D: PhantomCamera2D = PhantomCamera2D.new() - _instantiate_node(scene_root, pcam_2D, _constants.PCAM_2D_NODE_NAME) - _constants.PCAM_3D_NODE_NAME: - var pcam_3D: PhantomCamera3D = PhantomCamera3D.new() - _instantiate_node(scene_root, pcam_3D, _constants.PCAM_3D_NODE_NAME) - - _visibility_check() - - -func _instantiate_node(scene_root: Node, node: Node, name: String, parent: Node = scene_root) -> void: - node.set_name(name) - parent.add_child(node) - node.owner = scene_root - - -func _set_viewfinder(root: Node, editor: bool) -> void: - pcam_host_group = get_tree().root.get_node(_constants.PCAM_MANAGER_NODE_NAME).get_phantom_camera_hosts() - if pcam_host_group.size() != 0: - if pcam_host_group.size() == 1: - _pcam_host_list.visible = false - _set_viewfinder_camera(pcam_host_group[0], editor) - else: - _pcam_host_list.visible = true - _set_viewfinder_camera(pcam_host_group[0], editor) - for i in pcam_host_group.size(): - var is_default: bool = false - if i == 0: - is_default = true - _pcam_host_list.add_pcam_host(pcam_host_group[i], is_default) - - -func _set_viewfinder_camera(new_pcam_host: PhantomCameraHost, editor: bool) -> void: - pcam_host = new_pcam_host - - if _is_2d: - _selected_camera = pcam_host.camera_2d - - if editor: - sub_viewport.disable_3d = true - pcam_host = pcam_host - _camera_2d.zoom = pcam_host.camera_2d.zoom - _camera_2d.offset = pcam_host.camera_2d.offset - _camera_2d.ignore_rotation = pcam_host.camera_2d.ignore_rotation - - sub_viewport.world_2d = pcam_host.camera_2d.get_world_2d() - sub_viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS - sub_viewport.render_target_clear_mode = SubViewport.CLEAR_MODE_ALWAYS - sub_viewport.size_2d_override_stretch = true - else: - _selected_camera = pcam_host.camera_3d - if editor: - var camera_3d_rid: RID = _selected_camera.get_camera_rid() - sub_viewport.disable_3d = false - sub_viewport.world_3d = pcam_host.camera_3d.get_world_3d() - RenderingServer.viewport_attach_camera(sub_viewport.get_viewport_rid(), camera_3d_rid) - - if _selected_camera.keep_aspect == Camera3D.KeepAspect.KEEP_HEIGHT: - aspect_ratio_container.set_stretch_mode(AspectRatioContainer.STRETCH_HEIGHT_CONTROLS_WIDTH) - else: - aspect_ratio_container.set_stretch_mode(AspectRatioContainer.STRETCH_WIDTH_CONTROLS_HEIGHT) - - set_process(true) - - if not pcam_host.viewfinder_update.is_connected(_on_update_editor_viewfinder): - pcam_host.viewfinder_update.connect(_on_update_editor_viewfinder) - - if not pcam_host.viewfinder_disable_dead_zone.is_connected(_disconnect_dead_zone): - pcam_host.viewfinder_disable_dead_zone.connect(_disconnect_dead_zone) - - if not aspect_ratio_container.resized.is_connected(_resized): - aspect_ratio_container.resized.connect(_resized) - - if is_instance_valid(pcam_host.get_active_pcam()): - _active_pcam = pcam_host.get_active_pcam() - else: - _framed_view_visible(false) - _active_pcam = null - return - - if not _active_pcam.follow_mode == PhantomCamera2D.FollowMode.FRAMED: return - - _framed_view_visible(true) - _on_dead_zone_changed() - _connect_dead_zone() - - -func _connect_dead_zone() -> void: - if not _active_pcam and is_instance_valid(pcam_host.get_active_pcam()): - _active_pcam = pcam_host.get_active_pcam() - - if not _active_pcam.dead_zone_changed.is_connected(_on_dead_zone_changed): - _active_pcam.dead_zone_changed.connect(_on_dead_zone_changed) - - _framed_view_visible(true) - _viewfinder.visible = true - _on_dead_zone_changed() - -func _disconnect_dead_zone() -> void: - if not is_instance_valid(_active_pcam): return - _framed_view_visible(_is_framed_pcam()) - - if _active_pcam.follow_mode_changed.is_connected(_check_follow_mode): - _active_pcam.follow_mode_changed.disconnect(_check_follow_mode) - - if _active_pcam.dead_zone_changed.is_connected(_on_dead_zone_changed): - _active_pcam.dead_zone_changed.disconnect(_on_dead_zone_changed) - - -func _resized() -> void: - _on_dead_zone_changed() - - -func _is_framed_pcam() -> bool: - if not is_instance_valid(pcam_host): return false - _active_pcam = pcam_host.get_active_pcam() - if not is_instance_valid(_active_pcam): return false - if not _active_pcam.follow_mode == PhantomCamera2D.FollowMode.FRAMED: return false - - return true - - -func _framed_view_visible(should_show: bool) -> void: - if should_show: - target_point.visible = true - _dead_zone_h_box_container.visible = true - else: - target_point.visible = false - _dead_zone_h_box_container.visible = false - - -func _on_dead_zone_changed() -> void: - if not is_instance_valid(_active_pcam): return - if not _active_pcam.follow_mode == _active_pcam.FollowMode.FRAMED: return - - # Waits until the camera_viewport_panel has been resized when launching the game - if camera_viewport_panel.size.x == 0: - await camera_viewport_panel.resized - - if not _active_pcam == pcam_host.get_active_pcam(): - _active_pcam == pcam_host.get_active_pcam() - - var dead_zone_width: float = _active_pcam.dead_zone_width * camera_viewport_panel.size.x - var dead_zone_height: float = _active_pcam.dead_zone_height * camera_viewport_panel.size.y - dead_zone_center_hbox.set_custom_minimum_size(Vector2(dead_zone_width, 0)) - dead_zone_center_center_panel.set_custom_minimum_size(Vector2(0, dead_zone_height)) - dead_zone_left_center_panel.set_custom_minimum_size(Vector2(0, dead_zone_height)) - dead_zone_right_center_panel.set_custom_minimum_size(Vector2(0, dead_zone_height)) - - min_horizontal = 0.5 - _active_pcam.dead_zone_width / 2 - max_horizontal = 0.5 + _active_pcam.dead_zone_width / 2 - min_vertical = 0.5 - _active_pcam.dead_zone_height / 2 - max_vertical = 0.5 + _active_pcam.dead_zone_height / 2 - - -func _check_follow_mode() -> void: - _framed_view_visible(_is_framed_pcam()) - - -func _on_update_editor_viewfinder(check_framed_view: bool = false) -> void: - _active_pcam = pcam_host.get_active_pcam() - - if not is_instance_valid(_active_pcam): return - - if not _active_pcam.follow_mode_changed.is_connected(_check_follow_mode): - _active_pcam.follow_mode_changed.connect(_check_follow_mode) - - if _active_pcam.priority_override: - _priority_override_button.visible = true - _priority_override_name_label.set_text(_active_pcam.name) - _priority_override_button.set_tooltip_text(_active_pcam.name) - else: - _priority_override_button.visible = false - - _framed_view_visible(false) - if not check_framed_view: return - if _is_framed_pcam(): _connect_dead_zone() - - -func _select_override_pcam() -> void: - EditorInterface.get_selection().clear() - EditorInterface.get_selection().add_node(_active_pcam) - - -func _assign_manager() -> void: - if not is_instance_valid(_pcam_manager): - if Engine.has_singleton(_constants.PCAM_MANAGER_NODE_NAME): - _pcam_manager = Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME) - _pcam_manager.pcam_host_added_to_scene.connect(_pcam_changed) - _pcam_manager.pcam_host_removed_from_scene.connect(_pcam_host_removed_from_scene) - - _pcam_manager.pcam_added_to_scene.connect(_pcam_changed) - _pcam_manager.pcam_removed_from_scene.connect(_pcam_changed) - - _pcam_manager.viewfinder_pcam_host_switch.connect(_pcam_host_switch) - - -func _pcam_host_removed_from_scene(pcam_host: PhantomCameraHost) -> void: - if _pcam_manager.phantom_camera_hosts.size() < 2: - _pcam_host_list.visible = false - - _visibility_check() - - -func _pcam_changed(pcam: Node) -> void: - _visibility_check() - -#endregion - - -#region Public Functions - -func set_visibility(visible: bool) -> void: - if visible: - viewfinder_visible = true - _visibility_check() - else: - viewfinder_visible = false - - -func update_dead_zone() -> void: - _set_viewfinder(_root_node, true) - - -## TODO - Signal can be added directly to this file with the changes in Godot 4.5 (https://github.com/godotengine/godot/pull/102986) -func scene_changed(scene_root: Node) -> void: - _assign_manager() - _priority_override_button.visible = false - _pcam_host_list.clear_pcam_host_list() - - if not scene_root is Node2D and not scene_root is Node3D: - is_scene = false - _pcam_host_list.visible = false - _set_empty_viewfinder_state(_no_open_scene_string, _no_open_scene_icon) - _add_node_button.visible = false - else: - _visibility_check() - -#endregion diff --git a/addons/phantom_camera/scripts/panel/viewfinder/viewfinder.gd.uid b/addons/phantom_camera/scripts/panel/viewfinder/viewfinder.gd.uid deleted file mode 100644 index f115630..0000000 --- a/addons/phantom_camera/scripts/panel/viewfinder/viewfinder.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://drmv3363t8amc diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs deleted file mode 100644 index 7ee47a7..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs +++ /dev/null @@ -1,234 +0,0 @@ -using Godot; -using PhantomCamera.Noise; - -#nullable enable - -namespace PhantomCamera; - -public enum InactiveUpdateMode -{ - Always, - Never -} - -public abstract class PhantomCamera -{ - protected readonly GodotObject Node; - - public delegate void BecameActiveEventHandler(); - public delegate void BecameInactiveEventHandler(); - public delegate void FollowTargetChangedEventHandler(); - public delegate void DeadZoneChangedEventHandler(); - public delegate void TweenStartedEventHandler(); - public delegate void IsTweeningEventHandler(); - public delegate void TweenCompletedEventHandler(); - - public event BecameActiveEventHandler? BecameActive; - public event BecameInactiveEventHandler? BecameInactive; - public event FollowTargetChangedEventHandler? FollowTargetChanged; - public event DeadZoneChangedEventHandler? DeadZoneChanged; - public event TweenStartedEventHandler? TweenStarted; - public event IsTweeningEventHandler? IsTweening; - public event TweenCompletedEventHandler? TweenCompleted; - - public int Priority - { - get => (int)Node.Call(MethodName.GetPriority); - set => Node.Call(MethodName.SetPriority, value); - } - - public bool IsActive => (bool)Node.Call(MethodName.IsActive); - - public bool FollowDamping - { - get => (bool)Node.Call(MethodName.GetFollowDamping); - set => Node.Call(MethodName.SetFollowDamping, value); - } - - public bool IsFollowing => (bool)Node.Call(PhantomCamera.MethodName.IsFollowing); - - public float DeadZoneWidth - { - get => (float)Node.Get(PropertyName.DeadZoneWidth); - set => Node.Set(PropertyName.DeadZoneWidth, value); - } - - public float DeadZoneHeight - { - get => (float)Node.Get(PropertyName.DeadZoneHeight); - set => Node.Set(PropertyName.DeadZoneHeight, value); - } - - public PhantomCameraTween TweenResource - { - get => new((Resource)Node.Call(MethodName.GetTweenResource)); - set => Node.Call(MethodName.SetTweenResource, (GodotObject)value.Resource); - } - - public bool TweenSkip - { - get => (bool)Node.Call(MethodName.GetTweenSkip); - set => Node.Call(MethodName.SetTweenSkip, value); - } - - public float TweenDuration - { - get => (float)Node.Call(MethodName.GetTweenDuration); - set => Node.Call(MethodName.SetTweenDuration, value); - } - - public TransitionType TweenTransition - { - get => (TransitionType)(int)Node.Call(MethodName.GetTweenTransition); - set => Node.Call(MethodName.SetTweenTransition, (int)value); - } - - public EaseType TweenEase - { - get => (EaseType)(int)Node.Call(MethodName.GetTweenEase); - set => Node.Call(MethodName.SetTweenEase, (int)value); - } - - public bool TweenOnLoad - { - get => (bool)Node.Call(MethodName.GetTweenOnLoad); - set => Node.Call(MethodName.SetTweenOnLoad, value); - } - - public InactiveUpdateMode InactiveUpdateMode - { - get => (InactiveUpdateMode)(int)Node.Call(MethodName.GetInactiveUpdateMode); - set => Node.Call(MethodName.SetInactiveUpdateMode, (int)value); - } - - public int HostLayers - { - get => (int)Node.Call(MethodName.GetHostLayers); - set => Node.Call(MethodName.SetHostLayers, value); - } - - public int NoiseEmitterLayer - { - get => (int)Node.Call(MethodName.GetNoiseEmitterLayer); - set => Node.Call(MethodName.SetNoiseEmitterLayer, value); - } - - public void TeleportPosition() - { - Node.Call(MethodName.TeleportPosition); - } - - public void SetHostLayersValue(int layer, bool enabled) - { - Node.Call(MethodName.SetHostLayersValue, layer, enabled); - } - - protected PhantomCamera(GodotObject phantomCameraNode) - { - Node = phantomCameraNode; - - var callableBecameActive = Callable.From(() => BecameActive?.Invoke()); - var callableBecameInactive = Callable.From(() => BecameInactive?.Invoke()); - var callableFollowTargetChanged = Callable.From(() => FollowTargetChanged?.Invoke()); - var callableDeadZoneChanged = Callable.From(() => DeadZoneChanged?.Invoke()); - var callableTweenStarted = Callable.From(() => TweenStarted?.Invoke()); - var callableIsTweening = Callable.From(() => IsTweening?.Invoke()); - var callableTweenCompleted = Callable.From(() => TweenCompleted?.Invoke()); - - Node.Connect(SignalName.BecameActive, callableBecameActive); - Node.Connect(SignalName.BecameInactive, callableBecameInactive); - Node.Connect(SignalName.FollowTargetChanged, callableFollowTargetChanged); - Node.Connect(SignalName.DeadZoneChanged, callableDeadZoneChanged); - Node.Connect(SignalName.TweenStarted, callableTweenStarted); - Node.Connect(SignalName.IsTweening, callableIsTweening); - Node.Connect(SignalName.TweenCompleted, callableTweenCompleted); - } - - public static class MethodName - { - public static readonly StringName GetFollowMode = new("get_follow_mode"); - public static readonly StringName IsActive = new("is_active"); - - public static readonly StringName GetPriority = new("get_priority"); - public static readonly StringName SetPriority = new("set_priority"); - - public static readonly StringName IsFollowing = new("is_following"); - - public static readonly StringName GetFollowTarget = new("get_follow_target"); - public static readonly StringName SetFollowTarget = new("set_follow_target"); - - public static readonly StringName GetFollowTargets = new("get_follow_targets"); - public static readonly StringName SetFollowTargets = new("set_follow_targets"); - - public static readonly StringName TeleportPosition = new("teleport_position"); - - public static readonly StringName AppendFollowTargets = new("append_follow_targets"); - public static readonly StringName AppendFollowTargetsArray = new("append_follow_targets_array"); - public static readonly StringName EraseFollowTargets = new("erase_follow_targets"); - - public static readonly StringName GetFollowPath = new("get_follow_path"); - public static readonly StringName SetFollowPath = new("set_follow_path"); - - public static readonly StringName GetFollowOffset = new("get_follow_offset"); - public static readonly StringName SetFollowOffset = new("set_follow_offset"); - - public static readonly StringName GetFollowDamping = new("get_follow_damping"); - public static readonly StringName SetFollowDamping = new("set_follow_damping"); - - public static readonly StringName GetFollowDampingValue = new("get_follow_damping_value"); - public static readonly StringName SetFollowDampingValue = new("set_follow_damping_value"); - - public static readonly StringName GetFollowAxisLock = new("get_follow_axis_lock"); - public static readonly StringName SetFollowAxisLock = new("set_follow_axis_lock"); - - public static readonly StringName GetTweenResource = new("get_tween_resource"); - public static readonly StringName SetTweenResource = new("set_tween_resource"); - - public static readonly StringName GetTweenSkip = new("get_tween_skip"); - public static readonly StringName SetTweenSkip = new("set_tween_skip"); - - public static readonly StringName GetTweenDuration = new("get_tween_duration"); - public static readonly StringName SetTweenDuration = new("set_tween_duration"); - - public static readonly StringName GetTweenTransition = new("get_tween_transition"); - public static readonly StringName SetTweenTransition = new("set_tween_transition"); - - public static readonly StringName GetTweenEase = new("get_tween_ease"); - public static readonly StringName SetTweenEase = new("set_tween_ease"); - - public static readonly StringName GetTweenOnLoad = new("get_tween_on_load"); - public static readonly StringName SetTweenOnLoad = new("set_tween_on_load"); - - public static readonly StringName GetInactiveUpdateMode = new("get_inactive_update_mode"); - public static readonly StringName SetInactiveUpdateMode = new("set_inactive_update_mode"); - - public static readonly StringName GetHostLayers = new("get_host_layers"); - public static readonly StringName SetHostLayers = new("set_host_layers"); - public static readonly StringName SetHostLayersValue = new("set_host_layers_value"); - - public static readonly StringName GetNoiseEmitterLayer = new("get_noise_emitter_layer"); - public static readonly StringName SetNoiseEmitterLayer = new("set_noise_emitter_layer"); - - public static readonly StringName EmitNoise = new("emit_noise"); - } - - public static class PropertyName - { - public static readonly StringName DeadZoneWidth = new("dead_zone_width"); - public static readonly StringName DeadZoneHeight = new("dead_zone_height"); - } - - public static class SignalName - { - public static readonly StringName BecameActive = new("became_active"); - public static readonly StringName BecameInactive = new("became_inactive"); - public static readonly StringName FollowTargetChanged = new("follow_target_changed"); - public static readonly StringName DeadZoneChanged = new("dead_zone_changed"); - public static readonly StringName DeadZoneReached = new("dead_zone_reached"); - public static readonly StringName TweenStarted = new("tween_started"); - public static readonly StringName IsTweening = new("is_tweening"); - public static readonly StringName TweenCompleted = new("tween_completed"); - public static readonly StringName TweenInterrupted = new("tween_interrupted"); - public static readonly StringName NoiseEmitted = new("noise_emitted"); - } -} diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs.uid b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs.uid deleted file mode 100644 index 856fbdb..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d3wh0457i0i3 diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs deleted file mode 100644 index a0faf1d..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs +++ /dev/null @@ -1,326 +0,0 @@ -using System.Linq; -using Godot; -using Godot.Collections; -using PhantomCamera.Noise; - -#nullable enable - -namespace PhantomCamera; - -public enum FollowMode2D -{ - None, - Glued, - Simple, - Group, - Path, - Framed -} - -public enum FollowLockAxis2D -{ - None, - X, - Y, - XY -} - -public static class PhantomCamera2DExtensions -{ - public static PhantomCamera2D AsPhantomCamera2D(this Node2D node2D) - { - return new PhantomCamera2D(node2D); - } - - public static PhantomCameraNoiseEmitter2D AsPhantomCameraNoiseEmitter2D(this Node2D node2D) - { - return new PhantomCameraNoiseEmitter2D(node2D); - } - - public static PhantomCameraNoise2D AsPhantomCameraNoise2D(this Resource resource) - { - return new PhantomCameraNoise2D(resource); - } -} - -public class PhantomCamera2D : PhantomCamera -{ - public Node2D Node2D => (Node2D)Node; - - public delegate void TweenInterruptedEventHandler(Node2D pCam); - public delegate void DeadZoneReachedEventHandler(Vector2 side); - public delegate void NoiseEmittedEventHandler(Transform2D output); - - public event TweenInterruptedEventHandler? TweenInterrupted; - public event DeadZoneReachedEventHandler? DeadZoneReached; - public event NoiseEmittedEventHandler? NoiseEmitted; - - public Node2D FollowTarget - { - get => (Node2D)Node2D.Call(PhantomCamera.MethodName.GetFollowTarget); - set => Node2D.Call(PhantomCamera.MethodName.SetFollowTarget, value); - } - - public Node2D[] FollowTargets - { - get => Node2D.Call(PhantomCamera.MethodName.GetFollowTargets).AsGodotArray().ToArray(); - set => Node2D.Call(PhantomCamera.MethodName.SetFollowTargets, new Array(value)); - } - - public void AppendFollowTargets(Node2D target) => Node2D.Call(PhantomCamera.MethodName.AppendFollowTargets, target); - public void AppendFollowTargetsArray(Node2D[] targets) => Node2D.Call(PhantomCamera.MethodName.AppendFollowTargetsArray, targets); - public void EraseFollowTargets(Node2D target) => Node2D.Call(PhantomCamera.MethodName.EraseFollowTargets, target); - - public FollowMode2D FollowMode => (FollowMode2D)(int)Node.Call(PhantomCamera.MethodName.GetFollowMode); - - public Path2D FollowPath - { - get => (Path2D)Node2D.Call(PhantomCamera.MethodName.GetFollowPath); - set => Node2D.Call(PhantomCamera.MethodName.SetFollowPath, value); - } - - public Vector2 FollowOffset - { - get => (Vector2)Node2D.Call(PhantomCamera.MethodName.GetFollowOffset); - set => Node2D.Call(PhantomCamera.MethodName.SetFollowOffset, value); - } - - public Vector2 FollowDampingValue - { - get => (Vector2)Node2D.Call(PhantomCamera.MethodName.GetFollowDampingValue); - set => Node2D.Call(PhantomCamera.MethodName.SetFollowDampingValue, value); - } - - public FollowLockAxis2D FollowAxisLock - { - get => (FollowLockAxis2D)(int)Node2D.Call(PhantomCamera.MethodName.GetFollowAxisLock); - set => Node2D.Call(PhantomCamera.MethodName.SetFollowAxisLock, (int)value); - } - - public Vector2 Zoom - { - get => (Vector2)Node2D.Call(MethodName.GetZoom); - set => Node2D.Call(MethodName.SetZoom, value); - } - - public bool SnapToPixel - { - get => (bool)Node2D.Call(MethodName.GetSnapToPixel); - set => Node2D.Call(MethodName.SetSnapToPixel, value); - } - - public bool RotateWithTarget - { - get => (bool)Node2D.Call(MethodName.GetRotateWithTarget); - set => Node2D.Call(MethodName.SetRotateWithTarget, value); - } - - public float RotationOffset - { - get => (float)Node2D.Call(MethodName.GetRotationOffset); - set => Node2D.Call(MethodName.SetRotationOffset, value); - } - - public bool RotationDamping - { - get => (bool)Node2D.Call(MethodName.GetRotationDamping); - set => Node2D.Call(MethodName.SetRotationDamping, value); - } - - public float RotationDampingValue - { - get => (float)Node2D.Call(MethodName.GetRotationDampingValue); - set => Node2D.Call(MethodName.SetRotationDampingValue, value); - } - - public int LimitLeft - { - get => (int)Node2D.Call(MethodName.GetLimitLeft); - set => Node2D.Call(MethodName.SetLimitLeft, value); - } - - public int LimitTop - { - get => (int)Node2D.Call(MethodName.GetLimitTop); - set => Node2D.Call(MethodName.SetLimitTop, value); - } - - public int LimitRight - { - get => (int)Node2D.Call(MethodName.GetLimitRight); - set => Node2D.Call(MethodName.SetLimitRight, value); - } - - public int LimitBottom - { - get => (int)Node2D.Call(MethodName.GetLimitBottom); - set => Node2D.Call(MethodName.SetLimitBottom, value); - } - - public Vector4I LimitMargin - { - get => (Vector4I)Node2D.Call(MethodName.GetLimitMargin); - set => Node2D.Call(MethodName.SetLimitMargin, value); - } - - public bool AutoZoom - { - get => (bool)Node2D.Call(MethodName.GetAutoZoom); - set => Node2D.Call(MethodName.SetAutoZoom, value); - } - - public float AutoZoomMin - { - get => (float)Node2D.Call(MethodName.GetAutoZoomMin); - set => Node2D.Call(MethodName.SetAutoZoomMin, value); - } - - public float AutoZoomMax - { - get => (float)Node2D.Call(MethodName.GetAutoZoomMax); - set => Node2D.Call(MethodName.SetAutoZoomMax, value); - } - - public Vector4 AutoZoomMargin - { - get => (Vector4)Node2D.Call(MethodName.GetAutoZoomMargin); - set => Node2D.Call(MethodName.SetAutoZoomMargin, value); - } - - public bool DrawLimits - { - get => (bool)Node2D.Get(PropertyName.DrawLimits); - set => Node2D.Set(PropertyName.DrawLimits, value); - } - - public PhantomCameraNoise2D Noise - { - get => new((Resource)Node2D.Call(MethodName.GetNoise)); - set => Node2D.Call(MethodName.SetNoise, (GodotObject)value.Resource); - } - - public void EmitNoise(Transform2D transform) => Node2D.Call(PhantomCamera.MethodName.EmitNoise, transform); - - public NodePath LimitTarget - { - get => (NodePath)Node2D.Call(MethodName.GetLimitTarget); - set => Node2D.Call(MethodName.SetLimitTarget, value); - } - - public PhantomCamera2D(GodotObject phantomCameraNode) : base(phantomCameraNode) - { - var callableTweenInterrupted = Callable.From(pCam => TweenInterrupted?.Invoke(pCam)); - var callableDeadZoneReached = Callable.From((Vector2 side) => DeadZoneReached?.Invoke(side)); - var callableNoiseEmitted = Callable.From((Transform2D output) => NoiseEmitted?.Invoke(output)); - - Node2D.Connect(SignalName.TweenInterrupted, callableTweenInterrupted); - Node2D.Connect(SignalName.DeadZoneReached, callableDeadZoneReached); - Node2D.Connect(SignalName.NoiseEmitted, callableNoiseEmitted); - } - - public void SetLimitTarget(TileMapLayer tileMapLayer) - { - Node2D.Call(MethodName.SetLimitTarget, tileMapLayer.GetPath()); - } - - public void SetLimitTarget(CollisionShape2D shape2D) - { - Node2D.Call(MethodName.SetLimitTarget, shape2D.GetPath()); - } - - public LimitTargetQueryResult? GetLimitTarget() - { - var result = (NodePath)Node2D.Call(MethodName.GetLimitTarget); - return result.IsEmpty ? null : new LimitTargetQueryResult(Node2D.GetNode(result)); - } - - public void SetLimit(Side side, int value) - { - Node2D.Call(MethodName.SetLimit, (int)side, value); - } - - public int GetLimit(Side side) - { - return (int)Node2D.Call(MethodName.GetLimit, (int)side); - } - - public new static class MethodName - { - public static readonly StringName GetZoom = new("get_zoom"); - public static readonly StringName SetZoom = new("set_zoom"); - - public static readonly StringName GetSnapToPixel = new("get_snap_to_pixel"); - public static readonly StringName SetSnapToPixel = new("set_snap_to_pixel"); - - public static readonly StringName GetRotateWithTarget = new("get_rotate_with_target"); - public static readonly StringName SetRotateWithTarget = new("set_rotate_with_target"); - - public static readonly StringName GetRotationOffset = new("get_rotation_offset"); - public static readonly StringName SetRotationOffset = new("set_rotation_offset"); - - public static readonly StringName GetRotationDamping = new("get_rotation_damping"); - public static readonly StringName SetRotationDamping = new("set_rotation_damping"); - - public static readonly StringName GetRotationDampingValue = new("get_rotation_damping_value"); - public static readonly StringName SetRotationDampingValue = new("set_rotation_damping_value"); - - public static readonly StringName GetLimit = new("get_limit"); - public static readonly StringName SetLimit = new("set_limit"); - - public static readonly StringName GetLimitLeft = new("get_limit_left"); - public static readonly StringName SetLimitLeft = new("set_limit_left"); - - public static readonly StringName GetLimitTop = new("get_limit_top"); - public static readonly StringName SetLimitTop = new("set_limit_top"); - - public static readonly StringName GetLimitRight = new("get_limit_right"); - public static readonly StringName SetLimitRight = new("set_limit_right"); - - public static readonly StringName GetLimitBottom = new("get_limit_bottom"); - public static readonly StringName SetLimitBottom = new("set_limit_bottom"); - - public static readonly StringName GetLimitTarget = new("get_limit_target"); - public static readonly StringName SetLimitTarget = new("set_limit_target"); - - public static readonly StringName GetLimitMargin = new("get_limit_margin"); - public static readonly StringName SetLimitMargin = new("set_limit_margin"); - - public static readonly StringName GetAutoZoom = new("get_auto_zoom"); - public static readonly StringName SetAutoZoom = new("set_auto_zoom"); - - public static readonly StringName GetAutoZoomMin = new("get_auto_zoom_min"); - public static readonly StringName SetAutoZoomMin = new("set_auto_zoom_min"); - - public static readonly StringName GetAutoZoomMax = new("get_auto_zoom_max"); - public static readonly StringName SetAutoZoomMax = new("set_auto_zoom_max"); - - public static readonly StringName GetAutoZoomMargin = new("get_auto_zoom_margin"); - public static readonly StringName SetAutoZoomMargin = new("set_auto_zoom_margin"); - - public static readonly StringName GetNoise = new("get_noise"); - public static readonly StringName SetNoise = new("set_noise"); - } - - public new static class PropertyName - { - public static readonly StringName DrawLimits = new("draw_limits"); - } -} - -public class LimitTargetQueryResult(GodotObject godotObject) -{ - public bool IsTileMapLayer => godotObject.IsClass("TileMapLayer"); - - public bool IsCollisionShape2D => godotObject.IsClass("CollisionShape2D"); - - - public TileMapLayer? AsTileMapLayer() - { - return IsTileMapLayer ? (TileMapLayer)godotObject : null; - } - - public CollisionShape2D? AsCollisionShape2D() - { - return IsCollisionShape2D ? (CollisionShape2D)godotObject : null; - } -} diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs.uid b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs.uid deleted file mode 100644 index 8cc93f0..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera2D.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c38e5qhsf3fk3 diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs deleted file mode 100644 index 1e4cd07..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs +++ /dev/null @@ -1,482 +0,0 @@ -using System.Linq; -using Godot; -using Godot.Collections; -using PhantomCamera.Noise; - -#nullable enable - -namespace PhantomCamera; - -public enum LookAtMode -{ - None, - Mimic, - Simple, - Group -} - -public enum FollowMode3D -{ - None, - Glued, - Simple, - Group, - Path, - Framed, - ThirdPerson -} - -public enum FollowLockAxis3D -{ - None, - X, - Y, - Z, - XY, - XZ, - YZ, - XYZ -} - -public static class PhantomCamera3DExtensions -{ - public static PhantomCamera3D AsPhantomCamera3D(this Node3D node3D) - { - return new PhantomCamera3D(node3D); - } - - public static PhantomCameraNoiseEmitter3D AsPhantomCameraNoiseEmitter3D(this Node3D node3D) - { - return new PhantomCameraNoiseEmitter3D(node3D); - } - - public static PhantomCameraNoise3D AsPhantomCameraNoise3D(this Resource resource) - { - return new PhantomCameraNoise3D(resource); - } - - public static Camera3DResource AsCamera3DResource(this Resource resource) - { - return new Camera3DResource(resource); - } - - public static Vector3 GetThirdPersonRotation(this PhantomCamera3D pCam3D) => - (Vector3)pCam3D.Node3D.Call(PhantomCamera3D.MethodName.GetThirdPersonRotation); - - public static void SetThirdPersonRotation(this PhantomCamera3D pCam3D, Vector3 rotation) => - pCam3D.Node3D.Call(PhantomCamera3D.MethodName.SetThirdPersonRotation, rotation); - - public static Vector3 GetThirdPersonRotationDegrees(this PhantomCamera3D pCam3D) => - (Vector3)pCam3D.Node3D.Call(PhantomCamera3D.MethodName.GetThirdPersonRotationDegrees); - - public static void SetThirdPersonRotationDegrees(this PhantomCamera3D pCam3D, Vector3 rotation) => - pCam3D.Node3D.Call(PhantomCamera3D.MethodName.SetThirdPersonRotationDegrees, rotation); - - public static Quaternion GetThirdPersonQuaternion(this PhantomCamera3D pCam3D) => - (Quaternion)pCam3D.Node3D.Call(PhantomCamera3D.MethodName.GetThirdPersonQuaternion); - - public static void SetThirdPersonQuaternion(this PhantomCamera3D pCam3D, Quaternion quaternion) => - pCam3D.Node3D.Call(PhantomCamera3D.MethodName.SetThirdPersonQuaternion, quaternion); - -} - -public class PhantomCamera3D : PhantomCamera -{ - public Node3D Node3D => (Node3D)Node; - - public delegate void LookAtTargetChangedEventHandler(); - public delegate void DeadZoneReachedEventHandler(); - public delegate void Camera3DResourceChangedEventHandler(); - public delegate void Camera3DResourcePropertyChangedEventHandler(StringName property, Variant value); - public delegate void TweenInterruptedEventHandler(Node3D pCam); - public delegate void NoiseEmittedEventHandler(Transform3D output); - - public event LookAtTargetChangedEventHandler? LookAtTargetChanged; - public event DeadZoneReachedEventHandler? DeadZoneReached; - public event Camera3DResourceChangedEventHandler? Camera3DResourceChanged; - public event Camera3DResourcePropertyChangedEventHandler? Camera3DResourcePropertyChanged; - public event TweenInterruptedEventHandler? TweenInterrupted; - public event NoiseEmittedEventHandler? NoiseEmitted; - - public Node3D FollowTarget - { - get => (Node3D)Node3D.Call(PhantomCamera.MethodName.GetFollowTarget); - set => Node3D.Call(PhantomCamera.MethodName.SetFollowTarget, value); - } - - public Node3D[] FollowTargets - { - get => Node3D.Call(PhantomCamera.MethodName.GetFollowTargets).AsGodotArray().ToArray(); - set => Node3D.Call(PhantomCamera.MethodName.SetFollowTargets, new Array(value)); - } - - public void AppendFollowTarget(Node3D target) => Node3D.Call(PhantomCamera.MethodName.AppendFollowTargets, target); - public void AppendFollowTargetArray(Node3D[] targets) => Node3D.Call(PhantomCamera.MethodName.AppendFollowTargetsArray, targets); - public void EraseFollowTarget(Node3D target) => Node3D.Call(PhantomCamera.MethodName.EraseFollowTargets, target); - - public FollowMode3D FollowMode => (FollowMode3D)(int)Node.Call(PhantomCamera.MethodName.GetFollowMode); - - public Path3D FollowPath - { - get => (Path3D)Node3D.Call(PhantomCamera.MethodName.GetFollowPath); - set => Node3D.Call(PhantomCamera.MethodName.SetFollowPath, value); - } - - public Vector3 FollowOffset - { - get => (Vector3)Node3D.Call(PhantomCamera.MethodName.GetFollowOffset); - set => Node3D.Call(PhantomCamera.MethodName.SetFollowOffset, value); - } - - public Vector3 FollowDampingValue - { - get => (Vector3)Node3D.Call(PhantomCamera.MethodName.GetFollowDampingValue); - set => Node3D.Call(PhantomCamera.MethodName.SetFollowDampingValue, value); - } - - public FollowLockAxis3D FollowAxisLock - { - get => (FollowLockAxis3D)(int)Node3D.Call(PhantomCamera.MethodName.GetFollowAxisLock); - set => Node3D.Call(PhantomCamera.MethodName.SetFollowAxisLock, (int)value); - } - - public LookAtMode LookAtMode => (LookAtMode)(int)Node3D.Call(MethodName.GetLookAtMode); - - public Camera3DResource Camera3DResource - { - get => new((Resource)Node3D.Call(MethodName.GetCamera3DResource)); - set => Node3D.Call(MethodName.SetCamera3DResource, value.Resource); - } - - public float SpringLength - { - get => (float)Node3D.Call(MethodName.GetSpringLength); - set => Node3D.Call(MethodName.SetSpringLength, value); - } - - public float VerticalRotationOffset - { - get => (float)Node3D.Call(MethodName.GetVerticalRotationOffset); - set => Node3D.Call(MethodName.SetVerticalRotationOffset, value); - } - - public float HorizontalRotationOffset - { - get => (float)Node3D.Call(MethodName.GetHorizontalRotationOffset); - set => Node3D.Call(MethodName.SetHorizontalRotationOffset, value); - } - - public float FollowDistance - { - get => (float)Node3D.Call(MethodName.GetFollowDistance); - set => Node3D.Call(MethodName.SetFollowDistance, value); - } - - public bool AutoFollowDistance - { - get => (bool)Node3D.Call(MethodName.GetAutoFollowDistance); - set => Node3D.Call(MethodName.SetAutoFollowDistance, value); - } - - public float AutoFollowDistanceMin - { - get => (float)Node3D.Call(MethodName.GetAutoFollowDistanceMin); - set => Node3D.Call(MethodName.SetAutoFollowDistanceMin, value); - } - - public float AutoFollowDistanceMax - { - get => (float)Node3D.Call(MethodName.GetAutoFollowDistanceMax); - set => Node3D.Call(MethodName.SetAutoFollowDistanceMax, value); - } - - public float AutoFollowDistanceDivisor - { - get => (float)Node3D.Call(MethodName.GetAutoFollowDistanceDivisor); - set => Node3D.Call(MethodName.SetAutoFollowDistanceDivisor, value); - } - - public Node3D LookAtTarget - { - get => (Node3D)Node3D.Call(MethodName.GetLookAtTarget); - set => Node3D.Call(MethodName.SetLookAtTarget, value); - } - - public Node3D[] LookAtTargets - { - get => Node3D.Call(MethodName.GetLookAtTargets).AsGodotArray().ToArray(); - set => Node3D.Call(MethodName.SetLookAtTargets, new Array(value)); - } - - public bool IsLooking => (bool)Node3D.Call(MethodName.IsLooking); - - public int CollisionMask - { - get => (int)Node3D.Call(MethodName.GetCollisionMask); - set => Node3D.Call(MethodName.SetCollisionMask, value); - } - - public void SetCollisionMaskValue(int maskLayer, bool enable) => - Node3D.Call(MethodName.SetCollisionMaskValue, maskLayer, enable); - - public Shape3D Shape - { - get => (Shape3D)Node3D.Call(MethodName.GetShape); - set => Node3D.Call(MethodName.SetShape, value); - } - - public float Margin - { - get => (float)Node3D.Call(MethodName.GetMargin); - set => Node3D.Call(MethodName.SetMargin, value); - } - - public Vector3 LookAtOffset - { - get => (Vector3)Node3D.Call(MethodName.GetLookAtOffset); - set => Node3D.Call(MethodName.SetLookAtOffset, value); - } - - public bool LookAtDamping - { - get => (bool)Node3D.Call(MethodName.GetLookAtDamping); - set => Node3D.Call(MethodName.SetLookAtDamping, value); - } - - public float LookAtDampingValue - { - get => (float)Node3D.Call(MethodName.GetLookAtDampingValue); - set => Node3D.Call(MethodName.SetLookAtDampingValue, value); - } - - public Vector3 Up - { - get => (Vector3)Node3D.Call(MethodName.GetUp); - set => Node3D.Call(MethodName.SetUp, value); - } - - public Node3D UpTarget - { - get => (Node3D)Node3D.Call(MethodName.GetUpTarget); - set => Node3D.Call(MethodName.SetUpTarget, value); - } - - public int KeepAspect - { - get => (int)Node3D.Call(MethodName.GetKeepAspect); - set => Node3D.Call(MethodName.SetKeepAspect, value); - } - - public int CullMask - { - get => (int)Node3D.Call(MethodName.GetCullMask); - set => Node3D.Call(MethodName.SetCullMask, value); - } - - public float HOffset - { - get => (float)Node3D.Call(MethodName.GetHOffset); - set => Node3D.Call(MethodName.SetHOffset, value); - } - - public float VOffset - { - get => (float)Node3D.Call(MethodName.GetVOffset); - set => Node3D.Call(MethodName.SetVOffset, value); - } - - public ProjectionType Projection - { - get => (ProjectionType)(int)Node3D.Call(MethodName.GetProjection); - set => Node3D.Call(MethodName.SetProjection, (int)value); - } - - public float Fov - { - get => (float)Node3D.Call(MethodName.GetFov); - set => Node3D.Call(MethodName.SetFov, value); - } - - public float Size - { - get => (float)Node3D.Call(MethodName.GetSize); - set => Node3D.Call(MethodName.SetSize, value); - } - - public Vector2 FrustumOffset - { - get => (Vector2)Node3D.Call(MethodName.GetFrustumOffset); - set => Node3D.Call(MethodName.SetFrustumOffset, value); - } - - public float Far - { - get => (float)Node3D.Call(MethodName.GetFar); - set => Node3D.Call(MethodName.SetFar, value); - } - - public float Near - { - get => (float)Node3D.Call(MethodName.GetNear); - set => Node3D.Call(MethodName.SetNear, value); - } - - public Environment Environment - { - get => (Environment)Node3D.Call(MethodName.GetEnvironment); - set => Node3D.Call(MethodName.SetEnvironment, value); - } - - public CameraAttributes Attributes - { - get => (CameraAttributes)Node3D.Call(MethodName.GetAttributes); - set => Node3D.Call(MethodName.SetAttributes, value); - } - - public PhantomCameraNoise3D Noise - { - get => new((Resource)Node3D.Call(MethodName.GetNoise)); - set => Node3D.Call(MethodName.SetNoise, (GodotObject)value.Resource); - } - - public void EmitNoise(Transform3D transform) => Node3D.Call(PhantomCamera.MethodName.EmitNoise, transform); - - public PhantomCamera3D(GodotObject phantomCamera3DNode) : base(phantomCamera3DNode) - { - var callableLookAtTargetChanged = Callable.From(() => LookAtTargetChanged?.Invoke()); - var callableDeadZoneReached = Callable.From(() => DeadZoneReached?.Invoke()); - var callableCamera3DResourceChanged = Callable.From(() => Camera3DResourceChanged?.Invoke()); - var callableCamera3DResourcePropertyChanged = Callable.From((StringName property, Variant value) => - Camera3DResourcePropertyChanged?.Invoke(property, value)); - var callableTweenInterrupted = Callable.From(pCam => TweenInterrupted?.Invoke(pCam)); - var callableNoiseEmitted = Callable.From((Transform3D output) => NoiseEmitted?.Invoke(output)); - - Node3D.Connect(SignalName.LookAtTargetChanged, callableLookAtTargetChanged); - Node3D.Connect(PhantomCamera.SignalName.DeadZoneReached, callableDeadZoneReached); - Node3D.Connect(SignalName.Camera3DResourceChanged, callableCamera3DResourceChanged); - Node3D.Connect(SignalName.Camera3DResourcePropertyChanged, callableCamera3DResourcePropertyChanged); - Node3D.Connect(PhantomCamera.SignalName.TweenInterrupted, callableTweenInterrupted); - Node3D.Connect(PhantomCamera.SignalName.NoiseEmitted, callableNoiseEmitted); - } - - public new static class MethodName - { - public static readonly StringName GetLookAtMode = new("get_look_at_mode"); - - public static readonly StringName GetCamera3DResource = new("get_camera_3d_resource"); - public static readonly StringName SetCamera3DResource = new("set_camera_3d_resource"); - - public static readonly StringName GetThirdPersonRotation = new("get_third_person_rotation"); - public static readonly StringName SetThirdPersonRotation = new("set_third_person_rotation"); - - public static readonly StringName GetThirdPersonRotationDegrees = new("get_third_person_rotation_degrees"); - public static readonly StringName SetThirdPersonRotationDegrees = new("set_third_person_rotation_degrees"); - - public static readonly StringName GetThirdPersonQuaternion = new("get_third_person_quaternion"); - public static readonly StringName SetThirdPersonQuaternion = new("set_third_person_quaternion"); - - public static readonly StringName GetVerticalRotationOffset = new("get_vertical_rotation_offset"); - public static readonly StringName SetVerticalRotationOffset = new("set_vertical_rotation_offset"); - - public static readonly StringName GetHorizontalRotationOffset = new("get_horizontal_rotation_offset"); - public static readonly StringName SetHorizontalRotationOffset = new("set_horizontal_rotation_offset"); - - public static readonly StringName GetSpringLength = new("get_spring_length"); - public static readonly StringName SetSpringLength = new("set_spring_length"); - - public static readonly StringName GetFollowDistance = new("get_follow_distance"); - public static readonly StringName SetFollowDistance = new("set_follow_distance"); - - public static readonly StringName GetAutoFollowDistance = new("get_auto_follow_distance"); - public static readonly StringName SetAutoFollowDistance = new("set_auto_follow_distance"); - - public static readonly StringName GetAutoFollowDistanceMin = new("get_auto_follow_distance_min"); - public static readonly StringName SetAutoFollowDistanceMin = new("set_auto_follow_distance_min"); - - public static readonly StringName GetAutoFollowDistanceMax = new("get_auto_follow_distance_max"); - public static readonly StringName SetAutoFollowDistanceMax = new("set_auto_follow_distance_max"); - - public static readonly StringName GetAutoFollowDistanceDivisor = new("get_auto_follow_distance_divisor"); - public static readonly StringName SetAutoFollowDistanceDivisor = new("set_auto_follow_distance_divisor"); - - public static readonly StringName GetLookAtTarget = new("get_look_at_target"); - public static readonly StringName SetLookAtTarget = new("set_look_at_target"); - - public static readonly StringName GetLookAtTargets = new("get_look_at_targets"); - public static readonly StringName SetLookAtTargets = new("set_look_at_targets"); - - public static readonly StringName IsLooking = new("is_looking"); - - public static readonly StringName GetUp = new("get_up"); - public static readonly StringName SetUp = new("set_up"); - - public static readonly StringName GetUpTarget = new("get_up_target"); - public static readonly StringName SetUpTarget = new("set_up_target"); - - public static readonly StringName GetCollisionMask = new("get_collision_mask"); - public static readonly StringName SetCollisionMask = new("set_collision_mask"); - - public static readonly StringName SetCollisionMaskValue = new("set_collision_mask_value"); - - public static readonly StringName GetShape = new("get_shape"); - public static readonly StringName SetShape = new("set_shape"); - - public static readonly StringName GetMargin = new("get_margin"); - public static readonly StringName SetMargin = new("set_margin"); - - public static readonly StringName GetLookAtOffset = new("get_look_at_offset"); - public static readonly StringName SetLookAtOffset = new("set_look_at_offset"); - - public static readonly StringName GetLookAtDamping = new("get_look_at_damping"); - public static readonly StringName SetLookAtDamping = new("set_look_at_damping"); - - public static readonly StringName GetLookAtDampingValue = new("get_look_at_damping_value"); - public static readonly StringName SetLookAtDampingValue = new("set_look_at_damping_value"); - - public static readonly StringName GetKeepAspect = new("get_keep_aspect"); - public static readonly StringName SetKeepAspect = new("set_keep_aspect"); - - public static readonly StringName GetCullMask = new("get_cull_mask"); - public static readonly StringName SetCullMask = new("set_cull_mask"); - - public static readonly StringName GetHOffset = new("get_h_offset"); - public static readonly StringName SetHOffset = new("set_h_offset"); - - public static readonly StringName GetVOffset = new("get_v_offset"); - public static readonly StringName SetVOffset = new("set_v_offset"); - - public static readonly StringName GetProjection = new("get_projection"); - public static readonly StringName SetProjection = new("set_projection"); - - public static readonly StringName GetFov = new("get_fov"); - public static readonly StringName SetFov = new("set_fov"); - - public static readonly StringName GetSize = new("get_size"); - public static readonly StringName SetSize = new("set_size"); - - public static readonly StringName GetFrustumOffset = new("get_frustum_offset"); - public static readonly StringName SetFrustumOffset = new("set_frustum_offset"); - - public static readonly StringName GetFar = new("get_far"); - public static readonly StringName SetFar = new("set_far"); - - public static readonly StringName GetNear = new("get_near"); - public static readonly StringName SetNear = new("set_near"); - - public static readonly StringName GetEnvironment = new("get_environment"); - public static readonly StringName SetEnvironment = new("set_environment"); - - public static readonly StringName GetAttributes = new("get_attributes"); - public static readonly StringName SetAttributes = new("set_attributes"); - - public static readonly StringName GetNoise = new("get_noise"); - public static readonly StringName SetNoise = new("set_noise"); - } - - public new static class SignalName - { - public static readonly StringName LookAtTargetChanged = new("look_at_target_changed"); - public static readonly StringName Camera3DResourceChanged = new("camera_3d_resource_changed"); - public static readonly StringName Camera3DResourcePropertyChanged = new("camera_3d_resource_property_changed"); - } -} diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs.uid b/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs.uid deleted file mode 100644 index c1f0801..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCamera3D.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bx3g7jxtwhi04 diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter2D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter2D.cs deleted file mode 100644 index 4444dad..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter2D.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Godot; - -namespace PhantomCamera.Noise; - -public class PhantomCameraNoiseEmitter2D(GodotObject node) -{ - public Node2D Node2D = (Node2D)node; - - public PhantomCameraNoise2D Noise - { - get => new((Resource)Node2D.Call(MethodName.GetNoise)); - set => Node2D.Call(MethodName.SetNoise, (GodotObject)value.Resource); - } - - public bool Continuous - { - get => (bool)Node2D.Call(MethodName.GetContinuous); - set => Node2D.Call(MethodName.SetContinuous, value); - } - - public float GrowthTime - { - get => (float)Node2D.Call(MethodName.GetGrowthTime); - set => Node2D.Call(MethodName.SetGrowthTime, value); - } - - public float Duration - { - get => (float)Node2D.Call(MethodName.GetDuration); - set => Node2D.Call(MethodName.SetDuration, value); - } - - public float DecayTime - { - get => (float)Node2D.Call(MethodName.GetDecayTime); - set => Node2D.Call(MethodName.SetDecayTime, value); - } - - public int NoiseEmitterLayer - { - get => (int)Node2D.Call(MethodName.GetNoiseEmitterLayer); - set => Node2D.Call(MethodName.SetNoiseEmitterLayer, value); - } - - public void SetNoiseEmitterLayerValue(int layer, bool value) => - Node2D.Call(MethodName.SetNoiseEmitterLayerValue, layer, value); - - public void Emit() => Node2D.Call(MethodName.Emit); - - public bool IsEmitting() => (bool)Node2D.Call(MethodName.IsEmitting); - - public void Stop() => Node2D.Call(MethodName.Stop); - - public void Toggle() => Node2D.Call(MethodName.Toggle); - - public static class MethodName - { - public static readonly StringName GetNoise = new("get_noise"); - public static readonly StringName SetNoise = new("set_noise"); - - public static readonly StringName GetContinuous = new("get_continuous"); - public static readonly StringName SetContinuous = new("set_continuous"); - - public static readonly StringName GetGrowthTime = new("get_growth_time"); - public static readonly StringName SetGrowthTime = new("set_growth_time"); - - public static readonly StringName GetDuration = new("get_duration"); - public static readonly StringName SetDuration = new("set_duration"); - - public static readonly StringName GetDecayTime = new("get_decay_time"); - public static readonly StringName SetDecayTime = new("set_decay_time"); - - public static readonly StringName GetNoiseEmitterLayer = new("get_noise_emitter_layer"); - public static readonly StringName SetNoiseEmitterLayer = new("set_noise_emitter_layer"); - - public static readonly StringName SetNoiseEmitterLayerValue = new("set_noise_emitter_layer_value"); - - public static readonly StringName Emit = new("emit"); - public static readonly StringName IsEmitting = new("is_emitting"); - public static readonly StringName Stop = new("stop"); - public static readonly StringName Toggle = new("toggle"); - } -} diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter2D.cs.uid b/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter2D.cs.uid deleted file mode 100644 index 4f35145..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter2D.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://btom8l3wlkn2j diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter3D.cs b/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter3D.cs deleted file mode 100644 index 2821f1f..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter3D.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Godot; - -namespace PhantomCamera.Noise; - -public class PhantomCameraNoiseEmitter3D(GodotObject node) -{ - public Node3D Node3D = (Node3D)node; - - public PhantomCameraNoise3D Noise - { - get => new((Resource)Node3D.Call(MethodName.GetNoise)); - set => Node3D.Call(MethodName.SetNoise, (GodotObject)value.Resource); - } - - public bool Continuous - { - get => (bool)Node3D.Call(MethodName.GetContinuous); - set => Node3D.Call(MethodName.SetContinuous, value); - } - - public float GrowthTime - { - get => (float)Node3D.Call(MethodName.GetGrowthTime); - set => Node3D.Call(MethodName.SetGrowthTime, value); - } - - public float Duration - { - get => (float)Node3D.Call(MethodName.GetDuration); - set => Node3D.Call(MethodName.SetDuration, value); - } - - public float DecayTime - { - get => (float)Node3D.Call(MethodName.GetDecayTime); - set => Node3D.Call(MethodName.SetDecayTime, value); - } - - public int NoiseEmitterLayer - { - get => (int)Node3D.Call(MethodName.GetNoiseEmitterLayer); - set => Node3D.Call(MethodName.SetNoiseEmitterLayer, value); - } - - public void SetNoiseEmitterLayerValue(int layer, bool value) => - Node3D.Call(MethodName.SetNoiseEmitterLayerValue, layer, value); - - public void Emit() => Node3D.Call(MethodName.Emit); - - public bool IsEmitting() => (bool)Node3D.Call(MethodName.IsEmitting); - - public void Stop() => Node3D.Call(MethodName.Stop); - - public void Toggle() => Node3D.Call(MethodName.Toggle); - - public static class MethodName - { - public static readonly StringName GetNoise = new("get_noise"); - public static readonly StringName SetNoise = new("set_noise"); - - public static readonly StringName GetContinuous = new("get_continuous"); - public static readonly StringName SetContinuous = new("set_continuous"); - - public static readonly StringName GetGrowthTime = new("get_growth_time"); - public static readonly StringName SetGrowthTime = new("set_growth_time"); - - public static readonly StringName GetDuration = new("get_duration"); - public static readonly StringName SetDuration = new("set_duration"); - - public static readonly StringName GetDecayTime = new("get_decay_time"); - public static readonly StringName SetDecayTime = new("set_decay_time"); - - public static readonly StringName GetNoiseEmitterLayer = new("get_noise_emitter_layer"); - public static readonly StringName SetNoiseEmitterLayer = new("set_noise_emitter_layer"); - - public static readonly StringName SetNoiseEmitterLayerValue = new("set_noise_emitter_layer_value"); - - public static readonly StringName Emit = new("emit"); - public static readonly StringName IsEmitting = new("is_emitting"); - public static readonly StringName Stop = new("stop"); - public static readonly StringName Toggle = new("toggle"); - } -} diff --git a/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter3D.cs.uid b/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter3D.cs.uid deleted file mode 100644 index bf32a5b..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/PhantomCameraNoiseEmitter3D.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://buvda14filkjx diff --git a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd deleted file mode 100644 index 00c346d..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd +++ /dev/null @@ -1,1718 +0,0 @@ -@tool -@icon("res://addons/phantom_camera/icons/phantom_camera_2d.svg") -class_name PhantomCamera2D -extends Node2D - -## Controls a scene's [Camera2D] and applies logic to it. -## -## The scene's [param Camera2D] will follow the position of the -## [param PhantomCamera2D] with the highest priority. -## Each instance can have different positional and rotational logic applied -## to them. - -#region Constants - -const _constants := preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd") - -#endregion - -#region Signals - -## Emitted when the [param PhantomCamera2D] becomes active. -signal became_active - -## Emitted when the [param PhantomCamera2D] becomes inactive. -signal became_inactive - -## Emitted when the follow_mode changes. -## Note: This is for internal use only -signal follow_mode_changed - -## Emitted when [member follow_target] changes. -signal follow_target_changed - -## Emitted when dead zones changes.[br] -## [b]Note:[/b] Only applicable in [param Framed] [enum FollowMode]. -signal dead_zone_changed - -## Emitted when a target touches the edge of the dead zone in [param Framed] [enum FollowMode]. -signal dead_zone_reached(side: Vector2) - -## Emitted when the [param Camera2D] starts to tween to another [param PhantomCamera2D]. -signal tween_started - -## Emitted when the [param Camera2D] is to tweening towards another [param PhantomCamera2D]. -signal is_tweening - -## Emitted when the tween is interrupted due to another [param PhantomCamera2D] -## becoming active. The argument is the [param PhantomCamera2D] that interrupted -## the tween. -signal tween_interrupted(pcam_2d: PhantomCamera2D) - -## Emitted when the [param Camera2D] completes its tween to the -## [param PhantomCamera2D]. -signal tween_completed - -## Emitted when Noise should be applied to the Camera2D. -signal noise_emitted(noise_output: Transform2D) - -signal physics_target_changed - -#endregion - -#region Enums - -## Determines the positional logic for a given [param PhantomCamera2D] -## [br][br] -## The different modes have different functionalities and purposes, so choosing -## the correct one depends on what each [param PhantomCamera2D] is meant to do. -enum FollowMode { - NONE = 0, ## Default - No follow logic is applied. - GLUED = 1, ## Sticks to its target. - SIMPLE = 2, ## Follows its target with an optional offset. - GROUP = 3, ## Follows multiple targets with option to dynamically reframe itself. - PATH = 4, ## Follows a target while being positionally confined to a [Path2D] node. - FRAMED = 5, ## Applies a dead zone on the frame and only follows its target when it tries to leave it. -} - -## Determines how often an inactive [param PhantomCamera2D] should update -## its positional and rotational values. This is meant to reduce the amount -## of calculations inactive [param PhantomCamera2D] are doing when idling to -## improve performance. -enum InactiveUpdateMode { - ALWAYS, ## Always updates the [param PhantomCamera2D], even when it's inactive. - NEVER, ## Never updates the [param PhantomCamera2D] when it's inactive. Reduces the amount of computational resources when inactive. -# EXPONENTIALLY, -} - -enum FollowLockAxis { - NONE = 0, - X = 1, - Y = 2, - XY = 3, -} - -#endregion - -#region Exported Properties - -## To quickly preview a [param PhantomCamera2D] without adjusting its -## [member priority], this property allows the selected PCam to ignore the -## Priority system altogether and forcefully become the active one. It's -## partly designed to work within the Viewfinder, and will be disabled when -## running a build export of the game. -@export var priority_override: bool = false: - set(value): - priority_override = value - if Engine.is_editor_hint(): - if value: - if not Engine.has_singleton(_constants.PCAM_MANAGER_NODE_NAME): return - Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME).pcam_priority_override.emit(self, true) - else: - if not Engine.has_singleton(_constants.PCAM_MANAGER_NODE_NAME): return - Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME).pcam_priority_override.emit(self, false) - get: - return priority_override - - -## It defines which [param PhantomCamera2D] a scene's [param Camera2D] should -## be corresponding with and be attached to. This is decided by the PCam with -## the highest [param Priority]. -## [br][br] -## Changing [param Priority] will send an event to the scene's -## [PhantomCameraHost], which will then determine whether if the -## [param Priority] value is greater than or equal to the currently -## highest [param PhantomCamera2D]'s in the scene. The [param PhantomCamera2D] -## with the highest value will then reattach the [param Camera2D] accordingly. -@export var priority: int = 0: - set = set_priority, - get = get_priority - - -## Determines the positional logic for a given [param PhantomCamera2D]. -## The different modes have different functionalities and purposes, so -## choosing the correct one depends on what each [param PhantomCamera2D] -## is meant to do. -@export var follow_mode: FollowMode = FollowMode.NONE: - set(value): - follow_mode = value - - if follow_mode == FollowMode.NONE: - _should_follow = false - top_level = false - _is_parents_physics() - notify_property_list_changed() - return - - match follow_mode: - FollowMode.PATH: - if is_instance_valid(follow_path): - _should_follow_checker() - else: - _should_follow = false - FollowMode.GROUP: - _follow_targets_size_check() - _: - _should_follow_checker() - - if follow_mode == FollowMode.FRAMED: - if _follow_framed_initial_set and follow_target: - _follow_framed_initial_set = false - dead_zone_changed.connect(_on_dead_zone_changed) - else: - if dead_zone_changed.is_connected(_on_dead_zone_changed): - dead_zone_changed.disconnect(_on_dead_zone_changed) - - top_level = true - follow_mode_changed.emit() - notify_property_list_changed() - get: - return follow_mode - -## Determines which target should be followed. -## The [param Camera2D] will follow the position of the Follow Target -## based on the [member follow_mode] type and its parameters. -@export var follow_target: Node2D = null: - set = set_follow_target, - get = get_follow_target - -### Defines the targets that the [param PhantomCamera2D] should be following. -@export var follow_targets: Array[Node2D] = []: - set = set_follow_targets, - get = get_follow_targets - -## Determines the [Path2D] the [param PhantomCamera2D] -## should be bound to. -## The [param PhantomCamera2D] will follow the position of the -## [member follow_target] while sticking to the closest point on this path. -@export var follow_path: Path2D = null: - set = set_follow_path, - get = get_follow_path - - -## Applies a zoom level to the [param PhantomCamera2D], which effectively -## overrides the [param zoom] property of the [param Camera2D] node. -@export_custom(PROPERTY_HINT_LINK, "") var zoom: Vector2 = Vector2.ONE: - set = set_zoom, - get = get_zoom - - -## If enabled, will snap the [param Camera2D] to whole pixels as it moves. -## [br][br] -## This should be particularly useful in pixel art projects, -## where assets should always be aligned to the monitor's pixels to avoid -## unintended stretching. -@export var snap_to_pixel: bool = false: - set = set_snap_to_pixel, - get = get_snap_to_pixel - - -## Enables a preview of what the [PhantomCamera2D] will see in the -## scene. It works identically to how a [param Camera2D] shows which area -## will be visible during runtime. Likewise, this too will be affected by the -## [member zoom] property and the [param viewport_width] and -## [param Viewport Height] defined in the [param Project Settings]. -@export var frame_preview: bool = true: - set(value): - frame_preview = value - queue_redraw() - get: - return frame_preview - - -## Defines how the [param PhantomCamera2D] transition between one another. -## Changing the tween values for a given [param PhantomCamera2D] -## determines how transitioning to that instance will look like. -## This is a resource type that can be either used for one -## [param PhantomCamera] or reused across multiple - both 2D and 3D. -## By default, all [param PhantomCameras] will use a [param linear] -## transition, [param easeInOut] ease with a [param 1s] duration. -@export var tween_resource: PhantomCameraTween = PhantomCameraTween.new(): - set = set_tween_resource, - get = get_tween_resource - -## If enabled, the moment a [param PhantomCamera2D] is instantiated into -## a scene, and has the highest priority, it will perform its tween transition. -## This is most obvious if a [param PhantomCamera2D] has a long duration and -## is attached to a playable character that can be moved the moment a scene -## is loaded. Disabling the [param tween_on_load] property will -## disable this behaviour and skip the tweening entirely when instantiated. -@export var tween_on_load: bool = true: - set = set_tween_on_load, - get = get_tween_on_load - - -## Determines how often an inactive [param PhantomCamera2D] should update -## its positional and rotational values. This is meant to reduce the amount -## of calculations inactive [param PhantomCamera2Ds] are doing when idling -## to improve performance. -@export var inactive_update_mode: InactiveUpdateMode = InactiveUpdateMode.ALWAYS - - -## Determines which layers this [param PhantomCamera2D] should be able to communicate with [PhantomCameraHost] nodes.[br] -## A corresponding layer needs to be set on the [PhantomCameraHost] node. -@export_flags_2d_render var host_layers: int = 1: - set = set_host_layers, - get = get_host_layers - - -@export_group("Follow Parameters") -## Offsets the [member follow_target] position. -@export var follow_offset: Vector2 = Vector2.ZERO: - set = set_follow_offset, - get = get_follow_offset - -## Applies a damping effect on the [param Camera2D]'s movement. -## Leading to heavier / slower camera movement as the targeted node moves around. -## This is useful to avoid sharp and rapid camera movement. -@export var follow_damping: bool = false: - set = set_follow_damping, - get = get_follow_damping - -## Defines the damping amount. The ideal range should be somewhere between 0-1.[br][br] -## The damping amount can be specified in the individual axis.[br][br] -## [b]Lower value[/b] = faster / sharper camera movement.[br] -## [b]Higher value[/b] = slower / heavier camera movement. -@export_custom(PROPERTY_HINT_LINK, "") -var follow_damping_value: Vector2 = Vector2(0.1, 0.1): - set = set_follow_damping_value, - get = get_follow_damping_value - -## Prevents the [param PhantomCamera2D] from moving in a designated axis. -## This can be enabled or disabled at runtime or from the editor directly. -@export var follow_axis_lock: FollowLockAxis = FollowLockAxis.NONE: - set = set_lock_axis, - get = get_lock_axis -var _follow_axis_is_locked: bool = false -var _follow_axis_lock_value: Vector2 = Vector2.ZERO - -## Makes the [param PhantomCamera2D] copy the rotation of its [member follow_target][br] -## This behavior is only available when [member follow_mode] is set and only has one [member follow_target].[br][br] -## [b]Important:[/b] Be sure to disable [member Camera2D.ignore_rotation] on the [Camera2D] node to enable this feature. -@export var rotate_with_target: bool = false: - set = set_rotate_with_target, - get = get_rotate_with_target -var _should_rotate_with_target: bool = false - -## Offsets the rotation when [member rotate_with_target] is enabled. -@export_range(-360, 360, 0.001, "radians_as_degrees") var rotation_offset: float = 0: - set = set_rotation_offset, - get = get_rotation_offset - -## Enables rotational damping when [member rotate_with_target] is enabled. -@export var rotation_damping: bool = false: - set = set_rotation_damping, - get = get_rotation_damping - -## Defines the damping amount for the [member rotate_with_target]. -@export_range(0, 1) var rotation_damping_value: float = 0.1: - set = set_rotation_damping_value, - get = get_rotation_damping_value - - -@export_subgroup("Follow Group") -## Enables the [param PhantomCamera2D] to dynamically zoom in and out based on -## the targets' distances between each other. -## Once enabled, the [param Camera2D] will stay as zoomed in as possible, -## limited by the [member auto_zoom_max] and start zooming out as the targets -## move further apart, limited by the [member auto_zoom_min]. -## Note: Enabling this property hides and disables the [member zoom] property -## as this effectively overrides that value. -@export var auto_zoom: bool = false: - set = set_auto_zoom, - get = get_auto_zoom - -## Sets the param minimum zoom amount, in other words how far away the -## [param Camera2D] can be from scene.[br][br] -## This only works when [member auto_zoom] is enabled. -@export var auto_zoom_min: float = 1: - set = set_auto_zoom_min, - get = get_auto_zoom_min - -## Sets the maximum zoom amount, in other words how close the [param Camera2D] -## can move towards the scene.[br][br] -## This only works when [member auto_zoom] is enabled. -@export var auto_zoom_max: float = 5: - set = set_auto_zoom_max, - get = get_auto_zoom_max - -## Determines how close to the edges the targets are allowed to be. -## This is useful to avoid targets being cut off at the edges of the screen. -## [br][br] -## The Vector4 parameter order goes: [param Left] - [param Top] - [param Right] -## - [param Bottom]. -@export var auto_zoom_margin: Vector4 = Vector4.ZERO: - set = set_auto_zoom_margin, - get = get_auto_zoom_margin - - -@export_subgroup("Dead Zones") -## Defines the horizontal dead zone area. While the target is within it, the -## [param PhantomCamera2D] will not move in the horizontal axis. -## If the targeted node leaves the horizontal bounds, the -## [param PhantomCamera2D] will follow the target horizontally to keep -## it within bounds. -@export_range(0, 1) var dead_zone_width: float = 0: - set(value): - dead_zone_width = value - dead_zone_changed.emit() - get: - return dead_zone_width - -## Defines the vertical dead zone area. While the target is within it, the -## [param PhantomCamera2D] will not move in the vertical axis. -## If the targeted node leaves the vertical bounds, the -## [param PhantomCamera2D] will follow the target horizontally to keep -## it within bounds. -@export_range(0, 1) var dead_zone_height: float = 0: - set(value): - dead_zone_height = value - dead_zone_changed.emit() - get: - return dead_zone_height - -## Enables the [param dead zones] to be visible when running the game from the editor. -## [br] -## [param dead zones] will never be visible in build exports. -@export var show_viewfinder_in_play: bool = false - - -@export_group("Limit") - -## Shows the [param Camera2D]'s built-in limit border.[br] -## The [param PhantomCamera2D] and [param Camera2D] can move around anywhere within it. -@export var draw_limits: bool = false: - set(value): - _draw_limits = value - if Engine.is_editor_hint(): - _draw_camera_2d_limit() - get: - return _draw_limits - -## Defines the left side of the [param Camera2D] limit. -## The camera will not be able to move past this point. -@export var limit_left: int = -10000000: - set = set_limit_left, - get = get_limit_left -## Defines the top side of the [param Camera2D] limit. -## The camera will not be able to move past this point. -@export var limit_top: int = -10000000: - set = set_limit_top, - get = get_limit_top -## Defines the right side of the [param Camera2D] limit. -## The camera will not be able to move past this point. -@export var limit_right: int = 10000000: - set = set_limit_right, - get = get_limit_right -## Defines the bottom side of the [param Camera2D] limit. -## The camera will not be able to move past this point. -@export var limit_bottom: int = 10000000: - set = set_limit_bottom, - get = get_limit_bottom - -## Allows for setting either a [TileMapLayer] or [CollisionShape2D] node to -## automatically apply a limit size instead of manually adjusting the Left, -## Top, Right and Left properties.[br][br] -## [b]TileMapLayer[/b][br] -## The Limit will update after the [TileSet] of the [TileMapLayer] has changed.[br] -## [b]Note:[/b] The limit size will only update after closing the TileMap editor -## bottom panel. -## [br][br] -## [b]CollisionShape2D[/b][br] -## The limit will update in realtime as the Shape2D changes its size. -## Note: For performance reasons, resizing the [Shape2D] during runtime will not change the Limits sides. -@export_node_path("TileMapLayer", "CollisionShape2D") var limit_target: NodePath = NodePath(""): - set = set_limit_target, - get = get_limit_target - -## Applies an offset to the [TileMapLayer] Limit or [Shape2D] Limit. -## The values goes from [param Left], [param Top], [param Right] -## and [param Bottom]. -@export var limit_margin: Vector4i = Vector4.ZERO: - set = set_limit_margin, - get = get_limit_margin -#@export var limit_smoothed: bool = false: # TODO - Needs proper support - #set = set_limit_smoothing, - #get = get_limit_smoothing - -@export_group("Noise") -## Applies a noise, or shake, to a [Camera2D].[br] -## Once set, the noise will run continuously after the tween to the [PhantomCamera2D] is complete. -@export var noise: PhantomCameraNoise2D = null: - set = set_noise, - get = get_noise - -## If true, will trigger the noise while in the editor.[br] -## Useful in cases where you want to temporarily disable the noise in the editor without removing -## the resource.[br][br] -## [b]Note:[/b] This property has no effect on runtime behaviour. -@export var _preview_noise: bool = true: - set(value): - _preview_noise = value - if not value: - _transform_noise = Transform2D() - -## Enable a corresponding layer for a [member PhantomCameraNoiseEmitter2D.noise_emitter_layer] -## to make this [PhantomCamera2D] be affect by it. -@export_flags_2d_render var noise_emitter_layer: int = 0: - set = set_noise_emitter_layer, - get = get_noise_emitter_layer - -#region Private Variables - -var _is_active: bool = false - -var _should_follow: bool = false -var _follow_framed_offset: Vector2 = Vector2.ZERO -var _follow_target_physics_based: bool = false -var _physics_interpolation_enabled: bool = false # NOTE - Enable for Godot 4.3 and when PhysicsInterpolationMode bug is resolved - -var _has_multiple_follow_targets: bool = false -var _follow_targets_single_target_index: int = 0 -var _follow_targets: Array[Node2D] = [] - -var _follow_velocity_ref: Vector2 = Vector2.ZERO # Stores and applies the velocity of the follow movement -var _rotation_velocity_ref: float = 0 # Stores and applies the velocity of the rotation movement - -var _has_follow_path: bool = false - -var _tween_skip: bool = false - -## Defines the position of the [member follow_target] within the viewport.[br] -## This is only used for when [member follow_mode] is set to [param Framed]. -var _follow_framed_initial_set: bool = false - -static var _draw_limits: bool = false - -var _limit_sides: Vector4i = _limit_sides_default -var _limit_sides_default: Vector4i = Vector4i(-10000000, -10000000, 10000000, 10000000) - -var _limit_node: Node2D = null -var _tile_size_perspective_scaler: Vector2 = Vector2.ONE - -var _limit_inactive_pcam: bool = false - -var _follow_target_position: Vector2 = Vector2.ZERO - -var _transform_output: Transform2D = Transform2D() -var _transform_noise: Transform2D = Transform2D() - -var _has_noise_resource: bool = false - -# NOTE - Temp solution until Godot has better plugin autoload recognition out-of-the-box. -var _phantom_camera_manager: Node = null - -#endregion - -#region Public Variables - -var tween_duration: float: - set = set_tween_duration, - get = get_tween_duration -var tween_transition: PhantomCameraTween.TransitionType: - set = set_tween_transition, - get = get_tween_transition -var tween_ease: PhantomCameraTween.EaseType: - set = set_tween_ease, - get = get_tween_ease - -var viewport_position: Vector2 - -#endregion - -#region Private Functions - -func _validate_property(property: Dictionary) -> void: - ################ - ## Follow Target - ################ - if property.name == "follow_target": - if follow_mode == FollowMode.NONE or \ - follow_mode == FollowMode.GROUP: - property.usage = PROPERTY_USAGE_NO_EDITOR - - elif property.name == "follow_path" and \ - follow_mode != FollowMode.PATH: - property.usage = PROPERTY_USAGE_NO_EDITOR - - - #################### - ## Follow Parameters - #################### - if follow_mode == FollowMode.NONE: - match property.name: - "follow_offset", \ - "follow_damping", \ - "follow_damping_value", \ - "follow_axis_lock", \ - "rotate_with_target": - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == "follow_offset": - if follow_mode == FollowMode.GLUED: - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == "follow_damping_value" and not follow_damping: - property.usage = PROPERTY_USAGE_NO_EDITOR - - ############### - ## Follow Group - ############### - if follow_mode != FollowMode.GROUP: - match property.name: - "follow_targets", \ - "auto_zoom": - property.usage = PROPERTY_USAGE_NO_EDITOR - - if not auto_zoom or follow_mode != FollowMode.GROUP: - match property.name: - "auto_zoom_min", \ - "auto_zoom_max", \ - "auto_zoom_margin": - property.usage = PROPERTY_USAGE_NO_EDITOR - - ################ - ## Follow Framed - ################ - if not follow_mode == FollowMode.FRAMED: - match property.name: - "dead_zone_width", \ - "dead_zone_height", \ - "show_viewfinder_in_play": - property.usage = PROPERTY_USAGE_NO_EDITOR - - - ##################### - ## Rotate With Target - ##################### - if property.name == "rotate_with_target" and follow_mode == FollowMode.GROUP: - property.usage = PROPERTY_USAGE_NO_EDITOR - - - if not rotate_with_target or follow_mode == FollowMode.GROUP: - match property.name: - "rotation_damping", \ - "rotation_offset", \ - "rotation_damping_value": - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == "rotation_damping_value": - if not rotation_damping: - property.usage = PROPERTY_USAGE_NO_EDITOR - - - ####### - ## Zoom - ####### - if property.name == "zoom" and follow_mode == FollowMode.GROUP and auto_zoom: - property.usage = PROPERTY_USAGE_NO_EDITOR - - ######## - ## Limit - ######## - if is_instance_valid(_limit_node): - match property.name: - "limit_left", \ - "limit_top", \ - "limit_right", \ - "limit_bottom": - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == "limit_margin" and not _limit_node: - property.usage = PROPERTY_USAGE_NO_EDITOR - - -func _enter_tree() -> void: - _phantom_camera_manager = Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME) - _tween_skip = !tween_on_load - - _phantom_camera_manager.pcam_added(self) - - priority_override = false - - match follow_mode: - FollowMode.NONE: - _is_parents_physics() - FollowMode.PATH: - if is_instance_valid(follow_path): - _should_follow_checker() - else: - _should_follow = false - FollowMode.GROUP: - _follow_targets_size_check() - _: - _should_follow_checker() - - if not visibility_changed.is_connected(_check_visibility): - visibility_changed.connect(_check_visibility) - - update_limit_all_sides() - - -func _exit_tree() -> void: - if not follow_mode == FollowMode.GROUP: - follow_targets = [] - - if not is_instance_valid(_phantom_camera_manager): return - _phantom_camera_manager.pcam_removed(self) - - -func _ready() -> void: - _transform_output = global_transform - - _phantom_camera_manager.noise_2d_emitted.connect(_noise_emitted) - - if not Engine.is_editor_hint(): - _preview_noise = true - - -func _process(delta: float) -> void: - if _follow_target_physics_based or _is_active: return - process_logic(delta) - - -func _physics_process(delta: float) -> void: - if not _follow_target_physics_based or _is_active: return - process_logic(delta) - - -func process_logic(delta: float) -> void: - if _is_active: - if _has_noise_resource and _preview_noise: - _transform_noise = noise.get_noise_transform(delta) - else: - match inactive_update_mode: - InactiveUpdateMode.NEVER: return - InactiveUpdateMode.ALWAYS: - # Only triggers if limit isn't default - if _limit_inactive_pcam: - global_position = _set_limit_clamp_position(global_position) - # InactiveUpdateMode.EXPONENTIALLY: - # TODO - Trigger positional updates less frequently as more PCams gets added - - _limit_checker() - - if _should_follow: - _follow(delta) - else: - _transform_output = global_transform - - if _follow_axis_is_locked: - match follow_axis_lock: - FollowLockAxis.X: - _transform_output.origin.x = _follow_axis_lock_value.x - FollowLockAxis.Y: - _transform_output.origin.y = _follow_axis_lock_value.y - FollowLockAxis.XY: - _transform_output.origin.x = _follow_axis_lock_value.x - _transform_output.origin.y = _follow_axis_lock_value.y - - -func _limit_checker() -> void: - ## TODO - Needs to see if this can be triggerd only from CollisionShape2D Transform changes - if not Engine.is_editor_hint(): return - if draw_limits: - update_limit_all_sides() - - -func _follow(delta: float) -> void: - _set_follow_position() - _interpolate_position(_follow_target_position, delta) - - -func _set_follow_position() -> void: - match follow_mode: - FollowMode.GLUED: - _follow_target_position = follow_target.global_position - - FollowMode.SIMPLE: - _follow_target_position = _get_target_position_offset() - - FollowMode.GROUP: - if _has_multiple_follow_targets: - var rect: Rect2 = Rect2(_follow_targets[0].global_position, Vector2.ZERO) - for target in _follow_targets: - rect = rect.expand(target.global_position) - if auto_zoom: - rect = rect.grow_individual( - auto_zoom_margin.x, - auto_zoom_margin.y, - auto_zoom_margin.z, - auto_zoom_margin.w - ) - - if rect.size.x > rect.size.y * _phantom_camera_manager.screen_size.aspect(): - zoom = clamp(_phantom_camera_manager.screen_size.x / rect.size.x, auto_zoom_min, auto_zoom_max) * Vector2.ONE - else: - zoom = clamp(_phantom_camera_manager.screen_size.y / rect.size.y, auto_zoom_min, auto_zoom_max) * Vector2.ONE - _follow_target_position = rect.get_center() + follow_offset - else: - _follow_target_position = follow_targets[_follow_targets_single_target_index].global_position + follow_offset - - FollowMode.PATH: - var path_position: Vector2 = follow_path.global_position - - _follow_target_position = \ - follow_path.curve.get_closest_point( - _get_target_position_offset() - path_position - ) + path_position - - FollowMode.FRAMED: - if not Engine.is_editor_hint(): - if not _is_active: - _follow_target_position = _get_target_position_offset() - else: - viewport_position = (get_follow_target().get_global_transform_with_canvas().get_origin() + follow_offset) / get_viewport_rect().size - var framed_side_offset: Vector2 = _get_framed_side_offset() - - if framed_side_offset != Vector2.ZERO: - var glo_pos: Vector2 - var target_position: Vector2 = _get_target_position_offset() + _follow_framed_offset - - if dead_zone_width == 0 || dead_zone_height == 0: - if dead_zone_width == 0 && dead_zone_height != 0: - _follow_target_position = _get_target_position_offset() - elif dead_zone_width != 0 && dead_zone_height == 0: - glo_pos = _get_target_position_offset() - glo_pos.x += target_position.x - global_position.x - _follow_target_position = glo_pos - else: - _follow_target_position = _get_target_position_offset() - - # If a horizontal dead zone is reached - if framed_side_offset.x != 0 and framed_side_offset.y == 0: - _follow_target_position.y = _transform_output.origin.y - _follow_target_position.x = target_position.x - _follow_framed_offset.y = global_position.y - _get_target_position_offset().y - dead_zone_reached.emit(Vector2(framed_side_offset.x, 0)) - # If a vertical dead zone is reached - elif framed_side_offset.x == 0 and framed_side_offset.y != 0: - _follow_target_position.x = _transform_output.origin.x - _follow_target_position.y = target_position.y - _follow_framed_offset.x = global_position.x - _get_target_position_offset().x - dead_zone_reached.emit(Vector2(0, framed_side_offset.y)) - # If a deadzone corner is reached - else: - _follow_target_position = target_position - dead_zone_reached.emit(Vector2(framed_side_offset.x, framed_side_offset.y)) - else: - _follow_framed_offset = _transform_output.origin - _get_target_position_offset() - _follow_target_position = global_position - return - else: - _follow_target_position = _get_target_position_offset() - - -func _set_follow_velocity(index: int, value: float): - _follow_velocity_ref[index] = value - -func _set_rotation_velocity(index: int, value: float): - _rotation_velocity_ref = value - -func _interpolate_position(target_position: Vector2, delta: float) -> void: - var output_rotation: float = global_transform.get_rotation() - if rotate_with_target: - if rotation_damping and not Engine.is_editor_hint(): - output_rotation = _smooth_damp( - follow_target.get_rotation() + rotation_offset, - _transform_output.get_rotation(), - 0, - _rotation_velocity_ref, - _set_rotation_velocity, - rotation_damping_value, - delta - ) - else: - output_rotation = follow_target.get_rotation() + rotation_offset - - if _limit_inactive_pcam and not _tween_skip: - target_position = _set_limit_clamp_position(target_position) - - global_position = target_position - - if follow_damping and not Engine.is_editor_hint(): - var output_position: Vector2 - for i in 2: - output_position[i] = _smooth_damp( - global_position[i], - _transform_output.origin[i], - i, - _follow_velocity_ref[i], - _set_follow_velocity, - follow_damping_value[i], - delta - ) - _transform_output = Transform2D(output_rotation, output_position) - else: - _transform_output = Transform2D(output_rotation, target_position) - - -func _smooth_damp(target_axis: float, self_axis: float, index: int, current_velocity: float, set_velocity: Callable, damping_time: float, delta: float) -> float: - damping_time = maxf(0.0001, damping_time) - var omega: float = 2 / damping_time - var x: float = omega * delta - var exponential: float = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x) - var diff: float = self_axis - target_axis - var _target_axis: float = target_axis - - var max_change: float = INF * damping_time - diff = clampf(diff, -max_change, max_change) - target_axis = self_axis - diff - - var temp: float = (current_velocity + omega * diff) * delta - set_velocity.call(index, (current_velocity - omega * temp) * exponential) - var output: float = target_axis + (diff + temp) * exponential - - ## To prevent overshooting - if (_target_axis - self_axis > 0.0) == (output > _target_axis): - output = _target_axis - set_velocity.call(index, (output - _target_axis) / delta) - - return output - - -func _set_limit_clamp_position(value: Vector2) -> Vector2: - var camera_frame_rect_size: Vector2 = _camera_frame_rect().size - value.x = clampf(value.x, _limit_sides.x + camera_frame_rect_size.x / 2, _limit_sides.z - camera_frame_rect_size.x / 2) - value.y = clampf(value.y, _limit_sides.y + camera_frame_rect_size.y / 2, _limit_sides.w - camera_frame_rect_size.y / 2) - return value - - -func _draw() -> void: - if not Engine.is_editor_hint(): return - - if frame_preview and not _is_active: - draw_rect(_camera_frame_rect(), Color("3ab99a"), false, 2) - - -func _camera_frame_rect() -> Rect2: - var screen_size_zoom: Vector2 = Vector2(_phantom_camera_manager.screen_size.x / get_zoom().x, _phantom_camera_manager.screen_size.y / get_zoom().y) - - return Rect2(-screen_size_zoom / 2, screen_size_zoom) - - -func _on_tile_map_changed() -> void: - update_limit_all_sides() - - -func _get_target_position_offset() -> Vector2: - return follow_target.global_position + follow_offset - - -func _on_dead_zone_changed() -> void: - global_position = _get_target_position_offset() - - -func _get_framed_side_offset() -> Vector2: - var frame_out_bounds: Vector2 - - if viewport_position.x < 0.5 - dead_zone_width / 2: - # Is outside left edge - frame_out_bounds.x = -1 - - if viewport_position.y < 0.5 - dead_zone_height / 2: - # Is outside top edge - frame_out_bounds.y = 1 - - if viewport_position.x > 0.5 + dead_zone_width / 2: - # Is outside right edge - frame_out_bounds.x = 1 - - if viewport_position.y > 0.5001 + dead_zone_height / 2: # 0.501 to resolve an issue where the bottom vertical Dead Zone never becoming 0 when the Dead Zone Vertical parameter is set to 0 - # Is outside bottom edge - frame_out_bounds.y = -1 - - return frame_out_bounds - - -func _draw_camera_2d_limit() -> void: - if not is_instance_valid(_phantom_camera_manager): return - _phantom_camera_manager.draw_limit_2d.emit(draw_limits) - - -func _check_limit_is_not_default() -> void: - if _limit_sides == _limit_sides_default: - _limit_inactive_pcam = false - else: - _limit_inactive_pcam = true - - -func _check_visibility() -> void: - _phantom_camera_manager.pcam_visibility_changed.emit(self) - - -func _follow_target_tree_exiting(target: Node) -> void: - if target == follow_target: - _should_follow = false - if _follow_targets.has(target): - _follow_targets.erase(target) - - -func _should_follow_checker() -> void: - if follow_mode == FollowMode.NONE: - _should_follow = false - return - - if not follow_mode == FollowMode.GROUP: - if is_instance_valid(follow_target): - _should_follow = true - else: - _should_follow = false - - -func _follow_targets_size_check() -> void: - var targets_size: int = 0 - _follow_target_physics_based = false - _follow_targets = [] - for i in follow_targets.size(): - if follow_targets[i] == null: continue - if is_instance_valid(follow_targets[i]): - _follow_targets.append(follow_targets[i]) - targets_size += 1 - _follow_targets_single_target_index = i - _check_physics_body(follow_targets[i]) - if not follow_targets[i].tree_exiting.is_connected(_follow_target_tree_exiting): - follow_targets[i].tree_exiting.connect(_follow_target_tree_exiting.bind(follow_targets[i])) - - match targets_size: - 0: - _should_follow = false - _has_multiple_follow_targets = false - 1: - _should_follow = true - _has_multiple_follow_targets = false - _: - _should_follow = true - _has_multiple_follow_targets = true - - -func _noise_emitted(emitter_noise_output: Transform2D, emitter_layer: int) -> void: - if noise_emitter_layer & emitter_layer != 0: - noise_emitted.emit(emitter_noise_output) - - -func _set_layer(current_layers: int, layer_number: int, value: bool) -> int: - var mask: int = current_layers - - # From https://github.com/godotengine/godot/blob/51991e20143a39e9ef0107163eaf283ca0a761ea/scene/3d/camera_3d.cpp#L638 - if layer_number < 1 or layer_number > 20: - printerr("Render layer must be between 1 and 20.") - else: - if value: - mask |= 1 << (layer_number - 1) - else: - mask &= ~(1 << (layer_number - 1)) - - return mask - - -func _check_physics_body(target: Node2D) -> void: - if target is PhysicsBody2D: - var show_jitter_tips := ProjectSettings.get_setting("phantom_camera/tips/show_jitter_tips") - var physics_interpolation_enabled := ProjectSettings.get_setting("physics/common/physics_interpolation") - - ## NOTE - Feature Toggle - if Engine.get_version_info().major == 4 and \ - Engine.get_version_info().minor < 3: - if show_jitter_tips == null: # Default value is null when referencing custom Project Setting - print_rich("Following a [b]PhysicsBody2D[/b] node will likely result in jitter - on lower physics ticks in particular.") - print_rich("If possible, will recommend upgrading to Godot 4.3, as it has built-in support for 2D Physics Interpolation, which will mitigate this issue.") - print_rich("Otherwise, try following the guide on the [url=https://phantom-camera.dev/support/faq#i-m-seeing-jitter-what-can-i-do]documentation site[/url] for better results.") - print_rich("This tip can be disabled from within [code]Project Settings / Phantom Camera / Tips / Show Jitter Tips[/code]") - return - ## NOTE - Only supported in Godot 4.3 or above - elif not physics_interpolation_enabled and show_jitter_tips == null: # Default value is null when referencing custom Project Setting - printerr("Physics Interpolation is disabled in the Project Settings, recommend enabling it to smooth out physics-based camera movement") - print_rich("This tip can be disabled from within [code]Project Settings / Phantom Camera / Tips / Show Jitter Tips[/code]") - _follow_target_physics_based = true - else: - _is_parents_physics(target) - physics_target_changed.emit() - - -func _is_parents_physics(target: Node = self) -> void: - var current_node: Node = target - while current_node: - current_node = current_node.get_parent() - if not current_node is PhysicsBody2D: continue - _follow_target_physics_based = true - -#endregion - - -#region Public Functions - -## Updates the limit sides based what has been set to define it -## This should be automatic, but can be called manully if need be. -func update_limit_all_sides() -> void: - var limit_rect: Rect2 - - if not is_instance_valid(_limit_node): - _limit_sides.x = limit_left - _limit_sides.y = limit_top - _limit_sides.z = limit_right - _limit_sides.w = limit_bottom - elif _limit_node is TileMapLayer: - var tile_map: TileMapLayer = _limit_node - - if not tile_map.tile_set: return # TODO: This should be removed once https://github.com/godotengine/godot/issues/96898 is resolved - - var tile_map_size: Vector2 = Vector2(tile_map.get_used_rect().size) * Vector2(tile_map.tile_set.tile_size) * tile_map.get_scale() - var tile_map_position: Vector2 = tile_map.global_position + Vector2(tile_map.get_used_rect().position) * Vector2(tile_map.tile_set.tile_size) * tile_map.get_scale() - - ## Calculates the Rect2 based on the Tile Map position and size + margin - limit_rect = Rect2( - tile_map_position + Vector2(limit_margin.x, limit_margin.y), - tile_map_size - Vector2(limit_margin.x, limit_margin.y) - Vector2(limit_margin.z, limit_margin.w) - ) - - # Left - _limit_sides.x = roundi(limit_rect.position.x) - # Top - _limit_sides.y = roundi(limit_rect.position.y) - # Right - _limit_sides.z = roundi(limit_rect.position.x + limit_rect.size.x) - # Bottom - _limit_sides.w = roundi(limit_rect.position.y + limit_rect.size.y) - elif _limit_node is CollisionShape2D: - var collision_shape_2d: CollisionShape2D = _limit_node as CollisionShape2D - - if not collision_shape_2d.get_shape(): return - - var shape_2d: Shape2D = collision_shape_2d.get_shape() - var shape_2d_size: Vector2 = shape_2d.get_rect().size - var shape_2d_position: Vector2 = collision_shape_2d.global_position + Vector2(shape_2d.get_rect().position) - - ## Calculates the Rect2 based on the Tile Map position and size - limit_rect = Rect2(shape_2d_position, shape_2d_size) - - ## Calculates the Rect2 based on the Tile Map position and size + margin - limit_rect = Rect2( - limit_rect.position + Vector2(limit_margin.x, limit_margin.y), - limit_rect.size - Vector2(limit_margin.x, limit_margin.y) - Vector2(limit_margin.z, limit_margin.w) - ) - - # Left - _limit_sides.x = roundi(limit_rect.position.x) - # Top - _limit_sides.y = roundi(limit_rect.position.y) - # Right - _limit_sides.z = roundi(limit_rect.position.x + limit_rect.size.x) - # Bottom - _limit_sides.w = roundi(limit_rect.position.y + limit_rect.size.y) - - _check_limit_is_not_default() - if not _is_active: return - if not is_instance_valid(_phantom_camera_manager): return - _phantom_camera_manager.limit_2d_changed.emit(SIDE_LEFT, _limit_sides.x) - _phantom_camera_manager.limit_2d_changed.emit(SIDE_TOP, _limit_sides.y) - _phantom_camera_manager.limit_2d_changed.emit(SIDE_RIGHT, _limit_sides.z) - _phantom_camera_manager.limit_2d_changed.emit(SIDE_BOTTOM, _limit_sides.w) - _phantom_camera_manager.draw_limit_2d.emit(draw_limits) - - -func reset_limit() -> void: - if not is_instance_valid(_phantom_camera_manager): return - _phantom_camera_manager.limit_2d_changed.emit(SIDE_LEFT, _limit_sides_default.x) - _phantom_camera_manager.limit_2d_changed.emit(SIDE_TOP, _limit_sides_default.y) - _phantom_camera_manager.limit_2d_changed.emit(SIDE_RIGHT, _limit_sides_default.z) - _phantom_camera_manager.limit_2d_changed.emit(SIDE_BOTTOM, _limit_sides_default.w) - _phantom_camera_manager.draw_limit_2d.emit(draw_limits) - - -## Assigns the value of the [param has_tweened] property. -## [b][color=yellow]Important:[/color][/b] This value can only be changed -## from the [PhantomCameraHost] script. -func set_tween_skip(caller: Node, value: bool) -> void: - if is_instance_of(caller, PhantomCameraHost): - _tween_skip = value - else: - printerr("Can only be called PhantomCameraHost class") -## Returns the current [param has_tweened] value. -func get_tween_skip() -> bool: - return _tween_skip - -## Returns the [Transform3D] value based on the [member follow_mode] / [member look_at_mode] target value. -func get_transform_output() -> Transform2D: - return _transform_output - - -## Returns the noise [Transform3D] value. -func get_noise_transform() -> Transform2D: - return _transform_noise - - -## Emits a noise based on a custom [Transform2D] value.[br] -## Use this function if you wish to make use of external noise patterns from, for example, other addons. -func emit_noise(value: Transform2D) -> void: - noise_emitted.emit(value) - - -## Teleports the [param PhantomCamera2D] and [Camera2D] to their designated position, -## bypassing the damping process. -func teleport_position() -> void: - _follow_velocity_ref = Vector2.ZERO - _set_follow_position() - _transform_output.origin = _follow_target_position - _phantom_camera_manager.pcam_teleport.emit(self) - - -# TODO: Enum link does link to anywhere is being tracked in: https://github.com/godotengine/godot/issues/106828 -## Returns true if this [param PhantomCamera2D]'s [member follow_mode] is not set to [enum FollowMode] -## and has a valid [member follow_target]. -func is_following() -> bool: - return _should_follow - -#endregion - - -#region Setter & Getter Functions - -## Assigns new [member zoom] value. -func set_zoom(value: Vector2) -> void: - zoom = value - queue_redraw() - -## Gets current [member zoom] value. -func get_zoom() -> Vector2: - return zoom - - -## Assigns new [member priority] value. -func set_priority(value: int) -> void: - priority = maxi(0, value) - if not is_node_ready(): return - if not Engine.has_singleton(_constants.PCAM_MANAGER_NODE_NAME): return - Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME).pcam_priority_changed.emit(self) - -## Gets current [member priority] value. -func get_priority() -> int: - return priority - - -## Assigns a new PhantomCameraTween resource to the PhantomCamera2D -func set_tween_resource(value: PhantomCameraTween) -> void: - tween_resource = value - -## Gets the PhantomCameraTween resource assigned to the PhantomCamera2D -## Returns null if there's nothing assigned to it. -func get_tween_resource() -> PhantomCameraTween: - return tween_resource - - -## Assigns a new [param Tween Duration] to the [member tween_resource] value.[br] -## The duration value is in seconds. -func set_tween_duration(value: float) -> void: - tween_resource.duration = value - -## Gets the current [param Tween Duration] value inside the -## [member tween_resource].[br] -## The duration value is in seconds. -func get_tween_duration() -> float: - return tween_resource.duration - - -## Assigns a new [param Tween Transition] value inside the -## [member tween_resource]. -func set_tween_transition(value: int) -> void: - tween_resource.transition = value - -## Gets the current [param Tween Transition] value inside the -## [member tween_resource]. -func get_tween_transition() -> int: - return tween_resource.transition - - -## Assigns a new [param Tween Ease] value inside the [member tween_resource]. -func set_tween_ease(value: int) -> void: - tween_resource.ease = value - -## Gets the current [param Tween Ease] value inside the [member tween_resource]. -func get_tween_ease() -> int: - return tween_resource.ease - - -## Sets the [param PhantomCamera2D] active state.[br] -## [b][color=yellow]Important:[/color][/b] This value can only be changed -## from the [PhantomCameraHost] script. -func set_is_active(node, value) -> void: - if node is PhantomCameraHost: - _is_active = value - queue_redraw() - else: - printerr("PCams can only be set from the PhantomCameraHost") - -## Gets current active state of the [param PhantomCamera2D]. -## If it returns true, it means the [param PhantomCamera2D] is what the -## [param Camera2D] is currently following. -func is_active() -> bool: - return _is_active - - -## Enables or disables the [member tween_on_load]. -func set_tween_on_load(value: bool) -> void: - tween_on_load = value - -## Gets the current [member tween_on_load] value. -func get_tween_on_load() -> bool: - return tween_on_load - -## Sets the [member host_layers] value. -func set_host_layers(value: int) -> void: - host_layers = value - if is_instance_valid(_phantom_camera_manager): - _phantom_camera_manager.pcam_host_layer_changed.emit(self) - -## Enables or disables a given layer of [member host_layers]. -func set_host_layers_value(layer: int, value: bool) -> void: - host_layers = _set_layer(host_layers, layer, value) - -## Gets the current [member host_layers]. -func get_host_layers() -> int: - return host_layers - - -## Gets the current follow mode as an enum int based on [enum FollowMode].[br] -## [b]Note:[/b] Setting [enum FollowMode] purposely not added. -## A separate PCam should be used instead. -func get_follow_mode() -> int: - return follow_mode - - -## Assigns a new [Node2D] as the [member follow_target]. -func set_follow_target(value: Node2D) -> void: - if follow_mode == FollowMode.NONE or follow_mode == FollowMode.GROUP: return - if follow_target == value: return - follow_target = value - _follow_target_physics_based = false - if is_instance_valid(value): - if follow_mode == FollowMode.PATH: - if is_instance_valid(follow_path): - _should_follow = true - else: - _should_follow = false - else: - _should_follow = true - _check_physics_body(value) - if not follow_target.tree_exiting.is_connected(_follow_target_tree_exiting): - follow_target.tree_exiting.connect(_follow_target_tree_exiting.bind(follow_target)) - else: - _should_follow = false - follow_target_changed.emit() - notify_property_list_changed() - -## Erases the current [member follow_target]. -func erase_follow_target() -> void: - if follow_target == null: return - _should_follow = false - follow_target = null - _follow_target_physics_based = false - follow_target_changed.emit() - -## Gets the current [member follow_target]. -func get_follow_target() -> Node2D: - return follow_target - - -## Assigns a new [Path2D] to the [member follow_path]. -func set_follow_path(value: Path2D) -> void: - follow_path = value - if is_instance_valid(follow_path): - _should_follow_checker() - else: - _should_follow = false - -## Erases the current [Path2D] from the [member follow_path] property. -func erase_follow_path() -> void: - follow_path = null - -## Gets the current [Path2D] from the [member follow_path]. -func get_follow_path() -> Path2D: - return follow_path - - -## Assigns a new [param follow_targets] array value. -func set_follow_targets(value: Array[Node2D]) -> void: - if follow_mode != FollowMode.GROUP: return - if follow_targets == value: return - follow_targets = value - _follow_targets_size_check() - -## Appends a single [Node2D] to [member follow_targets]. -func append_follow_targets(value: Node2D) -> void: - if not is_instance_valid(value): - printerr(value, " is not a valid Node2D instance") - return - - if not follow_targets.has(value): - follow_targets.append(value) - _follow_targets_size_check() - else: - printerr(value, " is already part of Follow Group") - -## Adds an Array of type [Node2D] to [member follow_targets]. -func append_follow_targets_array(value: Array[Node2D]) -> void: - for target in value: - if not is_instance_valid(target): continue - if not follow_targets.has(target): - follow_targets.append(target) - _follow_targets_size_check() - else: - printerr(value, " is already part of Follow Group") - -## Removes a [Node2D] from [member follow_targets] array. -func erase_follow_targets(value: Node2D) -> void: - follow_targets.erase(value) - _follow_targets_size_check() - -## Gets all [Node2D] from [member follow_targets] array. -func get_follow_targets() -> Array[Node2D]: - return follow_targets - - -## Assigns a new Vector2 for the Follow Target Offset property. -func set_follow_offset(value: Vector2) -> void: - var temp_offset: Vector2 = follow_offset - - follow_offset = value - - if follow_axis_lock != FollowLockAxis.NONE: - temp_offset = temp_offset - value - match value: - FollowLockAxis.X: - _follow_axis_lock_value.x = _transform_output.origin.x + temp_offset.x - FollowLockAxis.Y: - _follow_axis_lock_value.y = _transform_output.origin.y + temp_offset.y - FollowLockAxis.XY: - _follow_axis_lock_value.x = _transform_output.origin.x + temp_offset.x - _follow_axis_lock_value.y = _transform_output.origin.y + temp_offset.y - - -## Gets the current Vector2 for the Follow Target Offset property. -func get_follow_offset() -> Vector2: - return follow_offset - - -## Enables or disables Follow Damping. -func set_follow_damping(value: bool) -> void: - follow_damping = value - notify_property_list_changed() - -## Gets the current Follow Damping property. -func get_follow_damping() -> bool: - return follow_damping - - -## Assigns new Damping value. -func set_follow_damping_value(value: Vector2) -> void: - ## TODO - Should be using @export_range once minimum version support is Godot 4.3 - if value.x < 0: value.x = 0 - elif value.y < 0: value.y = 0 - follow_damping_value = value - -## Gets the current Follow Damping value. -func get_follow_damping_value() -> Vector2: - return follow_damping_value - - -## Assigns a new [member follow_axis] member. Value is based on [enum FollowLockAxis] enum. -func set_lock_axis(value: FollowLockAxis) -> void: - follow_axis_lock = value - - # Wait for the node to be ready before setting lock - if not is_node_ready(): await ready - - # Prevent axis lock from working in the editor - if value != FollowLockAxis.NONE and not Engine.is_editor_hint(): - _follow_axis_is_locked = true - match value: - FollowLockAxis.X: - _follow_axis_lock_value.x = _transform_output.origin.x - FollowLockAxis.Y: - _follow_axis_lock_value.y = _transform_output.origin.y - FollowLockAxis.XY: - _follow_axis_lock_value.x = _transform_output.origin.x - _follow_axis_lock_value.y = _transform_output.origin.y - else: - _follow_axis_is_locked = false - -## Gets the current [member follow_axis_lock] value. Value is based on [enum FollowLockAxis] enum. -func get_lock_axis() -> FollowLockAxis: - return follow_axis_lock - - -## Enables or disables [member rotate_with_target]. -func set_rotate_with_target(value: bool) -> void: - rotate_with_target = value - notify_property_list_changed() - -## Gets the current [member rotate_with_target] value. -func get_rotate_with_target() -> bool: - return rotate_with_target - - -## Sets the [member rotation_offset]. -func set_rotation_offset(value: float) -> void: - rotation_offset = value - -## Gets the current [member rotation_offset] value. -func get_rotation_offset() -> float: - return rotation_offset - - -## Enables or disables [member rotation_damping]. -func set_rotation_damping(value: bool) -> void: - rotation_damping = value - notify_property_list_changed() - -## Gets the [member rotation_damping] value. -func get_rotation_damping() -> bool: - return rotation_damping - - -## Set the [member rotation_damping_value]. -func set_rotation_damping_value(value: float) -> void: - rotation_damping_value = value - -## Gets the [member rotation_damping_value] value. -func get_rotation_damping_value() -> float: - return rotation_damping_value - - -## Enables or disables [member snap_to_pixel]. -func set_snap_to_pixel(value: bool) -> void: - snap_to_pixel = value - -## Gets the current [member snap_to_pixel] value. -func get_snap_to_pixel() -> bool: - return snap_to_pixel - - -## Enables or disables Auto zoom when using Group Follow. -func set_auto_zoom(value: bool) -> void: - auto_zoom = value - notify_property_list_changed() - -## Gets Auto Zoom state. -func get_auto_zoom() -> bool: - return auto_zoom - - -## Assigns new Min Auto Zoom value. -func set_auto_zoom_min(value: float) -> void: - auto_zoom_min = value - -## Gets Min Auto Zoom value. -func get_auto_zoom_min() -> float: - return auto_zoom_min - - -## Assigns new Max Auto Zoom value. -func set_auto_zoom_max(value: float) -> void: - auto_zoom_max = value - -## Gets Max Auto Zoom value. -func get_auto_zoom_max() -> float: - return auto_zoom_max - - -## Assigns new Zoom Auto Margin value. -func set_auto_zoom_margin(value: Vector4) -> void: - auto_zoom_margin = value - -## Gets Zoom Auto Margin value. -func get_auto_zoom_margin() -> Vector4: - return auto_zoom_margin - - -## Sets a limit side based on the side parameter.[br] -## It's recommended to pass the [enum Side] enum as the sid parameter. -func set_limit(side: int, value: int) -> void: - match side: - SIDE_LEFT: limit_left = value - SIDE_TOP: limit_top = value - SIDE_RIGHT: limit_right = value - SIDE_BOTTOM: limit_bottom = value - _: printerr("Not a valid Side.") - -## Gets the limit side -func get_limit(value: int) -> int: - match value: - SIDE_LEFT: return limit_left - SIDE_TOP: return limit_top - SIDE_RIGHT: return limit_right - SIDE_BOTTOM: return limit_bottom - _: - printerr("Not a valid Side.") - return -1 - - -## Assign a the Camera2D Left Limit Side value. -func set_limit_left(value: int) -> void: - _limit_target_exist_error() - limit_left = value - update_limit_all_sides() - -## Gets the Camera2D Left Limit value. -func get_limit_left() -> int: - return limit_left - - -## Assign a the Camera2D Top Limit Side value. -func set_limit_top(value: int) -> void: - _limit_target_exist_error() - limit_top = value - update_limit_all_sides() - -## Gets the Camera2D Top Limit value. -func get_limit_top() -> int: - return limit_top - - -## Assign a the Camera2D Right Limit Side value. -func set_limit_right(value: int) -> void: - _limit_target_exist_error() - limit_right = value - update_limit_all_sides() - -## Gets the Camera2D Right Limit value. -func get_limit_right() -> int: - return limit_right - - -## Assign a the Camera2D Bottom Limit Side value. -func set_limit_bottom(value: int) -> void: - _limit_target_exist_error() - limit_bottom = value - update_limit_all_sides() - -## Gets the Camera2D Bottom Limit value. -func get_limit_bottom() -> int: - return limit_bottom - - -func _limit_target_exist_error() -> void: - if not limit_target.is_empty(): - printerr("Unable to set Limit Side due to Limit Target ", _limit_node.name, " being assigned") - - -# Sets a [memeber limit_target] node. -func set_limit_target(value: NodePath) -> void: - limit_target = value - - # Waits for PCam2d's _ready() before trying to validate limit_node_path - if not is_node_ready(): await ready - - # Removes signal from existing TileMapLayer node - if is_instance_valid(get_node_or_null(value)): - var prev_limit_node: Node2D = _limit_node - var new_limit_node: Node2D = get_node(value) - - if prev_limit_node: - if prev_limit_node is TileMapLayer: - if prev_limit_node.changed.is_connected(_on_tile_map_changed): - prev_limit_node.changed.disconnect(_on_tile_map_changed) - - if new_limit_node is TileMapLayer: - if not new_limit_node.changed.is_connected(_on_tile_map_changed): - new_limit_node.changed.connect(_on_tile_map_changed) - elif new_limit_node is CollisionShape2D: - var col_shape: CollisionShape2D = get_node(value) - - if col_shape.shape == null: - printerr("No Shape2D in: ", col_shape.name) - reset_limit() - limit_target = "" - return - else: - printerr("Limit Target is not a TileMapLayer or CollisionShape2D node") - return - elif value == NodePath(""): - reset_limit() - limit_target = "" - else: - printerr("Limit Target cannot be found") - return - - _limit_node = get_node_or_null(value) - - notify_property_list_changed() - update_limit_all_sides() - -## Get [member limit_target] node. -func get_limit_target() -> NodePath: - if not limit_target: # TODO - Fixes an spam error if if limit_taret is empty - return NodePath("") - else: - return limit_target - - -## Set Tile Map Limit Margin. -func set_limit_margin(value: Vector4i) -> void: - limit_margin = value - update_limit_all_sides() -## Get Tile Map Limit Margin. -func get_limit_margin() -> Vector4i: - return limit_margin - - -### Enables or disables the Limit Smoothing beaviour. -#func set_limit_smoothing(value: bool) -> void: - #limit_smoothed = value -### Returns the Limit Smoothing beaviour. -#func get_limit_smoothing() -> bool: - #return limit_smoothed - - -## Sets a [PhantomCameraNoise2D] resource. -func set_noise(value: PhantomCameraNoise2D) -> void: - noise = value - if value != null: - _has_noise_resource = true - noise.set_trauma(1) - else: - _has_noise_resource = false - _transform_noise = Transform2D() - -## Returns the [PhantomCameraNoise2D] resource. -func get_noise() -> PhantomCameraNoise2D: - return noise - -func has_noise_resource() -> bool: - return _has_noise_resource - - -## Sets the [member noise_emitter_layer] value. -func set_noise_emitter_layer(value: int) -> void: - noise_emitter_layer = value - -## Enables or disables a given layer of [member noise_emitter_layer]. -func set_noise_emitter_layer_value(value: int, enabled: bool) -> void: - noise_emitter_layer = _set_layer(noise_emitter_layer, value, enabled) - -## Returns the [member noise_emitter_layer] -func get_noise_emitter_layer() -> int: - return noise_emitter_layer - - -## Sets [member inactive_update_mode] property. -func set_inactive_update_mode(value: int) -> void: - inactive_update_mode = value - -## Gets [enum InactiveUpdateMode] value. -func get_inactive_update_mode() -> int: - return inactive_update_mode - - -func get_follow_target_physics_based() -> bool: - return _follow_target_physics_based - - -func get_class() -> String: - return "PhantomCamera2D" - - -func is_class(value) -> bool: - return value == "PhantomCamera2D" - -#endregion diff --git a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd.uid b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd.uid deleted file mode 100644 index 66fa7de..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bhexx6mj1xv3q diff --git a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd deleted file mode 100644 index 95608c0..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd +++ /dev/null @@ -1,2471 +0,0 @@ -@tool -@icon("res://addons/phantom_camera/icons/phantom_camera_3d.svg") -class_name PhantomCamera3D -extends Node3D - -## Controls a scene's [Camera3D] and applies logic to it. -## -## The scene's [Camera3D] will follow the position of the -## [param PhantomCamera3D] with the highest priority. -## Each instance can have different positional and rotational logic applied -## to them. - -#region Constants - -const _constants = preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd") - -#endregion - -#region Signals - -## Emitted when the [param PhantomCamera3D] becomes active. -signal became_active - -## Emitted when the [param PhantomCamera3D] becomes inactive. -signal became_inactive - -## Emitted when the follow_mode changes. -## Note: This is for internal use only -signal follow_mode_changed - -## Emitted when [member follow_target] changes. -signal follow_target_changed - -## Emitted when [member look_at_target] changes. -signal look_at_target_changed - -## Emitted when dead zones changes.[br] -## [b]Note:[/b] Only applicable in [param Framed] [member FollowMode]. -signal dead_zone_changed - -## Emitted when a target touches the edge of the dead zone in [param Framed] [enum FollowMode]. -signal dead_zone_reached - -## Emitted when the [param Camera3D] starts to tween to another -## [param PhantomCamera3D]. -signal tween_started - -## Emitted when the [param Camera3D] is to tweening towards another -## [param PhantomCamera3D]. -signal is_tweening - -## Emitted when the tween is interrupted due to another [param PhantomCamera3D] -## becoming active. The argument is the [param PhantomCamera3D] that -## interrupted the tween. -signal tween_interrupted(pcam_3d: PhantomCamera3D) - -## Emitted when the [param Camera3D] completes its tween to the -## [param PhantomCamera3D]. -signal tween_completed - -## Emitted when Noise should be applied to the [param Camera3D]. -signal noise_emitted(noise_output: Transform3D) - -signal physics_target_changed - -signal camera_3d_resource_property_changed(property: StringName, value: Variant) -signal camera_3d_resource_changed - -#endregion - - -#region Enums - -## Determines the positional logic for a given [param PhantomCamera3D] -## [br][br] -## The different modes have different functionalities and purposes, so choosing -## the correct one depends on what each [param PhantomCamera3D] is meant to do. -enum FollowMode { - NONE = 0, ## Default - No follow logic is applied. - GLUED = 1, ## Sticks to its target. - SIMPLE = 2, ## Follows its target with an optional offset. - GROUP = 3, ## Follows multiple targets with option to dynamically reframe itself. - PATH = 4, ## Follows a target while being positionally confined to a [Path3D] node. - FRAMED = 5, ## Applies a dead zone on the frame and only follows its target when it tries to leave it. - THIRD_PERSON = 6, ## Applies a [SpringArm3D] node to the target's position and allows for rotating around it. -} - -## Determines the rotational logic for a given [param PhantomCamera3D].[br][br] -## The different modes has different functionalities and purposes, so -## choosing the correct mode depends on what each [param PhantomCamera3D] -## is meant to do. -enum LookAtMode { - NONE = 0, ## Default - No Look At logic is applied. - MIMIC = 1, ## Copies its target's rotational value. - SIMPLE = 2, ## Looks at its target in a straight line. - GROUP = 3, ## Looks at the centre of its targets. -} - -## Determines how often an inactive [param PhantomCamera3D] should update -## its positional and rotational values. This is meant to reduce the amount -## of calculations inactive [param PhantomCamera3D] are doing when idling -## to improve performance. -enum InactiveUpdateMode { - ALWAYS, ## Always updates the [param PhantomCamera3D], even when it's inactive. - NEVER, ## Never updates the [param PhantomCamera3D] when it's inactive. Reduces the amount of computational resources when inactive. -# EXPONENTIALLY, -} - -enum FollowLockAxis { - NONE = 0, - X = 1, - Y = 2, - Z = 3, - XY = 4, - XZ = 5, - YZ = 6, - XYZ = 7, -} - -#endregion - - -#region Exported Properties - -## To quickly preview a [param PhantomCamera3D] without adjusting its -## [member Priority], this property allows the selected [param PhantomCamera3D] -## to ignore the Priority system altogether and forcefully become the active -## one. It's partly designed to work within the [param viewfinder], and will be -## disabled when running a build export of the game. -@export var priority_override: bool = false: - set(value): - priority_override = value - if Engine.is_editor_hint(): - if value: - if not Engine.has_singleton(_constants.PCAM_MANAGER_NODE_NAME): return - Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME).pcam_priority_override.emit(self, priority_override) - else: - if not Engine.has_singleton(_constants.PCAM_MANAGER_NODE_NAME): return - Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME).pcam_priority_override.emit(self, priority_override) - get: - return priority_override - - -## It defines which [param PhantomCamera3D] a scene's [param Camera3D] should -## be corresponding with and be attached to. This is decided by the -## [param PhantomCamera3D] with the highest [param priority]. -## [br][br] -## Changing [param priority] will send an event to the scene's -## [PhantomCameraHost], which will then determine whether if the -## [param priority] value is greater than or equal to the currently -## highest [param PhantomCamera3D]'s in the scene. The -## [param PhantomCamera3D] with the highest value will then reattach the -## Camera accordingly. -@export var priority: int = 0: - set = set_priority, - get = get_priority - - -## Determines the positional logic for a given [param PhantomCamera3D]. -## The different modes have different functionalities and purposes, so -## choosing the correct one depends on what each [param PhantomCamera3D] -## is meant to do. -@export var follow_mode: FollowMode = FollowMode.NONE: - set(value): - follow_mode = value - - if follow_mode == FollowMode.NONE: - _should_follow = false - top_level = false - _draw_follow_gizmo_line = false - _check_draw_gizmo() - _is_parents_physics() - notify_property_list_changed() - return - - match follow_mode: - FollowMode.PATH: - if is_instance_valid(follow_path): - _should_follow_checker() - else: - _should_follow = false - FollowMode.GROUP: - _follow_targets_size_check() - _: - _should_follow_checker() - - ## Disables Follow Gizmo Line for Follow Glued - if follow_mode == FollowMode.GLUED: - _draw_follow_gizmo_line = false - _check_draw_gizmo() - else: - if draw_follow_line: _draw_follow_gizmo_line = true - _check_draw_gizmo() - - if follow_mode == FollowMode.FRAMED: - if _follow_framed_initial_set and follow_target: - _follow_framed_initial_set = false - dead_zone_changed.connect(_on_dead_zone_changed) - else: - if dead_zone_changed.is_connected(_on_dead_zone_changed): - dead_zone_changed.disconnect(_on_dead_zone_changed) - - if follow_mode == FollowMode.THIRD_PERSON: - top_level = false - _is_third_person_follow = true - else: - top_level = true - _is_third_person_follow = false - - follow_mode_changed.emit() - notify_property_list_changed() - - ## NOTE - Warning that Look At + Follow Mode hasn't been fully tested together yet - if look_at_mode != LookAtMode.NONE: - print_rich("[color=#EAA15E]Warning: Using both Look At and Follow Mode on the same PCam3D has not been fully tested yet, proceed with caution![/color]") - get: - return follow_mode - -## Determines which target should be followed. -## The [param Camera3D] will follow the position of the Follow Target based on -## the [member follow_mode] type and its parameters. -@export var follow_target: Node3D = null: - set = set_follow_target, - get = get_follow_target - -## Defines the targets that the [param PhantomCamera3D] should be following. -@export var follow_targets: Array[Node3D] = []: - set = set_follow_targets, - get = get_follow_targets - -## Determines the [Path3D] node the [param PhantomCamera3D] -## should be bound to. -## The [param PhantomCamera3D] will follow the position of the -## [member follow_target] while sticking to the closest point on this path. -@export var follow_path: Path3D = null: - set = set_follow_path, - get = get_follow_path - - -## Determines the rotational logic for a given [param PhantomCamera3D]. -## The different modes has different functionalities and purposes, -## so choosing the correct mode depends on what each -## [param PhantomCamera3D] is meant to do. -@export var look_at_mode: LookAtMode = LookAtMode.NONE: - set(value): - look_at_mode = value - - if look_at_mode == LookAtMode.NONE: - _should_look_at = false - _draw_look_at_gizmo_line = false - _check_draw_gizmo() - notify_property_list_changed() - return - - if look_at_mode == LookAtMode.MIMIC: - _draw_look_at_gizmo_line = false - _check_draw_gizmo() - else: - if draw_look_at_line: _draw_look_at_gizmo_line = true - _check_draw_gizmo() - - if not look_at_mode == LookAtMode.GROUP: - if look_at_target is Node3D: - _should_look_at = true - else: # If Look At Group - _look_at_targets_size_check() - - notify_property_list_changed() - - ## NOTE - Warning that Look At + Follow Mode hasn't been fully tested together yet - if follow_mode != FollowMode.NONE: - print_rich("[color=#EAA15E]Warning: Using both Look At and Follow Mode on the same PCam3D has not been fully tested yet, proceed with caution![/color]") - get: - return look_at_mode - -## Determines which target should be looked at. -## The [param PhantomCamera3D] will update its rotational value as the -## target changes its position. -@export var look_at_target: Node3D = null: - set = set_look_at_target, - get = get_look_at_target - -## Defines the targets that the camera should looking at. -## It will be looking at the centre of all the assigned targets. -@export var look_at_targets: Array[Node3D] = []: - set = set_look_at_targets, - get = get_look_at_targets - - -## Defines how [param ]PhantomCamera3Ds] transition between one another. -## Changing the tween values for a given [param PhantomCamera3D] -## determines how transitioning to that instance will look like. -## This is a resource type that can be either used for one -## [param PhantomCamera] or reused across multiple - both 2D and 3D. -## By default, all [param PhantomCameras] will use a [param linear] -## transition, [param easeInOut] ease with a [param 1s] duration. -@export var tween_resource: PhantomCameraTween = PhantomCameraTween.new(): - set = set_tween_resource, - get = get_tween_resource - -## If enabled, the moment a [param PhantomCamera3D] is instantiated into -## a scene, and has the highest priority, it will perform its tween transition. -## This is most obvious if a [param PhantomCamera3D] has a long duration and -## is attached to a playable character that can be moved the moment a scene -## is loaded. Disabling the [param tween_on_load] property will -## disable this behaviour and skip the tweening entirely when instantiated. -@export var tween_on_load: bool = true: - set = set_tween_on_load, - get = get_tween_on_load - - -## Determines how often an inactive [param PhantomCamera3D] should update -## its positional and rotational values. This is meant to reduce the amount -## of calculations inactive [param PhantomCamera3Ds] are doing when idling -## to improve performance. -@export var inactive_update_mode: InactiveUpdateMode = InactiveUpdateMode.ALWAYS: - set = set_inactive_update_mode, - get = get_inactive_update_mode - - -## Determines which layers this [param PhantomCamera3D] should be able to communicate with [PhantomCameraHost] nodes.[br] -## A corresponding layer needs to be set on the [PhantomCameraHost] node. -@export_flags_3d_render var host_layers: int = 1: - set = set_host_layers, - get = get_host_layers - - -## A resource type that allows for overriding the [param Camera3D] node's -## properties. -@export var camera_3d_resource: Camera3DResource = null: - set = set_camera_3d_resource, - get = get_camera_3d_resource - - -## Overrides the [member Camera3D.attribuets] resource property. -@export var attributes: CameraAttributes = null: - set = set_attributes, - get = get_attributes - - -## Overrides the [member Camera3D.environment] resource property. -@export var environment: Environment = null: - set = set_environment, - get = get_environment - - -## Overrides the [member Camera3D.compositor] resource property. -@export var compositor: Compositor = null: - set = set_compositor, - get = get_compositor - - -@export_group("Follow Parameters") -## Offsets the [member follow_target] position. -@export var follow_offset: Vector3 = Vector3.ZERO: - set = set_follow_offset, - get = get_follow_offset - -## Applies a damping effect on the camera's movement. -## Leading to heavier / slower camera movement as the targeted node moves around. -## This is useful to avoid sharp and rapid camera movement. -@export var follow_damping: bool = false: - set = set_follow_damping, - get = get_follow_damping - -## Defines the damping amount. The ideal range should be somewhere between 0-1.[br][br] -## The damping amount can be specified in the individual axis.[br][br] -## [b]Lower value[/b] = faster / sharper camera movement.[br] -## [b]Higher value[/b] = slower / heavier camera movement. -@export_custom(PROPERTY_HINT_LINK, "") -var follow_damping_value: Vector3 = Vector3(0.1, 0.1, 0.1): - set = set_follow_damping_value, - get = get_follow_damping_value - - -## Prevents the [param PhantomCamera2D] from moving in a designated axis. -## This can be enabled or disabled at runtime or from the editor directly. -@export var follow_axis_lock: FollowLockAxis = FollowLockAxis.NONE: - set = set_follow_axis_lock, - get = get_follow_axis_lock -var _follow_axis_is_locked: bool = false -var _follow_axis_lock_value: Vector3 = Vector3.ZERO - - -## Sets a distance offset from the centre of the target's position. -## The distance is applied to the [param PhantomCamera3D]'s local z axis. -@export var follow_distance: float = 1: - set = set_follow_distance, - get = get_follow_distance - -## Enables the [param PhantomCamera3D] to automatically distance -## itself as the [param follow targets] move further apart.[br] -## It looks at the longest axis between the different targets and interpolates -## the distance length between the [member auto_follow_distance_min] and -## [member follow_group_distance] properties.[br][br] -## Note: Enabling this property hides and disables the [member follow_distance] -## property as this effectively overrides that property. -@export var auto_follow_distance: bool = false: - set = set_auto_follow_distance, - get = get_auto_follow_distance - -## Sets the minimum distance between the Camera and centre of [AABB]. -## [br][br] -## Note: This distance will only ever be reached when all the targets are in -## the exact same [param Vector3] coordinate, which will very unlikely -## happen, so adjust the value here accordingly. -## [br][br] -## If only one follow target is assigned to [member follow_targets], this value will be used as the `follow_distance`. -@export var auto_follow_distance_min: float = 1: - set = set_auto_follow_distance_min, - get = get_auto_follow_distance_min - -## Sets the maximum distance between the Camera and centre of [AABB]. -@export var auto_follow_distance_max: float = 5: - set = set_auto_follow_distance_max, - get = get_auto_follow_distance_max - -## Determines how fast the [member auto_follow_distance] moves between the -## maximum and minimum distance. The higher the value, the sooner the -## maximum distance is reached.[br][br] -## This value should be based on the sizes of the [member auto_follow_distance_min] -## and [member auto_follow_distance_max].[br] -## E.g. if the value between the [member auto_follow_distance_min] and -## [member auto_follow_distance_max] is small, consider keeping the number low -## and vice versa. -@export var auto_follow_distance_divisor: float = 10: - set = set_auto_follow_distance_divisor, - get = get_auto_follow_distance_divisor - - -@export_subgroup("Dead Zones") -## Defines the horizontal dead zone area. While the target is within it, the -## [param PhantomCamera3D] will not move in the horizontal axis. -## If the targeted node leaves the horizontal bounds, the -## [param PhantomCamera3D] will follow the target horizontally to keep -## it within bounds. -@export_range(0, 1) var dead_zone_width: float = 0: - set(value): - dead_zone_width = value - dead_zone_changed.emit() - get: - return dead_zone_width - -## Defines the vertical dead zone area. While the target is within it, the -## [param PhantomCamera3D] will not move in the vertical axis. -## If the targeted node leaves the vertical bounds, the -## [param PhantomCamera3D] will follow the target horizontally to keep -## it within bounds. -@export_range(0, 1) var dead_zone_height: float = 0: - set(value): - dead_zone_height = value - dead_zone_changed.emit() - get: - return dead_zone_height - -## Enables the dead zones to be visible when running the game from the editor. -## Dead zones will never be visible in build exports. -@export var show_viewfinder_in_play: bool = false - -## Defines the position of the [member follow_target] within the viewport.[br] -## This is only used for when [member follow_mode] is set to [param Framed]. -@export_subgroup("Spring Arm") - -## Applies a rotational offset to the Third Person [member follow_mode] in the [code]X[/code] axis. -@export_range(-360, 360, 0.1,"or_greater", "or_less", "radians_as_degrees") -var vertical_rotation_offset: float = 0: - set = set_vertical_rotation_offset, - get = get_vertical_rotation_offset - -## Applies a rotational offset to the Third Person [member follow_mode] in the [code]Y[/code] axis. -@export_range(-360, 360, 0.1, "or_greater", "or_less", "radians_as_degrees") -var horizontal_rotation_offset: float = 0: - set = set_horizontal_rotation_offset, - get = get_horizontal_rotation_offset - -## Defines the [member SpringArm3D.spring_length]. -@export var spring_length: float = 1: - set = set_spring_length, - get = get_spring_length - -## Defines the [member SpringArm3D.collision_mask] node's Collision Mask. -@export_flags_3d_physics var collision_mask: int = 1: - set = set_collision_mask, - get = get_collision_mask - -## Defines the [member SpringArm3D.shape] node's Shape3D. -@export var shape: Shape3D = null: - set = set_shape, - get = get_shape - -## Defines the [member SpringArm3D.margin] node's Margin. -@export var margin: float = 0.01: - set = set_margin, - get = get_margin - -@export_group("Look At Parameters") -## Offsets the target's [param Vector3] position that the -## [param PhantomCamera3D] is looking at. -@export var look_at_offset: Vector3 = Vector3.ZERO: - set = set_look_at_offset, - get = get_look_at_offset - -## Applies a damping effect on the camera's rotation. -## Leading to heavier / slower camera movement as the targeted node moves around. -## This is useful to avoid sharp and rapid camera rotation. -@export var look_at_damping: bool = false: - set = set_look_at_damping, - get = get_look_at_damping - -## Defines the Rotational damping amount. The ideal range is typically somewhere between 0-1.[br][br] -## The damping amount can be specified in the individual axis.[br][br] -## [b]Lower value[/b] = faster / sharper camera rotation.[br] -## [b]Higher value[/b] = slower / heavier camera rotation. -@export_range(0.0, 1.0, 0.001, "or_greater") var look_at_damping_value: float = 0.25: - set = set_look_at_damping_value, - get = get_look_at_damping_value - -@export_subgroup("Up Direction") -## Defines the upward direction of the [param PhantomCamera3D] when [member look_at_mode] is set. [br] -## This value will be overriden if [member up_target] is defined. -@export var up: Vector3 = Vector3.UP: - set = set_up, - get = get_up - -## Applies and continuously updates the [param up] direction of the [param PhantomCamera3D] based on this target when [member look_at_mode] is set.[br] -## Setting a value here will override the [member up] value. -@export var up_target: Node3D = null: - set = set_up_target, - get = get_up_target - - -@export_group("Noise") -## Applies a noise, or shake, to a [Camera3D].[br] -## Once set, the noise will run continuously after the tween to the [PhantomCamera3D] instance is complete. -@export var noise: PhantomCameraNoise3D = null: - set = set_noise, - get = get_noise - -## If true, will trigger the noise while in the editor.[br] -## Useful in cases where you want to temporarily disalbe the noise in the editor without removing -## the resource.[br][br] -## [b]Note:[/b] This property has no effect on runtime behaviour. -@export var _preview_noise: bool = true: - set(value): - _preview_noise = value - if not value: - _transform_noise = Transform3D() - -## Enable a corresponding layer for a [member PhantomCameraNoiseEmitter3D.noise_emitter_layer] -## to make this [PhantomCamera3D] be affect by it. -@export_flags_3d_render var noise_emitter_layer: int = 0: - set = set_noise_emitter_layer, - get = get_noise_emitter_layer - - -@export_group("Editor") -@export_subgroup("Align with View") -## Adds an editor button that positions and rotates the [param PhantomCamera3D] to match the 3D viewport's transform.[br][br] -## This editor button is not visible if the [param PhantomCamera3D] is following [i]or[/i] looking at a target.[br][br] -## [b]Note[/b]: This is only functional in the editor. -@export_tool_button("Align Transform with View", "CenterView") -var align_transform_with_view: Callable = func(): - var undo_redo: EditorUndoRedoManager = EditorInterface.get_editor_undo_redo() - var property: StringName = &"global_transform" - undo_redo.create_action("Aligned " + name + "'s transform with view") - undo_redo.add_do_property(self, property, EditorInterface.get_editor_viewport_3d(viewport_index).get_camera_3d().global_transform) - undo_redo.add_undo_property(self, property, global_transform) - undo_redo.commit_action() - -## Adds an editor button that positions the [param PhantomCamera3D] to match the 3D viewport's position.[br][br] -## This editor button is not visible if the [param PhantomCamera3D] is following a target.[br][br] -## [b]Note[/b]: This is only functional in the editor. -@export_tool_button("Align Position with View", "ToolMove") -var align_position_with_view: Callable = func(): - var undo_redo: EditorUndoRedoManager = EditorInterface.get_editor_undo_redo() - var property: StringName = &"global_position" - undo_redo.create_action("Aligned " + name + "'s position with view") - undo_redo.add_do_property(self, property, EditorInterface.get_editor_viewport_3d(viewport_index).get_camera_3d().global_position) - undo_redo.add_undo_property(self, property, global_position) - undo_redo.commit_action() - -## Adds an editor button that rotates the [param PhantomCamera3D] to match the 3D viewport's rotation.[br][br] -## This editor button is not visible if the [param PhantomCamera3D] is looking at a target.[br][br] -## [b]Note[/b]: This is only functional in the editor. -@export_tool_button("Align Rotation with View", "ToolRotate") -var align_rotation_with_view: Callable = func(): - var undo_redo: EditorUndoRedoManager = EditorInterface.get_editor_undo_redo() - var property: StringName = &"global_rotation" - undo_redo.create_action("Aligned " + name + "'s rotation with view") - undo_redo.add_do_property(self, property, EditorInterface.get_editor_viewport_3d(viewport_index).get_camera_3d().global_rotation) - undo_redo.add_undo_property(self, property, global_rotation) - undo_redo.commit_action() - -## Change which viewport the alignment buttons should be based on.[br] -## [b]Note:[/b] If you are only using 1 viewport, keep the default value to 0. -@export_range(0, 3) var viewport_index: int = 0: - set(value): - viewport_index = value - get: - return viewport_index - - -@export_subgroup("Gizmo Line") -## Draws a line between the [param PhantomCamera3D] and its follow target position.[br] -## This is only drawn when a valid [member follow_target] or [member follow_targets] is set. -@export var draw_follow_line: bool = false: - set = set_draw_follow_line, - get = get_draw_follow_line - -## Draws a line between the [param PhantomCamera3D] and its look at target position.[br] -## This is only drawn when a valid [member follow_target] or [member look_at_targets] is set. -@export var draw_look_at_line: bool = false: - set = set_draw_look_at_line, - get = get_draw_look_at_line - -#endregion - -#region Private Variables - -var _is_active: bool = false - -var _is_third_person_follow: bool = false -var _camera_target: Node3D = self # Calculates the position of the camera in the editor, uses instantiated SpringArm3D node when running the scene - -var _should_follow: bool = false -var _follow_target_physics_based: bool = false -var _physics_interpolation_enabled: bool = false ## TOOD - Should be enbled once toggling physics_interpolation_mode ON, when previously OFF, works in 3D - -var _has_multiple_follow_targets: bool = false -var _follow_targets_single_target_index: int = 0 -var _follow_targets: Array[Node3D] = [] - -var _should_look_at: bool = false -var _look_at_target_physics_based: bool = false - -var _has_multiple_look_at_targets: bool = false -var _look_at_targets_single_target_index: int = 0 - -var _current_rotation: Vector3 = Vector3.ZERO - -var _up: Vector3 = Vector3.UP -var _has_up_target: bool = false - -var _follow_target_output_position: Vector3 = Vector3.ZERO -var _look_at_target_output_position: Vector3 = Vector3.ZERO - -var _transform_output: Transform3D = Transform3D() -var _transform_noise: Transform3D = Transform3D() - -var _tween_skip: bool = false - -var _follow_velocity_ref: Vector3 = Vector3.ZERO # Stores and applies the velocity of the movement - -var _follow_framed_initial_set: bool = false -var _follow_framed_offset: Vector3 = Vector3.ZERO - -var _follow_spring_arm: SpringArm3D = null -var _has_follow_spring_arm: bool = false - -var _has_noise_resource: bool = false - -var _draw_gizmo: bool = false -var _draw_follow_gizmo_line: bool = false -var _follow_target_position: Vector3 = Vector3.ZERO -var _draw_look_at_gizmo_line: bool = false - -# NOTE - Temp solution until Godot has better plugin autoload recognition out-of-the-box. -var _phantom_camera_manager: Node = null - -#endregion - -#region Public Variable - -var tween_duration: float: - set = set_tween_duration, - get = get_tween_duration -var tween_transition: PhantomCameraTween.TransitionType: - set = set_tween_transition, - get = get_tween_transition -var tween_ease: PhantomCameraTween.EaseType: - set = set_tween_ease, - get = get_tween_ease - -var keep_aspect: int: - set = set_keep_aspect, - get = get_keep_aspect -var cull_mask: int: - set = set_cull_mask, - get = get_cull_mask -var h_offset: float: - set = set_h_offset, - get = get_h_offset -var v_offset: float: - set = set_v_offset, - get = get_v_offset -var projection: Camera3DResource.ProjectionType: - set = set_projection, - get = get_projection -var fov: float: - set = set_fov, - get = get_fov -var size: float: - set = set_size, - get = get_size -var frustum_offset: Vector2: - set = set_frustum_offset, - get = get_frustum_offset -var far: float: - set = set_far, - get = get_far -var near: float: - set = set_near, - get = get_near - -var viewport_position: Vector2 - -#endregion - - -#region Private Functions - -func _validate_property(property: Dictionary) -> void: - ################ - ## Follow Target - ################ - if property.name == "follow_path" and \ - follow_mode != FollowMode.PATH: - property.usage = PROPERTY_USAGE_NO_EDITOR - - #################### - ## Follow Parameters - #################### - if follow_mode == FollowMode.NONE: - match property.name: - "follow_target", \ - "follow_offset", \ - "follow_damping", \ - "follow_damping_value", \ - "follow_axis_lock", \ - "draw_follow_line": - property.usage = PROPERTY_USAGE_NO_EDITOR - - if follow_mode == FollowMode.GROUP: - match property.name: - "follow_target": - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == "follow_offset": - if follow_mode == FollowMode.GLUED: - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == "follow_damping_value" and not follow_damping: - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == "follow_distance": - if not follow_mode == FollowMode.FRAMED: - if not follow_mode == FollowMode.GROUP or \ - auto_follow_distance: \ - property.usage = PROPERTY_USAGE_NO_EDITOR - - ############### - ## Group Follow - ############### - if property.name == "follow_targets" and \ - not follow_mode == FollowMode.GROUP: - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == "auto_follow_distance" and \ - not follow_mode == FollowMode.GROUP: - property.usage = PROPERTY_USAGE_NO_EDITOR - - if not auto_follow_distance or not follow_mode == FollowMode.GROUP: - match property.name: - "auto_follow_distance_min", \ - "auto_follow_distance_max", \ - "auto_follow_distance_divisor": - property.usage = PROPERTY_USAGE_NO_EDITOR - - ############### - ## Framed Follow - ############### - if not follow_mode == FollowMode.FRAMED: - match property.name: - "dead_zone_width", \ - "dead_zone_height", \ - "show_viewfinder_in_play": - property.usage = PROPERTY_USAGE_NO_EDITOR - - ###################### - ## Third Person Follow - ###################### - if not follow_mode == FollowMode.THIRD_PERSON: - match property.name: - "vertical_rotation_offset", \ - "horizontal_rotation_offset", \ - "spring_length", \ - "collision_mask", \ - "shape", \ - "margin": - property.usage = PROPERTY_USAGE_NO_EDITOR - - ########## - ## Look At - ########## - match look_at_mode: - LookAtMode.NONE: - match property.name: - "look_at_target", \ - "look_at_offset" , \ - "look_at_damping", \ - "look_at_damping_value", \ - "up", \ - "up_target", \ - "draw_look_at_line": - property.usage = PROPERTY_USAGE_NO_EDITOR - LookAtMode.MIMIC: - match property.name: - "draw_look_at_line": - property.usage = PROPERTY_USAGE_NO_EDITOR - LookAtMode.GROUP: - match property.name: - "look_at_target", \ - "draw_look_at_line": - property.usage = PROPERTY_USAGE_NO_EDITOR - - - if property.name == "look_at_target": - if look_at_mode == LookAtMode.NONE or \ - look_at_mode == LookAtMode.GROUP: - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == "look_at_targets" and \ - not look_at_mode == LookAtMode.GROUP: - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == "look_at_damping_value" and \ - not look_at_damping: - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == "up" and _has_up_target: - property.usage = PROPERTY_USAGE_NO_EDITOR - - ########################## - ## Align With Viewport - ########################## - if property.name == 'align_transform_with_view' and \ - (_should_look_at == true or _should_follow == true): - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == 'align_position_with_view' and \ - ( - _should_follow == true or \ - _should_follow == true and follow_mode == FollowMode.THIRD_PERSON - ): - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == 'align_rotation_with_view' and \ - ( - _should_look_at == true or \ - _should_follow == true and follow_mode == FollowMode.THIRD_PERSON - ): - property.usage = PROPERTY_USAGE_NO_EDITOR - - -func _enter_tree() -> void: - _phantom_camera_manager = Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME) - _tween_skip = !tween_on_load - - _phantom_camera_manager.pcam_added(self) - - priority_override = false - - if not visibility_changed.is_connected(_check_visibility): - visibility_changed.connect(_check_visibility) - - match follow_mode: - FollowMode.NONE: - _is_parents_physics() - FollowMode.PATH: - if is_instance_valid(follow_path): - _should_follow_checker() - else: - _should_follow = false - FollowMode.GROUP: - _follow_targets_size_check() - _should_follow_checker() - _: - _should_follow_checker() - - _should_look_at_checker() - if look_at_mode == LookAtMode.GROUP: - _look_at_targets_size_check() - - #if not get_parent() is SpringArm3D: - #if look_at_target: - #_look_at_target_node = look_at_target - #elif look_at_targets: - #_look_at_group_nodes.clear() - #for path in look_at_targets: - #if not path.is_empty() and path: - #_should_look_at = true - #_has_look_at_targets = true - #_look_at_group_nodes.append(path) - - -func _exit_tree() -> void: - if not follow_mode == FollowMode.GROUP: - follow_targets = [] - - if not is_instance_valid(_phantom_camera_manager): return - _phantom_camera_manager.pcam_removed(self) - - -func _ready(): - match follow_mode: - FollowMode.THIRD_PERSON: - _is_third_person_follow = true - if _should_follow: _transform_output.origin = _get_target_position_offset_distance() - if not Engine.is_editor_hint(): - if not is_instance_valid(_follow_spring_arm): - _follow_spring_arm = SpringArm3D.new() - _follow_spring_arm.top_level = true - _follow_spring_arm.spring_length = spring_length - _follow_spring_arm.collision_mask = collision_mask - _follow_spring_arm.shape = shape - _follow_spring_arm.margin = margin - # Stores the rotation value as the rotation gets skewed after - # the SpringArm3D is instantiated for some reason... - var initial_rotation: Vector3 = global_rotation - if _should_follow: _follow_spring_arm.add_excluded_object(follow_target) - get_parent().add_child.call_deferred(_follow_spring_arm) - - # Waits for the SpringArm3D to be ready and then apply rotation - # Resolves an issue most prominent in Godot 4.4 - await _follow_spring_arm.ready - reparent.call_deferred(_follow_spring_arm) - _camera_target = _follow_spring_arm - _follow_spring_arm.global_position = _get_target_position_offset() if is_instance_valid(follow_target) else global_position - _follow_spring_arm.global_rotation = initial_rotation - _has_follow_spring_arm = true - top_level = false - _: - _transform_output.origin = global_position - - if not Engine.is_editor_hint(): - _preview_noise = true - - ## NOTE - Only here to set position for Framed View on startup. - ## Should be removed once https://github.com/ramokz/phantom-camera/issues/161 is complete - _transform_output = global_transform - - _phantom_camera_manager.noise_3d_emitted.connect(_noise_emitted) - - -func _process(delta: float) -> void: - if Engine.is_editor_hint() and _draw_gizmo: - update_gizmos() - - if _follow_target_physics_based or _is_active: return - process_logic(delta) - - -func _physics_process(delta: float) -> void: - if not _follow_target_physics_based or _is_active: return - process_logic(delta) - - -func process_logic(delta: float) -> void: - if _is_active: - if _has_noise_resource and _preview_noise: - _transform_noise = noise.get_noise_transform(delta) - else: - match inactive_update_mode: - InactiveUpdateMode.NEVER: return - # InactiveUpdateMode.EXPONENTIALLY: - # TODO - Trigger positional updates less frequently as more PCams gets added - - if _should_follow: - _follow(delta) - else: - _transform_output.origin = global_position - - if _should_look_at: - _look_at(delta) - elif not _is_third_person_follow: - _transform_output.basis = global_basis - - if _follow_axis_is_locked: - match follow_axis_lock: - FollowLockAxis.X: - _transform_output.origin.x = _follow_axis_lock_value.x - FollowLockAxis.Y: - _transform_output.origin.y = _follow_axis_lock_value.y - FollowLockAxis.Z: - _transform_output.origin.z = _follow_axis_lock_value.z - FollowLockAxis.XY: - _transform_output.origin.x = _follow_axis_lock_value.x - _transform_output.origin.y = _follow_axis_lock_value.y - FollowLockAxis.XZ: - _transform_output.origin.x = _follow_axis_lock_value.x - _transform_output.origin.z = _follow_axis_lock_value.z - FollowLockAxis.YZ: - _transform_output.origin.y = _follow_axis_lock_value.y - _transform_output.origin.z = _follow_axis_lock_value.z - FollowLockAxis.XYZ: - _transform_output.origin.x = _follow_axis_lock_value.x - _transform_output.origin.y = _follow_axis_lock_value.y - _transform_output.origin.z = _follow_axis_lock_value.z - - -func _follow(delta: float) -> void: - _set_follow_position() - _interpolate_position(delta) - -func _look_at(delta: float) -> void: - _set_look_at_position() - _interpolate_rotation(delta) - - -func _set_follow_position() -> void: - match follow_mode: - FollowMode.GLUED: - _follow_target_output_position = follow_target.global_position - - FollowMode.SIMPLE: - _follow_target_output_position = _get_target_position_offset() - _set_follow_gizmo_line_position(follow_target.global_position) - - FollowMode.GROUP: - if _has_multiple_follow_targets: - var bounds: AABB = AABB(_follow_targets[0].global_position, Vector3.ZERO) - for target in _follow_targets: - bounds = bounds.expand(target.global_position) - var distance: float - if auto_follow_distance: - distance = lerpf(auto_follow_distance_min, auto_follow_distance_max, bounds.get_longest_axis_size() / auto_follow_distance_divisor) - distance = clampf(distance, auto_follow_distance_min, auto_follow_distance_max) - else: - distance = follow_distance - - _follow_target_output_position = \ - bounds.get_center() + \ - follow_offset + \ - global_basis.z * \ - distance - - _set_follow_gizmo_line_position(bounds.get_center()) - else: - _follow_target_output_position = \ - follow_targets[_follow_targets_single_target_index].global_position + \ - follow_offset + \ - global_basis.z * \ - auto_follow_distance_min - - FollowMode.PATH: - var path_position: Vector3 = follow_path.global_position - _follow_target_output_position = \ - follow_path.curve.get_closest_point( - _get_target_position_offset() - path_position - ) + path_position - _set_follow_gizmo_line_position(_get_target_position_offset()) - - FollowMode.FRAMED: - if not Engine.is_editor_hint(): - if not _is_active: - _follow_target_output_position = _get_target_position_offset_distance() - else: - viewport_position = get_viewport().get_camera_3d().unproject_position(_get_target_position_offset()) - var visible_rect_size: Vector2 = get_viewport().get_visible_rect().size - viewport_position = viewport_position / visible_rect_size - _current_rotation = global_rotation - - if _current_rotation != global_rotation: - _follow_target_output_position = _get_target_position_offset_distance() - - if _get_framed_side_offset() != Vector2.ZERO: - var framed_offset: Vector2 = _get_framed_side_offset() - var target_position: Vector3 = _get_target_position_offset() + _follow_framed_offset - var glo_pos: Vector3 - - if dead_zone_width == 0 || dead_zone_height == 0: - if dead_zone_width == 0 && dead_zone_height != 0: - glo_pos = _get_target_position_offset_distance() - glo_pos.z = target_position.z - _follow_target_output_position = glo_pos - elif dead_zone_width != 0 && dead_zone_height == 0: - glo_pos = _get_target_position_offset_distance() - glo_pos.x = target_position.x - _follow_target_output_position = glo_pos - else: - _follow_target_output_position = _get_target_position_offset_distance() - else: - if _current_rotation != global_rotation: - var opposite: float = sin(-global_rotation.x) * follow_distance + _get_target_position_offset().y - glo_pos.y = _get_target_position_offset().y + opposite - glo_pos.z = sqrt(pow(follow_distance, 2) - pow(opposite, 2)) + _get_target_position_offset().z - glo_pos.x = global_position.x - - _follow_target_output_position = glo_pos - _current_rotation = global_rotation - else: - dead_zone_reached.emit() - - # FIX: Only move camera in the axis where dead zone is breached - var current_global_position: Vector3 = global_position - var current_offset: Vector3 = global_position - _get_target_position_offset() - - # Update stored offset for non-breached axes - if framed_offset.x == 0: - _follow_framed_offset.x = current_offset.x - if framed_offset.y == 0: - _follow_framed_offset.z = current_offset.z - - # Lock camera position on non-breached axes - if framed_offset.x == 0: - target_position.x = current_global_position.x - if framed_offset.y == 0: - target_position.z = current_global_position.z - - _follow_target_output_position = target_position - else: - _follow_framed_offset = global_position - _get_target_position_offset() - _follow_target_position = global_position - _current_rotation = global_rotation - return - else: - _follow_target_output_position = _get_target_position_offset_distance() - var unprojected_position: Vector2 = _get_raw_unprojected_position() - var viewport_width: float = get_viewport().size.x - var viewport_height: float = get_viewport().size.y - var camera_aspect: int = get_viewport().get_camera_3d().keep_aspect - var visible_rect_size: Vector2 = get_viewport().get_visible_rect().size - - unprojected_position = unprojected_position - visible_rect_size / 2 - if camera_aspect == Camera3D.KEEP_HEIGHT: - # Landscape View - var aspect_ratio_scale: float = viewport_width / viewport_height - unprojected_position.x = (unprojected_position.x / aspect_ratio_scale + 1) / 2 - unprojected_position.y = (unprojected_position.y + 1) / 2 - else: - # Portrait View - var aspect_ratio_scale: float = viewport_height / viewport_width - unprojected_position.x = (unprojected_position.x + 1) / 2 - unprojected_position.y = (unprojected_position.y / aspect_ratio_scale + 1) / 2 - - viewport_position = unprojected_position - _set_follow_gizmo_line_position(follow_target.global_position) - - FollowMode.THIRD_PERSON: - if not Engine.is_editor_hint(): - if not _has_follow_spring_arm: return - _follow_target_output_position = _get_target_position_offset() - else: - _follow_target_output_position = _get_target_position_offset_distance_direction() -# _follow_target_position = _get_target_position_offset_distance_direction() - _set_follow_gizmo_line_position(follow_target.global_position) - - -func _set_look_at_position() -> void: - match look_at_mode: - LookAtMode.MIMIC: - _look_at_target_output_position = global_position - look_at_target.global_basis.z - - LookAtMode.SIMPLE: - _look_at_target_output_position = look_at_target.global_position - - LookAtMode.GROUP: - if not _has_multiple_look_at_targets: - _look_at_target_output_position = look_at_targets[_look_at_targets_single_target_index].global_position - else: - var bounds: AABB = AABB(look_at_targets[0].global_position, Vector3.ZERO) - for node in look_at_targets: - bounds = bounds.expand(node.global_position) - _look_at_target_output_position = bounds.get_center() - - _look_at_target_output_position += look_at_offset - -func _get_target_position_offset() -> Vector3: - return follow_target.global_position + follow_offset - - -func _get_target_position_offset_distance() -> Vector3: - return _get_target_position_offset() + \ - transform.basis.z * follow_distance - -# Used in the editor for setting initial Third Person position and angle -func _get_target_position_offset_distance_direction() -> Vector3: - return _get_target_position_offset() + \ - follow_target.global_basis.z * \ - follow_distance * \ - Quaternion(follow_target.global_basis.x, vertical_rotation_offset) * \ - Quaternion(follow_target.global_basis.y, horizontal_rotation_offset) - - -func _set_follow_velocity(index: int, value: float) -> void: - _follow_velocity_ref[index] = value - -func _interpolate_position(delta: float) -> void: - if follow_damping and not Engine.is_editor_hint(): - if not _is_third_person_follow: - global_position = _follow_target_output_position - for i in 3: - _transform_output.origin[i] = _smooth_damp( - global_position[i], - _transform_output.origin[i], - i, - _follow_velocity_ref[i], - _set_follow_velocity, - follow_damping_value[i], - delta - ) - else: - for i in 3: - _camera_target.global_position[i] = _smooth_damp( - _follow_target_output_position[i], - _camera_target.global_position[i], - i, - _follow_velocity_ref[i], - _set_follow_velocity, - follow_damping_value[i], - delta - ) - _transform_output.origin = global_position - else: - _camera_target.global_position = _follow_target_output_position - _transform_output.origin = global_position - - if _is_third_person_follow: - var target_quat: Quaternion = _look_at_target_quat(_get_target_position_offset(), follow_target.global_basis.y) - var target_basis: Basis = Basis(target_quat) - _transform_output.basis = target_basis - global_basis = target_basis - - -func _look_at_target_quat(target_position: Vector3, up_direction: Vector3 = Vector3.UP) -> Quaternion: - var direction: Vector3 = -(target_position - global_position).normalized() - - var basis_z: Vector3 = direction.normalized() - var basis_x: Vector3 = up_direction.cross(basis_z) - var basis_y: Vector3 = basis_z.cross(basis_x.normalized()) - - var target_basis: Basis = Basis(basis_x, basis_y, basis_z) - - if target_basis.determinant() == 0: - if target_basis.z == Vector3.UP: - global_rotation_degrees.x = -90 - else: - global_rotation_degrees.x = 90 - - _transform_output.basis = global_basis - return quaternion - - return target_basis.get_rotation_quaternion().normalized() - -func _interpolate_rotation(delta: float) -> void: - if _has_up_target: - _up = up_target.global_basis.y - - var target_quat: Quaternion = _look_at_target_quat(_look_at_target_output_position, _up) - - if look_at_damping: - var current_quat: Quaternion = quaternion.normalized() - var damping_time: float = max(0.0001, look_at_damping_value) - var t: float = min(1.0, delta / damping_time) - - var dot: float = current_quat.dot(target_quat) - - if dot < 0.0: - target_quat = -target_quat - dot = -dot - - dot = clampf(dot, -1.0, 1.0) - - var theta: float = acos(dot) * t - var sin_theta: float = sin(theta) - var sin_theta_total: float = sin(acos(dot)) - - if is_zero_approx(sin_theta_total): return - - var ratio_a: float = cos(theta) - dot * sin_theta / sin_theta_total - var ratio_b: float = sin_theta / sin_theta_total - var output: Quaternion = current_quat * ratio_a + target_quat * ratio_b - - _transform_output.basis = Basis(output) - quaternion = output - else: - _transform_output.basis = Basis(target_quat) - quaternion = target_quat - - -func _smooth_damp(target_axis: float, self_axis: float, index: int, current_velocity: float, set_velocity: Callable, damping_time: float, delta: float) -> float: - damping_time = maxf(0.0001, damping_time) - var omega: float = 2 / damping_time - var x: float = omega * delta - var exponential: float = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x) - var diff: float = self_axis - target_axis - var _target_axis: float = target_axis - - var max_change: float = INF * damping_time - diff = clampf(diff, -max_change, max_change) - target_axis = self_axis - diff - - var temp: float = (current_velocity + omega * diff) * delta - set_velocity.call(index, (current_velocity - omega * temp) * exponential) - var output: float = target_axis + (diff + temp) * exponential - - ## To prevent overshooting - if (_target_axis - self_axis > 0.0) == (output > _target_axis): - output = _target_axis - set_velocity.call(index, (output - _target_axis) / delta) - - return output - - -func _get_raw_unprojected_position() -> Vector2: - return get_viewport().get_camera_3d().unproject_position(follow_target.global_position + follow_offset) - - -func _on_dead_zone_changed() -> void: - global_position = _get_target_position_offset_distance() - - -func _get_framed_side_offset() -> Vector2: - var frame_out_bounds: Vector2 - - if viewport_position.x < 0.5 - dead_zone_width / 2: - # Is outside left edge - frame_out_bounds.x = -1 - - if viewport_position.y < 0.5 - dead_zone_height / 2: - # Is outside top edge - frame_out_bounds.y = 1 - - if viewport_position.x > 0.5 + dead_zone_width / 2: - # Is outside right edge - frame_out_bounds.x = 1 - - if viewport_position.y > 0.5001 + dead_zone_height / 2: # 0.501 to resolve an issue where the bottom vertical Dead Zone never becoming 0 when the Dead Zone Vertical parameter is set to 0 - # Is outside bottom edge - frame_out_bounds.y = -1 - - return frame_out_bounds - - -func _set_layer(current_layers: int, layer_number: int, value: bool) -> int: - var mask: int = current_layers - - # From https://github.com/godotengine/godot/blob/51991e20143a39e9ef0107163eaf283ca0a761ea/scene/3d/camera_3d.cpp#L638 - if layer_number < 1 or layer_number > 20: - printerr("Render layer must be between 1 and 20.") - else: - if value: - mask |= 1 << (layer_number - 1) - else: - mask &= ~(1 << (layer_number - 1)) - - return mask - - -func _check_visibility() -> void: - _phantom_camera_manager.pcam_visibility_changed.emit(self) - - -func _follow_target_tree_exiting(target: Node) -> void: - if target == follow_target: - _should_follow = false - if _follow_targets.has(target): - _follow_targets.erase(target) - - -func _should_follow_checker() -> void: - if follow_mode == FollowMode.NONE: - _should_follow = false - return - - if not follow_mode == FollowMode.GROUP: - if is_instance_valid(follow_target): - _should_follow = true - else: - _should_follow = false - - -func _follow_targets_size_check() -> void: - var targets_size: int = 0 - _follow_target_physics_based = false - _follow_targets = [] - for i in follow_targets.size(): - if follow_targets[i] == null: continue - if is_instance_valid(follow_targets[i]): - _follow_targets.append(follow_targets[i]) - targets_size += 1 - _follow_targets_single_target_index = i - _check_physics_body(follow_targets[i]) - if not follow_targets[i].tree_exiting.is_connected(_follow_target_tree_exiting): - follow_targets[i].tree_exiting.connect(_follow_target_tree_exiting.bind(follow_targets[i])) - - match targets_size: - 0: - _should_follow = false - _has_multiple_follow_targets = false - 1: - _should_follow = true - _has_multiple_follow_targets = false - _: - _should_follow = true - _has_multiple_follow_targets = true - - -func _look_at_target_tree_exiting(target: Node) -> void: - if target == look_at_target: - _should_look_at = false - if look_at_targets.has(target): - erase_look_at_targets(target) - -func _up_target_tree_exiting() -> void: - up_target = null - - -func _should_look_at_checker() -> void: - if look_at_mode == LookAtMode.NONE: - _should_look_at = false - return - - if not look_at_mode == LookAtMode.GROUP: - if is_instance_valid(look_at_target): - _should_look_at = true - else: - _should_look_at = false - - -func _look_at_targets_size_check() -> void: - var targets_size: int = 0 - _look_at_target_physics_based = false - - for i in look_at_targets.size(): - if is_instance_valid(look_at_targets[i]): - targets_size += 1 - _look_at_targets_single_target_index = i - _check_physics_body(look_at_targets[i]) - if not look_at_targets[i].tree_exiting.is_connected(_look_at_target_tree_exiting): - look_at_targets[i].tree_exiting.connect(_look_at_target_tree_exiting.bind(look_at_targets[i])) - - match targets_size: - 0: - _should_look_at = false - _has_multiple_look_at_targets = false - 1: - _should_look_at = true - _has_multiple_look_at_targets = false - _: - _should_look_at = true - _has_multiple_look_at_targets = true - - -func _noise_emitted(emitter_noise_output: Transform3D, emitter_layer: int) -> void: - if noise_emitter_layer & emitter_layer != 0: - noise_emitted.emit(emitter_noise_output) - - -func _check_physics_body(target: Node3D) -> void: - if target is PhysicsBody3D: - var show_jitter_tips := ProjectSettings.get_setting("phantom_camera/tips/show_jitter_tips") - var physics_interpolation_enabled := ProjectSettings.get_setting("physics/common/physics_interpolation") - - ## NOTE - Feature Toggle - if Engine.get_version_info().major == 4 and \ - Engine.get_version_info().minor < 4: - if show_jitter_tips == null: # Default value is null when referencing custom Project Setting - print_rich("Following or Looking at a [b]PhysicsBody3D[/b] node will likely result in jitter - on lower physics ticks in particular.") - print_rich("If possible, will recommend upgrading to Godot 4.4, as it has built-in support for 3D Physics Interpolation, which will mitigate this issue.") - print_rich("Until then, try following the guide on the [url=https://phantom-camera.dev/support/faq#i-m-seeing-jitter-what-can-i-do]documentation site[/url] for better results.") - print_rich("This tip can be disabled from within [code]Project Settings / Phantom Camera / Tips / Show Jitter Tips[/code]") - return - ## NOTE - Only supported in Godot 4.4 or above - elif not physics_interpolation_enabled and show_jitter_tips == null: # Default value is null when referencing custom Project Setting - printerr("Physics Interpolation is disabled in the Project Settings, recommend enabling it to smooth out physics-based camera movement") - print_rich("This tip can be disabled from within [code]Project Settings / Phantom Camera / Tips / Show Jitter Tips[/code]") - _follow_target_physics_based = true - else: - _is_parents_physics(target) - physics_target_changed.emit() - - -func _is_parents_physics(target: Node = self) -> void: - var current_node: Node = target - while current_node: - current_node = current_node.get_parent() - if not current_node is PhysicsBody3D: continue - _follow_target_physics_based = true - - -func _camera_resource_changed() -> void: - camera_3d_resource_changed.emit() - - -func _set_follow_gizmo_line_position(target_position: Vector3) -> void: - if Engine.is_editor_hint(): - _follow_target_position = target_position - -func _check_draw_gizmo() -> void: - if _draw_follow_gizmo_line or _draw_look_at_gizmo_line: - _draw_gizmo = true - else: - _draw_gizmo = false - update_gizmos() - -#endregion - -#region Public Functions - -# TBD -#func get_unprojected_position() -> Vector2: - #var unprojected_position: Vector2 = _get_raw_unprojected_position() - #var viewport_width: float = get_viewport().size.x - #var viewport_height: float = get_viewport().size.y - #var camera_aspect: Camera3D.KeepAspect = get_viewport().get_camera_3d().keep_aspect - #var visible_rect_size: Vector2 = get_viewport().size -# - #unprojected_position = unprojected_position - visible_rect_size / 2 - #if camera_aspect == Camera3D.KeepAspect.KEEP_HEIGHT: -## print("Landscape View") - #var aspect_ratio_scale: float = viewport_width / viewport_height - #unprojected_position.x = (unprojected_position.x / aspect_ratio_scale + 1) / 2 - #unprojected_position.y = (unprojected_position.y + 1) / 2 - #else: -## print("Portrait View") - #var aspect_ratio_scale: float = viewport_height / viewport_width - #unprojected_position.x = (unprojected_position.x + 1) / 2 - #unprojected_position.y = (unprojected_position.y / aspect_ratio_scale + 1) / 2 -# - #return unprojected_position - - -## Returns the [Transform3D] value based on the [member follow_mode] / [member look_at_mode] target value. -func get_transform_output() -> Transform3D: - return _transform_output - - -## Returns the noise [Transform3D] value. -func get_noise_transform() -> Transform3D: - return _transform_noise - - -## Emits a noise based on a custom [Transform3D] value.[br] -## Use this function if you wish to make use of external noise patterns from, for example, other addons. -func emit_noise(value: Transform3D) -> void: - noise_emitted.emit(value) - - -## Teleports the [param PhantomCamera3D] and [Camera3D] to their designated position, -## bypassing the damping process. -func teleport_position() -> void: - _follow_velocity_ref = Vector3.ZERO - _set_follow_position() - _transform_output.origin = _follow_target_output_position - _phantom_camera_manager.pcam_teleport.emit(self) - - -# TODO: Enum link does link to anywhere is being tracked in: https://github.com/godotengine/godot/issues/106828 -## Returns [code]true[/code] if this [param PhantomCamera3D]'s [member follow_mode] is not set to [constant FollowMode.NONE] -## and has a valid [member follow_target]. -func is_following() -> bool: - return _should_follow - -# TODO: Enum link does link to anywhere is being tracked in: https://github.com/godotengine/godot/issues/106828 -## Returns [code]true[/code] if this [param PhantomCamera3D]'s [member look_at_mode] is not set to [constant LookAtMode.NONE] -## and has a valid [member look_at_target]. -func is_looking() -> bool: - return _should_look_at - - -## Returns the world space coodinate of where the target the camera is following. -## In most cases, this is the -func get_follow_target_position() -> Vector3: - return _follow_target_position - -## Returns the world space coordinate of where the camera is looking at. -func get_look_at_target_position() -> Vector3: - return _look_at_target_output_position - -#endregion - - -#region Setter & Getter Functions - -## Assigns the value of the [param has_tweened] property.[br] -## [b][color=yellow]Important:[/color][/b] This value can only be changed -## from the [PhantomCameraHost] script. -func set_tween_skip(caller: Node, value: bool) -> void: - if is_instance_of(caller, PhantomCameraHost): - _tween_skip = value - else: - printerr("Can only be called PhantomCameraHost class") -## Returns the current [param has_tweened] value. -func get_tween_skip() -> bool: - return _tween_skip - - -## Assigns new [member priority] value. -func set_priority(value: int) -> void: - priority = maxi(0, value) - if not is_node_ready(): return - if not Engine.has_singleton(_constants.PCAM_MANAGER_NODE_NAME): return - Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME).pcam_priority_changed.emit(self) -## Gets current [param Priority] value. -func get_priority() -> int: - return priority - - -## Assigns a new [PhantomCameraTween] resource to the [param PhantomCamera3D]. -func set_tween_resource(value: PhantomCameraTween) -> void: - tween_resource = value -## Gets the [param PhantomCameraTween] resource assigned to the [param PhantomCamera3D]. -## Returns null if there's nothing assigned to it. -func get_tween_resource() -> PhantomCameraTween: - return tween_resource - -## Assigns a new [param Tween Duration] to the [member tween_resource] value.[br] -## The duration value is in seconds. -func set_tween_duration(value: float) -> void: - tween_resource.duration = value -## Gets the current [param Tween] Duration value. The duration value is in -## [param seconds]. -func get_tween_duration() -> float: - return tween_resource.duration - -## Assigns a new [param Tween Transition] to the [member tween_resource] value.[br] -## The duration value is in seconds. -func set_tween_transition(value: int) -> void: - tween_resource.transition = value -## Gets the current [param Tween Transition] value. -func get_tween_transition() -> int: - return tween_resource.transition - -## Assigns a new [param Tween Ease] to the [member tween_resource] value.[br] -## The duration value is in seconds. -func set_tween_ease(value: int) -> void: - tween_resource.ease = value -## Gets the current [param Tween Ease] value. -func get_tween_ease() -> int: - return tween_resource.ease - -## Sets the [param PhantomCamera3D] active state[br] -## [b][color=yellow]Important:[/color][/b] This value can only be changed -## from the [PhantomCameraHost] script. -func set_is_active(node: Node, value: bool) -> void: - if node is PhantomCameraHost: - _is_active = value - else: - printerr("PCams can only be set from the PhantomCameraHost") -## Gets current active state of the [param PhantomCamera3D]. -## If it returns true, it means the [param PhantomCamera3D] is what the -## [param Camera3D] is currently following. -func is_active() -> bool: - return _is_active - - -## Enables or disables the [member tween_on_load]. -func set_tween_on_load(value: bool) -> void: - tween_on_load = value -## Gets the current [member tween_on_load] value. -func get_tween_on_load() -> bool: - return tween_on_load - - -## Sets the [member host_layers] value. -func set_host_layers(value: int) -> void: - host_layers = value - if is_instance_valid(_phantom_camera_manager): - _phantom_camera_manager.pcam_host_layer_changed.emit(self) - -## Enables or disables a given layer of [member host_layers]. -func set_host_layers_value(layer: int, value: bool) -> void: - host_layers = _set_layer(host_layers, layer, value) - -## Gets the current [member host_layers]. -func get_host_layers() -> int: - return host_layers - - -## Gets the current follow mode as an enum int based on [member FollowMode] enum.[br] -## [b]Note:[/b] Setting [member follow_mode] has purposely not been added. -## A separate [param PhantomCamera3D] instance should be used instead. -func get_follow_mode() -> int: - return follow_mode - - -## Assigns a new [Node3D] as the [member follow_target]. -func set_follow_target(value: Node3D) -> void: - if follow_mode == FollowMode.NONE or follow_mode == FollowMode.GROUP: return - if follow_target == value: return - follow_target = value - _follow_target_physics_based = false - if is_instance_valid(value): - if follow_mode == FollowMode.PATH: - if is_instance_valid(follow_path): - _should_follow = true - else: - _should_follow = false - else: - _should_follow = true - _check_physics_body(value) - if follow_mode == FollowMode.THIRD_PERSON and is_instance_valid(_follow_spring_arm): - _follow_spring_arm.add_excluded_object(follow_target) - if not follow_target.tree_exiting.is_connected(_follow_target_tree_exiting): - follow_target.tree_exiting.connect(_follow_target_tree_exiting.bind(follow_target)) - else: - if not follow_mode == FollowMode.GROUP: - _should_follow = false - follow_target_changed.emit() - update_gizmos() - notify_property_list_changed() -## Removes the current [Node3D] [member follow_target]. -func erase_follow_target() -> void: - follow_target = null -## Gets the current Node3D target. -func get_follow_target() -> Node3D: - return follow_target - - -## Assigns a new [Path3D] to the [member follow_path] property. -func set_follow_path(value: Path3D) -> void: - follow_path = value - if is_instance_valid(follow_path): - _should_follow_checker() - else: - _should_follow = false - -## Erases the current [Path3D] from [member follow_path] property. -func erase_follow_path() -> void: - follow_path = null - -## Gets the current [Path3D] from the [member follow_path] property. -func get_follow_path() -> Path3D: - return follow_path - - -## Assigns a new [param follow_targets] array value. -func set_follow_targets(value: Array[Node3D]) -> void: - if not follow_mode == FollowMode.GROUP: return - if follow_targets == value: return - follow_targets = value - _follow_targets_size_check() - - -## Adds a single [Node3D] to [member follow_targets] array. -func append_follow_targets(value: Node3D) -> void: - if not is_instance_valid(value): - printerr(value, " is not a valid Node3D instance") - return - - if not follow_targets.has(value): - follow_targets.append(value) - _follow_targets_size_check() - else: - printerr(value, " is already part of Follow Group") - -## Adds an Array of type [Node3D] to [member follow_targets] array. -func append_follow_targets_array(value: Array[Node3D]) -> void: - for target in value: - if not is_instance_valid(target): continue - if not follow_targets.has(target): - follow_targets.append(target) - _follow_targets_size_check() - else: - printerr(value, " is already part of Follow Group") - -## Removes [Node3D] from [member follow_targets]. -func erase_follow_targets(value: Node3D) -> void: - follow_targets.erase(value) - _follow_targets_size_check() - - -## Gets all [Node3D] from [follow_targets]. -func get_follow_targets() -> Array[Node3D]: - return follow_targets - - -## Assigns a new [param Vector3] for the [param follow_offset] property. -func set_follow_offset(value: Vector3) -> void: - var temp_offset: Vector3 = follow_offset - follow_offset = value - - if follow_axis_lock != FollowLockAxis.NONE: - temp_offset = temp_offset - value - match value: - FollowLockAxis.X: - _follow_axis_lock_value.x = _transform_output.origin.x + temp_offset.x - FollowLockAxis.Y: - _follow_axis_lock_value.y = _transform_output.origin.y + temp_offset.y - FollowLockAxis.Z: - _follow_axis_lock_value.z = _transform_output.origin.z + temp_offset.z - FollowLockAxis.XY: - _follow_axis_lock_value.x = _transform_output.origin.x + temp_offset.x - _follow_axis_lock_value.y = _transform_output.origin.y + temp_offset.y - FollowLockAxis.XZ: - _follow_axis_lock_value.x = _transform_output.origin.x + temp_offset.x - _follow_axis_lock_value.z = _transform_output.origin.z + temp_offset.z - FollowLockAxis.YZ: - _follow_axis_lock_value.y = _transform_output.origin.y + temp_offset.y - _follow_axis_lock_value.z = _transform_output.origin.z + temp_offset.z - FollowLockAxis.XYZ: - _follow_axis_lock_value.x = _transform_output.origin.x + temp_offset.x - _follow_axis_lock_value.y = _transform_output.origin.y + temp_offset.y - _follow_axis_lock_value.z = _transform_output.origin.z + temp_offset.z - -## Gets the current [param Vector3] for the [param follow_offset] property. -func get_follow_offset() -> Vector3: - return follow_offset - - -## Enables or disables [member follow_damping]. -func set_follow_damping(value: bool) -> void: - follow_damping = value - notify_property_list_changed() - -## Gets the currents [member follow_damping] property. -func get_follow_damping() -> bool: - return follow_damping - - -## Assigns new [member follow_damping_value] value. -func set_follow_damping_value(value: Vector3) -> void: - ## TODO - Should be using @export_range once minimum version support is Godot 4.3 - if value.x < 0: value.x = 0 - elif value.y < 0: value.y = 0 - elif value.z < 0: value.z = 0 - follow_damping_value = value - -## Gets the currents [member follow_damping_value] value. -func get_follow_damping_value() -> Vector3: - return follow_damping_value - - -## Assigns a new [member follow_distance] value. -func set_follow_distance(value: float) -> void: - follow_distance = value - -## Gets [member follow_distance] value. -func get_follow_distance() -> float: - return follow_distance - - -## Enables or disables [member auto_follow_distance] when using Group Follow. -func set_auto_follow_distance(value: bool) -> void: - auto_follow_distance = value - notify_property_list_changed() - -## Gets [member auto_follow_distance] state. -func get_auto_follow_distance() -> bool: - return auto_follow_distance - - -## Assigns new [member auto_follow_distance_min] value. -func set_auto_follow_distance_min(value: float) -> void: - auto_follow_distance_min = value - -## Gets [member auto_follow_distance_min] value. -func get_auto_follow_distance_min() -> float: - return auto_follow_distance_min - - -## Assigns new [member auto_follow_distance_max] value. -func set_auto_follow_distance_max(value: float) -> void: - auto_follow_distance_max = value -## Gets [member auto_follow_distance_max] value. -func get_auto_follow_distance_max() -> float: - return auto_follow_distance_max - - -## Assigns new [member auto_follow_distance_divisor] value. -func set_auto_follow_distance_divisor(value: float) -> void: - auto_follow_distance_divisor = value - -## Gets [member auto_follow_distance_divisor] value. -func get_auto_follow_distance_divisor() -> float: - return auto_follow_distance_divisor - - -## Assigns new rotation (in radians) value to [SpringArm3D] for -## [param ThirdPerson] [enum FollowMode]. -func set_third_person_rotation(value: Vector3) -> void: - if not _is_third_person_follow: - printerr("Follow Mode is not set to Third Person") - return - _follow_spring_arm.rotation = value - -## Gets the rotation value (in radians) from the [SpringArm3D] for -## [param ThirdPerson] [enum FollowMode]. -func get_third_person_rotation() -> Vector3: - if not _is_third_person_follow: - printerr("Follow Mode is not set to Third Person") - return Vector3.ZERO - return _follow_spring_arm.rotation - - -## Assigns new rotation (in degrees) value to [SpringArm3D] for -## [param ThirdPerson] [enum FollowMode]. -func set_third_person_rotation_degrees(value: Vector3) -> void: - if not _is_third_person_follow: - printerr("Follow Mode is not set to Third Person") - return - _follow_spring_arm.rotation_degrees = value - -## Gets the rotation value (in degrees) from the [SpringArm3D] for -## [param ThirdPerson] [enum FollowMode]. -func get_third_person_rotation_degrees() -> Vector3: - if not _is_third_person_follow: - printerr("Follow Mode is not set to Third Person") - return Vector3.ZERO - return _follow_spring_arm.rotation_degrees - - -## Assigns new [Quaternion] value to [SpringArm3D] for [param ThirdPerson] -## [enum FollowMode]. -func set_third_person_quaternion(value: Quaternion) -> void: - if not _is_third_person_follow: - printerr("Follow Mode is not set to Third Person") - return - _follow_spring_arm.quaternion = value - -## Gets the [Quaternion] value of the [SpringArm3D] for [param ThirdPerson] -## [enum Follow mode]. -func get_third_person_quaternion() -> Quaternion: - if not _is_third_person_follow: - printerr("Follow Mode is not set to Third Person") - return Quaternion.IDENTITY - return _follow_spring_arm.quaternion - - -## Assigns a new [member set_vertical_rotation_offset] value. -func set_vertical_rotation_offset(value: float) -> void: - vertical_rotation_offset = value - -## Gets the [member vertical_rotation] value. -func get_vertical_rotation_offset() -> float: - return vertical_rotation_offset - - -func set_horizontal_rotation_offset(value: float) -> void: - horizontal_rotation_offset = value - -## Gets the [member horizontal_rotation] value. -func get_horizontal_rotation_offset() -> float: - return horizontal_rotation_offset - - -## Assigns a new ThirdPerson [member SpringArm3D.length] value. -func set_spring_length(value: float) -> void: - follow_distance = value - if not is_instance_valid(_follow_spring_arm): return - _follow_spring_arm.spring_length = value - -## Gets the [member SpringArm3D.length] -## from a [param ThirdPerson] [enum follow_mode] instance. -func get_spring_length() -> float: - return follow_distance - - -## Assigns a new [member collision_mask] to the [SpringArm3D] when [enum FollowMode] -## is set to [param ThirdPerson]. -func set_collision_mask(value: int) -> void: - collision_mask = value - if not is_instance_valid(_follow_spring_arm): return - _follow_spring_arm.collision_mask = collision_mask - -## Enables or disables a specific [member collision_mask] layer for the -## [SpringArm3D] when [enum FollowMode] is set to [param ThirdPerson]. -func set_collision_mask_value(value: int, enabled: bool) -> void: - collision_mask = _set_layer(collision_mask, value, enabled) - if not is_instance_valid(_follow_spring_arm): return - _follow_spring_arm.collision_mask = collision_mask - -## Gets [member collision_mask] from the [SpringArm3D] when [enum FollowMode] -## is set to [param ThirdPerson]. -func get_collision_mask() -> int: - return collision_mask - - -## Assigns a new [SpringArm3D.shape] when [enum FollowMode] -## is set to [param ThirdPerson]. -func set_shape(value: Shape3D) -> void: - shape = value - if not is_instance_valid(_follow_spring_arm): return - _follow_spring_arm.shape = shape - -## Gets [param ThirdPerson] [member SpringArm3D.shape] value. -func get_shape() -> Shape3D: - return shape - - -## Assigns a new [member SpringArm3D.margin] value when [enum FollowMode] -## is set to [param ThirdPerson]. -func set_margin(value: float) -> void: - margin = value - if not is_instance_valid(_follow_spring_arm): return - _follow_spring_arm.margin = margin - -## Gets the [SpringArm3D.margin] when [enum FollowMode] is set to -## [param ThirdPerson]. -func get_margin() -> float: - return margin - - -func set_draw_follow_line(value: bool) -> void: - draw_follow_line = value - _draw_follow_gizmo_line = value - _check_draw_gizmo() - -func get_draw_follow_line() -> bool: - return draw_follow_line - - -func set_draw_look_at_line(value: bool) -> void: - draw_look_at_line = value - _draw_look_at_gizmo_line = value - _check_draw_gizmo() - -func get_draw_look_at_line() -> bool: - return draw_look_at_line - - -## Gets the current [member look_at_mode]. Value is based on [enum LookAtMode] -## enum.[br] -## Note: To set a new [member look_at_mode], a separate [param PhantomCamera3D] should be used. -func get_look_at_mode() -> int: - return look_at_mode - - -## Assigns new [Node3D] as [member look_at_target]. -func set_look_at_target(value: Node3D) -> void: - if look_at_mode == LookAtMode.NONE: return - if look_at_target == value: return - look_at_target = value - if not look_at_mode == LookAtMode.GROUP: - if is_instance_valid(look_at_target): - _should_look_at = true - _check_physics_body(value) - if not look_at_target.tree_exiting.is_connected(_look_at_target_tree_exiting): - look_at_target.tree_exiting.connect(_look_at_target_tree_exiting.bind(look_at_target)) - else: - _should_look_at = false - elif look_at_targets.size() == 0: - _should_look_at = false - - look_at_target_changed.emit() - notify_property_list_changed() - -## Gets current [Node3D] from [member look_at_target] property. -func get_look_at_target() -> Node3D: - return look_at_target - - -## Sets an array of type [Node3D] to [member set_look_at_targets]. -func set_look_at_targets(value: Array[Node3D]) -> void: - if not look_at_mode == LookAtMode.GROUP: return - if look_at_targets == value: return - look_at_targets = value - - _look_at_targets_size_check() - notify_property_list_changed() - -## Appends a [Node3D] to [member look_at_targets] array. -func append_look_at_target(value: Node3D) -> void: - if not is_instance_valid(value): - printerr(value, "is an invalid Node3D instance") - return - - if not look_at_targets.has(value): - look_at_targets.append(value) - _look_at_targets_size_check() - else: - printerr(value, " is already part of Look At Group") - - -## Appends an array of type [Node3D] to [member look_at_targets] array. -func append_look_at_targets_array(value: Array[Node3D]) -> void: - for val in value: - if not is_instance_valid(val): continue - if not look_at_targets.has(val): - look_at_targets.append(val) - _look_at_targets_size_check() - else: - printerr(val, " is already part of Look At Group") - -## Removes [Node3D] from [member look_at_targets] array. -func erase_look_at_targets(value: Node3D) -> void: - if look_at_targets.has(value): - look_at_targets.erase(value) - _look_at_targets_size_check() - else: - printerr(value, " is not part of Look At Group") - - -## Removes [Node3D] from [member look_at_targets] array. [br] -## @deprecated: Use [member erase_look_at_targets] instead. -func erase_look_at_targets_member(value: Node3D) -> void: - printerr("erase_look_at_targets_member is deprecated, use erase_look_at_targets instead") - erase_look_at_targets(value) - -## Gets all the [Node3D] instances in [member look_at_targets]. -func get_look_at_targets() -> Array[Node3D]: - return look_at_targets - - -## Assigns a new [Vector3] to the [member look_at_offset] value. -func set_look_at_offset(value: Vector3) -> void: - look_at_offset = value - -## Gets the current [member look_at_offset] value. -func get_look_at_offset() -> Vector3: - return look_at_offset - - -## Enables or disables [member look_at_damping]. -func set_look_at_damping(value: bool) -> void: - look_at_damping = value - notify_property_list_changed() - -## Gets the currents [member look_at_damping] property. -func get_look_at_damping() -> bool: - return look_at_damping - - -## Assigns new [member look_at_damping_value] value. -func set_look_at_damping_value(value: float) -> void: - look_at_damping_value = value - -## Gets the currents [member look_at_damping_value] value. -func get_look_at_damping_value() -> float: - return look_at_damping_value - -## Assigns the Follow Axis. -func set_follow_axis_lock(value: FollowLockAxis) -> void: - follow_axis_lock = value - - # Wait for the node to be ready before setting lock - if not is_node_ready(): await ready - - # Prevent axis lock from working in the editor - if value != FollowLockAxis.NONE and not Engine.is_editor_hint(): - _follow_axis_is_locked = true - match value: - FollowLockAxis.X: - _follow_axis_lock_value.x = _transform_output.origin.x - FollowLockAxis.Y: - _follow_axis_lock_value.y = _transform_output.origin.y - FollowLockAxis.Z: - _follow_axis_lock_value.z = _transform_output.origin.z - FollowLockAxis.XY: - _follow_axis_lock_value.x = _transform_output.origin.x - _follow_axis_lock_value.y = _transform_output.origin.y - FollowLockAxis.XZ: - _follow_axis_lock_value.x = _transform_output.origin.x - _follow_axis_lock_value.z = _transform_output.origin.z - FollowLockAxis.YZ: - _follow_axis_lock_value.y = _transform_output.origin.y - _follow_axis_lock_value.z = _transform_output.origin.z - FollowLockAxis.XYZ: - _follow_axis_lock_value.x = _transform_output.origin.x - _follow_axis_lock_value.y = _transform_output.origin.y - _follow_axis_lock_value.z = _transform_output.origin.z - else: - _follow_axis_is_locked = false - -## Gets the current [member follow_axis_lock] property. Value is based on [enum FollowLockAxis] enum. -func get_follow_axis_lock() -> FollowLockAxis: - return follow_axis_lock - - -## Sets the [member up] value. -func set_up(value: Vector3) -> void: - if value == Vector3.ZERO: - value = Vector3.UP - push_warning("Up value cannot be (0, 0, 0), resetting to (0, 1, 0).") - - up = value - if not _has_up_target: - _up = value - -## Gets the [member up] value. -func get_up() -> Vector3: - return up - - -## Sets the [member up_target]. -func set_up_target(value: Node3D) -> void: - up_target = value - if is_instance_valid(value): - _has_up_target = true - if not value.tree_exiting.is_connected(_up_target_tree_exiting): - value.tree_exiting.connect(_up_target_tree_exiting) - else: - _has_up_target = false - _up = up - notify_property_list_changed() - -## Gets the [member up_target]. -func get_up_target() -> Node3D: - return up_target - - -## Sets a [PhantomCameraNoise3D] resource. -func set_noise(value: PhantomCameraNoise3D) -> void: - noise = value - if value != null: - _has_noise_resource = true - noise.set_trauma(1) - else: - _has_noise_resource = false - _transform_noise = Transform3D() - -func get_noise() -> PhantomCameraNoise3D: - return noise - -func has_noise_resource() -> bool: - return _has_noise_resource - - -## Sets the [member noise_emitter_layer] value. -func set_noise_emitter_layer(value: int) -> void: - noise_emitter_layer = value - -## Enables or disables a given layer of [member noise_emitter_layer]. -func set_noise_emitter_layer_value(value: int, enabled: bool) -> void: - noise_emitter_layer = _set_layer(noise_emitter_layer, value, enabled) - -## Returns the [member noise_emitter_layer]. -func get_noise_emitter_layer() -> int: - return noise_emitter_layer - - -## Sets [member inactive_update_mode] property. -func set_inactive_update_mode(value: int) -> void: - inactive_update_mode = value - -## Gets [member inactive_update_mode] property. -func get_inactive_update_mode() -> int: - return inactive_update_mode - - -## Assigns a [Camera3DResource]. -func set_camera_3d_resource(value: Camera3DResource) -> void: - camera_3d_resource = value - camera_3d_resource_changed.emit() - if value: - if not camera_3d_resource.changed.is_connected(_camera_resource_changed): - camera_3d_resource.changed.connect(_camera_resource_changed) - -## Gets the [Camera3DResource]. -func get_camera_3d_resource() -> Camera3DResource: - return camera_3d_resource - - -func set_keep_aspect(value: int) -> void: - if not camera_3d_resource: - printerr("Can't assign a keep_aspect value. No Camera3DResource assigned to ", name) - return - keep_aspect = value - camera_3d_resource_property_changed.emit("keep_aspect", value) - -func get_keep_aspect() -> Variant: - if not camera_3d_resource: return null - return camera_3d_resource.keep_aspect - - -## Assigns a new [member Camera3D.cull_mask] value.[br] -## [b]Note:[/b] This will override and make the [param Camera3DResource] unique to -## this [param PhantomCamera3D]. -func set_cull_mask(value: int) -> void: - if not camera_3d_resource: - printerr("Can't assign a cull_mask value. No Camera3DResource assigned to ", name) - return - camera_3d_resource.cull_mask = value - camera_3d_resource_property_changed.emit("cull_mask", value) - -## Enables or disables a specific [member Camera3D.cull_mask] layer.[br] -## [b]Note:[/b] This will override and make the [param Camera3DResource] unique to -## this [param PhantomCamera3D]. -func set_cull_mask_value(layer_number: int, value: bool) -> void: - if not camera_3d_resource: - printerr("Can't assign a cull_mask value. No Camera3DResource assigned to ", name) - return - var mask: int = _set_layer(get_cull_mask(), layer_number, value) - camera_3d_resource.cull_mask = mask - camera_3d_resource_property_changed.emit("cull_mask", mask) - -## Gets the [member Camera3D.cull_mask] value assigned to the [Camera3DResource]. -func get_cull_mask() -> Variant: - if not camera_3d_resource: return null - return camera_3d_resource.cull_mask - - -## Assigns a new [Environment] resource to the [Camera3DResource]. -func set_environment(value: Environment) -> void: - environment = value - camera_3d_resource_property_changed.emit("environment", value) - -## Gets the [Camera3D.environment] value assigned to the [Camera3DResource]. -func get_environment() -> Environment: - return environment - - -## Assigns a new [CameraAttributes] resource to the [Camera3DResource]. -func set_attributes(value: CameraAttributes) -> void: - attributes = value - camera_3d_resource_property_changed.emit("attributes", value) - -## Gets the [Camera3D.attributes] value assigned to the [Camera3DResource]. -func get_attributes() -> CameraAttributes: - return attributes - -## Assigns a new [Compositor] resource to the [Camera3DResource]. -func set_compositor(value: Compositor) -> void: - compositor = value - camera_3d_resource_property_changed.emit("compositor", value) - -## Gets the [Camera3D.compositor] value assigned to the [Camera3DResource]. -func get_compositor() -> Compositor: - return compositor - - -## Assigns a new [member Camera3D.h_offset] value.[br] -## [b]Note:[/b] This will override and make the [param Camera3DResource] unique to -## this [param PhantomCamera3D]. -func set_h_offset(value: float) -> void: - if not camera_3d_resource: - printerr("Can't assign a h_offset value. No Camera3DResource assigned to ", name) - return - camera_3d_resource.h_offset = value - camera_3d_resource_property_changed.emit("h_offset", value) - -## Gets the [member Camera3D.h_offset] value assigned to the [param Camera3DResource]. -func get_h_offset() -> Variant: - if not camera_3d_resource: return null - return camera_3d_resource.h_offset - - -## Assigns a new [Camera3D.v_offset] value.[br] -## [b]Note:[/b] This will override and make the [param Camera3DResource] unique to -## this [param PhantomCamera3D]. -func set_v_offset(value: float) -> void: - if not camera_3d_resource: - printerr("Can't assign a v_offset value. No Camera3DResource assigned to ", name) - return - camera_3d_resource.v_offset = value - camera_3d_resource_property_changed.emit("v_offset", value) - -## Gets the [member Camera3D.v_offset] value assigned to the [param Camera3DResource]. -func get_v_offset() -> Variant: - if not camera_3d_resource: return null - return camera_3d_resource.v_offset - - -## Assigns a new [Camera3D.projection] value.[br] -## [b]Note:[/b] This will override and make the [param Camera3DResource] unique to -## this [param PhantomCamera3D]. -func set_projection(value: int) -> void: - if not camera_3d_resource: - printerr("Can't assign a projection value. No Camera3DResource assigned to ", name) - return - camera_3d_resource.projection = value - camera_3d_resource_property_changed.emit("projection", value) - -## Gets the [member Camera3D.projection] value assigned to the [param Camera3DResource]. -func get_projection() -> Variant: - if not camera_3d_resource: return null - return camera_3d_resource.projection - - -## Assigns a new [member Camera3D.fov] value.[br] -## [b]Note:[/b] This will override and make the [param Camera3DResource] unique to -## this [param PhantomCamera3D]. -func set_fov(value: float) -> void: - if not camera_3d_resource: - printerr("Can't assign a fov value. No Camera3DResource assigned to ", name) - return - camera_3d_resource.fov = value - camera_3d_resource_property_changed.emit("fov", value) - -## Gets the [member Camera3D.fov] value assigned to the [param Camera3DResource]. -func get_fov() -> Variant: - if not camera_3d_resource: return null - return camera_3d_resource.fov - - -## Assigns a new [member Camera3D.size] value.[br] -## [b]Note:[/b] This will override and make the [param Camera3DResource] unique to -## this [param PhantomCamera3D]. -func set_size(value: float) -> void: - if not camera_3d_resource: - printerr("Can't assign a size value. No Camera3DResource assigned to ", name) - return - camera_3d_resource.size = value - camera_3d_resource_property_changed.emit("size", value) - -## Gets the [member Camera3D.size] value assigned to the [param Camera3DResource]. -func get_size() -> Variant: - if not camera_3d_resource: return null - return camera_3d_resource.size - - -## Assigns a new [member Camera3D.frustum_offset] value.[br] -## [b]Note:[/b] This will override and make the [param Camera3DResource] unique to -## this [param PhantomCamera3D]. -func set_frustum_offset(value: Vector2) -> void: - if not camera_3d_resource: - printerr("Can't assign a frustum_offset value. No Camera3DResource assigned to ", name) - return - camera_3d_resource.frustum_offset = value - camera_3d_resource_property_changed.emit("frustum_offset", value) - -## Gets the [member Camera3D.frustum_offset] value assigned to the [param Camera3DResource]. -func get_frustum_offset() -> Variant: - if not camera_3d_resource: return null - return camera_3d_resource.frustum_offset - - -## Assigns a new [member Camera3D.near] value.[br] -## [b]Note:[/b] This will override and make the [param Camera3DResource] unique to -## this [param PhantomCamera3D]. -func set_near(value: float) -> void: - if not camera_3d_resource: - printerr("Can't assign a near value. No Camera3DResource assigned to ", name) - return - camera_3d_resource.near = value - camera_3d_resource_property_changed.emit("near", value) - -## Gets the [member Camera3D.near] value assigned to the [param Camera3DResource]. -func get_near() -> Variant: - if not camera_3d_resource: return null - return camera_3d_resource.near - - -## Assigns a new [member Camera3D.far] value.[br] -## [b]Note:[/b] This will override and make the [param Camera3DResource] unique to -## this [param PhantomCamera3D]. -func set_far(value: float) -> void: - if not camera_3d_resource: - printerr("Can't assign a far value. No Camera3DResource assigned to ", name) - return - camera_3d_resource.far = value - camera_3d_resource_property_changed.emit("far", value) - -## Gets the [member Camera3D.far] value assigned to the [param Camera3DResource]. -func get_far() -> Variant: - if not camera_3d_resource: return null - return camera_3d_resource.far - - -func get_follow_target_physics_based() -> bool: - return _follow_target_physics_based - - -## Used internally in phantom_camera_3d_gizmo.gd to check if the Follow line should be shown. -func draw_follow_gizmo_line() -> bool: - return _draw_follow_gizmo_line - -## Used internally in phantom_camera_3d_gizmo.gd to check if the Look At line should be shown. -func draw_look_at_gizmo_line() -> bool: - return _draw_look_at_gizmo_line - - -func get_class() -> String: - return "PhantomCamera3D" - - -func is_class(value) -> bool: - return value == "PhantomCamera3D" - -#endregion diff --git a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd.uid b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd.uid deleted file mode 100644 index 31a2fc9..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_3d.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://csjccrhj5wnx7 diff --git a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd deleted file mode 100644 index 43f6cf4..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd +++ /dev/null @@ -1,29 +0,0 @@ -@tool -extends RefCounted - -#region Constants - -#const PhantomCameraHost: Script = preload("res://addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd") - -const CAMERA_2D_NODE_NAME: StringName = "Camera2D" -const CAMERA_3D_NODE_NAME: StringName = "Camera3D" -const PCAM_HOST_NODE_NAME: StringName = "PhantomCameraHost" -const PCAM_MANAGER_NODE_NAME: String = "PhantomCameraManager" # TODO - Convert to StringName once https://github.com/godotengine/godot/pull/72702 is merged -const PCAM_2D_NODE_NAME: StringName = "PhantomCamera2D" -const PCAM_3D_NODE_NAME: StringName = "PhantomCamera3D" -const PCAM_HOST: StringName = "phantom_camera_host" - -const COLOR_2D: Color = Color("8DA5F3") -const COLOR_3D: Color = Color("FC7F7F") -const COLOR_PCAM: Color = Color("3AB99A") -const COLOR_PCAM_33: Color = Color("3ab99a33") -const PCAM_HOST_COLOR: Color = Color("E0E0E0") - -#endregion - -#region Group Names - -const PCAM_GROUP_NAME: StringName = "phantom_camera_group" -const PCAM_HOST_GROUP_NAME: StringName = "phantom_camera_host_group" - -#endregion diff --git a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd.uid b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd.uid deleted file mode 100644 index b1e3789..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dn74j5b5hdxu diff --git a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_2d.gd b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_2d.gd deleted file mode 100644 index 7306810..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_2d.gd +++ /dev/null @@ -1,264 +0,0 @@ -@tool -@icon("res://addons/phantom_camera/icons/phantom_camera_noise_emitter_2d.svg") -class_name PhantomCameraNoiseEmitter2D -extends Node2D - -## Emits positional and rotational noise to active [PhantomCamera2D]s and its corresponding [Camera2D]. -## -## Is a node meant to apply positional and rotational noise, also referred to as shake, to the [Camera2D]. -## It is designed for use cases such as when hitting or when being hit, earthquakes or to add a -## bit of slight movement to the camera to make it feel less static. -## The emitter can affect multiple [PhantomCamera2D] in a given scene based on which [member noise_emitter_layer] -## are enabled by calling its [method emit] function. At least one corresponding layer has to be -## set on the [PhantomCamera2D] and the emitter node. - -const _constants = preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd") - -#region Exported Proerpties - -## The [PhantomCameraNoise2D] resource that defines the noise pattern. -@export var noise: PhantomCameraNoise2D = null: - set = set_noise, - get = get_noise - -## If true, previews the noise in the editor - can be seen in the viewfinder. -@export var preview: bool = false: - set(value): - preview = value - _play = value - get: - return preview - -## If true, repeats the noise indefinitely once started. Otherwise, it will only be triggered once. [br] -@export var continuous: bool = false: - set = set_continuous, - get = get_continuous - -## Determines how long the noise should take to reach full [member intensity] once started.[br] -## The value is set in [b]seconds[/b]. -@export_exp_easing("positive_only", "suffix: s") var growth_time: float = 0: - set = set_growth_time, - get = get_growth_time - -## Sets the duration for the camera noise if [member continuous] is set to [b]false[/b].[br][br] -## The value is set in [b]seconds[/b]. -@export_range(0, 10, 0.001, "or_greater", "suffix: s") var duration: float = 1.0: - set = set_duration, - get = get_duration - -## Determines how long the noise should take to come to a full stop.[br] -## The value is set in [b]seconds[/b]. -@export_exp_easing("attenuation", "positive_only", "suffix: s") var decay_time: float = 0: - set = set_decay_time, - get = get_decay_time - -## Enabled layers will affect [PhantomCamera2D] nodes with at least one corresponding layer enabled.[br] -## Enabling multiple corresponding layers on the same [PhantomCamera2D] causes no additional effect. -@export_flags_2d_render var noise_emitter_layer: int = 1: - set = set_noise_emitter_layer, - get = get_noise_emitter_layer - -#endregion - - -#region Private Variables - -var _play: bool = false: - set(value): - _play = value - if value: - _elasped_play_time = 0 - _decay_countdown = 0 - _play = true - _should_grow = true - _start_duration_countdown = false - _should_decay = false - else: - _should_decay = true - if noise.randomize_noise_seed: - noise.noise_seed = randi() & 1000 - else: - noise.reset_noise_time() - get: - return _play - -var _start_duration_countdown: bool = false - -var _decay_countdown: float = 0 - -var _should_grow: bool = false - -var _should_decay: bool = false - -var _elasped_play_time: float = 0 - -var _noise_output: Transform2D = Transform2D() - -# NOTE - Temp solution until Godot has better plugin autoload recognition out-of-the-box. -var _phantom_camera_manager: Node - -#endregion - -#region Private Functions - -func _get_configuration_warnings() -> PackedStringArray: - if noise == null: - return ["Noise resource is required in order to trigger emitter."] - else: - return [] - - -func _validate_property(property) -> void: - if property.name == "duration" and continuous: - property.usage = PROPERTY_USAGE_NO_EDITOR - - -func _enter_tree() -> void: - _phantom_camera_manager = get_tree().root.get_node(_constants.PCAM_MANAGER_NODE_NAME) - - -func _process(delta: float) -> void: - if not _play and not _should_decay: return - if noise == null: - printerr("Noise resource missing in ", name) - _play = false - return - - _elasped_play_time += delta - - if _should_grow: - noise.set_trauma(minf(_elasped_play_time / growth_time, 1)) - if _elasped_play_time >= growth_time: - _should_grow = false - _start_duration_countdown = true - noise.set_trauma(1) - else: - noise.set_trauma(1) - - if not continuous: - if _start_duration_countdown: - if _elasped_play_time >= duration + growth_time: - _should_decay = true - _start_duration_countdown = false - - if _should_decay: - _decay_countdown += delta - noise.set_trauma(maxf(1 - (_decay_countdown / decay_time), 0)) - if _decay_countdown >= decay_time: - noise.set_trauma(0) - _play = false - preview = false - _should_decay = false - _elasped_play_time = 0 - _decay_countdown = 0 - - _noise_output = noise.get_noise_transform(delta) - _phantom_camera_manager.noise_2d_emitted.emit(_noise_output, noise_emitter_layer) - - -func _set_layer(current_layers: int, layer_number: int, value: bool) -> int: - var mask: int = current_layers - - # From https://github.com/godotengine/godot/blob/51991e20143a39e9ef0107163eaf283ca0a761ea/scene/3d/camera_3d.cpp#L638 - if layer_number < 1 or layer_number > 20: - printerr("Layer must be between 1 and 20.") - else: - if value: - mask |= 1 << (layer_number - 1) - else: - mask &= ~(1 << (layer_number - 1)) - - return mask - -#endregion - - -#region Public Functions - -## Emits noise to the [PhantomCamera2D]s that has at least one matching layers. -func emit() -> void: - if _play: _play = false - _play = true - -## Returns the state for the emitter. If true, the emitter is currently emitting. -func is_emitting() -> bool: - return _play - -## Stops the emitter from emitting noise. -func stop(should_decay: bool = true) -> void: - if should_decay: - _should_decay = true - else: - _play = false - -## Toggles the emitter on and off. -func toggle() -> void: - _play = !_play - -#endregion - - -#region Setter & Getter Functions - -## Sets the [member noise] resource. -func set_noise(value: PhantomCameraNoise2D) -> void: - noise = value - update_configuration_warnings() - -## Returns the [member noise] resource. -func get_noise() -> PhantomCameraNoise2D: - return noise - - -## Sets the [member continous] value. -func set_continuous(value: bool) -> void: - continuous = value - notify_property_list_changed() - -## Gets the [member continous] value. -func get_continuous() -> bool: - return continuous - - -## Sets the [member growth_time] value. -func set_growth_time(value: float) -> void: - growth_time = value - -## Returns the [member growth_time] value. -func get_growth_time() -> float: - return growth_time - - -## Sets the [member duration] value. -func set_duration(value: float) -> void: - duration = value - if duration == 0: - duration = 0.001 - -## Returns the [member duration] value. -func get_duration() -> float: - return duration - - -## Sets the [member decay_time] value. -func set_decay_time(value: float) -> void: - decay_time = value - -## Returns the [member decay_time] value. -func get_decay_time() -> float: - return decay_time - - -## Sets the [member noise_emitter_layer] value. -func set_noise_emitter_layer(value: int) -> void: - noise_emitter_layer = value - -## Enables or disables a given layer of [member noise_emitter_layer]. -func set_noise_emitter_value(value: int, enabled: bool) -> void: - noise_emitter_layer = _set_layer(noise_emitter_layer, value, enabled) - -## Returns the [member noise_emitter_layer] value. -func get_noise_emitter_layer() -> int: - return noise_emitter_layer - -#endregion diff --git a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_2d.gd.uid b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_2d.gd.uid deleted file mode 100644 index 19dbc96..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_2d.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bhd4nuiu23e7l diff --git a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_3d.gd b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_3d.gd deleted file mode 100644 index cd6c634..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_3d.gd +++ /dev/null @@ -1,265 +0,0 @@ -@tool -@icon("res://addons/phantom_camera/icons/phantom_camera_noise_emitter_3d.svg") -class_name PhantomCameraNoiseEmitter3D -extends Node3D - -## Emits positional and rotational noise to active [PhantomCamera3D]s and its corresponding [Camera3D]. -## -## Is a node meant to apply positional and rotational noise, also referred to as shake, to the [Camera3D]. -## It is designed for use cases such as when hitting or when being hit, earthquakes or to add a -## bit of slight movement to the camera to make it feel less static. -## The emitter can affect multiple [PhantomCamera3D] in a given scene based on which [member noise_emitter_layer] -## are enabled by calling its [method emit] function. At least one corresponding layer has to be -## set on the [PhantomCamera3D] and the emitter node. - -const _constants = preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd") - -#region Exported Properties - -## The [PhantomCameraNoise3D] resource that defines the noise pattern. -@export var noise: PhantomCameraNoise3D = null: - set = set_noise, - get = get_noise - -## If true, previews the noise in the Viewfinder. -@export var preview: bool = false: - set(value): - preview = value - _play = value - get: - return preview - -## If true, repeats the noise indefinitely once started.Otherwise, it will only be triggered once. [br] -## [b]Note:[/b] This will always be enabled if the resource is assigned the the [PhantomCamera3D]'s -## [member PhantomCamera3D.noise] property. -@export var continuous: bool = false: - set = set_continuous, - get = get_continuous - -## Determines how long the noise should take to reach full [member intensity] once started.[br] -## The value is set in [b]seconds[/b]. -@export_exp_easing("positive_only", "suffix: s") var growth_time: float = 0: - set = set_growth_time, - get = get_growth_time - -## Sets the duration for the camera noise if [member loop] is set to false.[br] -## If the duration is [param 0] then [member continous] becomes enabled.[br] -## The value is set in [b]seconds[/b]. -@export_range(0, 10, 0.001, "or_greater", "suffix: s") var duration: float = 1.0: - set = set_duration, - get = get_duration - -## Determines how long the noise should take to come to a full stop.[br] -## The value is set in [b]seconds[/b]. -@export_exp_easing("attenuation", "positive_only", "suffix: s") var decay_time: float = 0: - set = set_decay_time, - get = get_decay_time - -## Enabled layers will affect [PhantomCamera3D] nodes with at least one corresponding layer enabled.[br] -## Enabling multiple corresponding layers on the same [PhantomCamera3D] causes no additional effect. -@export_flags_3d_render var noise_emitter_layer: int = 1: - set = set_noise_emitter_layer, - get = get_noise_emitter_layer - -#endregion - -#region Private Variables - -var _play: bool = false: - set(value): - _play = value - if value: - _elasped_play_time = 0 - _decay_countdown = 0 - _play = true - _should_grow = true - _start_duration_countdown = false - _should_decay = false - else: - _should_decay = true - if noise.randomize_noise_seed: - noise.noise_seed = randi() & 1000 - else: - noise.reset_noise_time() - get: - return _play - -var _start_duration_countdown: bool = false - -var _decay_countdown: float = 0 - -var _should_grow: bool = false - -var _should_decay: bool = false - -var _elasped_play_time: float = 0 - -var _noise_output: Transform3D = Transform3D() - -# NOTE - Temp solution until Godot has better plugin autoload recognition out-of-the-box. -var _phantom_camera_manager: Node - -#endregion - -#region Private Functions - -func _get_configuration_warnings() -> PackedStringArray: - if noise == null: - return ["Noise resource is required in order to trigger emitter."] - else: - return [] - - -func _validate_property(property) -> void: - if property.name == "duration" and continuous: - property.usage = PROPERTY_USAGE_NO_EDITOR - - -func _enter_tree() -> void: - _phantom_camera_manager = get_tree().root.get_node(_constants.PCAM_MANAGER_NODE_NAME) - - -func _process(delta: float) -> void: - if not _play and not _should_decay: return - if noise == null: - printerr("Noise resource missing in ", name) - _play = false - return - - _elasped_play_time += delta - - if _should_grow: - noise.set_trauma(minf(_elasped_play_time / growth_time, 1)) - if _elasped_play_time >= growth_time: - _should_grow = false - _start_duration_countdown = true - noise.set_trauma(1) - - if not continuous: - if _start_duration_countdown: - if _elasped_play_time >= duration + growth_time: - _should_decay = true - _start_duration_countdown = false - - if _should_decay: - _decay_countdown += delta - noise.set_trauma(maxf(1 - (_decay_countdown / decay_time), 0)) - if _decay_countdown >= decay_time: - noise.set_trauma(0) - _play = false - preview = false - _should_decay = false - _elasped_play_time = 0 - _decay_countdown = 0 - - _noise_output = noise.get_noise_transform(delta) - _phantom_camera_manager.noise_3d_emitted.emit(_noise_output, noise_emitter_layer) - - -func _set_layer(current_layers: int, layer_number: int, value: bool) -> int: - var mask: int = current_layers - - # From https://github.com/godotengine/godot/blob/51991e20143a39e9ef0107163eaf283ca0a761ea/scene/3d/camera_3d.cpp#L638 - if layer_number < 1 or layer_number > 20: - printerr("Layer must be between 1 and 20.") - else: - if value: - mask |= 1 << (layer_number - 1) - else: - mask &= ~(1 << (layer_number - 1)) - - return mask - -#endregion - -#region Public Functions - -## Emits noise to the [PhantomCamera3D]s that has at least one matching layers. -func emit() -> void: - if _play: _play = false - _play = true - - -## Returns the state for the emitter. If true, the emitter is currently emitting. -func is_emitting() -> bool: - return _play - - -## Stops the emitter from emitting noise. -func stop(should_decay: bool = true) -> void: - if should_decay: - _should_decay = true - else: - _play = false - - -## Toggles the emitter on and off.[br] -func toggle() -> void: - _play = !_play - -#endregion - -#region Setter & Getter Functions - -## Sets the [member noise] resource. -func set_noise(value: PhantomCameraNoise3D) -> void: - noise = value - update_configuration_warnings() - -## Returns the [member noise] resource. -func get_noise() -> PhantomCameraNoise3D: - return noise - - -## Sets the [member continous] value. -func set_continuous(value: bool) -> void: - continuous = value - notify_property_list_changed() - -## Gets the [member continous] value. -func get_continuous() -> bool: - return continuous - - -## Sets the [member growth_time] value. -func set_growth_time(value: float) -> void: - growth_time = value - -## Returns the [member growth_time] value. -func get_growth_time() -> float: - return growth_time - - -## Sets the [member duration] value. -func set_duration(value: float) -> void: - duration = value - if duration == 0: - duration = 0.001 - -## Returns the [member duration] value. -func get_duration() -> float: - return duration - - -## Sets the [member decay_time] value. -func set_decay_time(value: float) -> void: - decay_time = value - -## Returns the [member decay_time] value. -func get_decay_time() -> float: - return decay_time - - -## Sets the [member noise_emitter_layer] value. -func set_noise_emitter_layer(value: int) -> void: - noise_emitter_layer = value - -## Enables or disables a given layer of [member noise_emitter_layer]. -func set_noise_emitter_value(value: int, enabled: bool) -> void: - noise_emitter_layer = _set_layer(noise_emitter_layer, value, enabled) - -## Returns the [member noise_emitter_layer] value. -func get_noise_emitter_layer() -> int: - return noise_emitter_layer - - #endregion diff --git a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_3d.gd.uid b/addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_3d.gd.uid deleted file mode 100644 index aa7e880..0000000 --- a/addons/phantom_camera/scripts/phantom_camera/phantom_camera_noise_emitter_3d.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ccmiitq0sdh7j diff --git a/addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs b/addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs deleted file mode 100644 index 7bd2a01..0000000 --- a/addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs +++ /dev/null @@ -1,113 +0,0 @@ -using Godot; - -#nullable enable - -namespace PhantomCamera; - -public enum InterpolationMode -{ - Auto, - Idle, - Physics -} - -public static class PhantomCameraHostExtensions -{ - public static PhantomCameraHost AsPhantomCameraHost(this Node node) - { - return new PhantomCameraHost(node); - } -} - -public class PhantomCameraHost() -{ - public Node Node { get; } = null!; - - public PhantomCameraHost(GodotObject node) : this() - { - Node = node as Node; - - var callablePCamBecameActive = Callable.From(pCam => PCamBecameActive?.Invoke(pCam)); - var callablePCamBecameInactive = Callable.From(pCam => PCamBecameInactive?.Invoke(pCam)); - - Node.Connect(SignalName.PCamBecameActive, callablePCamBecameActive); - Node.Connect(SignalName.PCamBecameInactive, callablePCamBecameInactive); - } - - public delegate void PCamBecameActiveEventHandler(Node pCam); - public delegate void PCamBecameInactiveEventHandler(Node pCam); - - public event PCamBecameActiveEventHandler? PCamBecameActive; - public event PCamBecameInactiveEventHandler? PCamBecameInactive; - - - private readonly Callable _callablePCamBecameActive; - private readonly Callable _callablePCamBecameInactive; - - // For when Godot becomes the minimum version - // public InterpolationMode InterpolationMode - // { - // get => (InterpolationMode)(int)Node.Call(MethodName.GetInterpolationMode); - // set => Node.Call(MethodName.SetInterpolationMode, (int)value); - // } - - public int HostLayers - { - get => (int)Node.Call(PhantomCamera.MethodName.GetHostLayers); - set => Node.Call(PhantomCamera.MethodName.SetHostLayers, value); - } - - public void SetHostLayersValue(int layer, bool value) => Node.Call(MethodName.SetHostLayersValue, layer, value); - - public Camera2D? Camera2D => (Camera2D?)Node.Get(PropertyName.Camera2D); - - public Camera3D? Camera3D => (Camera3D?)Node.Get(PropertyName.Camera3D); - - public InterpolationMode InterpolationMode - { - get => (InterpolationMode)(int)Node.Call(MethodName.GetInterpolationMode); - set => Node.Call(MethodName.SetInterpolationMode, (int)value); - } - - public bool TriggerPhantomCameraTween => (bool)Node.Call(MethodName.GetTriggerPhantomCameraTween); - - public PhantomCamera? GetActivePhantomCamera() - { - var result = Node.Call(MethodName.GetActivePhantomCamera); - - if (result.Obj is Node2D node2D) - { - return new PhantomCamera2D(node2D); - } - - if (result.Obj is Node3D node3D) - { - return new PhantomCamera3D(node3D); - } - - return null; - } - - public static class PropertyName - { - public static readonly StringName Camera2D = new("camera_2d"); - public static readonly StringName Camera3D = new("camera_3d"); - } - - public static class MethodName - { - public static readonly StringName GetActivePhantomCamera = new("get_active_pcam"); - public static readonly StringName GetTriggerPhantomCameraTween = new("get_trigger_pcam_tween"); - - public static readonly StringName GetInterpolationMode = new("get_interpolation_mode"); - public static readonly StringName SetInterpolationMode = new("set_interpolation_mode"); - - public static readonly StringName SetHostLayersValue = new("set_host_layers_value"); - } - - public static class SignalName - { - public static readonly StringName PCamBecameActive = new("pcam_became_active"); - public static readonly StringName PCamBecameInactive = new("pcam_became_inactive"); - } -} diff --git a/addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs.uid b/addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs.uid deleted file mode 100644 index ad4b4f5..0000000 --- a/addons/phantom_camera/scripts/phantom_camera_host/PhantomCameraHost.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cr8brwrls2nn3 diff --git a/addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd b/addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd deleted file mode 100644 index e5c5dd1..0000000 --- a/addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd +++ /dev/null @@ -1,1418 +0,0 @@ -@tool -@icon("res://addons/phantom_camera/icons/phantom_camera_host.svg") -class_name PhantomCameraHost -extends Node - -## Controls a scene's [Camera2D] (2D scenes) and [Camera3D] (3D scenes). -## -## All instantiated [param PhantomCameras] in a scene are assigned to a specific layer, where a -## PhantomCameraHost will react to those that corresponds. It is what determines which [param PhantomCamera] should -## be active. - -#region Constants - -const _constants := preload("res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_constants.gd") - -#endregion - - -#region Signals - -## Updates the viewfinder [param dead zones] sizes.[br] -## [b]Note:[/b] This is only being used in the editor viewfinder UI. -#signal update_editor_viewfinder -signal viewfinder_update(check_framed_view: bool) -signal viewfinder_disable_dead_zone - -## Used internally to check if the [param PhantomCameraHost] is valid. -## The result will be visible in the viewfinder when multiple instances are present. -signal has_error() - -## Emitted when a new [param PhantomCamera] becomes active and assigned to this [param PhantomCameraHost]. -signal pcam_became_active(pcam: Node) - -## Emitted when the currently active [param PhantomCamera] goes from being active to inactive. -signal pcam_became_inactive(pcam: Node) - -#endregion - - -#region Enums - -## Dictates whether if [param PhantomCameraHost]'s logic should be called in the physics or idle (process) frames. -enum InterpolationMode { - AUTO = 0, ## Automatically sets the [param Camera]'s logic to run in either physics or idle (process) frames depending on its active [param PhantomCamera]'s [param Follow] / [param Look At] Target - IDLE = 1, ## Always run the [param Camera] logic in idle (process) frames - PHYSICS = 2, ## Always run the [param Camera] logic in physics frames -} - -#endregion - - -#region Public Variables - -## Determines which [PhantomCamera2D] / [PhantomCamera3D] nodes this [param PhantomCameraHost] should recognise. -## At least one corresponding layer needs to be set on the [param PhantomCamera] for the [param PhantomCameraHost] node to work. -@export_flags_2d_render var host_layers: int = 1: - set = set_host_layers, - get = get_host_layers - -## Determines whether the [PhantomCamera2D] / [PhantomCamera3D] nodes this [param PhantomCameraHost] controls should use physics interpolation or not. -@export var interpolation_mode: InterpolationMode = InterpolationMode.AUTO: - set = set_interpolation_mode, - get = get_interpolation_mode - -#endregion - - -#region Private Variables - -var _active_pcam_2d: PhantomCamera2D = null -var _active_pcam_3d: Node = null ## Note: To support disable_3d export templates for 2D projects, this is purposely not strongly typed. -var _active_pcam_priority: int = -1 -var _active_pcam_missing: bool = true -var _active_pcam_has_damping: bool = false -var _follow_target_physics_based: bool = false - -var _prev_active_pcam_2d_transform: Transform2D = Transform2D() -var _prev_active_pcam_3d_transform: Transform3D = Transform3D() - -var _trigger_pcam_tween: bool = false -var _tween_elapsed_time: float = 0 -var _tween_duration: float = 0 -var _tween_is_instant: bool = false - -var _multiple_pcam_hosts: bool = false - -var _is_child_of_camera: bool = false -var _is_2d: bool = false - -var _viewfinder_node: Control = null -var _viewfinder_needed_check: bool = true - -var _camera_zoom: Vector2 = Vector2.ONE - -#region Camera3DResource - -var _prev_cam_attributes: CameraAttributes = null -var _cam_attribute_type: int = 0 # 0 = CameraAttributesPractical, 1 = CameraAttributesPhysical -var _cam_attribute_changed: bool = false -var _cam_attribute_assigned: bool = false - -#region CameraAttributes - -var _prev_cam_auto_exposure_scale: float = 0.4 -var _cam_auto_exposure_scale_changed: bool = false - -var _prev_cam_auto_exposure_speed: float = 0.5 -var _cam_auto_exposure_speed_changed: bool = false - -var _prev_cam_exposure_multiplier: float = 1.0 -var _cam_exposure_multiplier_changed: bool = false - -var _prev_cam_exposure_sensitivity: float = 100.0 -var _cam_exposure_sensitivity_changed: bool = false - -#region CameraAttributesPractical -var _prev_cam_exposure_min_sensitivity: float = 0.0 -var _cam_exposure_min_sensitivity_changed: bool = false - -var _prev_cam_exposure_max_sensitivity: float = 800.0 -var _cam_exposure_max_sensitivity_changed: bool = false - -var _prev_cam_dof_blur_amount: float = 0.1 -var _cam_dof_blur_amount_changed: bool = false - -var _cam_dof_blur_far_distance_default: float = 10 -var _prev_cam_dof_blur_far_distance: float = _cam_dof_blur_far_distance_default -var _cam_dof_blur_far_distance_changed: bool = false - -var _cam_dof_blur_far_transition_default: float = 5 -var _prev_cam_dof_blur_far_transition: float = _cam_dof_blur_far_transition_default -var _cam_dof_blur_far_transition_changed: bool = false - -var _cam_dof_blur_near_distance_default: float = 2 -var _prev_cam_dof_blur_near_distance: float = _cam_dof_blur_near_distance_default -var _cam_dof_blur_near_distance_changed: bool = false - -var _cam_dof_blur_near_transition_default: float = 1 -var _prev_cam_dof_blur_near_transition: float = _cam_dof_blur_near_transition_default -var _cam_dof_blur_near_transition_changed: bool = false - -#endregion - -#region CameraAttributesPhysical - -var _prev_cam_exposure_min_exposure_value: float = 10.0 -var _cam_exposure_min_exposure_value_changed: bool = false - -var _prev_cam_exposure_max_exposure_value: float = -8.0 -var _cam_exposure_max_exposure_value_changed: bool = false - -var _prev_cam_exposure_aperture: float = 16.0 -var _cam_exposure_aperture_changed: bool = false - -var _prev_cam_exposure_shutter_speed: float = 100.0 -var _cam_exposure_shutter_speed_changed: bool = false - -var _prev_cam_frustum_far: float = 4000.0 -var _cam_frustum_far_changed: bool = false - -var _prev_cam_frustum_focal_length: float = 35.0 -var _cam_frustum_focal_length_changed: bool = false - -var _prev_cam_frustum_near: float = 0.05 -var _cam_frustum_near_changed: bool = false - -var _prev_cam_frustum_focus_distance: float = 10.0 -var _cam_frustum_focus_distance_changed: bool = false - -#endregion - -var _prev_cam_h_offset: float = 0 -var _cam_h_offset_changed: bool = false - -var _prev_cam_v_offset: float = 0 -var _cam_v_offset_changed: bool = false - -var _prev_cam_fov: float = 75 -var _cam_fov_changed: bool = false - -var _prev_cam_size: float = 1 -var _cam_size_changed: bool = false - -var _prev_cam_frustum_offset: Vector2 = Vector2.ZERO -var _cam_frustum_offset_changed: bool = false - -var _prev_cam_near: float = 0.05 -var _cam_near_changed: bool = false - -var _prev_cam_far: float = 4000 -var _cam_far_changed: bool = false - -#endregion - -var _active_pcam_2d_glob_transform: Transform2D = Transform2D() -var _active_pcam_3d_glob_transform: Transform3D = Transform3D() - -var _has_noise_emitted: bool = false -var _reset_noise_offset_2d: bool = false -var _noise_emitted_output_2d: Transform2D = Transform2D() -var _noise_emitted_output_3d: Transform3D = Transform3D() - -#endregion - -# NOTE - Temp solution until Godot has better plugin autoload recognition out-of-the-box. -var _phantom_camera_manager: Node = null - -#region Public Variables - -var show_warning: bool = false - -## For 2D scenes, is the [Camera2D] instance the [param PhantomCameraHost] controls. -var camera_2d: Camera2D = null - -## For 3D scenes, is the [Camera3D] instance the [param PhantomCameraHost] controls. -var camera_3d: Node = null ## Note: To support disable_3d export templates for 2D projects, this is purposely not strongly typed. - -#endregion - -#region Private Functions - -## TBD - For when Godot 4.3 becomes a minimum version -#func _validate_property(property: Dictionary) -> void: - #if property.name == "interpolation_mode" and get_parent() is Node3D: - #property.usage = PROPERTY_USAGE_NO_EDITOR - - -func _get_configuration_warnings() -> PackedStringArray: - var parent: Node = get_parent() - var first_pcam_host_child: PhantomCameraHost - - if _is_2d: - if not parent is Camera2D: - show_warning = true - has_error.emit() - return["Needs to be a child of a Camera2D in order to work."] - else: - if not parent.is_class("Camera3D"): ## Note: To support disable_3d export templates for 2D projects, this is purposely not strongly typed. - show_warning = true - has_error.emit() - return["Needs to be a child of a Camera3D in order to work."] - - for child in parent.get_children(): - if not child is PhantomCameraHost: continue - if not is_instance_valid(first_pcam_host_child): - first_pcam_host_child = child - continue - elif not first_pcam_host_child == self: - show_warning = true - has_error.emit() - return["Only the first PhantomCameraHost child will be used."] - child.update_configuration_warnings() - - show_warning = false - has_error.emit() - return[] - - -func _enter_tree() -> void: - var parent: Node = get_parent() - if parent is Camera2D or parent.is_class("Camera3D"): ## Note: To support disable_3d export templates for 2D projects, this is purposely not strongly typed. - _phantom_camera_manager = get_tree().root.get_node(_constants.PCAM_MANAGER_NODE_NAME) - _phantom_camera_manager.pcam_host_added(self) - - _is_child_of_camera = true - if parent is Camera2D: - _is_2d = true - camera_2d = parent - ## Force applies position smoothing to be disabled - ## This is to prevent overlap with the interpolation of the PCam2D. - camera_2d.set_position_smoothing_enabled(false) - else: - _is_2d = false - camera_3d = parent - - if not is_node_ready(): return - - if _is_2d: - if not _phantom_camera_manager.get_phantom_camera_2ds().is_empty(): - for pcam in _phantom_camera_manager.get_phantom_camera_2ds(): - _pcam_added_to_scene(pcam) - - if not _phantom_camera_manager.limit_2d_changed.is_connected(_update_limit_2d): - _phantom_camera_manager.limit_2d_changed.connect(_update_limit_2d) - if not _phantom_camera_manager.draw_limit_2d.is_connected(_draw_limit_2d): - _phantom_camera_manager.draw_limit_2d.connect(_draw_limit_2d) - - else: - if not _phantom_camera_manager.get_phantom_camera_3ds().is_empty(): - for pcam in _phantom_camera_manager.get_phantom_camera_3ds(): - _pcam_added_to_scene(pcam) - - -func _exit_tree() -> void: - if is_instance_valid(_phantom_camera_manager): - _phantom_camera_manager.pcam_host_removed(self) - - -func _ready() -> void: - # Waits for the first process tick to finish before initializing any logic - # This should help with avoiding ocassional erratic camera movement upon running a scene - await get_tree().process_frame - - process_priority = 300 - process_physics_priority = 300 - - # PCam Host Signals - if Engine.has_singleton(_constants.PCAM_MANAGER_NODE_NAME): - _phantom_camera_manager = Engine.get_singleton(_constants.PCAM_MANAGER_NODE_NAME) - _phantom_camera_manager.pcam_host_layer_changed.connect(_pcam_host_layer_changed) - - # PCam Signals - _phantom_camera_manager.pcam_added_to_scene.connect(_pcam_added_to_scene) - _phantom_camera_manager.pcam_removed_from_scene.connect(_pcam_removed_from_scene) - - _phantom_camera_manager.pcam_priority_changed.connect(pcam_priority_updated) - _phantom_camera_manager.pcam_priority_override.connect(_pcam_priority_override) - - _phantom_camera_manager.pcam_visibility_changed.connect(_pcam_visibility_changed) - - _phantom_camera_manager.pcam_teleport.connect(_pcam_teleported) - - if _is_2d: - if not _phantom_camera_manager.limit_2d_changed.is_connected(_update_limit_2d): - _phantom_camera_manager.limit_2d_changed.connect(_update_limit_2d) - if not _phantom_camera_manager.draw_limit_2d.is_connected(_draw_limit_2d): - _phantom_camera_manager.draw_limit_2d.connect(_draw_limit_2d) - else: - printerr("Could not find Phantom Camera Manager singleton") - printerr("Make sure the addon is enable or that the singleton hasn't been disabled inside Project Settings / Globals") - - _find_pcam_with_highest_priority() - - if _is_2d: - camera_2d.offset = Vector2.ZERO - if not is_instance_valid(_active_pcam_2d): return - _active_pcam_2d_glob_transform = _active_pcam_2d.get_transform_output() - else: - if not is_instance_valid(_active_pcam_3d): return - _active_pcam_3d_glob_transform = _active_pcam_3d.get_transform_output() - - -func _pcam_host_layer_changed(pcam: Node) -> void: - if _pcam_is_in_host_layer(pcam): - _check_pcam_priority(pcam) - else: - if _is_2d: - if _active_pcam_2d == pcam: - _active_pcam_missing = true - _active_pcam_2d = null - _active_pcam_priority = -1 - pcam.set_is_active(self, false) - else: - if _active_pcam_3d == pcam: - _active_pcam_missing = true - _active_pcam_3d = null - _active_pcam_priority = -1 - pcam.set_is_active(self, false) - _find_pcam_with_highest_priority() - - -func _pcam_is_in_host_layer(pcam: Node) -> bool: - if pcam.host_layers & host_layers != 0: return true - return false - - -func _find_pcam_with_highest_priority() -> void: - var pcam_list: Array - if _is_2d: - pcam_list = _phantom_camera_manager.phantom_camera_2ds - else: - pcam_list = _phantom_camera_manager.phantom_camera_3ds - - for pcam in pcam_list: - _check_pcam_priority(pcam) - - -func _check_pcam_priority(pcam: Node) -> void: - if not _pcam_is_in_host_layer(pcam): return - if not pcam.visible: return # Prevents hidden PCams from becoming active - if pcam.get_priority() >= _active_pcam_priority: - _assign_new_active_pcam(pcam) - _active_pcam_missing = false - pcam.set_tween_skip(self, false) - - -func _assign_new_active_pcam(pcam: Node) -> void: - # Only checks if the scene tree is still present. - # Prevents a few errors and checks from happening if the scene is exited. - if not is_inside_tree(): return - var no_previous_pcam: bool - if is_instance_valid(_active_pcam_2d) or is_instance_valid(_active_pcam_3d): - if OS.has_feature("debug"): - viewfinder_disable_dead_zone.emit() - - if _is_2d: - _prev_active_pcam_2d_transform = camera_2d.global_transform - _active_pcam_2d.queue_redraw() - _active_pcam_2d.set_is_active(self, false) - _active_pcam_2d.became_inactive.emit() - pcam_became_inactive.emit(_active_pcam_2d) - - if _active_pcam_2d.physics_target_changed.is_connected(_check_pcam_physics): - _active_pcam_2d.physics_target_changed.disconnect(_check_pcam_physics) - - if _active_pcam_2d.noise_emitted.is_connected(_noise_emitted_2d): - _active_pcam_2d.noise_emitted.disconnect(_noise_emitted_2d) - - if _trigger_pcam_tween: - _active_pcam_2d.tween_interrupted.emit(pcam) - else: - _prev_active_pcam_3d_transform = camera_3d.global_transform - _active_pcam_3d.set_is_active(self, false) - _active_pcam_3d.became_inactive.emit() - pcam_became_inactive.emit(_active_pcam_3d) - - if _active_pcam_3d.physics_target_changed.is_connected(_check_pcam_physics): - _active_pcam_3d.physics_target_changed.disconnect(_check_pcam_physics) - - if _active_pcam_3d.noise_emitted.is_connected(_noise_emitted_3d): - _active_pcam_3d.noise_emitted.disconnect(_noise_emitted_3d) - - if _active_pcam_3d.camera_3d_resource_changed.is_connected(_camera_3d_resource_changed): - _active_pcam_3d.camera_3d_resource_changed.disconnect(_camera_3d_resource_changed) - - if _active_pcam_3d.camera_3d_resource_property_changed.is_connected(_camera_3d_resource_property_changed): - _active_pcam_3d.camera_3d_resource_property_changed.disconnect(_camera_3d_resource_property_changed) - - if _trigger_pcam_tween: - _active_pcam_3d.tween_interrupted.emit(pcam) - - if camera_3d.attributes != null: - var _attributes: CameraAttributes = camera_3d.attributes - - _prev_cam_exposure_multiplier = _attributes.exposure_multiplier - _prev_cam_auto_exposure_scale = _attributes.auto_exposure_scale - _prev_cam_auto_exposure_speed = _attributes.auto_exposure_speed - - if camera_3d.attributes is CameraAttributesPractical: - _attributes = _attributes as CameraAttributesPractical - - _prev_cam_dof_blur_amount = _attributes.dof_blur_amount - - if _attributes.dof_blur_far_enabled: - _prev_cam_dof_blur_far_distance = _attributes.dof_blur_far_distance - _prev_cam_dof_blur_far_transition = _attributes.dof_blur_far_transition - else: - _prev_cam_dof_blur_far_distance = _cam_dof_blur_far_distance_default - _prev_cam_dof_blur_far_transition = _cam_dof_blur_far_transition_default - - if _attributes.dof_blur_near_enabled: - _prev_cam_dof_blur_near_distance = _attributes.dof_blur_near_distance - _prev_cam_dof_blur_near_transition = _attributes.dof_blur_near_transition - else: - _prev_cam_dof_blur_near_distance = _cam_dof_blur_near_distance_default - _prev_cam_dof_blur_near_transition = _cam_dof_blur_near_transition_default - - if _attributes.auto_exposure_enabled: - _prev_cam_exposure_max_sensitivity = _attributes.auto_exposure_max_sensitivity - _prev_cam_exposure_min_sensitivity = _attributes.auto_exposure_min_sensitivity - - elif camera_3d.attributes is CameraAttributesPhysical: - _attributes = _attributes as CameraAttributesPhysical - - _prev_cam_frustum_focus_distance = _attributes.frustum_focus_distance - _prev_cam_frustum_focal_length = _attributes.frustum_focal_length - _prev_cam_frustum_far = _attributes.frustum_far - _prev_cam_frustum_near = _attributes.frustum_near - _prev_cam_exposure_aperture = _attributes.exposure_aperture - _prev_cam_exposure_shutter_speed = _attributes.exposure_shutter_speed - - if _attributes.auto_exposure_enabled: - _prev_cam_exposure_min_exposure_value = _attributes.auto_exposure_min_exposure_value - _prev_cam_exposure_max_exposure_value = _attributes.auto_exposure_max_exposure_value - - _prev_cam_h_offset = camera_3d.h_offset - _prev_cam_v_offset = camera_3d.v_offset - _prev_cam_fov = camera_3d.fov - _prev_cam_size = camera_3d.size - _prev_cam_frustum_offset = camera_3d.frustum_offset - _prev_cam_near = camera_3d.near - _prev_cam_far = camera_3d.far - - else: - no_previous_pcam = true - - ## Assign newly active pcam - if _is_2d: - _active_pcam_2d = pcam - _active_pcam_priority = _active_pcam_2d.priority - _active_pcam_has_damping = _active_pcam_2d.follow_damping - _tween_duration = _active_pcam_2d.tween_duration - - if not _active_pcam_2d.physics_target_changed.is_connected(_check_pcam_physics): - _active_pcam_2d.physics_target_changed.connect(_check_pcam_physics) - - if not _active_pcam_2d.noise_emitted.is_connected(_noise_emitted_2d): - _active_pcam_2d.noise_emitted.connect(_noise_emitted_2d) - else: - _active_pcam_3d = pcam - _active_pcam_priority = _active_pcam_3d.priority - _active_pcam_has_damping = _active_pcam_3d.follow_damping - _tween_duration = _active_pcam_3d.tween_duration - - if not Engine.is_editor_hint(): - # Assigns a default shape to SpringArm3D node is none is supplied - if _active_pcam_3d.follow_mode == _active_pcam_3d.FollowMode.THIRD_PERSON: - if not _active_pcam_3d.shape: - - var pyramid_shape_data = Engine.get_singleton("PhysicsServer3D").call("shape_get_data", - camera_3d.get_pyramid_shape_rid() - ) - var shape = ClassDB.instantiate("ConvexPolygonShape3D") - shape.points = pyramid_shape_data - _active_pcam_3d.shape = shape - - if not _active_pcam_3d.physics_target_changed.is_connected(_check_pcam_physics): - _active_pcam_3d.physics_target_changed.connect(_check_pcam_physics) - - if not _active_pcam_3d.noise_emitted.is_connected(_noise_emitted_3d): - _active_pcam_3d.noise_emitted.connect(_noise_emitted_3d) - - if not _active_pcam_3d.camera_3d_resource_changed.is_connected(_camera_3d_resource_changed): - _active_pcam_3d.camera_3d_resource_changed.connect(_camera_3d_resource_changed) - - if not _active_pcam_3d.camera_3d_resource_property_changed.is_connected(_camera_3d_resource_property_changed): - _active_pcam_3d.camera_3d_resource_property_changed.connect(_camera_3d_resource_property_changed) - - # Checks if the Camera3DResource has changed from the previous active PCam3D - if _active_pcam_3d.camera_3d_resource: - # Signal to detect if the Camera3D properties are being changed in the inspector - # This is to prevent accidential misalignment between the Camera3D and Camera3DResource - if Engine.is_editor_hint(): - if not Engine.get_singleton(&"EditorInterface").get_inspector().property_edited.is_connected(_camera_3d_edited): - Engine.get_singleton(&"EditorInterface").get_inspector().property_edited.connect(_camera_3d_edited) - if _prev_cam_h_offset != _active_pcam_3d.h_offset: - _cam_h_offset_changed = true - if _prev_cam_v_offset != _active_pcam_3d.v_offset: - _cam_v_offset_changed = true - if _prev_cam_fov != _active_pcam_3d.fov: - _cam_fov_changed = true - if _prev_cam_size != _active_pcam_3d.size: - _cam_size_changed = true - if _prev_cam_frustum_offset != _active_pcam_3d.frustum_offset: - _cam_frustum_offset_changed = true - if _prev_cam_near != _active_pcam_3d.near: - _cam_near_changed = true - if _prev_cam_far != _active_pcam_3d.far: - _cam_far_changed = true - else: - _cam_h_offset_changed = false - _cam_v_offset_changed = false - _cam_fov_changed = false - _cam_size_changed = false - _cam_frustum_offset_changed = false - _cam_near_changed = false - _cam_far_changed = false - _cam_attribute_changed = false - if Engine.is_editor_hint(): - if Engine.get_singleton(&"EditorInterface").get_inspector().property_edited.is_connected(_camera_3d_edited): - Engine.get_singleton(&"EditorInterface").get_inspector().property_edited.disconnect(_camera_3d_edited) - - if _active_pcam_3d.attributes == null: - _cam_attribute_changed = false - else: - if _prev_cam_attributes != _active_pcam_3d.attributes: - _prev_cam_attributes = _active_pcam_3d.attributes - _cam_attribute_changed = true - var _attributes: CameraAttributes = _active_pcam_3d.attributes - - if _prev_cam_auto_exposure_scale != _attributes.auto_exposure_scale: - _cam_auto_exposure_scale_changed = true - if _prev_cam_auto_exposure_speed != _attributes.auto_exposure_speed: - _cam_auto_exposure_speed_changed = true - if _prev_cam_exposure_multiplier != _attributes.exposure_multiplier: - _cam_exposure_multiplier_changed = true - if _prev_cam_exposure_sensitivity != _attributes.exposure_sensitivity: - _cam_exposure_sensitivity_changed = true - - if _attributes is CameraAttributesPractical: - _cam_attribute_type = 0 - - if camera_3d.attributes == null: - camera_3d.attributes = CameraAttributesPractical.new() - camera_3d.attributes = _active_pcam_3d.attributes.duplicate() - _cam_attribute_assigned = true - - if _prev_cam_exposure_min_sensitivity != _attributes.auto_exposure_min_sensitivity: - _cam_exposure_min_sensitivity_changed = true - if _prev_cam_exposure_max_sensitivity != _attributes.auto_exposure_max_sensitivity: - _cam_exposure_max_sensitivity_changed = true - - if _prev_cam_dof_blur_amount != _attributes.dof_blur_amount: - _cam_dof_blur_amount_changed = true - - if _prev_cam_dof_blur_far_distance != _attributes.dof_blur_far_distance: - _cam_dof_blur_far_distance_changed = true - camera_3d.attributes.dof_blur_far_enabled = true - if _prev_cam_dof_blur_far_transition != _attributes.dof_blur_far_transition: - _cam_dof_blur_far_transition_changed = true - camera_3d.attributes.dof_blur_far_enabled = true - - if _prev_cam_dof_blur_near_distance != _attributes.dof_blur_near_distance: - _cam_dof_blur_near_distance_changed = true - camera_3d.attributes.dof_blur_near_enabled = true - if _prev_cam_dof_blur_near_transition != _attributes.dof_blur_near_transition: - _cam_dof_blur_near_transition_changed = true - camera_3d.attributes.dof_blur_near_enabled = true - elif _attributes is CameraAttributesPhysical: - _cam_attribute_type = 1 - - if camera_3d.attributes == null: - camera_3d.attributes = CameraAttributesPhysical.new() - camera_3d.attributes = _active_pcam_3d.attributes.duplicate() - - if _prev_cam_exposure_min_exposure_value != _attributes.auto_exposure_min_exposure_value: - _cam_exposure_min_exposure_value_changed = true - if _prev_cam_exposure_max_exposure_value != _attributes.auto_exposure_max_exposure_value: - _cam_exposure_max_exposure_value_changed = true - - if _prev_cam_exposure_aperture != _attributes.exposure_aperture: - _cam_exposure_aperture_changed = true - if _prev_cam_exposure_shutter_speed != _attributes.exposure_shutter_speed: - _cam_exposure_shutter_speed_changed = true - - if _prev_cam_frustum_far != _attributes.frustum_far: - _cam_frustum_far_changed = true - - if _prev_cam_frustum_focal_length != _attributes.frustum_focal_length: - _cam_frustum_focal_length_changed = true - - if _prev_cam_frustum_focus_distance != _attributes.frustum_focus_distance: - _cam_frustum_focus_distance_changed = true - - if _prev_cam_frustum_near != _attributes.frustum_near: - _cam_frustum_near_changed = true - - if OS.has_feature("debug"): - viewfinder_update.emit(false) - - if _is_2d: - if _active_pcam_2d.show_viewfinder_in_play: - _viewfinder_needed_check = true - - _active_pcam_2d.set_is_active(self, true) - _active_pcam_2d.became_active.emit() - pcam_became_active.emit(_active_pcam_2d) - _camera_zoom = camera_2d.zoom - else: - if _active_pcam_3d.show_viewfinder_in_play: - _viewfinder_needed_check = true - - _active_pcam_3d.set_is_active(self, true) - _active_pcam_3d.became_active.emit() - pcam_became_active.emit(_active_pcam_3d) - if _active_pcam_3d.camera_3d_resource: - camera_3d.keep_aspect = _active_pcam_3d.keep_aspect - camera_3d.cull_mask = _active_pcam_3d.cull_mask - camera_3d.projection = _active_pcam_3d.projection - - if no_previous_pcam: - if _is_2d: - _prev_active_pcam_2d_transform = _active_pcam_2d.get_transform_output() - else: - _prev_active_pcam_3d_transform = _active_pcam_3d.get_transform_output() - - if pcam.get_tween_skip() or pcam.tween_duration == 0: - _tween_elapsed_time = pcam.tween_duration - if Engine.get_version_info().major == 4 and \ - Engine.get_version_info().minor >= 3: - _tween_is_instant = true - else: - _tween_elapsed_time = 0 - - _check_pcam_physics() - - _trigger_pcam_tween = true - - -func _check_pcam_physics() -> void: - if _is_2d: - if _active_pcam_2d.get_follow_target_physics_based() and interpolation_mode != InterpolationMode.IDLE: - _follow_target_physics_based = true - camera_2d.reset_physics_interpolation() - camera_2d.physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_ON - if ProjectSettings.get_setting("physics/common/physics_interpolation"): - camera_2d.process_callback = Camera2D.CAMERA2D_PROCESS_PHYSICS # Prevents a warning - else: - camera_2d.process_callback = Camera2D.CAMERA2D_PROCESS_IDLE - else: - _follow_target_physics_based = false - camera_2d.physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_INHERIT - if get_tree().physics_interpolation: - camera_2d.process_callback = Camera2D.CAMERA2D_PROCESS_PHYSICS # Prevents a warning - else: - camera_2d.process_callback = Camera2D.CAMERA2D_PROCESS_IDLE - else: - ## NOTE - Only supported in Godot 4.4 or later - if Engine.get_version_info().major == 4 and \ - Engine.get_version_info().minor >= 4: - if (get_tree().physics_interpolation or _active_pcam_3d.get_follow_target_physics_based()) and interpolation_mode != InterpolationMode.IDLE: - #if get_tree().physics_interpolation or _active_pcam_3d.get_follow_target_physics_based(): - _follow_target_physics_based = true - camera_3d.reset_physics_interpolation() - camera_3d.physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_ON - else: - _follow_target_physics_based = false - camera_3d.physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_INHERIT - - -## TODO - For 0.8 release -#func _find_pcam_with_highest_priority() -> void: - #var highest_priority_pcam: Node - #for pcam in _pcam_list: - #if not pcam.visible: continue # Prevents hidden PCams from becoming active - #if pcam.priority > _active_pcam_priority: - #_active_pcam_priority = pcam.priority - #highest_priority_pcam = pcam - #pcam.set_has_tweened(self, false) -# - #_active_pcam_missing = false -# - #if is_instance_valid(highest_priority_pcam): - #_assign_new_active_pcam(highest_priority_pcam) - #else: - #_active_pcam_missing = true - - -func _process(delta: float) -> void: - if _active_pcam_missing: return - - if not _follow_target_physics_based: _tween_follow_checker(delta) - - -func _physics_process(delta: float) -> void: - if _active_pcam_missing or not _follow_target_physics_based: return - _tween_follow_checker(delta) - - -func _tween_follow_checker(delta: float) -> void: - if _is_2d: - if not is_instance_valid(_active_pcam_2d): - _active_pcam_missing = true - return - - _active_pcam_2d.process_logic(delta) - _active_pcam_2d_glob_transform = _active_pcam_2d.get_transform_output() - - if _reset_noise_offset_2d: - camera_2d.offset = Vector2.ZERO # Resets noise position - _reset_noise_offset_2d = false - else: - if not is_instance_valid(_active_pcam_3d): - _active_pcam_missing = true - return - - _active_pcam_3d.process_logic(delta) - _active_pcam_3d_glob_transform = _active_pcam_3d.get_transform_output() - - if not _trigger_pcam_tween: - # Rechecks physics target if PCam transitioned with an instant tween - if _tween_is_instant: - _check_pcam_physics() - _tween_is_instant = false - _pcam_follow(delta) - else: - _pcam_tween(delta) - - # Camera Noise - if _is_2d: - if not _has_noise_emitted and not _active_pcam_2d.has_noise_resource(): return - camera_2d.offset += _active_pcam_2d.get_noise_transform().origin + _noise_emitted_output_2d.origin - if camera_2d.ignore_rotation and _noise_emitted_output_2d.get_rotation() != 0: - push_warning(camera_2d.name, " has ignore_rotation enabled. Uncheck the property if you want to apply rotational noise.") - else: - camera_2d.rotation += _active_pcam_2d.get_noise_transform().get_rotation() + _noise_emitted_output_2d.get_rotation() - _has_noise_emitted = false - _reset_noise_offset_2d = true - else: - if not _has_noise_emitted and not _active_pcam_3d.has_noise_resource(): return - camera_3d.global_transform *= _active_pcam_3d.get_noise_transform() * _noise_emitted_output_3d - _has_noise_emitted = false - - -func _pcam_follow(_delta: float) -> void: - if _active_pcam_missing or not _is_child_of_camera: return - - if _is_2d: - if _active_pcam_2d.snap_to_pixel: - var snap_to_pixel_glob_transform: Transform2D = _active_pcam_2d_glob_transform - snap_to_pixel_glob_transform.origin = snap_to_pixel_glob_transform.origin.round() - camera_2d.global_transform = snap_to_pixel_glob_transform - else: - camera_2d.global_transform = _active_pcam_2d_glob_transform - camera_2d.zoom = _active_pcam_2d.zoom - else: - camera_3d.global_transform = _active_pcam_3d_glob_transform - - if _viewfinder_needed_check: - _show_viewfinder_in_play() - _viewfinder_needed_check = false - - if Engine.is_editor_hint(): - if not _is_2d: - # TODO - Signal-based solution pending merge of: https://github.com/godotengine/godot/pull/99729 - if _active_pcam_3d.attributes != null: - camera_3d.attributes = _active_pcam_3d.attributes.duplicate() - - # TODO - Signal-based solution pending merge of: https://github.com/godotengine/godot/pull/99873 - if _active_pcam_3d.environment != null: - camera_3d.environment = _active_pcam_3d.environment.duplicate() - - -func _noise_emitted_2d(noise_output: Transform2D) -> void: - _noise_emitted_output_2d = noise_output - _has_noise_emitted = true - - -func _noise_emitted_3d(noise_output: Transform3D) -> void: - _noise_emitted_output_3d = noise_output - _has_noise_emitted = true - - -func _camera_3d_resource_changed() -> void: - if _active_pcam_3d.camera_3d_resource: - if Engine.is_editor_hint(): - if not Engine.get_singleton(&"EditorInterface").get_inspector().property_edited.is_connected(_camera_3d_edited): - Engine.get_singleton(&"EditorInterface").get_inspector().property_edited.connect(_camera_3d_edited) - camera_3d.keep_aspect = _active_pcam_3d.keep_aspect - camera_3d.cull_mask = _active_pcam_3d.cull_mask - camera_3d.h_offset = _active_pcam_3d.h_offset - camera_3d.v_offset = _active_pcam_3d.v_offset - camera_3d.projection = _active_pcam_3d.projection - camera_3d.fov = _active_pcam_3d.fov - camera_3d.size = _active_pcam_3d.size - camera_3d.frustum_offset = _active_pcam_3d.frustum_offset - camera_3d.near = _active_pcam_3d.near - camera_3d.far = _active_pcam_3d.far - else: - if Engine.is_editor_hint(): - if Engine.get_singleton(&"EditorInterface").get_inspector().property_edited.is_connected(_camera_3d_edited): - Engine.get_singleton(&"EditorInterface").get_inspector().property_edited.disconnect(_camera_3d_edited) - -func _camera_3d_edited(value: String) -> void: - if not Engine.get_singleton(&"EditorInterface").get_inspector().get_edited_object() == camera_3d: return - camera_3d.set(value, _active_pcam_3d.camera_3d_resource.get(value)) - push_warning("Camera3D properties are being overridden by ", _active_pcam_3d.name, "'s Camera3DResource") - -func _camera_3d_resource_property_changed(property: StringName, value: Variant) -> void: - camera_3d.set(property, value) - - -func _pcam_tween(delta: float) -> void: - # TODO - Should be optimised - # Run at the first tween frame - if _tween_elapsed_time == 0: - if _is_2d: - _active_pcam_2d.tween_started.emit() - _active_pcam_2d.reset_limit() - else: - _active_pcam_3d.tween_started.emit() - - _tween_elapsed_time = min(_tween_duration, _tween_elapsed_time + delta) - - if _is_2d: - _active_pcam_2d.is_tweening.emit() - var interpolation_destination: Vector2 = _tween_interpolate_value( - _prev_active_pcam_2d_transform.origin, - _active_pcam_2d_glob_transform.origin, - _active_pcam_2d.tween_duration, - _active_pcam_2d.tween_transition, - _active_pcam_2d.tween_ease - ) - - if _active_pcam_2d.snap_to_pixel: - camera_2d.global_position = interpolation_destination.round() - else: - camera_2d.global_position = interpolation_destination - - camera_2d.rotation = _tween_interpolate_value( - _prev_active_pcam_2d_transform.get_rotation(), - _active_pcam_2d_glob_transform.get_rotation(), - _active_pcam_2d.tween_duration, - _active_pcam_2d.tween_transition, - _active_pcam_2d.tween_ease - ) - camera_2d.zoom = _tween_interpolate_value( - _camera_zoom, - _active_pcam_2d.zoom, - _active_pcam_2d.tween_duration, - _active_pcam_2d.tween_transition, - _active_pcam_2d.tween_ease - ) - else: - _active_pcam_3d.is_tweening.emit() - camera_3d.global_position = _tween_interpolate_value( - _prev_active_pcam_3d_transform.origin, - _active_pcam_3d_glob_transform.origin, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - - var prev_active_pcam_3d_quat: Quaternion = Quaternion(_prev_active_pcam_3d_transform.basis.orthonormalized()) - camera_3d.quaternion = \ - Tween.interpolate_value( - prev_active_pcam_3d_quat, \ - prev_active_pcam_3d_quat.inverse() * Quaternion(_active_pcam_3d_glob_transform.basis.orthonormalized()), - _tween_elapsed_time, \ - _active_pcam_3d.tween_duration, \ - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - - if _cam_attribute_changed: - if _active_pcam_3d.attributes.auto_exposure_enabled: - if _cam_auto_exposure_scale_changed: - camera_3d.attributes.auto_exposure_scale = \ - _tween_interpolate_value( - _prev_cam_auto_exposure_scale, - _active_pcam_3d.attributes.auto_exposure_scale, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - if _cam_auto_exposure_speed_changed: - camera_3d.attributes.auto_exposure_speed = \ - _tween_interpolate_value( - _prev_cam_auto_exposure_scale, - _active_pcam_3d.attributes.auto_exposure_scale, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - - if _cam_attribute_type == 0: # CameraAttributePractical - if _active_pcam_3d.attributes.auto_exposure_enabled: - if _cam_exposure_min_sensitivity_changed: - camera_3d.attributes.auto_exposure_min_sensitivity = \ - _tween_interpolate_value( - _prev_cam_exposure_min_sensitivity, - _active_pcam_3d.attributes.auto_exposure_min_sensitivity, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - if _cam_exposure_max_sensitivity_changed: - camera_3d.attributes.auto_exposure_max_sensitivity = \ - _tween_interpolate_value( - _prev_cam_exposure_max_sensitivity, - _active_pcam_3d.attributes.auto_exposure_max_sensitivity, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - if _cam_dof_blur_amount_changed: - camera_3d.attributes.dof_blur_amount = \ - _tween_interpolate_value( - _prev_cam_dof_blur_amount, - _active_pcam_3d.attributes.dof_blur_amount, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - if _cam_dof_blur_far_distance_changed: - camera_3d.attributes.dof_blur_far_distance = \ - _tween_interpolate_value( - _prev_cam_dof_blur_far_distance, - _active_pcam_3d.attributes.dof_blur_far_distance, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - if _cam_dof_blur_far_transition_changed: - camera_3d.attributes.dof_blur_far_transition = \ - _tween_interpolate_value( - _prev_cam_dof_blur_far_transition, - _active_pcam_3d.attributes.dof_blur_far_transition, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - if _cam_dof_blur_near_distance_changed: - camera_3d.attributes.dof_blur_near_distance = \ - _tween_interpolate_value( - _prev_cam_dof_blur_near_distance, - _active_pcam_3d.attributes.dof_blur_near_distance, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - if _cam_dof_blur_near_transition_changed: - camera_3d.attributes.dof_blur_near_transition = \ - _tween_interpolate_value( - _prev_cam_dof_blur_near_transition, - _active_pcam_3d.attributes.dof_blur_near_transition, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - elif _cam_attribute_type == 1: # CameraAttributePhysical - if _cam_dof_blur_near_transition_changed: - camera_3d.attributes.auto_exposure_max_exposure_value = \ - _tween_interpolate_value( - _prev_cam_exposure_max_exposure_value, - _active_pcam_3d.attributes.auto_exposure_max_exposure_value, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - if _cam_exposure_min_exposure_value_changed: - camera_3d.attributes.auto_exposure_min_exposure_value = \ - _tween_interpolate_value( - _prev_cam_exposure_min_exposure_value, - _active_pcam_3d.attributes.auto_exposure_min_exposure_value, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - if _cam_exposure_aperture_changed: - camera_3d.attributes.exposure_aperture = \ - _tween_interpolate_value( - _prev_cam_exposure_aperture, - _active_pcam_3d.attributes.exposure_aperture, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - if _cam_exposure_shutter_speed_changed: - camera_3d.attributes.exposure_shutter_speed = \ - _tween_interpolate_value( - _prev_cam_exposure_shutter_speed, - _active_pcam_3d.attributes.exposure_shutter_speed, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - if _cam_frustum_far_changed: - camera_3d.attributes.frustum_far = \ - _tween_interpolate_value( - _prev_cam_frustum_far, - _active_pcam_3d.attributes.frustum_far, - _active_pcam_3d.tween_duration(), - _active_pcam_3d.tween_transition(), - _active_pcam_3d.tween_ease - ) - if _cam_frustum_near_changed: - camera_3d.attributes.frustum_near = \ - _tween_interpolate_value( - _prev_cam_frustum_far, - _active_pcam_3d.attributes.frustum_near, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - if _cam_frustum_focal_length_changed: - camera_3d.attributes.frustum_focal_length = \ - _tween_interpolate_value( - _prev_cam_frustum_focal_length, - _active_pcam_3d.attributes.frustum_focal_length, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - if _cam_frustum_focus_distance_changed: - camera_3d.attributes.frustum_focus_distance = \ - _tween_interpolate_value( - _prev_cam_frustum_focus_distance, - _active_pcam_3d.attributes.frustum_focus_distance, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - - if _cam_h_offset_changed: - camera_3d.h_offset = \ - _tween_interpolate_value( - _prev_cam_h_offset, - _active_pcam_3d.h_offset, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - - if _cam_v_offset_changed: - camera_3d.v_offset = \ - _tween_interpolate_value( - _prev_cam_v_offset, - _active_pcam_3d.v_offset, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - - if _cam_fov_changed: - camera_3d.fov = \ - _tween_interpolate_value( - _prev_cam_fov, - _active_pcam_3d.fov, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - - if _cam_size_changed: - camera_3d.size = \ - _tween_interpolate_value( - _prev_cam_size, - _active_pcam_3d.size, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - - if _cam_frustum_offset_changed: - camera_3d.frustum_offset = \ - _tween_interpolate_value( - _prev_cam_frustum_offset, - _active_pcam_3d.frustum_offset, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - - if _cam_near_changed: - camera_3d.near = \ - _tween_interpolate_value( - _prev_cam_near, - _active_pcam_3d.near, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - - if _cam_far_changed: - camera_3d.far = \ - _tween_interpolate_value( - _prev_cam_far, - _active_pcam_3d.far, - _active_pcam_3d.tween_duration, - _active_pcam_3d.tween_transition, - _active_pcam_3d.tween_ease - ) - - # Forcefully disables physics interpolation when tweens are instant - if _tween_is_instant: - if _is_2d: - camera_2d.physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_OFF - camera_2d.reset_physics_interpolation() - else: - if Engine.get_version_info().major == 4 and \ - Engine.get_version_info().minor >= 4: - camera_3d.physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_OFF - camera_3d.reset_physics_interpolation() - - if _tween_elapsed_time < _tween_duration: return - - _trigger_pcam_tween = false - _tween_elapsed_time = 0 - viewfinder_update.emit(true) - - if _is_2d: - _active_pcam_2d.update_limit_all_sides() - _active_pcam_2d.tween_completed.emit() - _active_pcam_2d.set_tween_skip(self, false) - if Engine.is_editor_hint(): - _active_pcam_2d.queue_redraw() - else: - if _active_pcam_3d.camera_3d_resource and _active_pcam_3d.attributes != null: - if _cam_attribute_type == 0: - if not _active_pcam_3d.attributes.dof_blur_far_enabled: - camera_3d.attributes.dof_blur_far_enabled = false - if not _active_pcam_3d.attributes.dof_blur_near_enabled: - camera_3d.attributes.dof_blur_near_enabled = false - _cam_h_offset_changed = false - _cam_v_offset_changed = false - _cam_fov_changed = false - _cam_size_changed = false - _cam_frustum_offset_changed = false - _cam_near_changed = false - _cam_far_changed = false - _cam_attribute_changed = false - - _active_pcam_3d.set_tween_skip(self, false) - _active_pcam_3d.tween_completed.emit() - - -func _tween_interpolate_value(from: Variant, to: Variant, duration: float, transition_type: int, ease_type: int) -> Variant: - return Tween.interpolate_value( - from, \ - to - from, - _tween_elapsed_time, \ - duration, \ - transition_type, - ease_type, - ) - - -func _show_viewfinder_in_play() -> void: - # Don't show the viewfinder in the actual editor or project builds - if Engine.is_editor_hint() or !OS.has_feature("editor"): return - - # Default the viewfinder node to be hidden - if is_instance_valid(_viewfinder_node): - _viewfinder_node.visible = false - - if _is_2d: - if not _active_pcam_2d.show_viewfinder_in_play: return - if _active_pcam_2d.follow_mode != _active_pcam_2d.FollowMode.FRAMED: return - else: - if not _active_pcam_3d.show_viewfinder_in_play: return - if _active_pcam_3d.follow_mode != _active_pcam_2d.FollowMode.FRAMED: return - - var canvas_layer: CanvasLayer = CanvasLayer.new() - get_tree().get_root().add_child(canvas_layer) - - # Instantiate the viewfinder scene if it isn't already - if not is_instance_valid(_viewfinder_node): - var _viewfinder_scene := load("res://addons/phantom_camera/panel/viewfinder/viewfinder_panel.tscn") - _viewfinder_node = _viewfinder_scene.instantiate() - canvas_layer.add_child(_viewfinder_node) - - _viewfinder_node.visible = true - _viewfinder_node.update_dead_zone() - - -func _update_limit_2d(side: int, limit: int) -> void: - if is_instance_valid(camera_2d): - camera_2d.set_limit(side, limit) - -func _draw_limit_2d(enabled: bool) -> void: - camera_2d.set_limit_drawing_enabled(enabled) - - -## Called when a [param PhantomCamera] is added to the scene.[br] -## [b]Note:[/b] This can only be called internally from a [param PhantomCamera] node. -func _pcam_added_to_scene(pcam: Node) -> void: - if not pcam.is_node_ready(): await pcam.ready - _check_pcam_priority(pcam) - - -## Called when a [param PhantomCamera] is removed from the scene.[br] -## [b]Note:[/b] This can only be called internally from a -## [param PhantomCamera] node. -func _pcam_removed_from_scene(pcam: Node) -> void: - if _is_2d: - if pcam == _active_pcam_2d: - _active_pcam_2d = null - _active_pcam_missing = true - _active_pcam_priority = -1 - _find_pcam_with_highest_priority() - else: - if pcam == _active_pcam_3d: - _active_pcam_3d = null - _active_pcam_missing = true - _active_pcam_priority = -1 - _find_pcam_with_highest_priority() - - -func _pcam_visibility_changed(pcam: Node) -> void: - if pcam == _active_pcam_2d or pcam == _active_pcam_3d: - _active_pcam_priority = -1 - _find_pcam_with_highest_priority() - return - _check_pcam_priority(pcam) - - -func _pcam_teleported(pcam: Node) -> void: - if _is_2d: - if not pcam == _active_pcam_2d: return - if not is_instance_valid(camera_2d): return - camera_2d.global_position = _active_pcam_2d.get_transform_output().origin - camera_2d.reset_physics_interpolation() - else: - if not pcam == _active_pcam_3d: return - if not is_instance_valid(camera_3d): return - camera_3d.global_position = _active_pcam_3d.get_transform_output().origin - camera_3d.reset_physics_interpolation() - - -func _set_layer(current_layers: int, layer_number: int, value: bool) -> int: - var mask: int = current_layers - - # From https://github.com/godotengine/godot/blob/51991e20143a39e9ef0107163eaf283ca0a761ea/scene/3d/camera_3d.cpp#L638 - if layer_number < 1 or layer_number > 20: - printerr("Render layer must be between 1 and 20.") - else: - if value: - mask |= 1 << (layer_number - 1) - else: - mask &= ~(1 << (layer_number - 1)) - - return mask - -#endregion - -#region Public Functions - -func pcam_priority_updated(pcam: Node) -> void: - if not is_instance_valid(pcam): return - if not _pcam_is_in_host_layer(pcam): return - - if Engine.is_editor_hint(): - if _is_2d: - if _active_pcam_2d.priority_override: return - if not is_instance_valid(_active_pcam_2d): return - else: - if _active_pcam_3d.priority_override: return - if not is_instance_valid(_active_pcam_3d): return - - ## Currently active PCam changed Priority - if pcam == _active_pcam_2d or pcam == _active_pcam_3d: - ## If PCam Node has become invisible / disabled - if not pcam.visible: - refresh_pcam_list_priorty() - ## If currently active PCam has a reduced Priority - elif pcam.priority < _active_pcam_priority: - _active_pcam_priority = pcam.priority - _find_pcam_with_highest_priority() - ## Another PCam changed Priority - else: - ## Make new PCam active if Priority is higher or equal to the currently active - if pcam.priority >= _active_pcam_priority: - if _is_2d: - if pcam != _active_pcam_2d: - _assign_new_active_pcam(pcam) - else: - if pcam != _active_pcam_3d: - _assign_new_active_pcam(pcam) - pcam.set_tween_skip(self, false) - _active_pcam_missing = false - - -## Updates the viewfinder when a [param PhantomCamera] has its -## [param priority_ovrride] enabled.[br] -## [b]Note:[/b] This only affects the editor. -func _pcam_priority_override(pcam: Node, should_override: bool) -> void: - if not Engine.is_editor_hint(): return - if not _pcam_is_in_host_layer(pcam): return - if should_override: - if _is_2d: - if is_instance_valid(_active_pcam_2d): - if _active_pcam_2d.priority_override: - _active_pcam_2d.priority_override = false - else: - if is_instance_valid(_active_pcam_3d): - if _active_pcam_3d.priority_override: - _active_pcam_3d.priority_override = false - _assign_new_active_pcam(pcam) - else: - _find_pcam_with_highest_priority() - - viewfinder_update.emit(false) - - -## Updates the viewfinder when a [param PhantomCamera] has its -## [param priority_ovrride] disabled.[br] -## [b]Note:[/b] This only affects the editor. -func pcam_priority_override_disabled() -> void: - viewfinder_update.emit(false) - - -## Returns the currently active [param PhantomCamera] -func get_active_pcam() -> Node: - if _is_2d: - return _active_pcam_2d - else: - return _active_pcam_3d - - -## Returns whether if a [param PhantomCamera] should tween when it becomes -## active. If it's already active, the value will always be false. -## [b]Note:[/b] This can only be called internally from a -## [param PhantomCamera] node. -func get_trigger_pcam_tween() -> bool: - return _trigger_pcam_tween - - -## Refreshes the [param PhantomCamera] list and checks for the highest priority. [br] -## [b]Note:[/b] This should [b]not[/b] be necessary to call manually. -func refresh_pcam_list_priorty() -> void: - _active_pcam_priority = -1 - _find_pcam_with_highest_priority() - -#endregion - -#region Setters / Getters - -func set_interpolation_mode(value: int) -> void: - interpolation_mode = value - if is_inside_tree(): - _check_pcam_physics() -func get_interpolation_mode() -> int: - return interpolation_mode - -## Sets the [member host_layers] value. -func set_host_layers(value: int) -> void: - host_layers = value - - if not _is_child_of_camera: return - - if not _active_pcam_missing: - if _is_2d: - _pcam_host_layer_changed(_active_pcam_2d) - else: - _pcam_host_layer_changed(_active_pcam_3d) - else: - _find_pcam_with_highest_priority() - -## Enables or disables a given layer of [member host_layers]. -func set_host_layers_value(layer: int, value: bool) -> void: - host_layers = _set_layer(host_layers, layer, value) - -## Returns the [member host_layers] value. -func get_host_layers() -> int: - return host_layers - -#endregion diff --git a/addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd.uid b/addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd.uid deleted file mode 100644 index e96433d..0000000 --- a/addons/phantom_camera/scripts/phantom_camera_host/phantom_camera_host.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bd046eokvcnu2 diff --git a/addons/phantom_camera/scripts/resources/Camera3DResource.cs b/addons/phantom_camera/scripts/resources/Camera3DResource.cs deleted file mode 100644 index 54ad098..0000000 --- a/addons/phantom_camera/scripts/resources/Camera3DResource.cs +++ /dev/null @@ -1,116 +0,0 @@ -using Godot; - -namespace PhantomCamera; - -public enum KeepAspect -{ - KeepWidth, - KeepHeight -} - -public enum ProjectionType -{ - Perspective, - Orthogonal, - Frustum -} - -public class Camera3DResource(Resource resource) -{ - public readonly Resource Resource = resource; - - public KeepAspect KeepAspect - { - get => (KeepAspect)(int)Resource.Get(PropertyName.KeepAspect); - set => Resource.Set(PropertyName.KeepAspect, (int)value); - } - - public int CullMask - { - get => (int)Resource.Get(PropertyName.CullMask); - set => Resource.Set(PropertyName.CullMask, value); - } - - - public float HOffset - { - get => (float)Resource.Get(PropertyName.HOffset); - set => Resource.Set(PropertyName.HOffset, value); - } - - public float VOffset - { - get => (float)Resource.Get(PropertyName.VOffset); - set => Resource.Set(PropertyName.VOffset, value); - } - - public ProjectionType Projection - { - get => (ProjectionType)(int)Resource.Get(PropertyName.Projection); - set => Resource.Set(PropertyName.Projection, (int)value); - } - - public float Fov - { - get => (float)Resource.Get(PropertyName.Fov); - set => Resource.Set(PropertyName.Fov, Mathf.Clamp(value, 1, 179)); - } - - public float Size - { - get => (float)Resource.Get(PropertyName.Size); - set => Resource.Set(PropertyName.Size, Mathf.Clamp(value, 0.001f, float.PositiveInfinity)); - } - - public Vector2 FrustumOffset - { - get => (Vector2)Resource.Get(PropertyName.FrustumOffset); - set => Resource.Set(PropertyName.FrustumOffset, value); - } - - public float Near - { - get => (float)Resource.Get(PropertyName.Near); - set => Resource.Set(PropertyName.Near, Mathf.Clamp(value, 0.001f, float.PositiveInfinity)); - } - - public float Far - { - get => (float)Resource.Get(PropertyName.Far); - set => Resource.Set(PropertyName.Far, Mathf.Clamp(value, 0.01f, float.PositiveInfinity)); - } - - public static Camera3DResource New() - { - Resource resource = new(); -#if GODOT4_4_OR_GREATER - resource.SetScript(GD.Load("uid://b8hhnqsugykly")); -#else - resource.SetScript(GD.Load("res://addons/phantom_camera/scripts/resources/camera_3d_resource.gd")); -#endif - return new Camera3DResource(resource); - } - - public static class PropertyName - { - public static readonly StringName KeepAspect = new("keep_aspect"); - - public static readonly StringName CullMask = new("cull_mask"); - - public static readonly StringName HOffset = new("h_offset"); - - public static readonly StringName VOffset = new("v_offset"); - - public static readonly StringName Projection = new("projection"); - - public static readonly StringName Fov = new("fov"); - - public static readonly StringName Size = new("size"); - - public static readonly StringName FrustumOffset = new("frustum_offset"); - - public static readonly StringName Near = new("near"); - - public static readonly StringName Far = new("far"); - } -} diff --git a/addons/phantom_camera/scripts/resources/Camera3DResource.cs.uid b/addons/phantom_camera/scripts/resources/Camera3DResource.cs.uid deleted file mode 100644 index d66b43e..0000000 --- a/addons/phantom_camera/scripts/resources/Camera3DResource.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://jedyxlihuwbj diff --git a/addons/phantom_camera/scripts/resources/PhantomCameraNoise2D.cs b/addons/phantom_camera/scripts/resources/PhantomCameraNoise2D.cs deleted file mode 100644 index f19d445..0000000 --- a/addons/phantom_camera/scripts/resources/PhantomCameraNoise2D.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Godot; - -namespace PhantomCamera.Noise; - -public class PhantomCameraNoise2D(Resource resource) -{ - public readonly Resource Resource = resource; - - public float Amplitude - { - get => (float)Resource.Call(MethodName.GetAmplitude); - set => Resource.Call(MethodName.SetAmplitude, value); - } - - public float Frequency - { - get => (float)Resource.Call(MethodName.GetFrequency); - set => Resource.Call(MethodName.SetFrequency, value); - } - - public bool RandomizeNoiseSeed - { - get => (bool)Resource.Call(MethodName.GetRandomizeNoiseSeed); - set => Resource.Call(MethodName.SetRandomizeNoiseSeed, value); - } - - public int NoiseSeed - { - get => (int)Resource.Call(MethodName.GetNoiseSeed); - set => Resource.Call(MethodName.SetNoiseSeed, value); - } - - public bool RotationalNoise - { - get => (bool)Resource.Call(MethodName.GetRotationalNoise); - set => Resource.Call(MethodName.SetRotationalNoise, value); - } - - public bool PositionalNoise - { - get => (bool)Resource.Call(MethodName.GetPositionalNoise); - set => Resource.Call(MethodName.SetPositionalNoise, value); - } - - public float RotationalMultiplier - { - get => (float)Resource.Call(MethodName.GetRotationalMultiplier); - set => Resource.Call(MethodName.SetRotationalMultiplier, value); - } - - public float PositionalMultiplierX - { - get => (float)Resource.Call(MethodName.GetPositionalMultiplierX); - set => Resource.Call(MethodName.SetPositionalMultiplierX, value); - } - - public float PositionalMultiplierY - { - get => (float)Resource.Call(MethodName.GetPositionalMultiplierY); - set => Resource.Call(MethodName.SetPositionalMultiplierY, value); - } - - public static PhantomCameraNoise2D New() - { - Resource resource = new(); -#if GODOT4_4_OR_GREATER - resource.SetScript(GD.Load("uid://dimvdouy8g0sv")); -#else - resource.SetScript(GD.Load("res://addons/phantom_camera/scripts/resources/phantom_camera_noise_2d.gd")); -#endif - return new PhantomCameraNoise2D(resource); - } - - public static class MethodName - { - public static readonly StringName GetAmplitude = new("get_amplitude"); - public static readonly StringName SetAmplitude = new("set_amplitude"); - - public static readonly StringName GetFrequency = new("get_frequency"); - public static readonly StringName SetFrequency = new("set_frequency"); - - public static readonly StringName GetRandomizeNoiseSeed = new("get_randomize_noise_seed"); - public static readonly StringName SetRandomizeNoiseSeed = new("set_randomize_noise_seed"); - - public static readonly StringName GetNoiseSeed = new("get_noise_seed"); - public static readonly StringName SetNoiseSeed = new("set_noise_seed"); - - public static readonly StringName GetRotationalNoise = new("get_rotational_noise"); - public static readonly StringName SetRotationalNoise = new("set_rotational_noise"); - - public static readonly StringName GetPositionalNoise = new("get_positional_noise"); - public static readonly StringName SetPositionalNoise = new("set_positional_noise"); - - public static readonly StringName GetRotationalMultiplier = new("get_rotational_multiplier"); - public static readonly StringName SetRotationalMultiplier = new("set_rotational_multiplier"); - - public static readonly StringName GetPositionalMultiplierX = new("get_positional_multiplier_x"); - public static readonly StringName SetPositionalMultiplierX = new("set_positional_multiplier_x"); - - public static readonly StringName GetPositionalMultiplierY = new("get_positional_multiplier_y"); - public static readonly StringName SetPositionalMultiplierY = new("set_positional_multiplier_y"); - } -} diff --git a/addons/phantom_camera/scripts/resources/PhantomCameraNoise2D.cs.uid b/addons/phantom_camera/scripts/resources/PhantomCameraNoise2D.cs.uid deleted file mode 100644 index 16ddb84..0000000 --- a/addons/phantom_camera/scripts/resources/PhantomCameraNoise2D.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://capjdoxs6gs6r diff --git a/addons/phantom_camera/scripts/resources/PhantomCameraNoise3D.cs b/addons/phantom_camera/scripts/resources/PhantomCameraNoise3D.cs deleted file mode 100644 index 0c1a3ce..0000000 --- a/addons/phantom_camera/scripts/resources/PhantomCameraNoise3D.cs +++ /dev/null @@ -1,130 +0,0 @@ -using Godot; - -namespace PhantomCamera.Noise; - -public class PhantomCameraNoise3D(Resource resource) -{ - public readonly Resource Resource = resource; - - public float Amplitude - { - get => (float)Resource.Call(MethodName.GetAmplitude); - set => Resource.Call(MethodName.SetAmplitude, value); - } - - public float Frequency - { - get => (float)Resource.Call(MethodName.GetFrequency); - set => Resource.Call(MethodName.SetFrequency, value); - } - - public bool RandomizeNoiseSeed - { - get => (bool)Resource.Call(MethodName.GetRandomizeNoiseSeed); - set => Resource.Call(MethodName.SetRandomizeNoiseSeed, value); - } - - public int NoiseSeed - { - get => (int)Resource.Call(MethodName.GetNoiseSeed); - set => Resource.Call(MethodName.SetNoiseSeed, value); - } - - public bool RotationalNoise - { - get => (bool)Resource.Call(MethodName.GetRotationalNoise); - set => Resource.Call(MethodName.SetRotationalNoise, value); - } - - public bool PositionalNoise - { - get => (bool)Resource.Call(MethodName.GetPositionalNoise); - set => Resource.Call(MethodName.SetPositionalNoise, value); - } - - public float RotationalMultiplierX - { - get => (float)Resource.Call(MethodName.GetRotationalMultiplierX); - set => Resource.Call(MethodName.SetRotationalMultiplierX, value); - } - - public float RotationalMultiplierY - { - get => (float)Resource.Call(MethodName.GetRotationalMultiplierY); - set => Resource.Call(MethodName.SetRotationalMultiplierY, value); - } - - public float RotationalMultiplierZ - { - get => (float)Resource.Call(MethodName.GetRotationalMultiplierZ); - set => Resource.Call(MethodName.SetRotationalMultiplierZ, value); - } - - public float PositionalMultiplierX - { - get => (float)Resource.Call(MethodName.GetPositionalMultiplierX); - set => Resource.Call(MethodName.SetPositionalMultiplierX, value); - } - - public float PositionalMultiplierY - { - get => (float)Resource.Call(MethodName.GetPositionalMultiplierY); - set => Resource.Call(MethodName.SetPositionalMultiplierY, value); - } - - public float PositionalMultiplierZ - { - get => (float)Resource.Call(MethodName.GetPositionalMultiplierZ); - set => Resource.Call(MethodName.SetPositionalMultiplierZ, value); - } - - public static PhantomCameraNoise3D New() - { - Resource resource = new(); -#if GODOT4_4_OR_GREATER - resource.SetScript(GD.Load("uid://cuffvge5ad4aa")); -#else - resource.SetScript(GD.Load("res://addons/phantom_camera/scripts/resources/phantom_camera_noise_3d.gd")); -#endif - return new PhantomCameraNoise3D(resource); - } - - public static class MethodName - { - public static readonly StringName GetAmplitude = new("get_amplitude"); - public static readonly StringName SetAmplitude = new("set_amplitude"); - - public static readonly StringName GetFrequency = new("get_frequency"); - public static readonly StringName SetFrequency = new("set_frequency"); - - public static readonly StringName GetRandomizeNoiseSeed = new("get_randomize_noise_seed"); - public static readonly StringName SetRandomizeNoiseSeed = new("set_randomize_noise_seed"); - - public static readonly StringName GetNoiseSeed = new("get_noise_seed"); - public static readonly StringName SetNoiseSeed = new("set_noise_seed"); - - public static readonly StringName GetRotationalNoise = new("get_rotational_noise"); - public static readonly StringName SetRotationalNoise = new("set_rotational_noise"); - - public static readonly StringName GetPositionalNoise = new("get_positional_noise"); - public static readonly StringName SetPositionalNoise = new("set_positional_noise"); - - public static readonly StringName GetRotationalMultiplierX = new("get_rotational_multiplier_x"); - public static readonly StringName SetRotationalMultiplierX = new("set_rotational_multiplier_x"); - - public static readonly StringName GetRotationalMultiplierY = new("get_rotational_multiplier_y"); - public static readonly StringName SetRotationalMultiplierY = new("set_rotational_multiplier_y"); - - public static readonly StringName GetRotationalMultiplierZ = new("get_rotational_multiplier_z"); - public static readonly StringName SetRotationalMultiplierZ = new("set_rotational_multiplier_z"); - - public static readonly StringName GetPositionalMultiplierX = new("get_positional_multiplier_x"); - public static readonly StringName SetPositionalMultiplierX = new("set_positional_multiplier_x"); - - public static readonly StringName GetPositionalMultiplierY = new("get_positional_multiplier_y"); - public static readonly StringName SetPositionalMultiplierY = new("set_positional_multiplier_y"); - - public static readonly StringName GetPositionalMultiplierZ = new("get_positional_multiplier_z"); - public static readonly StringName SetPositionalMultiplierZ = new("set_positional_multiplier_z"); - } -} diff --git a/addons/phantom_camera/scripts/resources/PhantomCameraNoise3D.cs.uid b/addons/phantom_camera/scripts/resources/PhantomCameraNoise3D.cs.uid deleted file mode 100644 index 53d184f..0000000 --- a/addons/phantom_camera/scripts/resources/PhantomCameraNoise3D.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://chk7643ynhe4f diff --git a/addons/phantom_camera/scripts/resources/PhantomCameraTween.cs b/addons/phantom_camera/scripts/resources/PhantomCameraTween.cs deleted file mode 100644 index 500e165..0000000 --- a/addons/phantom_camera/scripts/resources/PhantomCameraTween.cs +++ /dev/null @@ -1,75 +0,0 @@ -using Godot; - -namespace PhantomCamera; - -public enum TransitionType -{ - Linear, - Sine, - Quint, - Quart, - Quad, - Expo, - Elastic, - Cubic, - Circ, - Bounce, - Back -} - -public enum EaseType -{ - EaseIn, - EaseOut, - EaseInOut, - EaseOutIn -} - -public static class PhantomCameraTweenExtensions -{ - public static PhantomCameraTween AsPhantomCameraTween(this Resource resource) - { - return new PhantomCameraTween(resource); - } -} - -public class PhantomCameraTween(Resource tweenResource) -{ - public Resource Resource { get; } = tweenResource; - - public float Duration - { - get => (float)Resource.Get(PropertyName.Duration); - set => Resource.Set(PropertyName.Duration, value); - } - - public TransitionType Transition - { - get => (TransitionType)(int)Resource.Get(PropertyName.Transition); - set => Resource.Set(PropertyName.Transition, (int)value); - } - - public EaseType Ease - { - get => (EaseType)(int)Resource.Get(PropertyName.Ease); - set => Resource.Set(PropertyName.Ease, (int)value); - } - - public static PhantomCameraTween New() - { - Resource resource = new(); -#if GODOT4_4_OR_GREATER - resource.SetScript(GD.Load("uid://8umksf8e80fw")); -#else - resource.SetScript(GD.Load("res://addons/phantom_camera/scripts/resources/tween_resource.gd")); -#endif - return new PhantomCameraTween(resource); - } - - public static class PropertyName - { - public static readonly StringName Duration = new("duration"); - public static readonly StringName Transition = new("transition"); - public static readonly StringName Ease = new("ease"); - } -} diff --git a/addons/phantom_camera/scripts/resources/PhantomCameraTween.cs.uid b/addons/phantom_camera/scripts/resources/PhantomCameraTween.cs.uid deleted file mode 100644 index dd50ead..0000000 --- a/addons/phantom_camera/scripts/resources/PhantomCameraTween.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ybr5c2s0tfvx diff --git a/addons/phantom_camera/scripts/resources/camera_3d_resource.gd b/addons/phantom_camera/scripts/resources/camera_3d_resource.gd deleted file mode 100644 index c630e16..0000000 --- a/addons/phantom_camera/scripts/resources/camera_3d_resource.gd +++ /dev/null @@ -1,110 +0,0 @@ -@tool -@icon("res://addons/phantom_camera/icons/phantom_camera_camera_3d_resource.svg") -class_name Camera3DResource -extends Resource - -## Resource for [PhantomCamera3D] to override various [Camera3D] properties. -## -## The overrides defined here will be applied to the [Camera3D] upon the -## [PhantomCamera3D] becoming active. - -enum KeepAspect { - KEEP_WIDTH = 0, ## Preserves the horizontal aspect ratio; also known as Vert- scaling. This is usually the best option for projects running in portrait mode, as taller aspect ratios will benefit from a wider vertical FOV. - KEEP_HEIGHT = 1, ## Preserves the vertical aspect ratio; also known as Hor+ scaling. This is usually the best option for projects running in landscape mode, as wider aspect ratios will automatically benefit from a wider horizontal FOV. -} - -enum ProjectionType { - PERSPECTIVE = 0, ## Perspective projection. Objects on the screen becomes smaller when they are far away. - ORTHOGONAL = 1, ## Orthogonal projection, also known as orthographic projection. Objects remain the same size on the screen no matter how far away they are. - FRUSTUM = 2, ## Frustum projection. This mode allows adjusting frustum_offset to create "tilted frustum" effects. -} - -## Overrides [member Camera3D.keep_aspect]. -@export var keep_aspect: KeepAspect = KeepAspect.KEEP_HEIGHT: - set(value): - keep_aspect = value - emit_changed() - get: - return keep_aspect - -## Overrides [member Camera3D.cull_mask]. -@export_flags_3d_render var cull_mask: int = 1048575: - set(value): - cull_mask = value - emit_changed() - get: - return cull_mask - -## Overrides [member Camera3D.h_offset]. -@export_range(0, 1, 0.001, "or_greater", "or_less", "hide_slider", "suffix:m") var h_offset: float = 0: - set(value): - h_offset = value - emit_changed() - get: - return h_offset - -## Overrides [member Camera3D.v_offset]. -@export_range(0, 1, 0.001, "or_greater", "or_less", "hide_slider", "suffix:m") var v_offset: float = 0: - set(value): - v_offset = value - emit_changed() - -## Overrides [member Camera3D.projection]. -@export var projection: ProjectionType = ProjectionType.PERSPECTIVE: - set(value): - projection = value - notify_property_list_changed() - emit_changed() - get: - return projection - -## Overrides [member Camera3D.fov]. -@export_range(1, 179, 0.1, "degrees") var fov: float = 75: - set(value): - fov = value - emit_changed() - get: - return fov - -## Overrides [member Camera3D.size]. -@export_range(0.001, 100, 0.001, "suffix:m", "or_greater") var size: float = 1: - set(value): - size = value - emit_changed() - get: - return size - -## Overrides [member Camera3d.frustum_offset]. -@export var frustum_offset: Vector2 = Vector2.ZERO: - set(value): - frustum_offset = value - emit_changed() - get: - return frustum_offset - -## Overrides [member Camera3D.near]. -@export_range(0.001, 10, 0.001, "suffix:m", "or_greater") var near: float = 0.05: - set(value): - near = value - emit_changed() - get: - return near - -## Overrides [member Camera3D.far]. -@export_range(0.01, 4000, 0.001, "suffix:m","or_greater") var far: float = 4000: - set(value): - far = value - emit_changed() - get: - return far - - -func _validate_property(property: Dictionary) -> void: - if property.name == "fov" and not projection == ProjectionType.PERSPECTIVE: - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == "size" and projection == ProjectionType.PERSPECTIVE: - property.usage = PROPERTY_USAGE_NO_EDITOR - - if property.name == "frustum_offset" and not projection == ProjectionType.FRUSTUM: - property.usage = PROPERTY_USAGE_NO_EDITOR diff --git a/addons/phantom_camera/scripts/resources/camera_3d_resource.gd.uid b/addons/phantom_camera/scripts/resources/camera_3d_resource.gd.uid deleted file mode 100644 index e8378eb..0000000 --- a/addons/phantom_camera/scripts/resources/camera_3d_resource.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bc2tn187qiatpcheck diff --git a/addons/phantom_camera/scripts/resources/phantom_camera_noise_2d.gd b/addons/phantom_camera/scripts/resources/phantom_camera_noise_2d.gd deleted file mode 100644 index cc87dba..0000000 --- a/addons/phantom_camera/scripts/resources/phantom_camera_noise_2d.gd +++ /dev/null @@ -1,228 +0,0 @@ -@tool -@icon("res://addons/phantom_camera/icons/phantom_camera_noise_resource.svg") -class_name PhantomCameraNoise2D -extends Resource - -## A resource type used to apply noise, or shake, to [Camera2D]s that have a [PhantomCameraHost] as a child. -## -## Is a resource type that defines, calculates and outputs the noise values to a [Camera2D] through active -## [PhantomCamera3D].[br] -## It can be applied to either [PhantomCameraNoiseEmitter2D] or a [PhantomCamera2D] noise property directly - -#region Exported Properties - -## Defines the size of the noise pattern.[br] -## Higher values will increase the range the noise can reach. -@export_range(0, 1000, 0.001, "or_greater") var amplitude: float = 10: - set = set_amplitude, - get = get_amplitude - -## Sets the density of the noise pattern.[br] -## Higher values will result in more erratic noise. -@export_range(0, 10, 0.001, "or_greater") var frequency: float = 0.5: - set = set_frequency, - get = get_frequency - -## If true, randomizes the noise pattern every time the noise is run.[br] -## If disabled, [member seed] can be used to define a fixed noise pattern. -@export var randomize_noise_seed: bool = true: - set = set_randomize_noise_seed, - get = get_randomize_noise_seed - -## Sets a predetermined seed noise value.[br] -## Useful if wanting to achieve a persistent noise pattern every time the noise is emitted. -@export var noise_seed: int = 0: - set = set_noise_seed, - get = get_noise_seed - -## Enables noise changes to the [member Camera2D.offset] position. -@export var positional_noise: bool = true: - set = set_positional_noise, - get = get_positional_noise - -## Enables noise changes to the [Camera2D]'s rotation. -@export var rotational_noise: bool = false: - set = set_rotational_noise, - get = get_rotational_noise - -@export_group("Positional Multiplier") -## Multiplies positional noise amount in the X-axis.[br] -## Set the value to [param 0] to disable noise in the axis. -@export_range(0, 1, 0.001, "or_greater") var positional_multiplier_x: float = 1: - set = set_positional_multiplier_x, - get = get_positional_multiplier_x - -## Multiplies positional noise amount in the Y-axis.[br] -## Set the value to [param 0] to disable noise in the axis. -@export_range(0, 1, 0.001, "or_greater") var positional_multiplier_y: float = 1: - set = set_positional_multiplier_y, - get = get_positional_multiplier_y - -@export_group("Rotational Multiplier") -## Multiplies rotational noise amount. -@export_range(0, 1, 0.001, "or_greater") var rotational_multiplier: float = 1: - set = set_rotational_multiplier, - get = get_rotational_multiplier - -#endregion - -#region Private Variables - -var _noise_algorithm: FastNoiseLite = FastNoiseLite.new() - -var _noise_positional_multiplier: Vector2 = Vector2( - positional_multiplier_x, - positional_multiplier_y -) - -var _trauma: float = 0.0: - set(value): - _trauma = value - -var _noise_time: float = 0.0 - -#endregion - -#region Private Functions - -func _init(): - _noise_algorithm.noise_type = FastNoiseLite.TYPE_PERLIN - if randomize_noise_seed: _noise_algorithm.seed = randi() - _noise_algorithm.frequency = frequency - - -func _validate_property(property: Dictionary) -> void: - if randomize_noise_seed and property.name == "noise_seed": - property.usage = PROPERTY_USAGE_NO_EDITOR - - if not rotational_noise and property.name == "rotational_multiplier": - property.usage = PROPERTY_USAGE_NO_EDITOR - - if not positional_noise: - match property.name: - "positional_multiplier_x", \ - "positional_multiplier_y": - property.usage = PROPERTY_USAGE_NO_EDITOR - - -func _get_noise_from_seed(noise_seed: int) -> float: - return _noise_algorithm.get_noise_2d(noise_seed, _noise_time) * amplitude - - -func set_trauma(value: float) -> void: - _trauma = value - -#endregion - -#region Public Functions - -func get_noise_transform(delta: float) -> Transform2D: - var output_position: Vector2 = Vector2.ZERO - var output_rotation: float = 0.0 - _noise_time += delta - _trauma = maxf(_trauma, 0.0) - - if positional_noise: - for i in 2: - output_position[i] = _noise_positional_multiplier[i] * pow(_trauma, 2) * _get_noise_from_seed(i + noise_seed) - if rotational_noise: - output_rotation = rotational_multiplier / 100 * pow(_trauma, 2) * _get_noise_from_seed(noise_seed) - - return Transform2D(output_rotation, output_position) - - -func reset_noise_time() -> void: - _noise_time = 0 - -#endregion - -#region Setters & Getters - -## Sets the [member amplitude] value. -func set_amplitude(value: float) -> void: - amplitude =value - -## Returns the [member amplitude] value. -func get_amplitude() -> float: - return amplitude - - -## Sets the [member frequency] value. -func set_frequency(value: float) -> void: - frequency = value - _noise_algorithm.frequency = value - -## Returns the [member frequency] value. -func get_frequency() -> float: - return frequency - - -## Sets the [member randomize_seed] value. -func set_randomize_noise_seed(value: int) -> void: - randomize_noise_seed = value - if value: _noise_algorithm.seed = randi() - notify_property_list_changed() - -## Returns the [member randomize_seed] value. -func get_randomize_noise_seed() -> int: - return randomize_noise_seed - - -## Sets the [member randomize_seed] value. -func set_noise_seed(value: int) -> void: - noise_seed = value - -## Returns the [member seed] value. -func get_noise_seed() -> int: - return noise_seed - - -## Sets the [member positional_noise] value. -func set_positional_noise(value: bool) -> void: - positional_noise = value - notify_property_list_changed() - -## Returns the [member positional_noise] value. -func get_positional_noise() -> bool: - return positional_noise - - -## Sets the [member rotational_noise] value. -func set_rotational_noise(value: bool) -> void: - rotational_noise = value - notify_property_list_changed() - -## Returns the [member rotational_noise] value. -func get_rotational_noise() -> bool: - return rotational_noise - - -## Sets the [member positional_multiplier_x] value. -func set_positional_multiplier_x(value: float) -> void: - positional_multiplier_x = value - _noise_positional_multiplier.x = value - -## Returns the [member positional_multiplier_x] value. -func get_positional_multiplier_x() -> float: - return positional_multiplier_x - - -## Sets the [member positional_multiplier_y] value. -func set_positional_multiplier_y(value: float) -> void: - positional_multiplier_y = value - _noise_positional_multiplier.y = value - -## Returns the [member positional_multiplier_y] value. -func get_positional_multiplier_y() -> float: - return positional_multiplier_y - - -## Sets the [member rotational_multiplier] value. -func set_rotational_multiplier(value: float) -> void: - rotational_multiplier = value - -## Returns the [member rotational_multiplier] value. -func get_rotational_multiplier() -> float: - return rotational_multiplier - -#endregion diff --git a/addons/phantom_camera/scripts/resources/phantom_camera_noise_2d.gd.uid b/addons/phantom_camera/scripts/resources/phantom_camera_noise_2d.gd.uid deleted file mode 100644 index 45ae480..0000000 --- a/addons/phantom_camera/scripts/resources/phantom_camera_noise_2d.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dimvdouy8g0sv diff --git a/addons/phantom_camera/scripts/resources/phantom_camera_noise_3d.gd b/addons/phantom_camera/scripts/resources/phantom_camera_noise_3d.gd deleted file mode 100644 index 6cf840f..0000000 --- a/addons/phantom_camera/scripts/resources/phantom_camera_noise_3d.gd +++ /dev/null @@ -1,301 +0,0 @@ -@tool -@icon("res://addons/phantom_camera/icons/phantom_camera_noise_resource.svg") -class_name PhantomCameraNoise3D -extends Resource - -## A resource type used to apply noise, or shake, to [Camera3D]s that have a [PhantomCameraHost] as a child. -## -## Is a resource type that defines, calculates and outputs the noise values to a [Camera3D] through active -## [PhantomCamera3D].[br] -## It can be applied to either [PhantomCameraNoiseEmitter3D] or a [PhantomCamera3D] noise property directly - -#region Exported Properties - -## Defines the size of the noise pattern.[br] -## Higher values will increase the range the noise can reach. -@export_range(0, 100, 0.001, "or_greater") var amplitude: float = 10: - set = set_amplitude, - get = get_amplitude - -## Sets the density of the noise pattern.[br] -## Higher values will result in more erratic noise. -@export_range(0, 10, 0.001, "or_greater") var frequency: float = 0.2: - set = set_frequency, - get = get_frequency - -## If true, randomizes the noise pattern every time the noise is run.[br] -## If disabled, [member seed] can be used to define a fixed noise pattern. -@export var randomize_noise_seed: bool = true: - set = set_randomize_noise_seed, - get = get_randomize_noise_seed - -## Sets a predetermined seed noise value.[br] -## Useful if wanting to achieve a persistent noise pattern every time the noise is emitted. -@export var noise_seed: int = 0: - set = set_noise_seed, - get = get_noise_seed - -## Enables noise changes to the [Camera3D]'s rotation. -@export var rotational_noise: bool = true: - set = set_rotational_noise, - get = get_rotational_noise - -## Enables noise changes to the camera's position.[br][br] -## [b]Important[/b][br]This can cause geometry clipping if the camera gets too close while this is active. -@export var positional_noise: bool = false: - set = set_positional_noise, - get = get_positional_noise - -@export_group("Rotational Multiplier") -## Multiplies rotational noise amount in the X-axis.[br] -## Set the value to [param 0] to disable noise in the axis. -@export_range(0, 1, 0.001, "or_greater") var rotational_multiplier_x: float = 1: - set = set_rotational_multiplier_x, - get = get_rotational_multiplier_x - -## Multiplies rotational noise amount in the Y-axis.[br] -## Set the value to [param 0] to disable noise in the axis. -@export_range(0, 1, 0.001, "or_greater") var rotational_multiplier_y: float = 1: - set = set_rotational_multiplier_y, - get = get_rotational_multiplier_y - -## Multiplies rotational noise amount in the Z-axis.[br] -## Set the value to [param 0] to disable noise in the axis. -@export_range(0, 1, 0.001, "or_greater") var rotational_multiplier_z: float = 1: - set = set_rotational_multiplier_z, - get = get_rotational_multiplier_z - -@export_group("Positional Multiplier") -## Multiplies positional noise amount in the X-axis.[br] -## Set the value to [param 0] to disable noise in the axis.[br] -## [b]Note:[/b] Rotational Offset is recommended to avoid potential camera clipping with the environment. -@export_range(0, 1, 0.001, "or_greater") var positional_multiplier_x: float = 1: - set = set_positional_multiplier_x, - get = get_positional_multiplier_x - -## Multiplies positional noise amount in the Y-axis.[br] -## Set the value to [param 0] to disable noise in the axis.[br] -## [b]Note:[/b] Rotational Offset is recommended to avoid potential camera clipping with the environment. -@export_range(0, 1, 0.001, "or_greater") var positional_multiplier_y: float = 1: - set = set_positional_multiplier_y, - get = get_positional_multiplier_y - -## Multiplies positional noise amount in the Z-axis.[br] -## Set the value to [param 0] to disable noise in the axis.[br] -## [b]Note:[/b] Rotational Offset is recommended to avoid potential camera clipping with the environment. -@export_range(0, 1, 0.001, "or_greater") var positional_multiplier_z: float = 1: - set = set_positional_multiplier_z, - get = get_positional_multiplier_z - -#endregion - -#region Private Variables - -var _noise_algorithm: FastNoiseLite = FastNoiseLite.new() - -var _noise_rotational_multiplier: Vector3 = Vector3( - rotational_multiplier_x, - rotational_multiplier_y, - rotational_multiplier_z, -) - -var _noise_positional_multiplier: Vector3 = Vector3( - positional_multiplier_x, - positional_multiplier_y, - positional_multiplier_z, -) - -var _trauma: float = 0.0: - set(value): - _trauma = value - if _trauma == 0.0: - _noise_time = 0.0 - -var _noise_time: float = 0.0 - -#endregion - -#region Private Functions - -func _init(): - _noise_algorithm.noise_type = FastNoiseLite.TYPE_PERLIN - - if randomize_noise_seed: _noise_algorithm.seed = randi() - _noise_algorithm.frequency = frequency - - -func _validate_property(property: Dictionary) -> void: - if randomize_noise_seed and property.name == "noise_seed": - property.usage = PROPERTY_USAGE_NO_EDITOR - - if not rotational_noise: - match property.name: - "rotational_multiplier_x", \ - "rotational_multiplier_y", \ - "rotational_multiplier_z": - property.usage = PROPERTY_USAGE_NO_EDITOR - - if not positional_noise: - match property.name: - "positional_multiplier_x", \ - "positional_multiplier_y", \ - "positional_multiplier_z": - property.usage = PROPERTY_USAGE_NO_EDITOR - - -func _get_noise_from_seed(noise_seed: int) -> float: - return _noise_algorithm.get_noise_2d(noise_seed, _noise_time) * amplitude - - -func set_trauma(value: float) -> void: - _trauma = value - -#endregion - -#region Public Functions - -func get_noise_transform(delta: float) -> Transform3D: - var output_rotation: Vector3 = Vector3.ZERO - var output_position: Vector3 = Vector3.ZERO - _noise_time += delta - _trauma = maxf(_trauma, 0.0) - - for i in 3: - if rotational_noise: - output_rotation[i] = deg_to_rad( - _noise_rotational_multiplier[i] * pow(_trauma, 2) * _get_noise_from_seed(i + noise_seed) - ) - - if positional_noise: - output_position[i] += _noise_positional_multiplier[i] / 10 * \ - pow(_trauma, 2) * _get_noise_from_seed(i + noise_seed) - - return Transform3D(Quaternion.from_euler(output_rotation), output_position) - - -func reset_noise_time() -> void: - _noise_time = 0 - -#endregion - -#region Setters & Getters - -## Sets the [member amplitude] value. -func set_amplitude(value: float) -> void: - amplitude =value - -## Returns the [member amplitude] value. -func get_amplitude() -> float: - return amplitude - - -## Sets the [member frequency] value. -func set_frequency(value: float) -> void: - frequency = value - _noise_algorithm.frequency = value - -## Returns the [member frequency] value. -func get_frequency() -> float: - return frequency - - -## Sets the [member randomize_seed] value. -func set_randomize_noise_seed(value: int) -> void: - randomize_noise_seed = value - if value: _noise_algorithm.seed = randi() - notify_property_list_changed() - -## Returns the [member randomize_seed] value. -func get_randomize_noise_seed() -> int: - return randomize_noise_seed - - -## Sets the [member randomize_seed] value. -func set_noise_seed(value: int) -> void: - noise_seed = value - -## Returns the [member seed] value. -func get_noise_seed() -> int: - return noise_seed - - -## Sets the [member positional_noise] value. -func set_positional_noise(value: bool) -> void: - positional_noise = value - notify_property_list_changed() - -## Returns the [member positional_noise] value. -func get_positional_noise() -> bool: - return positional_noise - - -## Sets the [member rotational_noise] value. -func set_rotational_noise(value: bool) -> void: - rotational_noise = value - notify_property_list_changed() - -## Returns the [member rotational_noise] value. -func get_rotational_noise() -> bool: - return rotational_noise - - -## Sets the [member positional_multiplier_x] value. -func set_positional_multiplier_x(value: float) -> void: - positional_multiplier_x = value - _noise_positional_multiplier.x = value - -## Returns the [member positional_multiplier_x] value. -func get_positional_multiplier_x() -> float: - return positional_multiplier_x - - -## Sets the [member positional_multiplier_y] value. -func set_positional_multiplier_y(value: float) -> void: - positional_multiplier_y = value - _noise_positional_multiplier.y = value - -## Returns the [member positional_multiplier_y] value. -func get_positional_multiplier_y() -> float: - return positional_multiplier_y - - -## Sets the [member positional_multiplier_z] value. -func set_positional_multiplier_z(value: float) -> void: - positional_multiplier_z = value - _noise_positional_multiplier.z = value - -## Returns the [member positional_multiplier_z] value. -func get_positional_multiplier_z() -> float: - return positional_multiplier_z - - -## Sets the [member rotational_multiplier_x] value. -func set_rotational_multiplier_x(value: float) -> void: - rotational_multiplier_x = value - _noise_rotational_multiplier.x = value - -## Returns the [member rotational_multiplier_x] value. -func get_rotational_multiplier_x() -> float: - return rotational_multiplier_x - - -## Sets the [member rotational_multiplier_y] value. -func set_rotational_multiplier_y(value: float) -> void: - rotational_multiplier_y = value - _noise_rotational_multiplier.y = value - -## Returns the [member rotational_multiplier_y] value. -func get_rotational_multiplier_y() -> float: - return rotational_multiplier_y - - -## Sets the [member rotational_multiplier_z] value. -func set_rotational_multiplier_z(value: float) -> void: - rotational_multiplier_z = value - _noise_rotational_multiplier.z = value - -## Returns the [member rotational_multiplier_z] value. -func get_rotational_multiplier_z() -> float: - return rotational_multiplier_z - - #endregion diff --git a/addons/phantom_camera/scripts/resources/phantom_camera_noise_3d.gd.uid b/addons/phantom_camera/scripts/resources/phantom_camera_noise_3d.gd.uid deleted file mode 100644 index 42a0694..0000000 --- a/addons/phantom_camera/scripts/resources/phantom_camera_noise_3d.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cuffvge5ad4aa diff --git a/addons/phantom_camera/scripts/resources/tween_resource.gd b/addons/phantom_camera/scripts/resources/tween_resource.gd deleted file mode 100644 index 0a3b46f..0000000 --- a/addons/phantom_camera/scripts/resources/tween_resource.gd +++ /dev/null @@ -1,41 +0,0 @@ -@icon("res://addons/phantom_camera/icons/phantom_camera_tween.svg") -class_name PhantomCameraTween -extends Resource - -## Tweening resource for [PhantomCamera2D] and [PhantomCamera3D]. -## -## Defines how [param PhantomCameras] transition between one another. -## Changing the tween values for a given [param PhantomCamera] determines how -## transitioning to that instance will look like. - -enum TransitionType { - LINEAR = 0, ## The animation is interpolated linearly. - SINE = 1, ## The animation is interpolated using a sine function. - QUINT = 2, ## The animation is interpolated with a quintic (to the power of 5) function. - QUART = 3, ## The animation is interpolated with a quartic (to the power of 4) function. - QUAD = 4, ## The animation is interpolated with a quadratic (to the power of 2) function. - EXPO = 5, ## The animation is interpolated with an exponential (to the power of x) function. - ELASTIC = 6, ## The animation is interpolated with elasticity, wiggling around the edges. - CUBIC = 7, ## The animation is interpolated with a cubic (to the power of 3) function. - CIRC = 8, ## The animation is interpolated with a function using square roots. - BOUNCE = 9, ## The animation is interpolated by bouncing at the end. - BACK = 10, ## The animation is interpolated backing out at ends. -# CUSTOM = 11, -# NONE = 12, -} - -enum EaseType { - EASE_IN = 0, ## The interpolation starts slowly and speeds up towards the end. - EASE_OUT = 1, ## The interpolation starts quickly and slows down towards the end. - EASE_IN_OUT = 2, ## A combination of EASE_IN and EASE_OUT. The interpolation is slowest at both ends. - EASE_OUT_IN = 3, ## A combination of EASE_IN and EASE_OUT. The interpolation is fastest at both ends. -} - -## The time it takes to tween to this PhantomCamera in [param seconds]. -@export var duration: float = 1.0 - -## The transition bezier type for the tween. The options are defined in the [enum TransitionType]. -@export var transition: TransitionType = TransitionType.LINEAR - -## The ease type for the tween. The options are defined in the [enum EaseType]. -@export var ease: EaseType = EaseType.EASE_IN_OUT diff --git a/addons/phantom_camera/scripts/resources/tween_resource.gd.uid b/addons/phantom_camera/scripts/resources/tween_resource.gd.uid deleted file mode 100644 index a0f2cf1..0000000 --- a/addons/phantom_camera/scripts/resources/tween_resource.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://8umksf8e80fw diff --git a/addons/phantom_camera/themes/button_focus.tres b/addons/phantom_camera/themes/button_focus.tres deleted file mode 100644 index e6fcc45..0000000 --- a/addons/phantom_camera/themes/button_focus.tres +++ /dev/null @@ -1,17 +0,0 @@ -[gd_resource type="StyleBoxFlat" format=3 uid="uid://p058hmj3uut0"] - -[resource] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.0784314, 0.109804, 0.129412, 1) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_radius_top_left = 8 -corner_radius_top_right = 8 -corner_radius_bottom_right = 8 -corner_radius_bottom_left = 8 diff --git a/addons/phantom_camera/themes/button_hover.tres b/addons/phantom_camera/themes/button_hover.tres deleted file mode 100644 index 9d37a86..0000000 --- a/addons/phantom_camera/themes/button_hover.tres +++ /dev/null @@ -1,13 +0,0 @@ -[gd_resource type="StyleBoxFlat" format=3 uid="uid://5weqvkjsfso3"] - -[resource] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.960784, 0.960784, 0.960784, 1) -border_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_radius_top_left = 8 -corner_radius_top_right = 8 -corner_radius_bottom_right = 8 -corner_radius_bottom_left = 8 diff --git a/addons/phantom_camera/themes/button_normal.tres b/addons/phantom_camera/themes/button_normal.tres deleted file mode 100644 index 4eae33d..0000000 --- a/addons/phantom_camera/themes/button_normal.tres +++ /dev/null @@ -1,17 +0,0 @@ -[gd_resource type="StyleBoxFlat" format=3 uid="uid://bclbwo3xrdat0"] - -[resource] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.0784314, 0.109804, 0.129412, 1) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_radius_top_left = 8 -corner_radius_top_right = 8 -corner_radius_bottom_right = 8 -corner_radius_bottom_left = 8 diff --git a/addons/phantom_camera/themes/theme.tres b/addons/phantom_camera/themes/theme.tres deleted file mode 100644 index 7ce53d8..0000000 --- a/addons/phantom_camera/themes/theme.tres +++ /dev/null @@ -1,102 +0,0 @@ -[gd_resource type="Theme" load_steps=12 format=3 uid="uid://bhppejri5dbsf"] - -[ext_resource type="FontFile" uid="uid://dve7mgsjik4dg" path="res://addons/phantom_camera/fonts/Nunito-Regular.ttf" id="1_5rtjh"] -[ext_resource type="StyleBox" uid="uid://5weqvkjsfso3" path="res://addons/phantom_camera/themes/button_hover.tres" id="2_du6h5"] -[ext_resource type="StyleBox" uid="uid://bclbwo3xrdat0" path="res://addons/phantom_camera/themes/button_normal.tres" id="3_a8j1f"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ek0y3"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_radius_top_left = 8 -corner_radius_top_right = 8 -corner_radius_bottom_right = 8 -corner_radius_bottom_left = 8 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rjkuq"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_radius_top_left = 8 -corner_radius_top_right = 8 -corner_radius_bottom_right = 8 -corner_radius_bottom_left = 8 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_x7u0w"] -content_margin_top = 2.0 -content_margin_right = 8.0 -bg_color = Color(0.0784314, 0.109804, 0.129412, 1) -border_width_top = 2 -border_width_right = 2 -border_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_radius_top_right = 10 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dln2q"] -content_margin_top = 8.0 -content_margin_bottom = 8.0 -draw_center = false - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wk7ot"] -bg_color = Color(0.227451, 0.72549, 0.603922, 1) -border_color = Color(0.227451, 0.72549, 0.603922, 1) -corner_radius_top_left = 8 -corner_radius_top_right = 8 -corner_radius_bottom_right = 8 -corner_radius_bottom_left = 8 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_jidrt"] -bg_color = Color(0.960784, 0.960784, 0.960784, 1) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -corner_radius_top_left = 8 -corner_radius_top_right = 8 -corner_radius_bottom_right = 8 -corner_radius_bottom_left = 8 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_o2xwc"] -bg_color = Color(0.960784, 0.960784, 0.960784, 1) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -corner_radius_top_left = 8 -corner_radius_top_right = 8 -corner_radius_bottom_right = 8 -corner_radius_bottom_left = 8 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ul127"] -draw_center = false -border_width_left = 4 -border_width_right = 4 -border_color = Color(0.8, 0.8, 0.8, 0) - -[resource] -default_font = ExtResource("1_5rtjh") -Button/colors/font_color = Color(0.227451, 0.72549, 0.603922, 1) -Button/colors/font_focus_color = Color(0.0784314, 0.109804, 0.129412, 1) -Button/colors/font_hover_color = Color(0.0784314, 0.109804, 0.129412, 1) -Button/colors/font_hover_pressed_color = Color(0.0784314, 0.109804, 0.129412, 1) -Button/colors/font_pressed_color = Color(0.0784314, 0.109804, 0.129412, 1) -Button/colors/icon_focus_color = Color(0.0784314, 0.109804, 0.129412, 1) -Button/colors/icon_hover_color = Color(0.0784314, 0.109804, 0.129412, 1) -Button/colors/icon_hover_pressed_color = Color(0.227451, 0.72549, 0.603922, 1) -Button/colors/icon_normal_color = Color(0.0784314, 0.109804, 0.129412, 1) -Button/colors/icon_pressed_color = Color(0.227451, 0.72549, 0.603922, 1) -Button/styles/focus = SubResource("StyleBoxFlat_ek0y3") -Button/styles/hover = ExtResource("2_du6h5") -Button/styles/hover_pressed = null -Button/styles/normal = ExtResource("3_a8j1f") -Button/styles/pressed = SubResource("StyleBoxFlat_rjkuq") -PanelContainer/styles/panel = SubResource("StyleBoxFlat_x7u0w") -ScrollContainer/styles/panel = SubResource("StyleBoxFlat_dln2q") -VBoxContainer/constants/separation = 8 -VScrollBar/styles/grabber = SubResource("StyleBoxFlat_wk7ot") -VScrollBar/styles/grabber_highlight = SubResource("StyleBoxFlat_jidrt") -VScrollBar/styles/grabber_pressed = SubResource("StyleBoxFlat_o2xwc") -VScrollBar/styles/scroll = SubResource("StyleBoxFlat_ul127") diff --git a/project.godot b/project.godot index 64d7af7..53652a9 100644 --- a/project.godot +++ b/project.godot @@ -17,7 +17,6 @@ config/icon="res://icon.svg" [autoload] -PhantomCameraManager="*res://addons/phantom_camera/scripts/managers/phantom_camera_manager.gd" CameraSystem="*res://_shared/camera/CameraSystem.tscn" GlobalEvent="*res://_shared/global_event.gd" ReedVFX="*res://addons/reedfx/vfx/ReedVFXSystem.tscn" @@ -33,7 +32,7 @@ window/stretch/mode="canvas_items" [editor_plugins] -enabled=PackedStringArray("res://addons/phantom_camera/plugin.cfg", "res://addons/reedcomponent/plugin.cfg", "res://addons/reedfx/plugin.cfg", "res://addons/reedinput/plugin.cfg", "res://addons/reedscene/plugin.cfg") +enabled=PackedStringArray("res://addons/reedcomponent/plugin.cfg", "res://addons/reedfx/plugin.cfg", "res://addons/reedinput/plugin.cfg", "res://addons/reedscene/plugin.cfg") [file_customization]