This commit is contained in:
Dan Baker 2025-12-02 07:45:23 +00:00
parent 7a8ee29dcb
commit 0fe23420ab
800 changed files with 16547 additions and 0 deletions

4
.editorconfig Normal file
View file

@ -0,0 +1,4 @@
root = true
[*]
charset = utf-8

2
.gitattributes vendored
View file

@ -1,3 +1,5 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf
*.ogg filter=lfs diff=lfs merge=lfs -text *.ogg filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text *.ttf filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text *.wav filter=lfs diff=lfs merge=lfs -text

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
# Godot 4+ specific ignores
.godot/
/android/

View file

@ -0,0 +1,21 @@
# MIT License
Copyright © 2023-present Hugo Locurcio and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,479 @@
extends CanvasLayer
@export var fps: Label
@export var frame_time: Label
@export var frame_number: Label
@export var frame_history_total_avg: Label
@export var frame_history_total_min: Label
@export var frame_history_total_max: Label
@export var frame_history_total_last: Label
@export var frame_history_cpu_avg: Label
@export var frame_history_cpu_min: Label
@export var frame_history_cpu_max: Label
@export var frame_history_cpu_last: Label
@export var frame_history_gpu_avg: Label
@export var frame_history_gpu_min: Label
@export var frame_history_gpu_max: Label
@export var frame_history_gpu_last: Label
@export var fps_graph: Panel
@export var total_graph: Panel
@export var cpu_graph: Panel
@export var gpu_graph: Panel
@export var information: Label
@export var settings: Label
## The number of frames to keep in history for graph drawing and best/worst calculations.
## Currently, this also affects how FPS is measured.
const HISTORY_NUM_FRAMES = 150
const GRAPH_SIZE = Vector2(150, 25)
const GRAPH_MIN_FPS = 10
const GRAPH_MAX_FPS = 160
const GRAPH_MIN_FRAMETIME = 1.0 / GRAPH_MIN_FPS
const GRAPH_MAX_FRAMETIME = 1.0 / GRAPH_MAX_FPS
## Debug menu display style.
enum Style {
HIDDEN, ## Debug menu is hidden.
VISIBLE_COMPACT, ## Debug menu is visible, with only the FPS, FPS cap (if any) and time taken to render the last frame.
VISIBLE_DETAILED, ## Debug menu is visible with full information, including graphs.
MAX, ## Represents the size of the Style enum.
}
## The style to use when drawing the debug menu.
var style := Style.HIDDEN:
set(value):
style = value
match style:
Style.HIDDEN:
visible = false
Style.VISIBLE_COMPACT, Style.VISIBLE_DETAILED:
visible = true
frame_number.visible = style == Style.VISIBLE_DETAILED
$DebugMenu/VBoxContainer/FrameTimeHistory.visible = style == Style.VISIBLE_DETAILED
$DebugMenu/VBoxContainer/FPSGraph.visible = style == Style.VISIBLE_DETAILED
$DebugMenu/VBoxContainer/TotalGraph.visible = style == Style.VISIBLE_DETAILED
$DebugMenu/VBoxContainer/CPUGraph.visible = style == Style.VISIBLE_DETAILED
$DebugMenu/VBoxContainer/GPUGraph.visible = style == Style.VISIBLE_DETAILED
information.visible = style == Style.VISIBLE_DETAILED
settings.visible = style == Style.VISIBLE_DETAILED
# Value of `Time.get_ticks_usec()` on the previous frame.
var last_tick := 0
var thread := Thread.new()
## Returns the sum of all values of an array (use as a parameter to `Array.reduce()`).
var sum_func := func avg(accum: float, number: float) -> float: return accum + number
# History of the last `HISTORY_NUM_FRAMES` rendered frames.
var frame_history_total: Array[float] = []
var frame_history_cpu: Array[float] = []
var frame_history_gpu: Array[float] = []
var fps_history: Array[float] = [] # Only used for graphs.
var frametime_avg := GRAPH_MIN_FRAMETIME
var frametime_cpu_avg := GRAPH_MAX_FRAMETIME
var frametime_gpu_avg := GRAPH_MIN_FRAMETIME
var frames_per_second := float(GRAPH_MIN_FPS)
var frame_time_gradient := Gradient.new()
func _init() -> void:
# This must be done here instead of `_ready()` to avoid having `visibility_changed` be emitted immediately.
visible = false
if not InputMap.has_action("cycle_debug_menu"):
# Create default input action if no user-defined override exists.
# We can't do it in the editor plugin's activation code as it doesn't seem to work there.
InputMap.add_action("cycle_debug_menu")
var event := InputEventKey.new()
event.keycode = KEY_F3
InputMap.action_add_event("cycle_debug_menu", event)
func _ready() -> void:
fps_graph.draw.connect(_fps_graph_draw)
total_graph.draw.connect(_total_graph_draw)
cpu_graph.draw.connect(_cpu_graph_draw)
gpu_graph.draw.connect(_gpu_graph_draw)
fps_history.resize(HISTORY_NUM_FRAMES)
frame_history_total.resize(HISTORY_NUM_FRAMES)
frame_history_cpu.resize(HISTORY_NUM_FRAMES)
frame_history_gpu.resize(HISTORY_NUM_FRAMES)
# NOTE: Both FPS and frametimes are colored following FPS logic
# (red = 10 FPS, yellow = 60 FPS, green = 110 FPS, cyan = 160 FPS).
# This makes the color gradient non-linear.
# Colors are taken from <https://tailwindcolor.com/>.
frame_time_gradient.set_color(0, Color8(239, 68, 68)) # red-500
frame_time_gradient.set_color(1, Color8(56, 189, 248)) # light-blue-400
frame_time_gradient.add_point(0.3333, Color8(250, 204, 21)) # yellow-400
frame_time_gradient.add_point(0.6667, Color8(128, 226, 95)) # 50-50 mix of lime-400 and green-400
get_viewport().size_changed.connect(update_settings_label)
# Display loading text while information is being queried,
# in case the user toggles the full debug menu just after starting the project.
information.text = "Loading hardware information...\n\n "
settings.text = "Loading project information..."
thread.start(
func():
# Disable thread safety checks as they interfere with this add-on.
# This only affects this particular thread, not other thread instances in the project.
# See <https://github.com/godotengine/godot/pull/78000> for details.
# Use a Callable so that this can be ignored on Godot 4.0 without causing a script error
# (thread safety checks were added in Godot 4.1).
if Engine.get_version_info()["hex"] >= 0x040100:
Callable(Thread, "set_thread_safety_checks_enabled").call(false)
# Enable required time measurements to display CPU/GPU frame time information.
# These lines are time-consuming operations, so run them in a separate thread.
RenderingServer.viewport_set_measure_render_time(get_viewport().get_viewport_rid(), true)
update_information_label()
update_settings_label()
)
func _input(event: InputEvent) -> void:
if event.is_action_pressed("cycle_debug_menu"):
style = wrapi(style + 1, 0, Style.MAX) as Style
func _exit_tree() -> void:
thread.wait_to_finish()
## Update hardware information label (this can change at runtime based on window
## size and graphics settings). This is only called when the window is resized.
## To update when graphics settings are changed, the function must be called manually
## using `DebugMenu.update_settings_label()`.
func update_settings_label() -> void:
settings.text = ""
if ProjectSettings.has_setting("application/config/version"):
settings.text += "Project Version: %s\n" % ProjectSettings.get_setting("application/config/version")
var rendering_method := str(ProjectSettings.get_setting_with_override("rendering/renderer/rendering_method"))
var rendering_method_string := rendering_method
match rendering_method:
"forward_plus":
rendering_method_string = "Forward+"
"mobile":
rendering_method_string = "Forward Mobile"
"gl_compatibility":
rendering_method_string = "Compatibility"
settings.text += "Rendering Method: %s\n" % rendering_method_string
var viewport := get_viewport()
# The size of the viewport rendering, which determines which resolution 3D is rendered at.
var viewport_render_size := Vector2i()
if viewport.content_scale_mode == Window.CONTENT_SCALE_MODE_VIEWPORT:
viewport_render_size = viewport.get_visible_rect().size
settings.text += "Viewport: %d×%d, Window: %d×%d\n" % [viewport.get_visible_rect().size.x, viewport.get_visible_rect().size.y, viewport.size.x, viewport.size.y]
else:
# Window size matches viewport size.
viewport_render_size = viewport.size
settings.text += "Viewport: %d×%d\n" % [viewport.size.x, viewport.size.y]
# Display 3D settings only if relevant.
if viewport.get_camera_3d():
var scaling_3d_mode_string := "(unknown)"
match viewport.scaling_3d_mode:
Viewport.SCALING_3D_MODE_BILINEAR:
scaling_3d_mode_string = "Bilinear"
Viewport.SCALING_3D_MODE_FSR:
scaling_3d_mode_string = "FSR 1.0"
Viewport.SCALING_3D_MODE_FSR2:
scaling_3d_mode_string = "FSR 2.2"
var antialiasing_3d_string := ""
if viewport.scaling_3d_mode == Viewport.SCALING_3D_MODE_FSR2:
# The FSR2 scaling mode includes its own temporal antialiasing implementation.
antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "FSR 2.2"
if viewport.scaling_3d_mode != Viewport.SCALING_3D_MODE_FSR2 and viewport.use_taa:
# Godot's own TAA is ignored when using FSR2 scaling mode, as FSR2 provides its own TAA implementation.
antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "TAA"
if viewport.msaa_3d >= Viewport.MSAA_2X:
antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "%d× MSAA" % pow(2, viewport.msaa_3d)
if viewport.screen_space_aa == Viewport.SCREEN_SPACE_AA_FXAA:
antialiasing_3d_string += (" + " if not antialiasing_3d_string.is_empty() else "") + "FXAA"
settings.text += "3D scale (%s): %d%% = %d×%d" % [
scaling_3d_mode_string,
viewport.scaling_3d_scale * 100,
viewport_render_size.x * viewport.scaling_3d_scale,
viewport_render_size.y * viewport.scaling_3d_scale,
]
if not antialiasing_3d_string.is_empty():
settings.text += "\n3D Antialiasing: %s" % antialiasing_3d_string
var environment := viewport.get_camera_3d().get_world_3d().environment
if environment:
if environment.ssr_enabled:
settings.text += "\nSSR: %d Steps" % environment.ssr_max_steps
if environment.ssao_enabled:
settings.text += "\nSSAO: On"
if environment.ssil_enabled:
settings.text += "\nSSIL: On"
if environment.sdfgi_enabled:
settings.text += "\nSDFGI: %d Cascades" % environment.sdfgi_cascades
if environment.glow_enabled:
settings.text += "\nGlow: On"
if environment.volumetric_fog_enabled:
settings.text += "\nVolumetric Fog: On"
var antialiasing_2d_string := ""
if viewport.msaa_2d >= Viewport.MSAA_2X:
antialiasing_2d_string = "%d× MSAA" % pow(2, viewport.msaa_2d)
if not antialiasing_2d_string.is_empty():
settings.text += "\n2D Antialiasing: %s" % antialiasing_2d_string
## Update hardware/software information label (this never changes at runtime).
func update_information_label() -> void:
var adapter_string := ""
# Make "NVIDIA Corporation" and "NVIDIA" be considered identical (required when using OpenGL to avoid redundancy).
if RenderingServer.get_video_adapter_vendor().trim_suffix(" Corporation") in RenderingServer.get_video_adapter_name():
# Avoid repeating vendor name before adapter name.
# Trim redundant suffix sometimes reported by NVIDIA graphics cards when using OpenGL.
adapter_string = RenderingServer.get_video_adapter_name().trim_suffix("/PCIe/SSE2")
else:
adapter_string = RenderingServer.get_video_adapter_vendor() + " - " + RenderingServer.get_video_adapter_name().trim_suffix("/PCIe/SSE2")
# Graphics driver version information isn't always availble.
var driver_info := OS.get_video_adapter_driver_info()
var driver_info_string := ""
if driver_info.size() >= 2:
driver_info_string = driver_info[1]
else:
driver_info_string = "(unknown)"
var release_string := ""
if OS.has_feature("editor"):
# Editor build (implies `debug`).
release_string = "editor"
elif OS.has_feature("debug"):
# Debug export template build.
release_string = "debug"
else:
# Release export template build.
release_string = "release"
var rendering_method := str(ProjectSettings.get_setting_with_override("rendering/renderer/rendering_method"))
var rendering_driver := str(ProjectSettings.get_setting_with_override("rendering/rendering_device/driver"))
var graphics_api_string := rendering_driver
if rendering_method != "gl_compatibility":
if rendering_driver == "d3d12":
graphics_api_string = "Direct3D 12"
elif rendering_driver == "metal":
graphics_api_string = "Metal"
elif rendering_driver == "vulkan":
if OS.has_feature("macos") or OS.has_feature("ios"):
graphics_api_string = "Vulkan via MoltenVK"
else:
graphics_api_string = "Vulkan"
else:
if rendering_driver == "opengl3_angle":
graphics_api_string = "OpenGL via ANGLE"
elif OS.has_feature("mobile") or rendering_driver == "opengl3_es":
graphics_api_string = "OpenGL ES"
elif OS.has_feature("web"):
graphics_api_string = "WebGL"
elif rendering_driver == "opengl3":
graphics_api_string = "OpenGL"
information.text = (
"%s, %d threads\n" % [OS.get_processor_name().replace("(R)", "").replace("(TM)", ""), OS.get_processor_count()]
+ "%s %s (%s %s), %s %s\n" % [OS.get_name(), "64-bit" if OS.has_feature("64") else "32-bit", release_string, "double" if OS.has_feature("double") else "single", graphics_api_string, RenderingServer.get_video_adapter_api_version()]
+ "%s, %s" % [adapter_string, driver_info_string]
)
func _fps_graph_draw() -> void:
var fps_polyline := PackedVector2Array()
fps_polyline.resize(HISTORY_NUM_FRAMES)
for fps_index in fps_history.size():
fps_polyline[fps_index] = Vector2(
remap(fps_index, 0, fps_history.size(), 0, GRAPH_SIZE.x),
remap(clampf(fps_history[fps_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0)
)
# Don't use antialiasing to speed up line drawing, but use a width that scales with
# viewport scale to keep the line easily readable on hiDPI displays.
fps_graph.draw_polyline(fps_polyline, frame_time_gradient.sample(remap(frames_per_second, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0)
func _total_graph_draw() -> void:
var total_polyline := PackedVector2Array()
total_polyline.resize(HISTORY_NUM_FRAMES)
for total_index in frame_history_total.size():
total_polyline[total_index] = Vector2(
remap(total_index, 0, frame_history_total.size(), 0, GRAPH_SIZE.x),
remap(clampf(frame_history_total[total_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0)
)
# Don't use antialiasing to speed up line drawing, but use a width that scales with
# viewport scale to keep the line easily readable on hiDPI displays.
total_graph.draw_polyline(total_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0)
func _cpu_graph_draw() -> void:
var cpu_polyline := PackedVector2Array()
cpu_polyline.resize(HISTORY_NUM_FRAMES)
for cpu_index in frame_history_cpu.size():
cpu_polyline[cpu_index] = Vector2(
remap(cpu_index, 0, frame_history_cpu.size(), 0, GRAPH_SIZE.x),
remap(clampf(frame_history_cpu[cpu_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0)
)
# Don't use antialiasing to speed up line drawing, but use a width that scales with
# viewport scale to keep the line easily readable on hiDPI displays.
cpu_graph.draw_polyline(cpu_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_cpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0)
func _gpu_graph_draw() -> void:
var gpu_polyline := PackedVector2Array()
gpu_polyline.resize(HISTORY_NUM_FRAMES)
for gpu_index in frame_history_gpu.size():
gpu_polyline[gpu_index] = Vector2(
remap(gpu_index, 0, frame_history_gpu.size(), 0, GRAPH_SIZE.x),
remap(clampf(frame_history_gpu[gpu_index], GRAPH_MIN_FPS, GRAPH_MAX_FPS), GRAPH_MIN_FPS, GRAPH_MAX_FPS, GRAPH_SIZE.y, 0.0)
)
# Don't use antialiasing to speed up line drawing, but use a width that scales with
# viewport scale to keep the line easily readable on hiDPI displays.
gpu_graph.draw_polyline(gpu_polyline, frame_time_gradient.sample(remap(1000.0 / frametime_gpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0)), 1.0)
func _process(_delta: float) -> void:
if visible:
fps_graph.queue_redraw()
total_graph.queue_redraw()
cpu_graph.queue_redraw()
gpu_graph.queue_redraw()
# Difference between the last two rendered frames in milliseconds.
var frametime := (Time.get_ticks_usec() - last_tick) * 0.001
frame_history_total.push_back(frametime)
if frame_history_total.size() > HISTORY_NUM_FRAMES:
frame_history_total.pop_front()
# Frametimes are colored following FPS logic (red = 10 FPS, yellow = 60 FPS, green = 110 FPS, cyan = 160 FPS).
# This makes the color gradient non-linear.
frametime_avg = frame_history_total.reduce(sum_func) / frame_history_total.size()
frame_history_total_avg.text = str(frametime_avg).pad_decimals(2)
frame_history_total_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
var frametime_min: float = frame_history_total.min()
frame_history_total_min.text = str(frametime_min).pad_decimals(2)
frame_history_total_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
var frametime_max: float = frame_history_total.max()
frame_history_total_max.text = str(frametime_max).pad_decimals(2)
frame_history_total_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
frame_history_total_last.text = str(frametime).pad_decimals(2)
frame_history_total_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
var viewport_rid := get_viewport().get_viewport_rid()
var frametime_cpu := RenderingServer.viewport_get_measured_render_time_cpu(viewport_rid) + RenderingServer.get_frame_setup_time_cpu()
frame_history_cpu.push_back(frametime_cpu)
if frame_history_cpu.size() > HISTORY_NUM_FRAMES:
frame_history_cpu.pop_front()
frametime_cpu_avg = frame_history_cpu.reduce(sum_func) / frame_history_cpu.size()
frame_history_cpu_avg.text = str(frametime_cpu_avg).pad_decimals(2)
frame_history_cpu_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
var frametime_cpu_min: float = frame_history_cpu.min()
frame_history_cpu_min.text = str(frametime_cpu_min).pad_decimals(2)
frame_history_cpu_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
var frametime_cpu_max: float = frame_history_cpu.max()
frame_history_cpu_max.text = str(frametime_cpu_max).pad_decimals(2)
frame_history_cpu_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
frame_history_cpu_last.text = str(frametime_cpu).pad_decimals(2)
frame_history_cpu_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_cpu, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
var frametime_gpu := RenderingServer.viewport_get_measured_render_time_gpu(viewport_rid)
frame_history_gpu.push_back(frametime_gpu)
if frame_history_gpu.size() > HISTORY_NUM_FRAMES:
frame_history_gpu.pop_front()
frametime_gpu_avg = frame_history_gpu.reduce(sum_func) / frame_history_gpu.size()
frame_history_gpu_avg.text = str(frametime_gpu_avg).pad_decimals(2)
frame_history_gpu_avg.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_avg, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
var frametime_gpu_min: float = frame_history_gpu.min()
frame_history_gpu_min.text = str(frametime_gpu_min).pad_decimals(2)
frame_history_gpu_min.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_min, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
var frametime_gpu_max: float = frame_history_gpu.max()
frame_history_gpu_max.text = str(frametime_gpu_max).pad_decimals(2)
frame_history_gpu_max.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu_max, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
frame_history_gpu_last.text = str(frametime_gpu).pad_decimals(2)
frame_history_gpu_last.modulate = frame_time_gradient.sample(remap(1000.0 / frametime_gpu, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
frames_per_second = 1000.0 / frametime_avg
fps_history.push_back(frames_per_second)
if fps_history.size() > HISTORY_NUM_FRAMES:
fps_history.pop_front()
fps.text = str(floor(frames_per_second)) + " FPS"
var frame_time_color := frame_time_gradient.sample(remap(frames_per_second, GRAPH_MIN_FPS, GRAPH_MAX_FPS, 0.0, 1.0))
fps.modulate = frame_time_color
frame_time.text = str(frametime).pad_decimals(2) + " mspf"
frame_time.modulate = frame_time_color
var vsync_string := ""
match DisplayServer.window_get_vsync_mode():
DisplayServer.VSYNC_ENABLED:
vsync_string = "V-Sync"
DisplayServer.VSYNC_ADAPTIVE:
vsync_string = "Adaptive V-Sync"
DisplayServer.VSYNC_MAILBOX:
vsync_string = "Mailbox V-Sync"
if Engine.max_fps > 0 or OS.low_processor_usage_mode:
# Display FPS cap determined by `Engine.max_fps` or low-processor usage mode sleep duration
# (the lowest FPS cap is used).
var low_processor_max_fps := roundi(1000000.0 / OS.low_processor_usage_mode_sleep_usec)
var fps_cap := low_processor_max_fps
if Engine.max_fps > 0:
fps_cap = mini(Engine.max_fps, low_processor_max_fps)
frame_time.text += " (cap: " + str(fps_cap) + " FPS"
if not vsync_string.is_empty():
frame_time.text += " + " + vsync_string
frame_time.text += ")"
else:
if not vsync_string.is_empty():
frame_time.text += " (" + vsync_string + ")"
frame_number.text = "Frame: " + str(Engine.get_frames_drawn())
last_tick = Time.get_ticks_usec()
func _on_visibility_changed() -> void:
if visible:
# Reset graphs to prevent them from looking strange before `HISTORY_NUM_FRAMES` frames
# have been drawn.
var frametime_last := (Time.get_ticks_usec() - last_tick) * 0.001
fps_history.resize(HISTORY_NUM_FRAMES)
fps_history.fill(1000.0 / frametime_last)
frame_history_total.resize(HISTORY_NUM_FRAMES)
frame_history_total.fill(frametime_last)
frame_history_cpu.resize(HISTORY_NUM_FRAMES)
var viewport_rid := get_viewport().get_viewport_rid()
frame_history_cpu.fill(RenderingServer.viewport_get_measured_render_time_cpu(viewport_rid) + RenderingServer.get_frame_setup_time_cpu())
frame_history_gpu.resize(HISTORY_NUM_FRAMES)
frame_history_gpu.fill(RenderingServer.viewport_get_measured_render_time_gpu(viewport_rid))

View file

@ -0,0 +1 @@
uid://dpeqyx00y40f6

View file

@ -0,0 +1,401 @@
[gd_scene load_steps=3 format=3 uid="uid://cggqb75a8w8r"]
[ext_resource type="Script" path="res://addons/debug_menu/debug_menu.gd" id="1_p440y"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ki0n8"]
bg_color = Color(0, 0, 0, 0.25098)
[node name="CanvasLayer" type="CanvasLayer" node_paths=PackedStringArray("fps", "frame_time", "frame_number", "frame_history_total_avg", "frame_history_total_min", "frame_history_total_max", "frame_history_total_last", "frame_history_cpu_avg", "frame_history_cpu_min", "frame_history_cpu_max", "frame_history_cpu_last", "frame_history_gpu_avg", "frame_history_gpu_min", "frame_history_gpu_max", "frame_history_gpu_last", "fps_graph", "total_graph", "cpu_graph", "gpu_graph", "information", "settings")]
layer = 128
script = ExtResource("1_p440y")
fps = NodePath("DebugMenu/VBoxContainer/FPS")
frame_time = NodePath("DebugMenu/VBoxContainer/FrameTime")
frame_number = NodePath("DebugMenu/VBoxContainer/FrameNumber")
frame_history_total_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalAvg")
frame_history_total_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalMin")
frame_history_total_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalMax")
frame_history_total_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/TotalLast")
frame_history_cpu_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUAvg")
frame_history_cpu_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUMin")
frame_history_cpu_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPUMax")
frame_history_cpu_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/CPULast")
frame_history_gpu_avg = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUAvg")
frame_history_gpu_min = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUMin")
frame_history_gpu_max = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPUMax")
frame_history_gpu_last = NodePath("DebugMenu/VBoxContainer/FrameTimeHistory/GPULast")
fps_graph = NodePath("DebugMenu/VBoxContainer/FPSGraph/Graph")
total_graph = NodePath("DebugMenu/VBoxContainer/TotalGraph/Graph")
cpu_graph = NodePath("DebugMenu/VBoxContainer/CPUGraph/Graph")
gpu_graph = NodePath("DebugMenu/VBoxContainer/GPUGraph/Graph")
information = NodePath("DebugMenu/VBoxContainer/Information")
settings = NodePath("DebugMenu/VBoxContainer/Settings")
[node name="DebugMenu" type="Control" parent="."]
custom_minimum_size = Vector2(400, 400)
layout_mode = 3
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -416.0
offset_top = 8.0
offset_right = -16.0
offset_bottom = 408.0
grow_horizontal = 0
size_flags_horizontal = 8
size_flags_vertical = 4
mouse_filter = 2
[node name="VBoxContainer" type="VBoxContainer" parent="DebugMenu"]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -300.0
offset_bottom = 374.0
grow_horizontal = 0
mouse_filter = 2
theme_override_constants/separation = 0
[node name="FPS" type="Label" parent="DebugMenu/VBoxContainer"]
modulate = Color(0, 1, 0, 1)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 5
theme_override_constants/line_spacing = 0
theme_override_font_sizes/font_size = 18
text = "60 FPS"
horizontal_alignment = 2
[node name="FrameTime" type="Label" parent="DebugMenu/VBoxContainer"]
modulate = Color(0, 1, 0, 1)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "16.67 mspf (cap: 123 FPS + Adaptive V-Sync)"
horizontal_alignment = 2
[node name="FrameNumber" type="Label" parent="DebugMenu/VBoxContainer"]
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "Frame: 1234"
horizontal_alignment = 2
[node name="FrameTimeHistory" type="GridContainer" parent="DebugMenu/VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 8
mouse_filter = 2
theme_override_constants/h_separation = 0
theme_override_constants/v_separation = 0
columns = 5
[node name="Spacer" type="Control" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
mouse_filter = 2
[node name="AvgHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "Average"
horizontal_alignment = 2
[node name="MinHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "Best"
horizontal_alignment = 2
[node name="MaxHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "Worst"
horizontal_alignment = 2
[node name="LastHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "Last"
horizontal_alignment = 2
[node name="TotalHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "Total:"
horizontal_alignment = 2
[node name="TotalAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
modulate = Color(0, 1, 0, 1)
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "123.45"
horizontal_alignment = 2
[node name="TotalMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
modulate = Color(0, 1, 0, 1)
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "123.45"
horizontal_alignment = 2
[node name="TotalMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
modulate = Color(0, 1, 0, 1)
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "123.45"
horizontal_alignment = 2
[node name="TotalLast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
modulate = Color(0, 1, 0, 1)
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "123.45"
horizontal_alignment = 2
[node name="CPUHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "CPU:"
horizontal_alignment = 2
[node name="CPUAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
modulate = Color(0, 1, 0, 1)
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "123.45"
horizontal_alignment = 2
[node name="CPUMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
modulate = Color(0, 1, 0, 1)
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "12.34"
horizontal_alignment = 2
[node name="CPUMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
modulate = Color(0, 1, 0, 1)
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "123.45"
horizontal_alignment = 2
[node name="CPULast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
modulate = Color(0, 1, 0, 1)
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "123.45"
horizontal_alignment = 2
[node name="GPUHeader" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "GPU:"
horizontal_alignment = 2
[node name="GPUAvg" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
modulate = Color(0, 1, 0, 1)
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "123.45"
horizontal_alignment = 2
[node name="GPUMin" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
modulate = Color(0, 1, 0, 1)
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "1.23"
horizontal_alignment = 2
[node name="GPUMax" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
modulate = Color(0, 1, 0, 1)
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "123.45"
horizontal_alignment = 2
[node name="GPULast" type="Label" parent="DebugMenu/VBoxContainer/FrameTimeHistory"]
modulate = Color(0, 1, 0, 1)
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "123.45"
horizontal_alignment = 2
[node name="FPSGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
alignment = 2
[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/FPSGraph"]
custom_minimum_size = Vector2(0, 27)
layout_mode = 2
size_flags_horizontal = 8
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "FPS: ↑"
vertical_alignment = 1
[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/FPSGraph"]
custom_minimum_size = Vector2(150, 25)
layout_mode = 2
size_flags_vertical = 0
mouse_filter = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8")
[node name="TotalGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
alignment = 2
[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/TotalGraph"]
custom_minimum_size = Vector2(0, 27)
layout_mode = 2
size_flags_horizontal = 8
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "Total: ↓"
vertical_alignment = 1
[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/TotalGraph"]
custom_minimum_size = Vector2(150, 25)
layout_mode = 2
size_flags_vertical = 0
mouse_filter = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8")
[node name="CPUGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
alignment = 2
[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/CPUGraph"]
custom_minimum_size = Vector2(0, 27)
layout_mode = 2
size_flags_horizontal = 8
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "CPU: ↓"
vertical_alignment = 1
[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/CPUGraph"]
custom_minimum_size = Vector2(150, 25)
layout_mode = 2
size_flags_vertical = 0
mouse_filter = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8")
[node name="GPUGraph" type="HBoxContainer" parent="DebugMenu/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
alignment = 2
[node name="Title" type="Label" parent="DebugMenu/VBoxContainer/GPUGraph"]
custom_minimum_size = Vector2(0, 27)
layout_mode = 2
size_flags_horizontal = 8
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "GPU: ↓"
vertical_alignment = 1
[node name="Graph" type="Panel" parent="DebugMenu/VBoxContainer/GPUGraph"]
custom_minimum_size = Vector2(150, 25)
layout_mode = 2
size_flags_vertical = 0
mouse_filter = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_ki0n8")
[node name="Information" type="Label" parent="DebugMenu/VBoxContainer"]
modulate = Color(1, 1, 1, 0.752941)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "12th Gen Intel(R) Core(TM) i0-1234K
Windows 12 64-bit (double precision), Vulkan 1.2.34
NVIDIA GeForce RTX 1234, 123.45.67"
horizontal_alignment = 2
[node name="Settings" type="Label" parent="DebugMenu/VBoxContainer"]
modulate = Color(0.8, 0.84, 1, 0.752941)
layout_mode = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 3
theme_override_font_sizes/font_size = 12
text = "Project Version: 1.2.3
Rendering Method: Forward+
Window: 1234×567, Viewport: 1234×567
3D Scale (FSR 1.0): 100% = 1234×567
3D Antialiasing: TAA + 2× MSAA + FXAA
SSR: 123 Steps
SSAO: On
SSIL: On
SDFGI: 1 Cascades
Glow: On
Volumetric Fog: On
2D Antialiasing: 2× MSAA"
horizontal_alignment = 2
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]

View file

@ -0,0 +1,7 @@
[plugin]
name="Debug Menu"
description="In-game debug menu displaying performance metrics and hardware information"
author="Calinou"
version="1.2.0"
script="plugin.gd"

View file

@ -0,0 +1,29 @@
@tool
extends EditorPlugin
func _enter_tree() -> void:
add_autoload_singleton("DebugMenu", "res://addons/debug_menu/debug_menu.tscn")
# FIXME: This appears to do nothing.
# if not ProjectSettings.has_setting("application/config/version"):
# ProjectSettings.set_setting("application/config/version", "1.0.0")
#
# ProjectSettings.set_initial_value("application/config/version", "1.0.0")
# ProjectSettings.add_property_info({
# name = "application/config/version",
# type = TYPE_STRING,
# })
#
# if not InputMap.has_action("cycle_debug_menu"):
# InputMap.add_action("cycle_debug_menu")
# var event := InputEventKey.new()
# event.keycode = KEY_F3
# InputMap.action_add_event("cycle_debug_menu", event)
#
# ProjectSettings.save()
func _exit_tree() -> void:
remove_autoload_singleton("DebugMenu")
# Don't remove the project setting's value and input map action,
# as the plugin may be re-enabled in the future.

View file

@ -0,0 +1 @@
uid://cdct5p0xa3k1r

21
addons/log/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Russell Matney
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,33 @@
[gd_resource type="Resource" script_class="LogColorTheme" load_steps=2 format=3 uid="uid://c2tfr7nw4y7ll"]
[ext_resource type="Script" uid="uid://br8socgd1vvih" path="res://addons/log/log_color_theme.gd" id="1_6x53w"]
[resource]
script = ExtResource("1_6x53w")
color_src_prefix = Color(0.498039, 1, 0.831373, 1)
color_addons_prefix = Color(0.803922, 0.521569, 0.247059, 1)
color_test_prefix = Color(0.678431, 1, 0.184314, 1)
color_comma = Color(1, 0.411765, 0.705882, 1)
color_ampersand = Color(1, 0.498039, 0.313726, 1)
color_pipe = Color(1, 0.498039, 0.313726, 1)
color_carrot = Color(1, 0.498039, 0.313726, 1)
colors_rainbow_delims = Array[Color]([Color(0.862745, 0.0784314, 0.235294, 1), Color(0.392157, 0.584314, 0.929412, 1), Color(1, 0.498039, 0.313726, 1), Color(1, 0.752941, 0.796078, 1), Color(0.803922, 0.521569, 0.247059, 1)])
color_nil = Color(1, 0.498039, 0.313726, 1)
color_bool = Color(1, 0.752941, 0.796078, 1)
color_int = Color(0.392157, 0.584314, 0.929412, 1)
color_float = Color(0.392157, 0.584314, 0.929412, 1)
color_vectors = Color(0.392157, 0.584314, 0.929412, 1)
color_rects = Color(0.392157, 0.584314, 0.929412, 1)
color_class_name = Color(0.372549, 0.619608, 0.627451, 1)
color_string = Color(0.662745, 0.662745, 0.662745, 1)
color_string_name = Color(1, 0.752941, 0.796078, 1)
color_node_path = Color(1, 0.752941, 0.796078, 1)
color_type_color = Color(1, 0.752941, 0.796078, 1)
color_rid = Color(1, 0.752941, 0.796078, 1)
color_object = Color(1, 0.752941, 0.796078, 1)
color_callable = Color(1, 0.752941, 0.796078, 1)
color_signal = Color(1, 0.752941, 0.796078, 1)
color_array = Color(1, 0.752941, 0.796078, 1)
color_dictionary = Color(1, 0.752941, 0.796078, 1)
color_packed_array = Color(1, 0.752941, 0.796078, 1)
color_type_max = Color(1, 0.752941, 0.796078, 1)

View file

@ -0,0 +1,33 @@
[gd_resource type="Resource" script_class="LogColorTheme" load_steps=2 format=3 uid="uid://biby87a3dypvn"]
[ext_resource type="Script" uid="uid://br8socgd1vvih" path="res://addons/log/log_color_theme.gd" id="1_kcklq"]
[resource]
script = ExtResource("1_kcklq")
color_src_prefix = Color(0, 0.545098, 0.545098, 1)
color_addons_prefix = Color(0.545098, 0, 0, 1)
color_test_prefix = Color(0, 0.392157, 0, 1)
color_comma = Color(0.862745, 0.0784314, 0.235294, 1)
color_ampersand = Color(1, 0.498039, 0.313726, 1)
color_pipe = Color(1, 0.498039, 0.313726, 1)
color_carrot = Color(1, 0.498039, 0.313726, 1)
colors_rainbow_delims = Array[Color]([Color(0.862745, 0.0784314, 0.235294, 1), Color(0, 0, 0.545098, 1), Color(0.294118, 0, 0.509804, 1), Color(0.501961, 0.501961, 0, 1), Color(0.545098, 0.270588, 0.0745098, 1)])
color_nil = Color(1, 0.498039, 0.313726, 1)
color_bool = Color(0.545098, 0, 0, 1)
color_int = Color(0.6, 0.196078, 0.8, 1)
color_float = Color(0.6, 0.196078, 0.8, 1)
color_vectors = Color(0.392157, 0.584314, 0.929412, 1)
color_rects = Color(0.392157, 0.584314, 0.929412, 1)
color_class_name = Color(0.372549, 0.619608, 0.627451, 1)
color_string = Color(0.545098, 0, 0, 1)
color_string_name = Color(0.545098, 0, 0, 1)
color_node_path = Color(0.545098, 0, 0, 1)
color_type_color = Color(0.545098, 0, 0, 1)
color_rid = Color(0.545098, 0, 0, 1)
color_object = Color(0.545098, 0, 0, 1)
color_callable = Color(0.545098, 0, 0, 1)
color_signal = Color(0.545098, 0, 0, 1)
color_array = Color(0.545098, 0, 0, 1)
color_dictionary = Color(0.545098, 0, 0, 1)
color_packed_array = Color(0.545098, 0, 0, 1)
color_type_max = Color(0.545098, 0, 0, 1)

767
addons/log/log.gd Normal file
View file

@ -0,0 +1,767 @@
## Log.gd - colorized pretty printing functions
##
## [code]Log.pr(...)[/code] and [code]Log.prn(...)[/code] are drop-in replacements for [code]print(...)[/code].
##
## [br][br]
## You can also [code]Log.warn(...)[/code] or [code]Log.error(...)[/code] to both print and push_warn/push_error.
##
## [br][br]
## Custom object output is supported by implementing [code]to_pretty()[/code] on the object.
##
## [br][br]
## For objects you don't own (built-ins or addons you don't want to edit),
## there is a [code]register_type_overwrite(key, handler)[/code] helper.
##
## [br][br]
## You can find up-to-date docs and examples in the Log.gd repo and docs site:
## [br]
## - https://github.com/russmatney/log.gd
## [br]
## - https://russmatney.github.io/log.gd
##
@tool
extends Object
class_name Log
# helpers ####################################
static func assoc(opts: Dictionary, key: String, val: Variant) -> Dictionary:
var _opts: Dictionary = opts.duplicate(true)
_opts[key] = val
return _opts
# settings helpers ####################################
static func initialize_setting(key: String, default_value: Variant, type: int, hint: int = PROPERTY_HINT_NONE, hint_string: String = "") -> void:
if not ProjectSettings.has_setting(key):
ProjectSettings.set(key, default_value)
ProjectSettings.set_initial_value(key, default_value)
ProjectSettings.add_property_info({name=key, type=type, hint=hint, hint_string=hint_string})
# settings keys and default ####################################
const KEY_PREFIX: String = "log_gd/config"
static var is_config_setup: bool = false
# TODO drop this key
const KEY_COLOR_THEME_DICT: String = "log_color_theme_dict"
const KEY_COLOR_THEME: String = "log_color_theme"
const KEY_COLOR_THEME_RESOURCE_PATH: String = "%s/color_resource_path" % KEY_PREFIX
const KEY_DISABLE_COLORS: String = "%s/disable_colors" % KEY_PREFIX
const KEY_MAX_ARRAY_SIZE: String = "%s/max_array_size" % KEY_PREFIX
const KEY_SKIP_KEYS: String = "%s/dictionary_skip_keys" % KEY_PREFIX
const KEY_USE_NEWLINES: String = "%s/use_newlines" % KEY_PREFIX
const KEY_NEWLINE_MAX_DEPTH: String = "%s/newline_max_depth" % KEY_PREFIX
const KEY_LOG_LEVEL: String = "%s/log_level" % KEY_PREFIX
const KEY_WARN_TODO: String = "%s/warn_todo" % KEY_PREFIX
const KEY_SHOW_LOG_LEVEL_SELECTOR: String = "%s/show_log_level_selector" % KEY_PREFIX
const KEY_SHOW_TIMESTAMPS: String = "%s/show_timestamps" % KEY_PREFIX
const KEY_TIMESTAMP_TYPE: String = "%s/timestamp_type" % KEY_PREFIX
const KEY_HUMAN_READABLE_TIMESTAMP_FORMAT: String = "%s/human_readable_timestamp_format" % KEY_PREFIX
enum Levels {
DEBUG,
INFO,
WARN,
ERROR
}
enum TimestampTypes {
UNIX,
TICKS_MSEC,
TICKS_USEC,
HUMAN_12HR,
HUMAN_24HR
}
const CONFIG_DEFAULTS := {
KEY_COLOR_THEME_RESOURCE_PATH: "res://addons/log/color_theme_dark.tres",
KEY_DISABLE_COLORS: false,
KEY_MAX_ARRAY_SIZE: 20,
KEY_SKIP_KEYS: ["layer_0/tile_data"],
KEY_USE_NEWLINES: false,
KEY_NEWLINE_MAX_DEPTH: -1,
KEY_LOG_LEVEL: Levels.INFO,
KEY_WARN_TODO: true,
KEY_SHOW_LOG_LEVEL_SELECTOR: false,
KEY_SHOW_TIMESTAMPS: false,
KEY_TIMESTAMP_TYPE: TimestampTypes.HUMAN_12HR,
KEY_HUMAN_READABLE_TIMESTAMP_FORMAT: "{hour}:{minute}:{second}",
}
# settings setup ####################################
static func setup_settings(opts: Dictionary = {}) -> void:
initialize_setting(KEY_COLOR_THEME_RESOURCE_PATH, CONFIG_DEFAULTS[KEY_COLOR_THEME_RESOURCE_PATH], TYPE_STRING, PROPERTY_HINT_FILE)
initialize_setting(KEY_DISABLE_COLORS, CONFIG_DEFAULTS[KEY_DISABLE_COLORS], TYPE_BOOL)
initialize_setting(KEY_MAX_ARRAY_SIZE, CONFIG_DEFAULTS[KEY_MAX_ARRAY_SIZE], TYPE_INT)
initialize_setting(KEY_SKIP_KEYS, CONFIG_DEFAULTS[KEY_SKIP_KEYS], TYPE_PACKED_STRING_ARRAY)
initialize_setting(KEY_USE_NEWLINES, CONFIG_DEFAULTS[KEY_USE_NEWLINES], TYPE_BOOL)
initialize_setting(KEY_NEWLINE_MAX_DEPTH, CONFIG_DEFAULTS[KEY_NEWLINE_MAX_DEPTH], TYPE_INT)
initialize_setting(KEY_LOG_LEVEL, CONFIG_DEFAULTS[KEY_LOG_LEVEL], TYPE_INT, PROPERTY_HINT_ENUM, "DEBUG,INFO,WARN,ERROR")
initialize_setting(KEY_WARN_TODO, CONFIG_DEFAULTS[KEY_WARN_TODO], TYPE_BOOL)
initialize_setting(KEY_SHOW_LOG_LEVEL_SELECTOR, CONFIG_DEFAULTS[KEY_SHOW_LOG_LEVEL_SELECTOR], TYPE_BOOL)
initialize_setting(KEY_SHOW_TIMESTAMPS, CONFIG_DEFAULTS[KEY_SHOW_TIMESTAMPS], TYPE_BOOL)
initialize_setting(KEY_TIMESTAMP_TYPE, CONFIG_DEFAULTS[KEY_TIMESTAMP_TYPE], TYPE_INT, PROPERTY_HINT_ENUM, "UNIX,TICKS_MSEC,TICKS_USEC,HUMAN_12HR,HUMAN_24HR")
initialize_setting(KEY_HUMAN_READABLE_TIMESTAMP_FORMAT, CONFIG_DEFAULTS[KEY_HUMAN_READABLE_TIMESTAMP_FORMAT], TYPE_STRING)
# config setup ####################################
static var config: Dictionary = {}
static func rebuild_config(opts: Dictionary = {}) -> void:
for key: String in CONFIG_DEFAULTS.keys():
# Keep config set in code before to_printable() is called for the first time
var val: Variant = Log.config.get(key, ProjectSettings.get_setting(key, CONFIG_DEFAULTS[key]))
Log.config[key] = val
# hardcoding a resource-load b/c it seems like custom-resources can't be loaded by the project settings
# https://github.com/godotengine/godot/issues/96219
if val != null and key == KEY_COLOR_THEME_RESOURCE_PATH:
Log.config[KEY_COLOR_THEME] = load(val)
Log.config[KEY_COLOR_THEME_DICT] = Log.config[KEY_COLOR_THEME].to_color_dict()
Log.is_config_setup = true
# config getters ###################################################################
static func get_max_array_size() -> int:
return Log.config.get(KEY_MAX_ARRAY_SIZE, CONFIG_DEFAULTS[KEY_MAX_ARRAY_SIZE])
static func get_dictionary_skip_keys() -> Array:
return Log.config.get(KEY_SKIP_KEYS, CONFIG_DEFAULTS[KEY_SKIP_KEYS])
static func get_disable_colors() -> bool:
return Log.config.get(KEY_DISABLE_COLORS, CONFIG_DEFAULTS[KEY_DISABLE_COLORS])
# TODO refactor away from the dict, create a termsafe LogColorTheme fallback
static var warned_about_termsafe_fallback := false
static func get_config_color_theme_dict() -> Dictionary:
var color_theme = Log.config.get(KEY_COLOR_THEME)
var color_dict = Log.config.get(KEY_COLOR_THEME_DICT)
if color_dict != null:
return color_dict
if not warned_about_termsafe_fallback:
print("Falling back to TERM_SAFE colors")
warned_about_termsafe_fallback = true
return LogColorTheme.COLORS_TERM_SAFE
static func get_config_color_theme() -> LogColorTheme:
var color_theme = Log.config.get(KEY_COLOR_THEME)
# TODO better warnings, fallbacks
return color_theme
static func get_use_newlines() -> bool:
return Log.config.get(KEY_USE_NEWLINES, CONFIG_DEFAULTS[KEY_USE_NEWLINES])
static func get_newline_max_depth() -> int:
return Log.config.get(KEY_NEWLINE_MAX_DEPTH, CONFIG_DEFAULTS[KEY_NEWLINE_MAX_DEPTH])
static func get_log_level() -> int:
return Log.config.get(KEY_LOG_LEVEL, CONFIG_DEFAULTS[KEY_LOG_LEVEL])
static func get_warn_todo() -> int:
return Log.config.get(KEY_WARN_TODO, CONFIG_DEFAULTS[KEY_WARN_TODO])
static func get_show_timestamps() -> bool:
return Log.config.get(KEY_SHOW_TIMESTAMPS, CONFIG_DEFAULTS[KEY_SHOW_TIMESTAMPS])
static func get_timestamp_type() -> TimestampTypes:
return Log.config.get(KEY_TIMESTAMP_TYPE, CONFIG_DEFAULTS[KEY_TIMESTAMP_TYPE])
static func get_timestamp_format() -> String:
return Log.config.get(KEY_HUMAN_READABLE_TIMESTAMP_FORMAT, CONFIG_DEFAULTS[KEY_HUMAN_READABLE_TIMESTAMP_FORMAT])
## config setters ###################################################################
## Disable color-wrapping output.
##
## [br][br]
## Useful to declutter the output if the environment does not support colors.
## Note that some environments support only a subset of colors - you may prefer
## [code]set_colors_termsafe()[/code].
static func disable_colors() -> void:
Log.config[KEY_DISABLE_COLORS] = true
## Re-enable color-wrapping output.
static func enable_colors() -> void:
Log.config[KEY_DISABLE_COLORS] = false
## Disable newlines in pretty-print output.
##
## [br][br]
## Useful if you want your log output on a single line, typically for use with
## log aggregation tools.
static func disable_newlines() -> void:
Log.config[KEY_USE_NEWLINES] = false
## Re-enable newlines in pretty-print output.
static func enable_newlines() -> void:
Log.config[KEY_USE_NEWLINES] = true
## Disable warning on Log.todo().
static func disable_warn_todo() -> void:
Log.config[KEY_WARN_TODO] = false
## Re-enable warning on Log.todo().
static func enable_warn_todo() -> void:
Log.config[KEY_WARN_TODO] = true
## Set the maximum depth of an object that will get its own newline.
##
## [br][br]
## Useful if you have deeply nested objects where you're primarly interested
## in easily parsing the information near the root of the object.
static func set_newline_max_depth(new_depth: int) -> void:
Log.config[KEY_NEWLINE_MAX_DEPTH] = new_depth
## Resets the maximum object depth for newlines to the default.
static func reset_newline_max_depth() -> void:
Log.config[KEY_USE_NEWLINES] = CONFIG_DEFAULTS[KEY_NEWLINE_MAX_DEPTH]
## Set the minimum level of logs that get printed
static func set_log_level(new_log_level: int) -> void:
Log.config[KEY_LOG_LEVEL] = new_log_level
## Show timestamps in log lines
static func show_timestamps() -> void:
Log.config[KEY_SHOW_TIMESTAMPS] = true
## Don't timestamps in log lines
static func hide_timestamps() -> void:
Log.config[KEY_SHOW_TIMESTAMPS] = false
## Use the given timestamp type
static func use_timestamp_type(timestamp_type: Log.TimestampTypes) -> void:
Log.config[KEY_TIMESTAMP_TYPE] = timestamp_type
## Use the given timestamp format
static func use_timestamp_format(timestamp_format: String) -> void:
Log.config[KEY_HUMAN_READABLE_TIMESTAMP_FORMAT] = timestamp_format
## set color theme ####################################
## Use the terminal safe color scheme, which should support colors in most tty-like environments.
static func set_colors_termsafe() -> void:
Log.config[KEY_COLOR_THEME_DICT] = LogColorTheme.COLORS_TERM_SAFE
## Use prettier colors - i.e. whatever LogColorTheme is configured.
static func set_colors_pretty() -> void:
var theme_path: Variant = Log.config.get(KEY_COLOR_THEME_RESOURCE_PATH)
# TODO proper string, file, resource load check here
if theme_path != null:
Log.config[KEY_COLOR_THEME] = load(theme_path)
Log.config[KEY_COLOR_THEME_DICT] = Log.config[KEY_COLOR_THEME].to_color_dict()
else:
print("WARNING no color theme resource path to load!")
## applying colors ####################################
static func should_use_color(opts: Dictionary = {}) -> bool:
if OS.has_feature("ios") or OS.has_feature("web"):
# ios and web (and likely others) don't handle colors well
return false
if Log.get_disable_colors():
return false
# supports per-print color skipping
if opts.get("disable_colors", false):
return false
return true
static func color_wrap(s: Variant, opts: Dictionary = {}) -> String:
# TODO refactor to use the color theme directly
var colors: Dictionary = get_config_color_theme_dict()
var color_theme: LogColorTheme = get_config_color_theme()
if not should_use_color(opts):
return str(s)
var color: Variant = opts.get("color", "")
if color == null or (color is String and color == ""):
var s_type: Variant = opts.get("typeof", typeof(s))
if s_type is String:
# type overwrites
color = colors.get(s_type)
elif s_type is int and s_type == TYPE_STRING:
# specific strings/punctuation
var s_trimmed: String = str(s).strip_edges()
if s_trimmed in colors:
color = colors.get(s_trimmed)
else:
# fallback string color
color = colors.get(s_type)
else:
# all other types
color = colors.get(s_type)
if color is String and color == "" or color == null:
print("Log.gd could not determine color for object: %s type: (%s)" % [str(s), typeof(s)])
if color is Array:
# support rainbow delimiters
if opts.get("typeof", "") in ["dict_key"]:
# subtract 1 for dict_keys
# we the keys are 'down' a nesting level, but we want the curly + dict keys to match
color = color[opts.get("newline_depth", 0) - 1 % len(color)]
else:
color = color[opts.get("newline_depth", 0) % len(color)]
if color is Color:
# get the colors back to something bb_code can handle
color = color.to_html(false)
if color_theme and color_theme.has_bg():
var bg_color: String = color_theme.get_bg_color(opts.get("newline_depth", 0)).to_html(false)
return "[bgcolor=%s][color=%s]%s[/color][/bgcolor]" % [bg_color, color, s]
return "[color=%s]%s[/color]" % [color, s]
## overwrites ###########################################################################
static var type_overwrites: Dictionary = {}
## Register a single type overwrite.
##
## [br][br]
## The key should be either obj.get_class() or typeof(var). (Note that using typeof(var) may overwrite more broadly than expected).
##
## [br][br]
## The handler is called with the object and an options dict.
## [code]func(obj): return {name=obj.name}[/code]
static func register_type_overwrite(key: String, handler: Callable) -> void:
# TODO warning on key exists? support multiple handlers for same type?
# validate the key/handler somehow?
type_overwrites[key] = handler
## Register a dictionary of type overwrite.
##
## [br][br]
## Expects a Dictionary like [code]{obj.get_class(): func(obj): return {key=obj.get_key()}}[/code].
##
## [br][br]
## It depends on [code]obj.get_class()[/code] then [code]typeof(obj)[/code] for the key.
## The handler is called with the object as the only argument. (e.g. [code]func(obj): return {name=obj.name}[/code]).
static func register_type_overwrites(overwrites: Dictionary) -> void:
type_overwrites.merge(overwrites, true)
static func clear_type_overwrites() -> void:
type_overwrites = {}
## to_pretty ###########################################################################
## Returns the passed object as a bb-colorized string.
##
## [br][br]
## The core of Log.gd's functionality.
##
## [br][br]
## Can be useful to feed directly into a RichTextLabel.
##
static func to_pretty(msg: Variant, opts: Dictionary = {}) -> String:
var newlines: bool = opts.get("newlines", Log.get_use_newlines())
var newline_depth: int = opts.get("newline_depth", 0)
var newline_max_depth: int = opts.get("newline_max_depth", Log.get_newline_max_depth())
var indent_level: int = opts.get("indent_level", 0)
if not newlines:
newline_max_depth = 0
elif newline_max_depth == 0:
newlines = false
# If newline_max_depth is negative, don't limit the depth
if newline_max_depth > 0 and newline_depth >= newline_max_depth:
newlines = false
if not "newline_depth" in opts:
opts["newline_depth"] = newline_depth
if not "indent_level" in opts:
opts["indent_level"] = indent_level
if not is_instance_valid(msg) and typeof(msg) == TYPE_OBJECT:
return str("invalid instance: ", msg)
if msg == null:
return Log.color_wrap(msg, opts)
if msg is Object and (msg as Object).get_class() in type_overwrites:
var fn: Callable = type_overwrites.get((msg as Object).get_class())
return Log.to_pretty(fn.call(msg), opts)
elif typeof(msg) in type_overwrites:
var fn: Callable = type_overwrites.get(typeof(msg))
return Log.to_pretty(fn.call(msg), opts)
# objects
if msg is Object and (msg as Object).has_method("to_pretty"):
# using a cast and `call.("blah")` here it's "type safe"
return Log.to_pretty((msg as Object).call("to_pretty"), opts)
if msg is Object and (msg as Object).has_method("data"):
return Log.to_pretty((msg as Object).call("data"), opts)
# DEPRECATED
if msg is Object and (msg as Object).has_method("to_printable"):
return Log.to_pretty((msg as Object).call("to_printable"), opts)
# arrays
if msg is Array or msg is PackedStringArray:
var msg_array: Array = msg
if len(msg) > Log.get_max_array_size():
pr("[DEBUG]: truncating large array. total:", len(msg))
msg_array = msg_array.slice(0, Log.get_max_array_size() - 1)
if newlines:
msg_array.append("...")
# shouldn't we be incrementing index_level here?
var tmp: String = Log.color_wrap("[ ", opts)
opts["newline_depth"] += 1
var last: int = len(msg) - 1
for i: int in range(len(msg)):
if newlines and last > 1:
tmp += Log.color_wrap("\n\t", opts)
tmp += Log.to_pretty(msg[i],
# duplicate here to prevent indenting-per-msg
# e.g. when printing an array of dictionaries
opts.duplicate(true))
if i != last:
tmp += Log.color_wrap(", ", opts)
opts["newline_depth"] -= 1
tmp += Log.color_wrap(" ]", opts)
return tmp
# dictionary
elif msg is Dictionary:
var tmp: String = Log.color_wrap("{ ", opts)
opts["newline_depth"] += 1
var ct: int = len(msg)
var last: Variant
if len(msg) > 0:
last = (msg as Dictionary).keys()[-1]
var indent_updated = false
for k: Variant in (msg as Dictionary).keys():
var val: Variant
if k in Log.get_dictionary_skip_keys():
val = "..."
else:
if not indent_updated:
indent_updated = true
opts["indent_level"] += 1
val = Log.to_pretty(msg[k], opts)
if newlines and ct > 1:
tmp += Log.color_wrap("\n\t", opts) \
+ Log.color_wrap(range(indent_level)\
.map(func(_i: int) -> String: return "\t")\
.reduce(func(a: String, b: Variant) -> String: return str(a, b), ""), opts)
var key: String = Log.color_wrap('"%s"' % k, Log.assoc(opts, "typeof", "dict_key"))
tmp += "%s%s%s" % [key, Log.color_wrap(": ", opts), val]
if last and str(k) != str(last):
tmp += Log.color_wrap(", ", opts)
opts["newline_depth"] -= 1
tmp += Log.color_wrap(" }", opts)
opts["indent_level"] -= 1 # ugh! updating the dict in-place
return tmp
# strings
elif msg is String:
if msg == "":
return '""'
if "[color=" in msg and "[/color]" in msg:
# passes through strings that might already be colorized?
# can't remember this use-case
# perhaps should use a regex and unit tests for something more robust
return msg
return Log.color_wrap(msg, opts)
elif msg is StringName:
return str(Log.color_wrap("&", opts), '"%s"' % msg)
elif msg is NodePath:
return str(Log.color_wrap("^", opts), '"%s"' % msg)
elif msg is Color:
# probably too opinionated, but seeing 4 floats for color is noisey
return Log.color_wrap(msg.to_html(false), Log.assoc(opts, "typeof", TYPE_COLOR))
# vectors
elif msg is Vector2 or msg is Vector2i:
return '%s%s%s%s%s' % [
Log.color_wrap("(", opts),
Log.color_wrap(msg.x, Log.assoc(opts, "typeof", "vector_value")),
Log.color_wrap(",", opts),
Log.color_wrap(msg.y, Log.assoc(opts, "typeof", "vector_value")),
Log.color_wrap(")", opts),
]
elif msg is Vector3 or msg is Vector3i:
return '%s%s%s%s%s%s%s' % [
Log.color_wrap("(", opts),
Log.color_wrap(msg.x, Log.assoc(opts, "typeof", "vector_value")),
Log.color_wrap(",", opts),
Log.color_wrap(msg.y, Log.assoc(opts, "typeof", "vector_value")),
Log.color_wrap(",", opts),
Log.color_wrap(msg.z, Log.assoc(opts, "typeof", "vector_value")),
Log.color_wrap(")", opts),
]
elif msg is Vector4 or msg is Vector4i:
return '%s%s%s%s%s%s%s%s%s' % [
Log.color_wrap("(", opts),
Log.color_wrap(msg.x, Log.assoc(opts, "typeof", "vector_value")),
Log.color_wrap(",", opts),
Log.color_wrap(msg.y, Log.assoc(opts, "typeof", "vector_value")),
Log.color_wrap(",", opts),
Log.color_wrap(msg.z, Log.assoc(opts, "typeof", "vector_value")),
Log.color_wrap(",", opts),
Log.color_wrap(msg.w, Log.assoc(opts, "typeof", "vector_value")),
Log.color_wrap(")", opts),
]
# packed scene
elif msg is PackedScene:
var msg_ps: PackedScene = msg
if msg_ps.resource_path != "":
return str(Log.color_wrap("PackedScene:", opts), '%s' % msg_ps.resource_path.get_file())
elif msg_ps.get_script() != null and msg_ps.get_script().resource_path != "":
var path: String = msg_ps.get_script().resource_path
return Log.color_wrap(path.get_file(), Log.assoc(opts, "typeof", "class_name"))
else:
return Log.color_wrap(msg_ps, opts)
# resource
elif msg is Resource:
var msg_res: Resource = msg
if msg_res.get_script() != null and msg_res.get_script().resource_path != "":
var path: String = msg_res.get_script().resource_path
return Log.color_wrap(path.get_file(), Log.assoc(opts, "typeof", "class_name"))
elif msg_res.resource_path != "":
var path: String = msg_res.resource_path
return str(Log.color_wrap("Resource:", opts), '%s' % path.get_file())
else:
return Log.color_wrap(msg_res, opts)
# refcounted
elif msg is RefCounted:
var msg_ref: RefCounted = msg
if msg_ref.get_script() != null and msg_ref.get_script().resource_path != "":
var path: String = msg_ref.get_script().resource_path
return Log.color_wrap(path.get_file(), Log.assoc(opts, "typeof", "class_name"))
else:
return Log.color_wrap(msg_ref.get_class(), Log.assoc(opts, "typeof", "class_name"))
# fallback to primitive-type lookup
else:
return Log.color_wrap(msg, opts)
## to_printable ###########################################################################
static func log_prefix(stack: Array) -> String:
if len(stack) > 1:
var call_site: Dictionary = stack[1]
var call_site_source: String = call_site.get("source", "")
var basename: String = call_site_source.get_file().get_basename()
var line_num: String = str(call_site.get("line", 0))
if call_site_source.match("*/test/*"):
return "{" + basename + ":" + line_num + "}: "
elif call_site_source.match("*/addons/*"):
return "<" + basename + ":" + line_num + ">: "
else:
return "[" + basename + ":" + line_num + "]: "
return ""
static func to_printable(msgs: Array, opts: Dictionary = {}) -> String:
if not Log.is_config_setup:
rebuild_config()
if not msgs is Array:
msgs = [msgs]
var stack: Array = opts.get("stack", [])
var pretty: bool = opts.get("pretty", true)
var m: String = ""
if get_show_timestamps():
m = "[%s]" % Log.timestamp()
if len(stack) > 0:
var prefix: String = Log.log_prefix(stack)
var prefix_type: String
if prefix != null and prefix[0] == "[":
prefix_type = "SRC"
elif prefix != null and prefix[0] == "{":
prefix_type = "TEST"
elif prefix != null and prefix[0] == "<":
prefix_type = "ADDONS"
if pretty:
m += Log.color_wrap(prefix, Log.assoc(opts, "typeof", prefix_type))
else:
m += prefix
for msg: Variant in msgs:
# add a space between msgs
if pretty:
m += "%s " % Log.to_pretty(msg, opts)
else:
m += "%s " % str(msg)
return m.trim_suffix(" ")
static func timestamp() -> String:
match Log.get_timestamp_type():
Log.TimestampTypes.UNIX:
return "%d" % Time.get_unix_time_from_system()
Log.TimestampTypes.TICKS_MSEC:
return "%d" % Time.get_ticks_msec()
Log.TimestampTypes.TICKS_USEC:
return "%d" % Time.get_ticks_usec()
Log.TimestampTypes.HUMAN_12HR:
var time: Dictionary = Time.get_datetime_dict_from_system()
var hour: int = time.hour % 12
if hour == 0:
hour = 12
var meridiem: String = "AM" if time.hour < 12 else "PM"
return Log.get_timestamp_format().format({
"year": time.year,
"month": "%02d" % time.month,
"day": "%02d" % time.day,
"hour": hour,
"minute": "%02d" % time.minute,
"second": "%02d" % time.second,
"meridiem": meridiem,
"dst": time.dst
})
Log.TimestampTypes.HUMAN_24HR:
var time: Dictionary = Time.get_datetime_dict_from_system()
return Log.get_timestamp_format().format({
"year": time.year,
"month": "%02d" % time.month,
"day": "%02d" % time.day,
"hour": "%02d" % time.hour,
"minute": "%02d" % time.minute,
"second": "%02d" % time.second,
"dst": time.dst
})
return "%d" % Time.get_unix_time_from_system()
## public print fns ###########################################################################
static func is_not_default(v: Variant) -> bool:
return not v is String or (v is String and v != "ZZZDEF")
## Pretty-print the passed arguments in a single line.
static func pr(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
msgs = msgs.filter(Log.is_not_default)
var m: String = Log.to_printable(msgs, {stack=get_stack()})
print_rich(m)
## Pretty-print the passed arguments, expanding dictionaries and arrays with a newline and indentation.
static func prn(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
msgs = msgs.filter(Log.is_not_default)
var m: String = Log.to_printable(msgs, {stack=get_stack(), newlines=true, newline_max_depth=1})
print_rich(m)
## Pretty-print the passed arguments, expanding dictionaries and arrays with two newlines and indentation.
static func prnn(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
msgs = msgs.filter(Log.is_not_default)
var m: String = Log.to_printable(msgs, {stack=get_stack(), newlines=true, newline_max_depth=2})
print_rich(m)
## Pretty-print the passed arguments, expanding dictionaries and arrays with three newlines and indentation.
static func prnnn(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
msgs = msgs.filter(Log.is_not_default)
var m: String = Log.to_printable(msgs, {stack=get_stack(), newlines=true, newline_max_depth=3})
print_rich(m)
## Pretty-print the passed arguments in a single line.
static func log(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
msgs = msgs.filter(Log.is_not_default)
var m: String = Log.to_printable(msgs, {stack=get_stack()})
print_rich(m)
## Pretty-print the passed arguments in a single line.
static func debug(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
if get_log_level() > Log.Levels.DEBUG:
return
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
msgs = msgs.filter(Log.is_not_default)
msgs.push_front("[DEBUG]")
var m: String = Log.to_printable(msgs, {stack=get_stack()})
print_rich(m)
## Pretty-print the passed arguments in a single line.
static func info(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
if get_log_level() > Log.Levels.INFO:
return
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
msgs = msgs.filter(Log.is_not_default)
msgs.push_front("[INFO]")
var m: String = Log.to_printable(msgs, {stack=get_stack()})
print_rich(m)
## Like [code]Log.pr()[/code], but also calls push_warning() with the pretty string.
static func warn(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
if get_log_level() > Log.Levels.WARN:
return
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
msgs = msgs.filter(Log.is_not_default)
var rich_msgs: Array = msgs.duplicate()
rich_msgs.push_front("[color=yellow][WARN][/color]")
print_rich(Log.to_printable(rich_msgs, {stack=get_stack()}))
# skip the 'color' features in warnings to keep them readable in the debugger
var m: String = Log.to_printable(msgs, {stack=get_stack(), disable_colors=true})
push_warning(m)
## Like [code]Log.pr()[/code], but prepends a "[TODO]" and calls push_warning() with the pretty string.
static func todo(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
if get_warn_todo() and get_log_level() > Log.Levels.WARN:
return
elif not get_warn_todo() and get_log_level() > Log.Levels.INFO:
return
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
msgs = msgs.filter(Log.is_not_default)
msgs.push_front("[TODO]")
var rich_msgs: Array = msgs.duplicate()
if get_warn_todo():
rich_msgs.push_front("[color=yellow][WARN][/color]")
print_rich(Log.to_printable(rich_msgs, {stack=get_stack()}))
if get_warn_todo():
var m: String = Log.to_printable(msgs, {stack=get_stack(), disable_colors=true})
push_warning(m)
## Like [code]Log.pr()[/code], but also calls push_error() with the pretty string.
static func err(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
msgs = msgs.filter(Log.is_not_default)
var rich_msgs: Array = msgs.duplicate()
rich_msgs.push_front("[color=red][ERR][/color]")
print_rich(Log.to_printable(rich_msgs, {stack=get_stack()}))
# skip the 'color' features in errors to keep them readable in the debugger
var m: String = Log.to_printable(msgs, {stack=get_stack(), disable_colors=true})
push_error(m)
## Like [code]Log.pr()[/code], but also calls push_error() with the pretty string.
static func error(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
msgs = msgs.filter(Log.is_not_default)
var rich_msgs: Array = msgs.duplicate()
rich_msgs.push_front("[color=red][ERR][/color]")
print_rich(Log.to_printable(rich_msgs, {stack=get_stack()}))
# skip the 'color' features in errors to keep them readable in the debugger
var m: String = Log.to_printable(msgs, {stack=get_stack(), disable_colors=true})
push_error(m)
static func blank() -> void:
print()
## Helper that will both print() and print_rich() the enriched string
static func _internal_debug(msg: Variant, msg2: Variant = "ZZZDEF", msg3: Variant = "ZZZDEF", msg4: Variant = "ZZZDEF", msg5: Variant = "ZZZDEF", msg6: Variant = "ZZZDEF", msg7: Variant = "ZZZDEF") -> void:
var msgs: Array = [msg, msg2, msg3, msg4, msg5, msg6, msg7]
msgs = msgs.filter(Log.is_not_default)
var m: String = Log.to_printable(msgs, {stack=get_stack()})
print("_internal_debug: ", m)
print_rich(m)
## DEPRECATED
static func merge_theme_overwrites(_opts = {}) -> void:
pass
## DEPRECATED
static func clear_theme_overwrites() -> void:
pass

1
addons/log/log.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://bjhtrex267w1y

View file

@ -0,0 +1,225 @@
## LogColorTheme - Bring Your Own Color Theme to Log.gd!
## [br][br]
## Create a new resource of this type, and assign this via the Project Settings.
## [br][br]
## Be sure to enable Log.gd via Project > Plugins to see this type in the editor!
##
@tool
extends Resource
class_name LogColorTheme
## cycles ########################################
@export var bg_colors: Array[Color] = []
@export var colors_rainbow_delims: Array[Color] = ["crimson", "cornflower_blue", "coral", "pink", "peru"]
## delimiters ########################################
@export var color_comma: Color = "crimson"
@export var color_ampersand: Color = "coral"
@export var color_pipe: Color = "coral"
@export var color_carrot: Color = "coral"
# @export var colors_dict_keys: Array[Color] = ["coral", "cadet_blue", "pink", "peru"]
## prefixes ########################################
@export var color_src_prefix: Color = "aquamarine"
@export var color_addons_prefix: Color = "peru"
@export var color_test_prefix: Color = "green_yellow"
## types ############################################
@export var color_nil: Color = "coral"
@export var color_bool: Color = "pink"
@export var color_int: Color = "cornflower_blue"
@export var color_float: Color = "cornflower_blue"
@export var color_vectors: Color = "cornflower_blue"
@export var color_rects: Color = "cornflower_blue"
@export var color_class_name: Color = "cadet_blue"
@export var color_string: Color = "dark_gray"
@export var color_string_name: Color = "pink"
@export var color_node_path: Color = "pink"
@export var color_type_color: Color = "pink"
@export var color_rid: Color = "pink"
@export var color_object: Color = "pink"
@export var color_callable: Color = "pink"
@export var color_signal: Color = "pink"
@export var color_array: Color = "pink"
@export var color_dictionary: Color = "pink"
@export var color_packed_array: Color = "pink"
@export var color_type_max: Color = "pink"
## to_color_dict ############################################
func to_color_dict() -> Dictionary:
var color_dict = {}
color_dict["SRC"] = color_src_prefix
color_dict["ADDONS"] = color_addons_prefix
color_dict["TEST"] = color_test_prefix
color_dict["|"] = color_pipe
color_dict["&"] = color_ampersand
color_dict["^"] = color_carrot
# consider rainbow for commas too
# color_dict[","] = colors_rainbow_delims
color_dict[","] = color_comma
color_dict["("] = colors_rainbow_delims
color_dict[")"] = colors_rainbow_delims
color_dict["["] = colors_rainbow_delims
color_dict["]"] = colors_rainbow_delims
color_dict["{"] = colors_rainbow_delims
color_dict["}"] = colors_rainbow_delims
color_dict["<"] = colors_rainbow_delims
color_dict[">"] = colors_rainbow_delims
color_dict["dict_key"] = colors_rainbow_delims
color_dict["vector_value"] = color_float
color_dict["class_name"] = color_class_name
color_dict[TYPE_NIL] = color_nil
color_dict[TYPE_BOOL] = color_bool
color_dict[TYPE_INT] = color_int
color_dict[TYPE_FLOAT] = color_float
color_dict[TYPE_VECTOR2] = color_vectors
color_dict[TYPE_VECTOR2I] = color_vectors
color_dict[TYPE_RECT2] = color_rects
color_dict[TYPE_RECT2I] = color_rects
color_dict[TYPE_VECTOR3] = color_vectors
color_dict[TYPE_VECTOR3I] = color_vectors
color_dict[TYPE_TRANSFORM2D] = color_rects
color_dict[TYPE_VECTOR4] = color_vectors
color_dict[TYPE_VECTOR4I] = color_vectors
color_dict[TYPE_PLANE] = color_rects
color_dict[TYPE_QUATERNION] = color_rects
color_dict[TYPE_AABB] = color_rects
color_dict[TYPE_BASIS] = color_rects
color_dict[TYPE_TRANSFORM3D] = color_rects
color_dict[TYPE_PROJECTION] = color_rects
color_dict[TYPE_STRING] = color_string
color_dict[TYPE_STRING_NAME] = color_string_name
color_dict[TYPE_NODE_PATH] = color_node_path
color_dict[TYPE_COLOR] = color_type_color
color_dict[TYPE_RID] = color_rid
color_dict[TYPE_OBJECT] = color_object
color_dict[TYPE_CALLABLE] = color_callable
color_dict[TYPE_SIGNAL] = color_signal
# Do these ever get through if we're walking these ourselves?
color_dict[TYPE_DICTIONARY] = color_dictionary
color_dict[TYPE_ARRAY] = color_array
# Maybe want a hint/label before array openers?
color_dict[TYPE_PACKED_BYTE_ARRAY] = color_packed_array
color_dict[TYPE_PACKED_INT32_ARRAY] = color_packed_array
color_dict[TYPE_PACKED_INT64_ARRAY] = color_packed_array
color_dict[TYPE_PACKED_FLOAT32_ARRAY] = color_packed_array
color_dict[TYPE_PACKED_FLOAT64_ARRAY] = color_packed_array
color_dict[TYPE_PACKED_STRING_ARRAY] = color_packed_array
color_dict[TYPE_PACKED_VECTOR2_ARRAY] = color_packed_array
color_dict[TYPE_PACKED_VECTOR3_ARRAY] = color_packed_array
color_dict[TYPE_PACKED_COLOR_ARRAY] = color_packed_array
color_dict[TYPE_MAX] = color_type_max
return color_dict
## bg color
func has_bg() -> bool:
if bg_colors == null:
return false
return len(bg_colors) > 0
func get_bg_color(i: int = 0) -> Color:
return bg_colors[i % len(bg_colors)]
### static term safe helpers
# terminal safe colors:
# - black
# - red
# - green
# - yellow
# - blue
# - magenta
# - pink
# - purple
# - cyan
# - white
# - orange
# - gray
static var TERMSAFE_RAINBOW: Array = ["red", "blue", "green", "pink", "orange"]
static var COLORS_TERM_SAFE: Dictionary = {
"SRC": "cyan",
"ADDONS": "red",
"TEST": "green",
",": "red",
"(": TERMSAFE_RAINBOW,
")": TERMSAFE_RAINBOW,
"[": TERMSAFE_RAINBOW,
"]": TERMSAFE_RAINBOW,
"{": TERMSAFE_RAINBOW,
"}": TERMSAFE_RAINBOW,
"<": TERMSAFE_RAINBOW,
">": TERMSAFE_RAINBOW,
"|": TERMSAFE_RAINBOW,
"&": "orange",
"^": "orange",
"dict_key": TERMSAFE_RAINBOW,
"vector_value": "green",
"class_name": "magenta",
TYPE_NIL: "pink",
TYPE_BOOL: "pink",
TYPE_INT: "green",
TYPE_FLOAT: "green",
TYPE_STRING: "pink",
TYPE_VECTOR2: "green",
TYPE_VECTOR2I: "green",
TYPE_RECT2: "green",
TYPE_RECT2I: "green",
TYPE_VECTOR3: "green",
TYPE_VECTOR3I: "green",
TYPE_TRANSFORM2D: "pink",
TYPE_VECTOR4: "green",
TYPE_VECTOR4I: "green",
TYPE_PLANE: "pink",
TYPE_QUATERNION: "pink",
TYPE_AABB: "pink",
TYPE_BASIS: "pink",
TYPE_TRANSFORM3D: "pink",
TYPE_PROJECTION: "pink",
TYPE_COLOR: "pink",
TYPE_STRING_NAME: "pink",
TYPE_NODE_PATH: "pink",
TYPE_RID: "pink",
TYPE_OBJECT: "pink",
TYPE_CALLABLE: "pink",
TYPE_SIGNAL: "pink",
TYPE_DICTIONARY: "pink",
TYPE_ARRAY: "pink",
TYPE_PACKED_BYTE_ARRAY: "pink",
TYPE_PACKED_INT32_ARRAY: "pink",
TYPE_PACKED_INT64_ARRAY: "pink",
TYPE_PACKED_FLOAT32_ARRAY: "pink",
TYPE_PACKED_FLOAT64_ARRAY: "pink",
TYPE_PACKED_STRING_ARRAY: "pink",
TYPE_PACKED_VECTOR2_ARRAY: "pink",
TYPE_PACKED_VECTOR3_ARRAY: "pink",
TYPE_PACKED_COLOR_ARRAY: "pink",
TYPE_MAX: "pink",
}

View file

@ -0,0 +1 @@
uid://br8socgd1vvih

14
addons/log/plugin.cfg Normal file
View file

@ -0,0 +1,14 @@
[plugin]
name="Log.gd"
description="A pretty-printing debug logger.
Log.pr(\"some str\", some_object)
- Colorizes printed data based on datatype
- Recurses through nested data structures (Arrays and Dictionaries)
- Prefixes logs with the callsite's source file and line_number
"
author="Russell Matney"
version="v0.2.0"
script="plugin.gd"

43
addons/log/plugin.gd Normal file
View file

@ -0,0 +1,43 @@
@tool
extends EditorPlugin
var override_log_level_option_button: OptionButton = OptionButton.new()
var icon_debug: Texture2D = EditorInterface.get_editor_theme().get_icon("Debug", "EditorIcons")
var icon_info: Texture2D = EditorInterface.get_editor_theme().get_icon("NodeInfo", "EditorIcons")
var icon_warn: Texture2D = EditorInterface.get_editor_theme().get_icon("NodeWarning", "EditorIcons")
var icon_err: Texture2D = EditorInterface.get_editor_theme().get_icon("StatusError", "EditorIcons")
func _enter_tree() -> void:
override_log_level_option_button.visible = ProjectSettings.get_setting("log_gd/config/show_log_level_selector", false)
override_log_level_option_button.add_icon_item(icon_debug, "DEBUG")
override_log_level_option_button.add_icon_item(icon_info, "INFO")
override_log_level_option_button.add_icon_item(icon_warn, "WARN")
override_log_level_option_button.add_icon_item(icon_err, "ERROR")
override_log_level_option_button.select(Log.get_log_level())
override_log_level_option_button.item_selected.connect(override_log_level)
add_control_to_container(CONTAINER_TOOLBAR, override_log_level_option_button)
override_log_level_option_button.get_parent().move_child(override_log_level_option_button, override_log_level_option_button.get_index() - 2)
Log.setup_settings()
Log.rebuild_config()
# TODO only run if some log-specific setting has changed
ProjectSettings.settings_changed.connect(on_settings_changed)
func _exit_tree() -> void:
remove_control_from_container(CONTAINER_TOOLBAR, override_log_level_option_button)
func on_settings_changed() -> void:
override_log_level_option_button.select(ProjectSettings.get_setting("log_gd/config/log_level"))
override_log_level_option_button.visible = ProjectSettings.get_setting("log_gd/config/show_log_level_selector")
Log.rebuild_config()
func override_log_level(value: Log.Levels) -> void:
Log.set_log_level(value)
ProjectSettings.set_setting("log_gd/config/log_level", value)
ProjectSettings.save()

1
addons/log/plugin.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://dc3akmw7hy6ok

View file

@ -0,0 +1,7 @@
[plugin]
name="Reload Current Scene"
description="Helper button to reload the current scene."
author="Russell Matney"
version="v0.1.0"
script="plugin.gd"

View file

@ -0,0 +1,25 @@
@tool
extends EditorPlugin
var reload_scene_btn: Button = Button.new()
func _enter_tree() -> void:
reload_scene_btn.pressed.connect(reload_scene)
reload_scene_btn.text = "Reload Scene"
reload_scene_btn.icon = EditorInterface.get_editor_theme().get_icon("Reload", "EditorIcons")
add_control_to_container(CONTAINER_TOOLBAR, reload_scene_btn)
reload_scene_btn.get_parent().move_child(reload_scene_btn, reload_scene_btn.get_index() - 2)
func _exit_tree() -> void:
remove_control_from_container(CONTAINER_TOOLBAR, reload_scene_btn)
func reload_scene() -> void:
Log.info("[ReloadScene] Reload initialized", Time.get_time_string_from_system())
var edited_scene: Node = EditorInterface.get_edited_scene_root()
Log.info("[ReloadScene] Edited scene", "%s.scene_file_path" % edited_scene, edited_scene.scene_file_path)
EditorInterface.reload_scene_from_path(edited_scene.scene_file_path)
Log.info("[ReloadScene] Scene reloaded", Time.get_time_string_from_system())

View file

@ -0,0 +1 @@
uid://df3sf44c3b64a

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
assets/audio/OGG/.DS_Store vendored Normal file

Binary file not shown.

BIN
assets/audio/OGG/BGS Loops/.DS_Store vendored Normal file

Binary file not shown.

BIN
assets/audio/OGG/BGS Loops/Beach/Beach Rain.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://b2p065yn3fkmr"
path="res://.godot/imported/Beach Rain.ogg-3cf3d2bdaf0006b574b2382b35f099b1.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Beach/Beach Rain.ogg"
dest_files=["res://.godot/imported/Beach Rain.ogg-3cf3d2bdaf0006b574b2382b35f099b1.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Beach/Beach Storm.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://cx7c627trpbkn"
path="res://.godot/imported/Beach Storm.ogg-2fdb485982ff8c57097c232bb37d4cfd.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Beach/Beach Storm.ogg"
dest_files=["res://.godot/imported/Beach Storm.ogg-2fdb485982ff8c57097c232bb37d4cfd.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Beach/Beach.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bijfvt28virsw"
path="res://.godot/imported/Beach.ogg-f2dcd786f77ce9a9578991b83b60178f.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Beach/Beach.ogg"
dest_files=["res://.godot/imported/Beach.ogg-f2dcd786f77ce9a9578991b83b60178f.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Cave/Cave Rain.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://mca3roqkbxtm"
path="res://.godot/imported/Cave Rain.ogg-7de26e9b92079c104b598e40099d4944.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Cave/Cave Rain.ogg"
dest_files=["res://.godot/imported/Cave Rain.ogg-7de26e9b92079c104b598e40099d4944.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Cave/Cave Storm.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dtnx4hcisp70a"
path="res://.godot/imported/Cave Storm.ogg-560a3a93a977971b3a617b7c44e9ebf5.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Cave/Cave Storm.ogg"
dest_files=["res://.godot/imported/Cave Storm.ogg-560a3a93a977971b3a617b7c44e9ebf5.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Cave/Cave.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bvm0enafi4lid"
path="res://.godot/imported/Cave.ogg-ed24212a08ff0a145ad3a0f800f84462.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Cave/Cave.ogg"
dest_files=["res://.godot/imported/Cave.ogg-ed24212a08ff0a145ad3a0f800f84462.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Forest Day/Forest Day Rain.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bchjcw8ad4j1l"
path="res://.godot/imported/Forest Day Rain.ogg-b97d927820281ac58a1dede8f943ea87.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Forest Day/Forest Day Rain.ogg"
dest_files=["res://.godot/imported/Forest Day Rain.ogg-b97d927820281ac58a1dede8f943ea87.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Forest Day/Forest Day Storm.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://jwky8uulu0lq"
path="res://.godot/imported/Forest Day Storm.ogg-9a27d5227196ae6da4d7634c836182bf.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Forest Day/Forest Day Storm.ogg"
dest_files=["res://.godot/imported/Forest Day Storm.ogg-9a27d5227196ae6da4d7634c836182bf.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Forest Day/Forest Day.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://qm25npqyo8kf"
path="res://.godot/imported/Forest Day.ogg-b4002b595fbe507830a5252141d8e24f.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Forest Day/Forest Day.ogg"
dest_files=["res://.godot/imported/Forest Day.ogg-b4002b595fbe507830a5252141d8e24f.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bx64pv2382ffj"
path="res://.godot/imported/Forest Night Rain.ogg-3a6b589e255a5faf0c4c9a2d16b8deff.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Forest Night/Forest Night Rain.ogg"
dest_files=["res://.godot/imported/Forest Night Rain.ogg-3a6b589e255a5faf0c4c9a2d16b8deff.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://jse6gk4ude5h"
path="res://.godot/imported/Forest Night Storm.ogg-4beb0cffaf29ba3669f719811075c821.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Forest Night/Forest Night Storm.ogg"
dest_files=["res://.godot/imported/Forest Night Storm.ogg-4beb0cffaf29ba3669f719811075c821.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Forest Night/Forest Night.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://btnkhrkfyrud0"
path="res://.godot/imported/Forest Night.ogg-ef10081668908103a1046b8872995b12.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Forest Night/Forest Night.ogg"
dest_files=["res://.godot/imported/Forest Night.ogg-ef10081668908103a1046b8872995b12.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Interior Day/Inside Day Rain.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://ct5njaa82imlk"
path="res://.godot/imported/Inside Day Rain.ogg-7e6acc06231cac5a8bcecea10b3dd300.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Interior Day/Inside Day Rain.ogg"
dest_files=["res://.godot/imported/Inside Day Rain.ogg-7e6acc06231cac5a8bcecea10b3dd300.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Interior Day/Inside Day Storm.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://gown1k3nl6rg"
path="res://.godot/imported/Inside Day Storm.ogg-0faca45ef8edd5ea1ece0e8c4406ac5e.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Interior Day/Inside Day Storm.ogg"
dest_files=["res://.godot/imported/Inside Day Storm.ogg-0faca45ef8edd5ea1ece0e8c4406ac5e.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Interior Day/Inside Day.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://32etm4omspbu"
path="res://.godot/imported/Inside Day.ogg-0e94bb354b2ce3a130f7273115a09d6d.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Interior Day/Inside Day.ogg"
dest_files=["res://.godot/imported/Inside Day.ogg-0e94bb354b2ce3a130f7273115a09d6d.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://f25kmpjr5tve"
path="res://.godot/imported/Inside Night Rain.ogg-e5809f01347b726e6555330c059d8ed1.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Interior Night/Inside Night Rain.ogg"
dest_files=["res://.godot/imported/Inside Night Rain.ogg-e5809f01347b726e6555330c059d8ed1.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://ckwyc2py0wajp"
path="res://.godot/imported/Inside Night Storm.ogg-c58f36c3031d57ca8002b2d7c843a355.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Interior Night/Inside Night Storm.ogg"
dest_files=["res://.godot/imported/Inside Night Storm.ogg-c58f36c3031d57ca8002b2d7c843a355.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Interior Night/Inside Night.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://pbnj4s0grd11"
path="res://.godot/imported/Inside Night.ogg-21401659a3d868ed60a5105f4a6c6ec5.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Interior Night/Inside Night.ogg"
dest_files=["res://.godot/imported/Inside Night.ogg-21401659a3d868ed60a5105f4a6c6ec5.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Sea/Sea Rain.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://ir0yr6rxnqd2"
path="res://.godot/imported/Sea Rain.ogg-cf01fe03108d2a570a90b3017f1b736d.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Sea/Sea Rain.ogg"
dest_files=["res://.godot/imported/Sea Rain.ogg-cf01fe03108d2a570a90b3017f1b736d.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Sea/Sea Storm.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bsx1fnbb7yjwd"
path="res://.godot/imported/Sea Storm.ogg-aeaf55b766dc15a0d76c70ecfc82aa66.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Sea/Sea Storm.ogg"
dest_files=["res://.godot/imported/Sea Storm.ogg-aeaf55b766dc15a0d76c70ecfc82aa66.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/BGS Loops/Sea/Sea.ogg (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://ds1q1p8xkec0"
path="res://.godot/imported/Sea.ogg-c2c1aef5e5d5edecf3e9e360abb85a05.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/BGS Loops/Sea/Sea.ogg"
dest_files=["res://.godot/imported/Sea.ogg-c2c1aef5e5d5edecf3e9e360abb85a05.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/audio/OGG/SFX/.DS_Store vendored Normal file

Binary file not shown.

BIN
assets/audio/OGG/SFX/Attacks/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://cqvdj22y1e8qe"
path="res://.godot/imported/Bow Attack 1.ogg-30c2ef29302dd593362401767d2cc30e.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Bow Attacks Hits and Blocks/Bow Attack 1.ogg"
dest_files=["res://.godot/imported/Bow Attack 1.ogg-30c2ef29302dd593362401767d2cc30e.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://cuw77vfqgqs3t"
path="res://.godot/imported/Bow Attack 2.ogg-2bcef4511736c9103ec70eca41c10872.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Bow Attacks Hits and Blocks/Bow Attack 2.ogg"
dest_files=["res://.godot/imported/Bow Attack 2.ogg-2bcef4511736c9103ec70eca41c10872.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://b3fyiasfaf2ca"
path="res://.godot/imported/Bow Blocked 1.ogg-e7d8067879ff1e24a62dc07513cb8abb.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Bow Attacks Hits and Blocks/Bow Blocked 1.ogg"
dest_files=["res://.godot/imported/Bow Blocked 1.ogg-e7d8067879ff1e24a62dc07513cb8abb.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://cfi0gobi3mv82"
path="res://.godot/imported/Bow Blocked 2.ogg-fbfffd28664a082ea8a12373d0f323c3.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Bow Attacks Hits and Blocks/Bow Blocked 2.ogg"
dest_files=["res://.godot/imported/Bow Blocked 2.ogg-fbfffd28664a082ea8a12373d0f323c3.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://ta64r403fgxu"
path="res://.godot/imported/Bow Blocked 3.ogg-717c068ad661e3475b00f6e2af572fd1.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Bow Attacks Hits and Blocks/Bow Blocked 3.ogg"
dest_files=["res://.godot/imported/Bow Blocked 3.ogg-717c068ad661e3475b00f6e2af572fd1.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://cplwxhs3heg31"
path="res://.godot/imported/Bow Impact Hit 1.ogg-e3166ff877eee1941f6f4093e5ffdd30.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Bow Attacks Hits and Blocks/Bow Impact Hit 1.ogg"
dest_files=["res://.godot/imported/Bow Impact Hit 1.ogg-e3166ff877eee1941f6f4093e5ffdd30.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://vhb3tkswixqy"
path="res://.godot/imported/Bow Impact Hit 2.ogg-047a45aaa2f443dafc3be4438f151e37.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Bow Attacks Hits and Blocks/Bow Impact Hit 2.ogg"
dest_files=["res://.godot/imported/Bow Impact Hit 2.ogg-047a45aaa2f443dafc3be4438f151e37.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bdsjref72mr21"
path="res://.godot/imported/Bow Impact Hit 3.ogg-e276f35f6375f095cb29d88724c9a97f.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Bow Attacks Hits and Blocks/Bow Impact Hit 3.ogg"
dest_files=["res://.godot/imported/Bow Impact Hit 3.ogg-e276f35f6375f095cb29d88724c9a97f.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bmllds5yc0lws"
path="res://.godot/imported/Bow Put Away 1.ogg-6d27ab8ee92c69a4c2f2725a05f073bc.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Bow Attacks Hits and Blocks/Bow Put Away 1.ogg"
dest_files=["res://.godot/imported/Bow Put Away 1.ogg-6d27ab8ee92c69a4c2f2725a05f073bc.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://xsbxiuyqb1rb"
path="res://.godot/imported/Bow Take Out 1.ogg-49aff99072c8ad8906469b3f597b5aa5.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Bow Attacks Hits and Blocks/Bow Take Out 1.ogg"
dest_files=["res://.godot/imported/Bow Take Out 1.ogg-49aff99072c8ad8906469b3f597b5aa5.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bnd3hvnnson2n"
path="res://.godot/imported/Sword Attack 1.ogg-76298e91efc61232abd1f569f58c5f81.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Sword Attacks Hits and Blocks/Sword Attack 1.ogg"
dest_files=["res://.godot/imported/Sword Attack 1.ogg-76298e91efc61232abd1f569f58c5f81.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://cs4xa8pl62ogr"
path="res://.godot/imported/Sword Attack 2.ogg-10586723473cffdb577100dbdac33f49.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Sword Attacks Hits and Blocks/Sword Attack 2.ogg"
dest_files=["res://.godot/imported/Sword Attack 2.ogg-10586723473cffdb577100dbdac33f49.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://b68dk4apixo08"
path="res://.godot/imported/Sword Attack 3.ogg-6f841ad3f64f5c2169b3e27af670cb88.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Sword Attacks Hits and Blocks/Sword Attack 3.ogg"
dest_files=["res://.godot/imported/Sword Attack 3.ogg-6f841ad3f64f5c2169b3e27af670cb88.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dq0ae0fs0ytpq"
path="res://.godot/imported/Sword Blocked 1.ogg-0398820db91f9b8d0ffe935136fa6299.oggvorbisstr"
[deps]
source_file="res://assets/audio/OGG/SFX/Attacks/Sword Attacks Hits and Blocks/Sword Blocked 1.ogg"
dest_files=["res://.godot/imported/Sword Blocked 1.ogg-0398820db91f9b8d0ffe935136fa6299.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Some files were not shown because too many files have changed in this diff Show more