Compare commits
No commits in common. "4df3d03d07d1cc8ae289068ee38ee1f9e439e312" and "2462174a0286325e338ca06142f3987e2aa4debb" have entirely different histories.
4df3d03d07
...
2462174a02
@ -1,39 +0,0 @@
|
|||||||
[main]
|
|
||||||
|
|
||||||
aliases={
|
|
||||||
"exit": "quit",
|
|
||||||
"source": "exec",
|
|
||||||
"usage": "help"
|
|
||||||
}
|
|
||||||
disable_in_release_build=false
|
|
||||||
print_to_stdout=false
|
|
||||||
pause_when_open=true
|
|
||||||
commands_disabled_in_release=["eval"]
|
|
||||||
|
|
||||||
[appearance]
|
|
||||||
|
|
||||||
custom_theme="res://addons/limbo_console_theme.tres"
|
|
||||||
height_ratio=0.5
|
|
||||||
open_speed=5.0
|
|
||||||
opacity=1.0
|
|
||||||
sparse_mode=false
|
|
||||||
|
|
||||||
[greet]
|
|
||||||
|
|
||||||
greet_user=true
|
|
||||||
greeting_message="{project_name}"
|
|
||||||
greet_using_ascii_art=true
|
|
||||||
|
|
||||||
[history]
|
|
||||||
|
|
||||||
persist_history=true
|
|
||||||
history_lines=1000
|
|
||||||
|
|
||||||
[autocomplete]
|
|
||||||
|
|
||||||
autocomplete_use_history_with_matches=true
|
|
||||||
|
|
||||||
[autoexec]
|
|
||||||
|
|
||||||
autoexec_script="user://autoexec.lcs"
|
|
||||||
autoexec_auto_create=true
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
# Contributing
|
|
||||||
|
|
||||||
Thanks for your interest in contributing. Please follow these guidelines to keep the project consistent and maintainable in the long-term.
|
|
||||||
|
|
||||||
## Vision
|
|
||||||
|
|
||||||
- This is a simple text-based interface. Follow the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle) – keep the code simple and easy to maintain, abstractions minimal, and the API easy to use without reading a manual.
|
|
||||||
- Don't run any logic if the console is closed or hidden.
|
|
||||||
|
|
||||||
## Code Style & Recommendations
|
|
||||||
|
|
||||||
- Follow the official [GDScript Style Guide](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_styleguide.html).
|
|
||||||
- Use [static typing](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/static_typing.html) wherever possible.
|
|
||||||
- Apply the 80/20 rule: if a feature only benefits a small number of users, make it optional or don’t include it.
|
|
||||||
- To avoid unnecessary whitespace changes, please enable this setting in Godot: `Editor Settings > Text Editor > Behavior > Files > Trim Trailing Whitespace on Save`
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2024 Serhii Snitsaruk
|
|
||||||
|
|
||||||
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.
|
|
||||||
@ -1,128 +0,0 @@
|
|||||||
<p align="left">
|
|
||||||
<img src=".github/logo.png" width=128 alt="LimboConsole logo">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
---
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
[](https://github.com/limbonaut/limbo_console/blob/master/LICENSE.md)
|
|
||||||
|
|
||||||
A simple and easy-to-use in-game dev console with a command interpreter for Godot Engine 4.
|
|
||||||
|
|
||||||
It supports auto-completion with `TAB` for commands and history, auto-correction, inline hints and highlighting, command help text generation, argument parsing for basic types, aliases, custom theming, and more.
|
|
||||||
|
|
||||||
This plugin is currently in development, so expect breaking changes.
|
|
||||||
|
|
||||||
[](https://ko-fi.com/Y8Y2TCNH0)
|
|
||||||
|
|
||||||
|
|
||||||
## How to use
|
|
||||||
|
|
||||||
> ℹ LimboConsole can be added as a Git submodule
|
|
||||||
|
|
||||||
Place the source code in the `res://addons/limbo_console/` directory, and enable this plugin in the project settings, then reload the project. Toggle the console with the `GRAVE ACCENT` key (aka backtick - the key to the left of the `1` key). This can be changed in the Input Map tab in the project settings.
|
|
||||||
|
|
||||||
Adding a new command is quite simple:
|
|
||||||
|
|
||||||
```gdscript
|
|
||||||
func _ready() -> void:
|
|
||||||
LimboConsole.register_command(multiply)
|
|
||||||
|
|
||||||
func multiply(a: float, b: float) -> void:
|
|
||||||
LimboConsole.info("a * b: " + str(a * b))
|
|
||||||
```
|
|
||||||
|
|
||||||
> ℹ For C# support, see the next section.
|
|
||||||
|
|
||||||
The example above adds a command that multiplies two numbers and prints the result (type `multiply 2 4`). Additionally, you can specify a name and a description:
|
|
||||||
|
|
||||||
```gdscript
|
|
||||||
LimboConsole.register_command(multiply, "multiply", "multiply two numbers")
|
|
||||||
```
|
|
||||||
|
|
||||||
You can add a command as a subcommand of another command:
|
|
||||||
|
|
||||||
```gdscript
|
|
||||||
# Register `multiply` as a subcommand under a new `math` command.
|
|
||||||
LimboConsole.register_command(multiply, "math multiply", "Multiply two numbers")
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, you can enter `math multiply 2 4` in the console. By the way, the parent command doesn't have to exist.
|
|
||||||
|
|
||||||
Several basic types are supported for command arguments, such as `bool`, `int`, `float`, `String` and `Vector{2,3,4}` types. To enter a `Vector2` argument, enclose its components in parentheses, like this: `(1 2)`. String arguments can also be enclosed in double quotation marks `"`.
|
|
||||||
|
|
||||||
Autocompletion works for both command names and history. It can also be implemented for specific command arguments, as shown in the following example:
|
|
||||||
```gdscript
|
|
||||||
LimboConsole.register_command(teleport, "teleport", "teleport to site on this level")
|
|
||||||
LimboConsole.add_argument_autocomplete_source("teleport", 1,
|
|
||||||
func(): return ["entrance", "caves", "boss"]
|
|
||||||
)
|
|
||||||
```
|
|
||||||
For a dynamically generated list of autocomplete values, the code could look like this:
|
|
||||||
```gdscript
|
|
||||||
LimboConsole.add_argument_autocomplete_source("teleport", 1,
|
|
||||||
func(): return get_tree().get_nodes_in_group("teleportation_site").map(
|
|
||||||
func(node): return node.name)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using in C#
|
|
||||||
|
|
||||||
A community-maintained C# wrapper for this project is available as a NuGet package: https://github.com/ryan-linehan/limbo_console_sharp
|
|
||||||
|
|
||||||
Thanks to @ryan-linehan for maintaining it!
|
|
||||||
|
|
||||||
### Methods and properties
|
|
||||||
|
|
||||||
Some notable methods and properties:
|
|
||||||
|
|
||||||
- LimboConsole.enabled
|
|
||||||
- LimboConsole.register_command(callable, command_name, description)
|
|
||||||
- LimboConsole.unregister_command(callable_or_command_name)
|
|
||||||
- LimboConsole.add_alias(alias_name, command_name)
|
|
||||||
- LimboConsole.info(text_line)
|
|
||||||
- LimboConsole.error(text_line)
|
|
||||||
- LimboConsole.warning(text_line)
|
|
||||||
- LimboConsole.toggle_console()
|
|
||||||
- LimboConsole.add_argument_autocomplete_source(command_name, argument, callable)
|
|
||||||
- LimboConsole.execute_script(path, silent)
|
|
||||||
|
|
||||||
This is not a complete list. For the rest, check out `limbo_console.gd`.
|
|
||||||
|
|
||||||
### Keyboard Shortcuts
|
|
||||||
|
|
||||||
- `Grave Accent` *(aka backtick - the key to the left of the `1` key)* — Toggle the console.
|
|
||||||
- `Enter` — Run entered command.
|
|
||||||
- `Tab` — Autocomplete command entry or cycle through autocomplete suggestions.
|
|
||||||
- `Shift+Tab` — Cycle through autocomplete suggestions in reverse.
|
|
||||||
- `Right` *(when cursor is at the end of command entry)* — Autocomplete according to inline hint (doesn't cycle like `Tab`).
|
|
||||||
- `Up/Down` — Cycle through command history, replacing contents of command entry.
|
|
||||||
- `Ctrl+R` — Toggle the history search interface (similar to [fzf](https://github.com/junegunn/fzf)).
|
|
||||||
- `Ctrl+C` *(when no text selected)* — Clear the command entry.
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
Options can be modified in the project-specific configuration file located at `res://addons/limbo_console.cfg`. This file is stored outside the plugin's directory to support adding the plugin as a Git submodule.
|
|
||||||
|
|
||||||
LimboConsole also supports UI theming. Simply duplicate the `default_theme.tres` file and rename it to `limbo_console_theme.tres`. The file path is important - it should be located at `res://addons/limbo_console_theme.tres`. You can change this location in the config file.
|
|
||||||
Open the theme resource in Godot to customize it for your game. Console text colors can be adjusted in the `ConsoleColors` category.
|
|
||||||
|
|
||||||
### Scripting
|
|
||||||
|
|
||||||
You can execute simple scripts containing a sequence of commands:
|
|
||||||
```shell
|
|
||||||
exec lcs/my_script.lcs
|
|
||||||
```
|
|
||||||
|
|
||||||
Simple rules:
|
|
||||||
- Commands must be provided in the same syntax as in the prompt, with each command on a separate line.
|
|
||||||
- The script must exist at the specified path, either in the `res://` or `user://` directory.
|
|
||||||
- The script must have the `.lcs` extension, but when running the `exec` command, you can omit the extension in the command line.
|
|
||||||
- A line that starts with a '#' is treated as a comment and is not executed as part of the script.
|
|
||||||
|
|
||||||
You can have a script execute automatically every time the game starts. There is a special script called `user://autoexec.lcs` that runs each time the game starts. This can be customized in the configuration file.
|
|
||||||
|
|
||||||
### Contributing
|
|
||||||
|
|
||||||
Check out [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
||||||
@ -1,208 +0,0 @@
|
|||||||
extends RefCounted
|
|
||||||
|
|
||||||
|
|
||||||
const boxed_map: Dictionary = {
|
|
||||||
'a': """
|
|
||||||
▒▄▀█
|
|
||||||
░█▀█
|
|
||||||
""",
|
|
||||||
'b': """
|
|
||||||
░█▄▄
|
|
||||||
▒█▄█
|
|
||||||
""",
|
|
||||||
'c': """
|
|
||||||
░▄▀▀
|
|
||||||
░▀▄▄
|
|
||||||
""",
|
|
||||||
'd': """
|
|
||||||
▒█▀▄
|
|
||||||
░█▄▀
|
|
||||||
""",
|
|
||||||
'e': """
|
|
||||||
░██▀
|
|
||||||
▒█▄▄
|
|
||||||
""",
|
|
||||||
'f': """
|
|
||||||
░█▀▀
|
|
||||||
░█▀░
|
|
||||||
""",
|
|
||||||
'g': """
|
|
||||||
▒▄▀▀
|
|
||||||
░▀▄█
|
|
||||||
""",
|
|
||||||
'h': """
|
|
||||||
░█▄█
|
|
||||||
▒█▒█
|
|
||||||
""",
|
|
||||||
'i': """
|
|
||||||
░█
|
|
||||||
░█
|
|
||||||
""",
|
|
||||||
'j': """
|
|
||||||
░░▒█
|
|
||||||
░▀▄█
|
|
||||||
""",
|
|
||||||
'k': """
|
|
||||||
░█▄▀
|
|
||||||
░█▒█
|
|
||||||
""",
|
|
||||||
'l': """
|
|
||||||
░█▒░
|
|
||||||
▒█▄▄
|
|
||||||
""",
|
|
||||||
'm': """
|
|
||||||
▒█▀▄▀█
|
|
||||||
░█▒▀▒█
|
|
||||||
""",
|
|
||||||
'n': """
|
|
||||||
░█▄░█
|
|
||||||
░█▒▀█
|
|
||||||
""",
|
|
||||||
'o': """
|
|
||||||
░█▀█
|
|
||||||
▒█▄█
|
|
||||||
""",
|
|
||||||
'p': """
|
|
||||||
▒█▀█
|
|
||||||
░█▀▀
|
|
||||||
""",
|
|
||||||
'q': """
|
|
||||||
░▄▀▄
|
|
||||||
░▀▄█
|
|
||||||
""",
|
|
||||||
'r': """
|
|
||||||
▒█▀█
|
|
||||||
░█▀▄
|
|
||||||
""",
|
|
||||||
's': """
|
|
||||||
░▄▀
|
|
||||||
▒▄█
|
|
||||||
""",
|
|
||||||
't': """
|
|
||||||
░▀█▀
|
|
||||||
░▒█▒
|
|
||||||
""",
|
|
||||||
'u': """
|
|
||||||
░█░█
|
|
||||||
▒█▄█
|
|
||||||
""",
|
|
||||||
'v': """
|
|
||||||
░█░█
|
|
||||||
▒▀▄▀
|
|
||||||
""",
|
|
||||||
'w': """
|
|
||||||
▒█░█░█
|
|
||||||
░▀▄▀▄▀
|
|
||||||
""",
|
|
||||||
'x': """
|
|
||||||
░▀▄▀
|
|
||||||
░█▒█
|
|
||||||
""",
|
|
||||||
'y': """
|
|
||||||
░▀▄▀
|
|
||||||
░▒█▒
|
|
||||||
""",
|
|
||||||
'z': """
|
|
||||||
░▀█
|
|
||||||
▒█▄
|
|
||||||
""",
|
|
||||||
' ': """
|
|
||||||
░
|
|
||||||
░
|
|
||||||
""",
|
|
||||||
'_': """
|
|
||||||
░░░
|
|
||||||
▒▄▄
|
|
||||||
""",
|
|
||||||
',': """
|
|
||||||
░▒
|
|
||||||
░█
|
|
||||||
""",
|
|
||||||
'.': """
|
|
||||||
░░
|
|
||||||
░▄
|
|
||||||
""",
|
|
||||||
'!': """
|
|
||||||
░█
|
|
||||||
░▄
|
|
||||||
""",
|
|
||||||
'-': """
|
|
||||||
░▒░
|
|
||||||
░▀▀
|
|
||||||
""",
|
|
||||||
'?': """
|
|
||||||
░▀▀▄
|
|
||||||
░▒█▀
|
|
||||||
""",
|
|
||||||
'\'': """
|
|
||||||
░▀
|
|
||||||
░░
|
|
||||||
""",
|
|
||||||
':': """
|
|
||||||
░▄░
|
|
||||||
▒▄▒
|
|
||||||
""",
|
|
||||||
'0': """
|
|
||||||
░▄▀▄
|
|
||||||
░▀▄▀
|
|
||||||
""",
|
|
||||||
'1': """
|
|
||||||
░▄█
|
|
||||||
░░█
|
|
||||||
""",
|
|
||||||
'2': """
|
|
||||||
░▀█
|
|
||||||
░█▄
|
|
||||||
""",
|
|
||||||
'3': """
|
|
||||||
░▀██
|
|
||||||
░▄▄█
|
|
||||||
""",
|
|
||||||
'4': """
|
|
||||||
░█▄
|
|
||||||
░░█
|
|
||||||
""",
|
|
||||||
'5': """
|
|
||||||
░█▀
|
|
||||||
░▄█
|
|
||||||
""",
|
|
||||||
'6': """
|
|
||||||
░█▀
|
|
||||||
░██
|
|
||||||
""",
|
|
||||||
'7': """
|
|
||||||
░▀█
|
|
||||||
░█░
|
|
||||||
""",
|
|
||||||
'8': """
|
|
||||||
░█▄█
|
|
||||||
░█▄█
|
|
||||||
""",
|
|
||||||
'9': """
|
|
||||||
░██
|
|
||||||
░▄█
|
|
||||||
""",
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsupported_char := """
|
|
||||||
░▒░
|
|
||||||
▒░▒
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
static func str_to_boxed_art(p_text: String) -> PackedStringArray:
|
|
||||||
var lines: PackedStringArray = []
|
|
||||||
lines.resize(2)
|
|
||||||
for c in p_text:
|
|
||||||
var ascii: String = boxed_map.get(c.to_lower(), unsupported_char)
|
|
||||||
var parts: PackedStringArray = ascii.split('\n')
|
|
||||||
lines[0] += parts[1]
|
|
||||||
lines[1] += parts[2]
|
|
||||||
return lines
|
|
||||||
|
|
||||||
|
|
||||||
static func is_boxed_art_supported(p_text: String) -> bool:
|
|
||||||
for c in p_text:
|
|
||||||
if not boxed_map.has(c.to_lower()):
|
|
||||||
return false
|
|
||||||
return true
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://coot1au4b7np
|
|
||||||
@ -1,169 +0,0 @@
|
|||||||
extends RefCounted
|
|
||||||
## BuiltinCommands
|
|
||||||
|
|
||||||
|
|
||||||
const Util := preload("res://addons/limbo_console/util.gd")
|
|
||||||
|
|
||||||
|
|
||||||
static func register_commands() -> void:
|
|
||||||
LimboConsole.register_command(cmd_alias, "alias", "add command alias")
|
|
||||||
LimboConsole.register_command(cmd_aliases, "aliases", "list all aliases")
|
|
||||||
LimboConsole.register_command(LimboConsole.clear_console, "clear", "clear console screen")
|
|
||||||
LimboConsole.register_command(cmd_commands, "commands", "list all commands")
|
|
||||||
LimboConsole.register_command(LimboConsole.info, "echo", "display a line of text")
|
|
||||||
LimboConsole.register_command(cmd_eval, "eval", "evaluate an expression")
|
|
||||||
LimboConsole.register_command(cmd_exec, "exec", "execute commands from file")
|
|
||||||
LimboConsole.register_command(cmd_fps_max, "fps_max", "limit framerate")
|
|
||||||
LimboConsole.register_command(cmd_fullscreen, "fullscreen", "toggle fullscreen mode")
|
|
||||||
LimboConsole.register_command(cmd_help, "help", "show command info")
|
|
||||||
LimboConsole.register_command(cmd_log, "log", "show recent log entries")
|
|
||||||
LimboConsole.register_command(cmd_quit, "quit", "exit the application")
|
|
||||||
LimboConsole.register_command(cmd_unalias, "unalias", "remove command alias")
|
|
||||||
LimboConsole.register_command(cmd_vsync, "vsync", "adjust V-Sync")
|
|
||||||
LimboConsole.register_command(LimboConsole.erase_history, "erase_history", "erases current history and persisted history")
|
|
||||||
LimboConsole.add_argument_autocomplete_source("help", 1, LimboConsole.get_command_names.bind(true))
|
|
||||||
|
|
||||||
|
|
||||||
static func cmd_alias(p_alias: String, p_command: String) -> void:
|
|
||||||
LimboConsole.info("Adding %s => %s" % [LimboConsole.format_name(p_alias), p_command])
|
|
||||||
LimboConsole.add_alias(p_alias, p_command)
|
|
||||||
|
|
||||||
|
|
||||||
static func cmd_aliases() -> void:
|
|
||||||
var aliases: Array = LimboConsole.get_aliases()
|
|
||||||
aliases.sort()
|
|
||||||
for alias in aliases:
|
|
||||||
var alias_argv: PackedStringArray = LimboConsole.get_alias_argv(alias)
|
|
||||||
var cmd_name: String = alias_argv[0]
|
|
||||||
var desc: String = LimboConsole.get_command_description(cmd_name)
|
|
||||||
alias_argv[0] = LimboConsole.format_name(cmd_name)
|
|
||||||
if desc.is_empty():
|
|
||||||
LimboConsole.info(LimboConsole.format_name(alias))
|
|
||||||
else:
|
|
||||||
LimboConsole.info("%s is alias of: %s %s" % [
|
|
||||||
LimboConsole.format_name(alias),
|
|
||||||
' '.join(alias_argv),
|
|
||||||
LimboConsole.format_tip(" // " + desc)
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
static func cmd_commands() -> void:
|
|
||||||
LimboConsole.info("Available commands:")
|
|
||||||
for name in LimboConsole.get_command_names(false):
|
|
||||||
var desc: String = LimboConsole.get_command_description(name)
|
|
||||||
name = LimboConsole.format_name(name)
|
|
||||||
LimboConsole.info(name if desc.is_empty() else "%s -- %s" % [name, desc])
|
|
||||||
|
|
||||||
|
|
||||||
static func cmd_eval(p_expression: String) -> Error:
|
|
||||||
var exp := Expression.new()
|
|
||||||
var err: int = exp.parse(p_expression, LimboConsole.get_eval_input_names())
|
|
||||||
if err != OK:
|
|
||||||
LimboConsole.error(exp.get_error_text())
|
|
||||||
return err
|
|
||||||
var result = exp.execute(LimboConsole.get_eval_inputs(),
|
|
||||||
LimboConsole.get_eval_base_instance())
|
|
||||||
if not exp.has_execute_failed():
|
|
||||||
if result != null:
|
|
||||||
LimboConsole.info(str(result))
|
|
||||||
return OK
|
|
||||||
else:
|
|
||||||
LimboConsole.error(exp.get_error_text())
|
|
||||||
return ERR_SCRIPT_FAILED
|
|
||||||
|
|
||||||
|
|
||||||
static func cmd_exec(p_file: String, p_silent: bool = true) -> void:
|
|
||||||
if not p_file.ends_with(".lcs"):
|
|
||||||
# Prevent users from reading other game assets.
|
|
||||||
p_file += ".lcs"
|
|
||||||
if not FileAccess.file_exists(p_file):
|
|
||||||
p_file = "user://" + p_file
|
|
||||||
LimboConsole.execute_script(p_file, p_silent)
|
|
||||||
|
|
||||||
|
|
||||||
static func cmd_fps_max(p_limit: int = -1) -> void:
|
|
||||||
if p_limit < 0:
|
|
||||||
if Engine.max_fps == 0:
|
|
||||||
LimboConsole.info("Framerate is unlimited.")
|
|
||||||
else:
|
|
||||||
LimboConsole.info("Framerate is limited to %d FPS." % [Engine.max_fps])
|
|
||||||
return
|
|
||||||
|
|
||||||
Engine.max_fps = p_limit
|
|
||||||
if p_limit > 0:
|
|
||||||
LimboConsole.info("Limiting framerate to %d FPS." % [p_limit])
|
|
||||||
elif p_limit == 0:
|
|
||||||
LimboConsole.info("Removing framerate limits.")
|
|
||||||
|
|
||||||
|
|
||||||
static func cmd_fullscreen() -> void:
|
|
||||||
if LimboConsole.get_viewport().mode == Window.MODE_WINDOWED:
|
|
||||||
# get_viewport().mode = Window.MODE_EXCLUSIVE_FULLSCREEN
|
|
||||||
LimboConsole.get_viewport().mode = Window.MODE_FULLSCREEN
|
|
||||||
LimboConsole.info("Window switched to fullscreen mode.")
|
|
||||||
else:
|
|
||||||
LimboConsole.get_viewport().mode = Window.MODE_WINDOWED
|
|
||||||
LimboConsole.info("Window switched to windowed mode.")
|
|
||||||
|
|
||||||
|
|
||||||
static func cmd_help(p_command_name: String = "") -> Error:
|
|
||||||
if p_command_name.is_empty():
|
|
||||||
LimboConsole.print_line(LimboConsole.format_tip("Type %s to list all available commands." %
|
|
||||||
[LimboConsole.format_name("commands")]))
|
|
||||||
LimboConsole.print_line(LimboConsole.format_tip("Type %s to get more info about the command." %
|
|
||||||
[LimboConsole.format_name("help command")]))
|
|
||||||
return OK
|
|
||||||
else:
|
|
||||||
return LimboConsole.usage(p_command_name)
|
|
||||||
|
|
||||||
|
|
||||||
static func cmd_log(p_num_lines: int = 10) -> Error:
|
|
||||||
var fn: String = ProjectSettings.get_setting("debug/file_logging/log_path")
|
|
||||||
var file = FileAccess.open(fn, FileAccess.READ)
|
|
||||||
if not file:
|
|
||||||
LimboConsole.error("Can't open file: " + fn)
|
|
||||||
return ERR_CANT_OPEN
|
|
||||||
var contents := file.get_as_text()
|
|
||||||
var lines := contents.split('\n')
|
|
||||||
if lines.size() and lines[lines.size() - 1].strip_edges() == "":
|
|
||||||
lines.remove_at(lines.size() - 1)
|
|
||||||
lines = lines.slice(maxi(lines.size() - p_num_lines, 0))
|
|
||||||
for line in lines:
|
|
||||||
LimboConsole.print_line(Util.bbcode_escape(line), false)
|
|
||||||
return OK
|
|
||||||
|
|
||||||
|
|
||||||
static func cmd_quit() -> void:
|
|
||||||
LimboConsole.get_tree().quit()
|
|
||||||
|
|
||||||
|
|
||||||
static func cmd_unalias(p_alias: String) -> void:
|
|
||||||
if LimboConsole.has_alias(p_alias):
|
|
||||||
LimboConsole.remove_alias(p_alias)
|
|
||||||
LimboConsole.info("Alias removed.")
|
|
||||||
else:
|
|
||||||
LimboConsole.warn("Alias not found.")
|
|
||||||
|
|
||||||
|
|
||||||
static func cmd_vsync(p_mode: int = -1) -> void:
|
|
||||||
if p_mode < 0:
|
|
||||||
var current: int = DisplayServer.window_get_vsync_mode()
|
|
||||||
if current == 0:
|
|
||||||
LimboConsole.info("V-Sync: disabled.")
|
|
||||||
elif current == 1:
|
|
||||||
LimboConsole.info('V-Sync: enabled.')
|
|
||||||
elif current == 2:
|
|
||||||
LimboConsole.info('Current V-Sync mode: adaptive.')
|
|
||||||
LimboConsole.info("Adjust V-Sync mode with an argument: 0 - disabled, 1 - enabled, 2 - adaptive.")
|
|
||||||
elif p_mode == DisplayServer.VSYNC_DISABLED:
|
|
||||||
LimboConsole.info("Changing to disabled.")
|
|
||||||
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
|
|
||||||
elif p_mode == DisplayServer.VSYNC_ENABLED:
|
|
||||||
LimboConsole.info("Changing to default V-Sync.")
|
|
||||||
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED)
|
|
||||||
elif p_mode == DisplayServer.VSYNC_ADAPTIVE:
|
|
||||||
LimboConsole.info("Changing to adaptive V-Sync.")
|
|
||||||
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ADAPTIVE)
|
|
||||||
else:
|
|
||||||
LimboConsole.error("Invalid mode.")
|
|
||||||
LimboConsole.info("Acceptable modes: 0 - disabled, 1 - enabled, 2 - adaptive.")
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://npao6ckwv7cm
|
|
||||||
@ -1,130 +0,0 @@
|
|||||||
extends TextEdit
|
|
||||||
## CommandEntry
|
|
||||||
|
|
||||||
|
|
||||||
signal text_submitted(command_line: String)
|
|
||||||
signal autocomplete_requested()
|
|
||||||
|
|
||||||
var autocomplete_hint: String:
|
|
||||||
set(value):
|
|
||||||
if autocomplete_hint != value:
|
|
||||||
autocomplete_hint = value
|
|
||||||
queue_redraw()
|
|
||||||
|
|
||||||
var _font: Font
|
|
||||||
var _font_size: int
|
|
||||||
var _hint_color: Color
|
|
||||||
var _sb_normal: StyleBox
|
|
||||||
|
|
||||||
func _init() -> void:
|
|
||||||
syntax_highlighter = CommandEntryHighlighter.new()
|
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
|
||||||
caret_multiple = false
|
|
||||||
autowrap_mode = TextServer.AUTOWRAP_OFF
|
|
||||||
scroll_fit_content_height = true
|
|
||||||
# placeholder_text = ""
|
|
||||||
|
|
||||||
get_v_scroll_bar().visibility_changed.connect(_hide_scrollbars)
|
|
||||||
get_h_scroll_bar().visibility_changed.connect(_hide_scrollbars)
|
|
||||||
_hide_scrollbars()
|
|
||||||
|
|
||||||
_font = get_theme_font("font")
|
|
||||||
_font_size = get_theme_font_size("font_size")
|
|
||||||
_hint_color = get_theme_color("hint_color")
|
|
||||||
_sb_normal = get_theme_stylebox("normal")
|
|
||||||
|
|
||||||
|
|
||||||
func _notification(what: int) -> void:
|
|
||||||
match what:
|
|
||||||
NOTIFICATION_FOCUS_ENTER:
|
|
||||||
set_process_input(true)
|
|
||||||
NOTIFICATION_FOCUS_EXIT:
|
|
||||||
set_process_input(false)
|
|
||||||
|
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
|
||||||
if not has_focus():
|
|
||||||
return
|
|
||||||
if event is InputEventKey:
|
|
||||||
if event.keycode == KEY_ENTER or event.keycode == KEY_KP_ENTER:
|
|
||||||
if event.is_pressed():
|
|
||||||
submit_text()
|
|
||||||
get_viewport().set_input_as_handled()
|
|
||||||
elif event.keycode == KEY_C and event.get_modifiers_mask() == KEY_MASK_CTRL and get_selected_text().is_empty():
|
|
||||||
# Clear input on Ctrl+C if no text selected.
|
|
||||||
if event.is_pressed():
|
|
||||||
text = ""
|
|
||||||
text_changed.emit()
|
|
||||||
get_viewport().set_input_as_handled()
|
|
||||||
elif event.keycode in [KEY_RIGHT, KEY_END] and get_caret_column() == text.length():
|
|
||||||
# Request autocomplete on RIGHT & END.
|
|
||||||
if event.is_pressed() and not autocomplete_hint.is_empty():
|
|
||||||
autocomplete_requested.emit()
|
|
||||||
get_viewport().set_input_as_handled()
|
|
||||||
|
|
||||||
|
|
||||||
func _draw() -> void:
|
|
||||||
var offset_x: int = 0
|
|
||||||
offset_x += _sb_normal.get_offset().x * 0.5
|
|
||||||
offset_x += get_line_width(0)
|
|
||||||
|
|
||||||
var offset_y: int = 0
|
|
||||||
offset_y += _sb_normal.get_offset().y * 0.5
|
|
||||||
offset_y += get_line_height() + 0.5 # + line_spacing
|
|
||||||
offset_y -= _font.get_descent(_font_size)
|
|
||||||
|
|
||||||
draw_string(_font, Vector2(offset_x, offset_y), autocomplete_hint, 0, -1, _font_size, _hint_color)
|
|
||||||
|
|
||||||
|
|
||||||
func submit_text() -> void:
|
|
||||||
text_submitted.emit(text)
|
|
||||||
|
|
||||||
|
|
||||||
func _hide_scrollbars() -> void:
|
|
||||||
get_v_scroll_bar().hide()
|
|
||||||
get_h_scroll_bar().hide()
|
|
||||||
|
|
||||||
|
|
||||||
class CommandEntryHighlighter extends SyntaxHighlighter:
|
|
||||||
var command_found_color: Color
|
|
||||||
var subcommand_color: Color
|
|
||||||
var command_not_found_color: Color
|
|
||||||
var text_color: Color
|
|
||||||
|
|
||||||
func _get_line_syntax_highlighting(line: int) -> Dictionary:
|
|
||||||
var text: String = get_text_edit().text
|
|
||||||
var command_end_idx: int = -1 # index where last recognized command ends (with subcommands)
|
|
||||||
|
|
||||||
var argv: PackedStringArray = [] # argument vector (aka tokens)
|
|
||||||
var argi: PackedInt32Array = [] # argument starting indices in text
|
|
||||||
var start: int = 0
|
|
||||||
var cur: int = 0
|
|
||||||
for char in text + ' ':
|
|
||||||
if char == ' ':
|
|
||||||
if cur > start:
|
|
||||||
argv.append(text.substr(start, cur - start))
|
|
||||||
argi.append(start)
|
|
||||||
var maybe_command: String = ' '.join(argv)
|
|
||||||
if LimboConsole.has_command(maybe_command) or LimboConsole.has_alias(maybe_command):
|
|
||||||
command_end_idx = cur
|
|
||||||
start = cur + 1
|
|
||||||
cur += 1
|
|
||||||
|
|
||||||
var command_color: Color
|
|
||||||
var arg_start_idx: int = 0 # index where arguments start
|
|
||||||
|
|
||||||
if command_end_idx > -1:
|
|
||||||
command_color = command_found_color
|
|
||||||
arg_start_idx = command_end_idx + 1
|
|
||||||
else:
|
|
||||||
command_color = command_not_found_color
|
|
||||||
arg_start_idx = argi[1] if argi.size() > 1 else text.length()
|
|
||||||
|
|
||||||
var result: Dictionary
|
|
||||||
result[0] = { "color": command_color }
|
|
||||||
if command_end_idx > -1 and argi.size() > 1:
|
|
||||||
result[argi[1]] = { "color": subcommand_color }
|
|
||||||
result[arg_start_idx] = { "color": text_color }
|
|
||||||
return result
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://ddtreeatktyov
|
|
||||||
@ -1,165 +0,0 @@
|
|||||||
extends RefCounted
|
|
||||||
## Manages command history.
|
|
||||||
|
|
||||||
|
|
||||||
const HISTORY_FILE := "user://limbo_console_history.log"
|
|
||||||
|
|
||||||
|
|
||||||
var _entries: PackedStringArray
|
|
||||||
var _hist_idx = -1
|
|
||||||
var _iterators: Array[WrappingIterator]
|
|
||||||
var _is_dirty: bool = false
|
|
||||||
|
|
||||||
|
|
||||||
func push_entry(p_entry: String) -> void:
|
|
||||||
_push_entry(p_entry)
|
|
||||||
_reset_iterators()
|
|
||||||
|
|
||||||
|
|
||||||
func _push_entry(p_entry: String) -> void:
|
|
||||||
var idx: int = _entries.find(p_entry)
|
|
||||||
if idx != -1:
|
|
||||||
# Duplicate commands not allowed in history.
|
|
||||||
_entries.remove_at(idx)
|
|
||||||
_entries.append(p_entry)
|
|
||||||
_is_dirty = true
|
|
||||||
|
|
||||||
|
|
||||||
func get_entry(p_index: int) -> String:
|
|
||||||
return _entries[clampi(p_index, 0, _entries.size())]
|
|
||||||
|
|
||||||
|
|
||||||
func create_iterator() -> WrappingIterator:
|
|
||||||
var it := WrappingIterator.new(_entries)
|
|
||||||
_iterators.append(it)
|
|
||||||
return it
|
|
||||||
|
|
||||||
|
|
||||||
func release_iterator(p_iter: WrappingIterator) -> void:
|
|
||||||
_iterators.erase(p_iter)
|
|
||||||
|
|
||||||
|
|
||||||
func size() -> int:
|
|
||||||
return _entries.size()
|
|
||||||
|
|
||||||
|
|
||||||
func trim(p_max_size: int) -> void:
|
|
||||||
if _entries.size() > p_max_size:
|
|
||||||
_entries.slice(p_max_size - _entries.size())
|
|
||||||
_reset_iterators()
|
|
||||||
|
|
||||||
|
|
||||||
func clear() -> void:
|
|
||||||
_entries.clear()
|
|
||||||
|
|
||||||
|
|
||||||
func load(p_path: String = HISTORY_FILE) -> void:
|
|
||||||
var file := FileAccess.open(p_path, FileAccess.READ)
|
|
||||||
if not file:
|
|
||||||
return
|
|
||||||
while not file.eof_reached():
|
|
||||||
var line: String = file.get_line().strip_edges()
|
|
||||||
if not line.is_empty():
|
|
||||||
_push_entry(line)
|
|
||||||
file.close()
|
|
||||||
_reset_iterators()
|
|
||||||
_is_dirty = false
|
|
||||||
|
|
||||||
|
|
||||||
func save(p_path: String = HISTORY_FILE) -> void:
|
|
||||||
if not _is_dirty:
|
|
||||||
return
|
|
||||||
var file := FileAccess.open(p_path, FileAccess.WRITE)
|
|
||||||
if not file:
|
|
||||||
push_error("LimboConsole: Failed to save console history to file: ", p_path)
|
|
||||||
return
|
|
||||||
for line in _entries:
|
|
||||||
file.store_line(line)
|
|
||||||
file.close()
|
|
||||||
_is_dirty = false
|
|
||||||
|
|
||||||
|
|
||||||
## Searches history and returns an array starting with most relevant entries.
|
|
||||||
func fuzzy_match(p_query: String) -> PackedStringArray:
|
|
||||||
if len(p_query) == 0:
|
|
||||||
var copy := _entries.duplicate()
|
|
||||||
copy.reverse()
|
|
||||||
return copy
|
|
||||||
|
|
||||||
var results: Array = []
|
|
||||||
for entry: String in _entries:
|
|
||||||
var score: int = _compute_match_score(p_query.to_lower(), entry.to_lower())
|
|
||||||
if score > 0:
|
|
||||||
results.append({"entry": entry, "score": score})
|
|
||||||
|
|
||||||
results.sort_custom(func(a, b): return a.score > b.score)
|
|
||||||
return results.map(func(rec): return rec.entry)
|
|
||||||
|
|
||||||
|
|
||||||
func _reset_iterators() -> void:
|
|
||||||
for it in _iterators:
|
|
||||||
it._reassign(_entries)
|
|
||||||
|
|
||||||
|
|
||||||
## Scoring function for fuzzy matching.
|
|
||||||
static func _compute_match_score(query: String, target: String) -> int:
|
|
||||||
var score: int = 0
|
|
||||||
var query_index: int = 0
|
|
||||||
|
|
||||||
# Exact match. give unbeatable score
|
|
||||||
if query == target:
|
|
||||||
score = 99999
|
|
||||||
return score
|
|
||||||
|
|
||||||
for i in range(target.length()):
|
|
||||||
if query_index < query.length() and target[i] == query[query_index]:
|
|
||||||
score += 10 # Base score for a match
|
|
||||||
if i == 0 or target[i - 1] == " ": # Bonus for word start
|
|
||||||
score += 5
|
|
||||||
query_index += 1
|
|
||||||
if query_index == query.length():
|
|
||||||
break
|
|
||||||
|
|
||||||
# Ensure full query matches
|
|
||||||
return score if query_index == query.length() else 0
|
|
||||||
|
|
||||||
|
|
||||||
## Iterator that wraps around and resets on history change.
|
|
||||||
class WrappingIterator:
|
|
||||||
extends RefCounted
|
|
||||||
|
|
||||||
var _idx: int = -1
|
|
||||||
var _entries: PackedStringArray
|
|
||||||
|
|
||||||
|
|
||||||
func _init(p_entries: PackedStringArray) -> void:
|
|
||||||
_entries = p_entries
|
|
||||||
|
|
||||||
|
|
||||||
func prev() -> String:
|
|
||||||
_idx = wrapi(_idx - 1, -1, _entries.size())
|
|
||||||
if _idx == -1:
|
|
||||||
return String()
|
|
||||||
return _entries[_idx]
|
|
||||||
|
|
||||||
|
|
||||||
func next() -> String:
|
|
||||||
_idx = wrapi(_idx + 1, -1, _entries.size())
|
|
||||||
if _idx == -1:
|
|
||||||
return String()
|
|
||||||
return _entries[_idx]
|
|
||||||
|
|
||||||
|
|
||||||
func current() -> String:
|
|
||||||
if _idx < 0 or _idx >= _entries.size():
|
|
||||||
return String()
|
|
||||||
return _entries[_idx]
|
|
||||||
|
|
||||||
|
|
||||||
func reset() -> void:
|
|
||||||
_idx = -1
|
|
||||||
|
|
||||||
|
|
||||||
func _reassign(p_entries: PackedStringArray) -> void:
|
|
||||||
_idx = -1
|
|
||||||
_entries = p_entries
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://dc55ouwu3ylf
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
@tool
|
|
||||||
extends RefCounted
|
|
||||||
## Store object properties in an INI-style configuration file.
|
|
||||||
|
|
||||||
|
|
||||||
const CONFIG_PATH_PROPERTY := &"CONFIG_PATH"
|
|
||||||
const MAIN_SECTION_PROPERTY := &"MAIN_SECTION"
|
|
||||||
const MAIN_SECTION_DEFAULT := "main"
|
|
||||||
|
|
||||||
static var verbose: bool = false
|
|
||||||
|
|
||||||
static func _get_config_file(p_object: Object) -> String:
|
|
||||||
var from_object_constant = p_object.get(CONFIG_PATH_PROPERTY)
|
|
||||||
return from_object_constant if from_object_constant is String else ""
|
|
||||||
|
|
||||||
|
|
||||||
static func _get_main_section(p_object: Object) -> String:
|
|
||||||
var from_object_constant = p_object.get(MAIN_SECTION_PROPERTY)
|
|
||||||
return from_object_constant if from_object_constant != null else MAIN_SECTION_DEFAULT
|
|
||||||
|
|
||||||
|
|
||||||
static func _msg(p_text: String, p_arg1 = "") -> void:
|
|
||||||
if verbose:
|
|
||||||
print(p_text, p_arg1)
|
|
||||||
|
|
||||||
|
|
||||||
## Load object properties from config file and return status. [br]
|
|
||||||
## If p_config_path is empty, configuration path is taken from object's CONFIG_PATH property.
|
|
||||||
static func load_from_config(p_object: Object, p_config_path: String = "") -> int:
|
|
||||||
var config_path: String = p_config_path
|
|
||||||
if config_path.is_empty():
|
|
||||||
config_path = _get_config_file(p_object)
|
|
||||||
var section: String = _get_main_section(p_object)
|
|
||||||
var config := ConfigFile.new()
|
|
||||||
var err: int = config.load(config_path)
|
|
||||||
if err != OK:
|
|
||||||
_msg("ConfigMapper: Failed to load config: %s err_code: %d" % [config_path, err])
|
|
||||||
return err
|
|
||||||
_msg("ConfigMapper: Loading config: ", config_path)
|
|
||||||
|
|
||||||
for prop_info in p_object.get_property_list():
|
|
||||||
if prop_info.usage & PROPERTY_USAGE_CATEGORY and prop_info.hint_string.is_empty():
|
|
||||||
_msg("ConfigMapper: Processing category: ", prop_info.name)
|
|
||||||
section = prop_info.name
|
|
||||||
elif prop_info.usage & PROPERTY_USAGE_SCRIPT_VARIABLE and prop_info.usage & PROPERTY_USAGE_STORAGE:
|
|
||||||
var value = null
|
|
||||||
if config.has_section_key(section, prop_info.name):
|
|
||||||
value = config.get_value(section, prop_info.name)
|
|
||||||
if value != null and typeof(value) == prop_info.type:
|
|
||||||
_msg("ConfigMapper: Loaded setting: %s section: %s value: %s" % [prop_info.name, section, value])
|
|
||||||
p_object.set(prop_info.name, value)
|
|
||||||
_msg("ConfigMapper: Finished with code: ", OK)
|
|
||||||
return OK
|
|
||||||
|
|
||||||
|
|
||||||
## Save object properties to config file and return status. [br]
|
|
||||||
## If p_config_path is empty, configuration path is taken from object's CONFIG_PATH property. [br]
|
|
||||||
## WARNING: replaces file contents!
|
|
||||||
static func save_to_config(p_object: Object, p_config_path: String = "") -> int:
|
|
||||||
var config_path: String = p_config_path
|
|
||||||
if config_path.is_empty():
|
|
||||||
config_path = _get_config_file(p_object)
|
|
||||||
var section: String = _get_main_section(p_object)
|
|
||||||
var config := ConfigFile.new()
|
|
||||||
_msg("ConfigMapper: Saving config: ", config_path)
|
|
||||||
|
|
||||||
for prop_info in p_object.get_property_list():
|
|
||||||
if prop_info.usage & PROPERTY_USAGE_CATEGORY and prop_info.hint_string.is_empty():
|
|
||||||
_msg("ConfigMapper: Processing category: ", prop_info.name)
|
|
||||||
section = prop_info.name
|
|
||||||
elif prop_info.usage & PROPERTY_USAGE_SCRIPT_VARIABLE and prop_info.usage & PROPERTY_USAGE_STORAGE:
|
|
||||||
_msg("ConfigMapper: Saving setting: %s section: %s value: %s" % [prop_info.name, section, p_object.get(prop_info.name)])
|
|
||||||
config.set_value(section, prop_info.name, p_object.get(prop_info.name))
|
|
||||||
|
|
||||||
var err: int = config.save(config_path)
|
|
||||||
_msg("ConfigMapper: Finished with code: ", err)
|
|
||||||
return err
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://dvokj0q23nb50
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
extends RefCounted
|
|
||||||
|
|
||||||
# Configuration is outside of limbo_console directory for compatibility with GIT submodules
|
|
||||||
const CONFIG_PATH := "res://addons/limbo_console.cfg"
|
|
||||||
|
|
||||||
@export var aliases := {
|
|
||||||
"exit": "quit",
|
|
||||||
"source": "exec",
|
|
||||||
"usage": "help",
|
|
||||||
}
|
|
||||||
@export var disable_in_release_build: bool = false
|
|
||||||
@export var print_to_stdout: bool = false
|
|
||||||
@export var pause_when_open: bool = true
|
|
||||||
|
|
||||||
@export var commands_disabled_in_release: Array = [
|
|
||||||
"eval" # enables arbitrary code execution and asset extraction in the running game.
|
|
||||||
]
|
|
||||||
|
|
||||||
@export_category("appearance")
|
|
||||||
@export var custom_theme: String = "res://addons/limbo_console_theme.tres"
|
|
||||||
@export var height_ratio: float = 0.5
|
|
||||||
@export var open_speed: float = 5.0 # For instant, set to a really high float like 99999.0
|
|
||||||
@export var opacity: float = 1.0
|
|
||||||
@export var sparse_mode: bool = false # Print empty line after each command execution.
|
|
||||||
|
|
||||||
@export_category("greet")
|
|
||||||
@export var greet_user: bool = true
|
|
||||||
@export var greeting_message: String = "{project_name}"
|
|
||||||
@export var greet_using_ascii_art: bool = true
|
|
||||||
|
|
||||||
@export_category("history")
|
|
||||||
@export var persist_history: bool = true
|
|
||||||
@export var history_lines: int = 1000
|
|
||||||
|
|
||||||
@export_category("autocomplete")
|
|
||||||
@export var autocomplete_use_history_with_matches: bool = true
|
|
||||||
|
|
||||||
@export_category("autoexec")
|
|
||||||
@export var autoexec_script: String = "user://autoexec.lcs"
|
|
||||||
@export var autoexec_auto_create: bool = true
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://c7a12a1pe5esr
|
|
||||||
@ -1,256 +0,0 @@
|
|||||||
extends Panel
|
|
||||||
|
|
||||||
const CommandHistory := preload("res://addons/limbo_console/command_history.gd")
|
|
||||||
|
|
||||||
# Visual Elements
|
|
||||||
var _last_highlighted_label: Label
|
|
||||||
var _history_labels: Array[Label]
|
|
||||||
var _scroll_bar: VScrollBar
|
|
||||||
var _scroll_bar_width: int = 12
|
|
||||||
|
|
||||||
# Indexing Results
|
|
||||||
var _command: String = "<placeholder>" # Needs default value so first search always processes
|
|
||||||
var _history: CommandHistory # Command history to search through
|
|
||||||
var _filter_results: PackedStringArray # Most recent results of performing a search for the _command in _history
|
|
||||||
|
|
||||||
var _display_count: int = 0 # Number of history items to display in search
|
|
||||||
var _offset: int = 0 # The offset _filter_results
|
|
||||||
var _sub_index: int = 0 # The highlight index
|
|
||||||
|
|
||||||
# Theme Cache
|
|
||||||
var _highlight_color: Color
|
|
||||||
|
|
||||||
|
|
||||||
# *** GODOT / VIRTUAL
|
|
||||||
|
|
||||||
|
|
||||||
func _init(p_history: CommandHistory) -> void:
|
|
||||||
_history = p_history
|
|
||||||
|
|
||||||
set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
||||||
size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
||||||
size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
||||||
|
|
||||||
# Create first label, and set placeholder text to determine the display size
|
|
||||||
# once this node is _ready(). There should always be one label at minimum
|
|
||||||
# anyways since this search is usless without a way to show results.
|
|
||||||
var new_item := Label.new()
|
|
||||||
new_item.size_flags_vertical = Control.SIZE_SHRINK_END
|
|
||||||
new_item.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
||||||
new_item.text = "<Placeholder>"
|
|
||||||
add_child(new_item)
|
|
||||||
_history_labels.append(new_item)
|
|
||||||
|
|
||||||
_scroll_bar = VScrollBar.new()
|
|
||||||
add_child(_scroll_bar)
|
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
|
||||||
# The sizing of the labels is dependant on visiblity.
|
|
||||||
visibility_changed.connect(_calculate_display_count)
|
|
||||||
_scroll_bar.scrolling.connect(_scroll_bar_scrolled)
|
|
||||||
|
|
||||||
_highlight_color = get_theme_color(&"history_highlight_color", &"ConsoleColors")
|
|
||||||
|
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
|
||||||
if not is_visible_in_tree():
|
|
||||||
return
|
|
||||||
|
|
||||||
# Scroll up/down on mouse wheel up/down
|
|
||||||
if event is InputEventMouseButton:
|
|
||||||
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
|
|
||||||
_increment_index()
|
|
||||||
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
|
|
||||||
_decrement_index()
|
|
||||||
|
|
||||||
# Remaining inputs are key press handles
|
|
||||||
if event is not InputEventKey:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Increment/Decrement index
|
|
||||||
if event.keycode == KEY_UP and event.is_pressed():
|
|
||||||
_increment_index()
|
|
||||||
get_viewport().set_input_as_handled()
|
|
||||||
elif event.keycode == KEY_DOWN and event.is_pressed():
|
|
||||||
_decrement_index()
|
|
||||||
get_viewport().set_input_as_handled()
|
|
||||||
|
|
||||||
|
|
||||||
# *** PUBLIC
|
|
||||||
|
|
||||||
|
|
||||||
## Set visibility of history search
|
|
||||||
func set_visibility(p_visible: bool) -> void:
|
|
||||||
if not visible and p_visible:
|
|
||||||
# It's possible the _history has updated while not visible
|
|
||||||
# make sure the filtered list is up-to-date
|
|
||||||
_search_and_filter()
|
|
||||||
visible = p_visible
|
|
||||||
|
|
||||||
|
|
||||||
## Move cursor downwards
|
|
||||||
func _decrement_index() -> void:
|
|
||||||
var current_index: int = _get_current_index()
|
|
||||||
if current_index - 1 < 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
if _sub_index == 0:
|
|
||||||
_offset -= 1
|
|
||||||
_update_scroll_list()
|
|
||||||
else:
|
|
||||||
_sub_index -= 1
|
|
||||||
_update_highlight()
|
|
||||||
|
|
||||||
|
|
||||||
## Move cursor upwards
|
|
||||||
func _increment_index() -> void:
|
|
||||||
var current_index: int = _get_current_index()
|
|
||||||
if current_index + 1 >= _filter_results.size():
|
|
||||||
return
|
|
||||||
|
|
||||||
if _sub_index >= _display_count - 1:
|
|
||||||
_offset += 1
|
|
||||||
_update_scroll_list()
|
|
||||||
else:
|
|
||||||
_sub_index += 1
|
|
||||||
_update_highlight()
|
|
||||||
|
|
||||||
|
|
||||||
## Get the current selected text
|
|
||||||
func get_current_text() -> String:
|
|
||||||
var current_text: String = _command
|
|
||||||
if _history_labels.size() != 0 and _filter_results.size() != 0:
|
|
||||||
current_text = _filter_results[_get_current_index()]
|
|
||||||
return current_text
|
|
||||||
|
|
||||||
|
|
||||||
## Search for the command in the history
|
|
||||||
func search(command: String) -> void:
|
|
||||||
# Don't process if we used the same command before
|
|
||||||
if command == _command:
|
|
||||||
return
|
|
||||||
_command = command
|
|
||||||
|
|
||||||
_search_and_filter()
|
|
||||||
|
|
||||||
|
|
||||||
# *** PRIVATE
|
|
||||||
|
|
||||||
|
|
||||||
## Update the text in the scroll list to match current offset and filtered results
|
|
||||||
func _update_scroll_list() -> void:
|
|
||||||
# Iterate through the number of displayed history items
|
|
||||||
for i in range(0, _display_count):
|
|
||||||
var filter_index: int = _offset + i
|
|
||||||
|
|
||||||
# Default empty
|
|
||||||
_history_labels[i].text = ""
|
|
||||||
|
|
||||||
# Set non empty if in range
|
|
||||||
var index_in_range: bool = filter_index < _filter_results.size()
|
|
||||||
if index_in_range:
|
|
||||||
_history_labels[i].text += _filter_results[filter_index]
|
|
||||||
|
|
||||||
_update_scroll_bar()
|
|
||||||
|
|
||||||
|
|
||||||
## Highlight the subindex
|
|
||||||
func _update_highlight() -> void:
|
|
||||||
if _sub_index < 0 or _history.size() == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
var style := StyleBoxFlat.new()
|
|
||||||
style.bg_color = _highlight_color
|
|
||||||
|
|
||||||
# Always clear out the highlight of the last label
|
|
||||||
if is_instance_valid(_last_highlighted_label):
|
|
||||||
_last_highlighted_label.remove_theme_stylebox_override("normal")
|
|
||||||
|
|
||||||
if _filter_results.size() <= 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
_history_labels[_sub_index].add_theme_stylebox_override("normal", style)
|
|
||||||
_last_highlighted_label = _history_labels[_sub_index]
|
|
||||||
|
|
||||||
|
|
||||||
## Get the current index of the selected item
|
|
||||||
func _get_current_index() -> int:
|
|
||||||
return _offset + _sub_index
|
|
||||||
|
|
||||||
|
|
||||||
## Reset offset and sub_indexes to scroll list back to bottom
|
|
||||||
func _reset_indexes() -> void:
|
|
||||||
_offset = 0
|
|
||||||
_sub_index = 0
|
|
||||||
|
|
||||||
|
|
||||||
## When the scrollbar has been scrolled (by mouse), scroll the list
|
|
||||||
func _scroll_bar_scrolled() -> void:
|
|
||||||
_offset = _scroll_bar.max_value - _display_count - _scroll_bar.value
|
|
||||||
_update_highlight()
|
|
||||||
_update_scroll_list()
|
|
||||||
|
|
||||||
|
|
||||||
func _calculate_display_count():
|
|
||||||
if not visible:
|
|
||||||
return
|
|
||||||
# The display count is finnicky to get right due to the label needing to be
|
|
||||||
# rendered so the fize can be determined. This gets the job done, it ain't
|
|
||||||
# pretty, but it works
|
|
||||||
var max_y: float = size.y
|
|
||||||
|
|
||||||
var label_size_y: float = (_history_labels[0] as Control).size.y
|
|
||||||
var label_size_x: float = size.x - _scroll_bar_width
|
|
||||||
|
|
||||||
var display_count: int = int(max_y) / int(label_size_y)
|
|
||||||
if _display_count != display_count and display_count != 0 and display_count > _display_count:
|
|
||||||
_display_count = (display_count as int)
|
|
||||||
|
|
||||||
# Since the labels are going from the bottom to the top, the label
|
|
||||||
# coordinates are offset from the bottom by label size.
|
|
||||||
# The first label already exists, so it's handlded by itself
|
|
||||||
_history_labels[0].position.y = size.y - label_size_y
|
|
||||||
_history_labels[0].set_size(Vector2(label_size_x, label_size_y))
|
|
||||||
# The remaining labels may or may not exist already, create them
|
|
||||||
for i in range(0, _display_count - _history_labels.size()):
|
|
||||||
var new_item := Label.new()
|
|
||||||
new_item.size_flags_vertical = Control.SIZE_SHRINK_END
|
|
||||||
new_item.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
||||||
|
|
||||||
# The +1 is due to the labels going upwards from the bottom, otherwise
|
|
||||||
# their position will be 1 row lower than they should be
|
|
||||||
var position_offset: int = _history_labels.size() + 1
|
|
||||||
new_item.position.y = size.y - (position_offset * label_size_y)
|
|
||||||
new_item.set_size(Vector2(label_size_x, label_size_y))
|
|
||||||
_history_labels.append(new_item)
|
|
||||||
add_child(new_item)
|
|
||||||
|
|
||||||
# Update the scroll bar to be positioned correctly
|
|
||||||
_scroll_bar.size.x = _scroll_bar_width
|
|
||||||
_scroll_bar.size.y = size.y
|
|
||||||
_scroll_bar.position.x = label_size_x
|
|
||||||
|
|
||||||
_reset_history_to_beginning()
|
|
||||||
|
|
||||||
|
|
||||||
func _update_scroll_bar() -> void:
|
|
||||||
if _display_count > 0:
|
|
||||||
var max_size: int = _filter_results.size()
|
|
||||||
_scroll_bar.max_value = max_size
|
|
||||||
_scroll_bar.page = _display_count
|
|
||||||
_scroll_bar.set_value_no_signal((max_size - _display_count) - _offset)
|
|
||||||
|
|
||||||
|
|
||||||
## Reset indexes to 0, scroll to the bottom of the history list, and update visuals
|
|
||||||
func _reset_history_to_beginning() -> void:
|
|
||||||
_reset_indexes()
|
|
||||||
_update_highlight()
|
|
||||||
_update_scroll_list()
|
|
||||||
|
|
||||||
|
|
||||||
## Search for the current command and filter the results
|
|
||||||
func _search_and_filter() -> void:
|
|
||||||
_filter_results = _history.fuzzy_match(_command)
|
|
||||||
|
|
||||||
_reset_history_to_beginning()
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://cpl5jb0mwxanh
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
|||||||
uid://dyxornv8vwibg
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
[plugin]
|
|
||||||
|
|
||||||
name="LimboConsole"
|
|
||||||
description="Yet another in-game console with a simple command interpreter."
|
|
||||||
author="Serhii Snitsaruk"
|
|
||||||
version="0.4.1"
|
|
||||||
script="plugin.gd"
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
@tool
|
|
||||||
extends EditorPlugin
|
|
||||||
|
|
||||||
const ConsoleOptions := preload("res://addons/limbo_console/console_options.gd")
|
|
||||||
const ConfigMapper := preload("res://addons/limbo_console/config_mapper.gd")
|
|
||||||
|
|
||||||
func _enter_tree() -> void:
|
|
||||||
add_autoload_singleton("LimboConsole", "res://addons/limbo_console/limbo_console.gd")
|
|
||||||
|
|
||||||
# Sync config file (create if not exists)
|
|
||||||
var console_options := ConsoleOptions.new()
|
|
||||||
var do_project_setting_save: bool = false
|
|
||||||
ConfigMapper.load_from_config(console_options)
|
|
||||||
ConfigMapper.save_to_config(console_options)
|
|
||||||
|
|
||||||
if not ProjectSettings.has_setting("input/limbo_console_toggle"):
|
|
||||||
print("LimboConsole: Adding \"limbo_console_toggle\" input action to project settings...")
|
|
||||||
|
|
||||||
var key_event := InputEventKey.new()
|
|
||||||
key_event.keycode = KEY_QUOTELEFT
|
|
||||||
|
|
||||||
ProjectSettings.set_setting("input/limbo_console_toggle", {
|
|
||||||
"deadzone": 0.5,
|
|
||||||
"events": [key_event],
|
|
||||||
})
|
|
||||||
do_project_setting_save = true
|
|
||||||
|
|
||||||
if not ProjectSettings.has_setting("input/limbo_auto_complete_reverse"):
|
|
||||||
print("LimboConsole: Adding \"limbo_auto_complete_reverse\" input action to project settings...")
|
|
||||||
var key_event = InputEventKey.new()
|
|
||||||
key_event.keycode = KEY_TAB
|
|
||||||
key_event.shift_pressed = true
|
|
||||||
|
|
||||||
ProjectSettings.set_setting("input/limbo_auto_complete_reverse", {
|
|
||||||
"deadzone": 0.5,
|
|
||||||
"events": [key_event],
|
|
||||||
})
|
|
||||||
do_project_setting_save = true
|
|
||||||
|
|
||||||
if not ProjectSettings.has_setting("input/limbo_console_search_history"):
|
|
||||||
print("LimboConsole: Adding \"limbo_console_search_history\" input action to project settings...")
|
|
||||||
var key_event = InputEventKey.new()
|
|
||||||
key_event.keycode = KEY_R
|
|
||||||
key_event.ctrl_pressed = true
|
|
||||||
|
|
||||||
ProjectSettings.set_setting("input/limbo_console_search_history", {
|
|
||||||
"deadzone": 0.5,
|
|
||||||
"events": [key_event],
|
|
||||||
})
|
|
||||||
do_project_setting_save = true
|
|
||||||
|
|
||||||
if do_project_setting_save:
|
|
||||||
ProjectSettings.save()
|
|
||||||
|
|
||||||
|
|
||||||
func _exit_tree() -> void:
|
|
||||||
remove_autoload_singleton("LimboConsole")
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://b4t0bfyjdn8i0
|
|
||||||
@ -1,176 +0,0 @@
|
|||||||
[gd_resource type="Theme" format=3 uid="uid://dq4nntds66bix"]
|
|
||||||
|
|
||||||
[ext_resource type="FontFile" uid="uid://dbbw833hg2v7o" path="res://addons/limbo_console/res/fonts/monaspace_argon_bold.otf" id="1_cry2i"]
|
|
||||||
[ext_resource type="FontFile" uid="uid://dmeyp84repfbw" path="res://addons/limbo_console/res/fonts/monaspace_argon_bold_italic.otf" id="2_h3f73"]
|
|
||||||
[ext_resource type="FontFile" uid="uid://dhm45nttm5i3s" path="res://addons/limbo_console/res/fonts/monaspace_argon_italic.otf" id="3_2qq11"]
|
|
||||||
[ext_resource type="FontFile" uid="uid://ds7dvyquauqub" path="res://addons/limbo_console/res/fonts/monaspace_argon_medium.otf" id="4_06w3f"]
|
|
||||||
[ext_resource type="FontFile" uid="uid://d4js20k8kslqt" path="res://addons/limbo_console/res/fonts/monaspace_argon_regular.otf" id="5_p4ppy"]
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_yia2g"]
|
|
||||||
content_margin_left = 6.0
|
|
||||||
content_margin_top = 4.0
|
|
||||||
content_margin_right = 6.0
|
|
||||||
content_margin_bottom = 4.0
|
|
||||||
bg_color = Color(0.147, 0.168, 0.203, 1)
|
|
||||||
corner_radius_top_left = 3
|
|
||||||
corner_radius_top_right = 3
|
|
||||||
corner_radius_bottom_right = 3
|
|
||||||
corner_radius_bottom_left = 3
|
|
||||||
corner_detail = 3
|
|
||||||
anti_aliasing = false
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_bt363"]
|
|
||||||
content_margin_left = 2.0
|
|
||||||
content_margin_top = 4.0
|
|
||||||
content_margin_right = 2.0
|
|
||||||
content_margin_bottom = 4.0
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_txn37"]
|
|
||||||
content_margin_left = 2.0
|
|
||||||
content_margin_top = 4.0
|
|
||||||
content_margin_right = 2.0
|
|
||||||
content_margin_bottom = 4.0
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ypc4n"]
|
|
||||||
content_margin_left = 2.0
|
|
||||||
content_margin_top = 4.0
|
|
||||||
content_margin_right = 2.0
|
|
||||||
content_margin_bottom = 4.0
|
|
||||||
bg_color = Color(0, 0, 0, 0)
|
|
||||||
border_width_top = 1
|
|
||||||
border_color = Color(0.211765, 0.239216, 0.290196, 1)
|
|
||||||
|
|
||||||
[sub_resource type="Image" id="Image_jqg6r"]
|
|
||||||
data = {
|
|
||||||
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 3, 255, 255, 255, 41, 255, 255, 255, 67, 255, 255, 255, 67, 255, 255, 255, 40, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 41, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 40, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 67, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 67, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 67, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 67, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 40, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 40, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 3, 255, 255, 255, 40, 255, 255, 255, 67, 255, 255, 255, 67, 255, 255, 255, 40, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
|
||||||
"format": "RGBA8",
|
|
||||||
"height": 12,
|
|
||||||
"mipmaps": false,
|
|
||||||
"width": 12
|
|
||||||
}
|
|
||||||
|
|
||||||
[sub_resource type="ImageTexture" id="ImageTexture_utg8u"]
|
|
||||||
image = SubResource("Image_jqg6r")
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_xismo"]
|
|
||||||
content_margin_left = 7.0
|
|
||||||
content_margin_top = 7.0
|
|
||||||
content_margin_right = 7.0
|
|
||||||
content_margin_bottom = 7.0
|
|
||||||
texture = SubResource("ImageTexture_utg8u")
|
|
||||||
texture_margin_left = 6.0
|
|
||||||
texture_margin_top = 6.0
|
|
||||||
texture_margin_right = 6.0
|
|
||||||
texture_margin_bottom = 6.0
|
|
||||||
|
|
||||||
[sub_resource type="Image" id="Image_rt1dl"]
|
|
||||||
data = {
|
|
||||||
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 6, 248, 248, 248, 102, 249, 249, 249, 168, 249, 249, 249, 168, 248, 248, 248, 101, 213, 213, 213, 6, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 248, 248, 248, 102, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 248, 248, 248, 101, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 249, 249, 249, 168, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 168, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 249, 249, 249, 168, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 248, 248, 248, 168, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 248, 248, 248, 101, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 250, 250, 250, 99, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 213, 213, 213, 6, 248, 248, 248, 101, 249, 249, 249, 168, 248, 248, 248, 168, 250, 250, 250, 99, 213, 213, 213, 6, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
|
||||||
"format": "RGBA8",
|
|
||||||
"height": 12,
|
|
||||||
"mipmaps": false,
|
|
||||||
"width": 12
|
|
||||||
}
|
|
||||||
|
|
||||||
[sub_resource type="ImageTexture" id="ImageTexture_foc76"]
|
|
||||||
image = SubResource("Image_rt1dl")
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_oajxf"]
|
|
||||||
content_margin_left = 6.0
|
|
||||||
content_margin_top = 6.0
|
|
||||||
content_margin_right = 6.0
|
|
||||||
content_margin_bottom = 6.0
|
|
||||||
texture = SubResource("ImageTexture_foc76")
|
|
||||||
texture_margin_left = 5.0
|
|
||||||
texture_margin_top = 5.0
|
|
||||||
texture_margin_right = 5.0
|
|
||||||
texture_margin_bottom = 5.0
|
|
||||||
|
|
||||||
[sub_resource type="Image" id="Image_2c5qw"]
|
|
||||||
data = {
|
|
||||||
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 213, 213, 213, 6, 180, 180, 180, 102, 181, 181, 181, 168, 181, 181, 181, 168, 179, 179, 179, 101, 170, 170, 170, 6, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 180, 180, 180, 102, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 179, 179, 179, 101, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 181, 181, 181, 168, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 181, 181, 181, 168, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 181, 181, 181, 168, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 179, 179, 179, 168, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 179, 179, 179, 101, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 181, 181, 181, 99, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 170, 170, 170, 6, 179, 179, 179, 101, 181, 181, 181, 168, 179, 179, 179, 168, 181, 181, 181, 99, 170, 170, 170, 6, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
|
||||||
"format": "RGBA8",
|
|
||||||
"height": 12,
|
|
||||||
"mipmaps": false,
|
|
||||||
"width": 12
|
|
||||||
}
|
|
||||||
|
|
||||||
[sub_resource type="ImageTexture" id="ImageTexture_alomt"]
|
|
||||||
image = SubResource("Image_2c5qw")
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_ev3il"]
|
|
||||||
content_margin_left = 7.0
|
|
||||||
content_margin_top = 7.0
|
|
||||||
content_margin_right = 7.0
|
|
||||||
content_margin_bottom = 7.0
|
|
||||||
texture = SubResource("ImageTexture_alomt")
|
|
||||||
texture_margin_left = 6.0
|
|
||||||
texture_margin_top = 6.0
|
|
||||||
texture_margin_right = 6.0
|
|
||||||
texture_margin_bottom = 6.0
|
|
||||||
|
|
||||||
[sub_resource type="Image" id="Image_c2fg1"]
|
|
||||||
data = {
|
|
||||||
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 7, 255, 255, 255, 19, 255, 255, 255, 19, 255, 255, 255, 7, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 19, 255, 255, 255, 21, 255, 255, 255, 21, 255, 255, 255, 19, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 19, 255, 255, 255, 21, 255, 255, 255, 21, 255, 255, 255, 19, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 7, 255, 255, 255, 19, 255, 255, 255, 19, 255, 255, 255, 6, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
|
||||||
"format": "RGBA8",
|
|
||||||
"height": 12,
|
|
||||||
"mipmaps": false,
|
|
||||||
"width": 12
|
|
||||||
}
|
|
||||||
|
|
||||||
[sub_resource type="ImageTexture" id="ImageTexture_lun2q"]
|
|
||||||
image = SubResource("Image_c2fg1")
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_lk88d"]
|
|
||||||
content_margin_left = 6.0
|
|
||||||
content_margin_top = 0.0
|
|
||||||
content_margin_right = 6.0
|
|
||||||
content_margin_bottom = 0.0
|
|
||||||
texture = SubResource("ImageTexture_lun2q")
|
|
||||||
texture_margin_left = 5.0
|
|
||||||
texture_margin_top = 5.0
|
|
||||||
texture_margin_right = 5.0
|
|
||||||
texture_margin_bottom = 5.0
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_4ui4a"]
|
|
||||||
content_margin_left = 6.0
|
|
||||||
content_margin_top = 6.0
|
|
||||||
content_margin_right = 6.0
|
|
||||||
content_margin_bottom = 6.0
|
|
||||||
texture = SubResource("ImageTexture_lun2q")
|
|
||||||
texture_margin_left = 5.0
|
|
||||||
texture_margin_top = 5.0
|
|
||||||
texture_margin_right = 5.0
|
|
||||||
texture_margin_bottom = 5.0
|
|
||||||
|
|
||||||
[resource]
|
|
||||||
default_font = ExtResource("5_p4ppy")
|
|
||||||
default_font_size = 18
|
|
||||||
ConsoleColors/colors/entry_command_found_color = Color(0.729412, 0.901961, 0.494118, 1)
|
|
||||||
ConsoleColors/colors/entry_command_not_found_color = Color(1, 0.2, 0.2, 1)
|
|
||||||
ConsoleColors/colors/entry_hint_color = Color(0.439216, 0.478431, 0.54902, 1)
|
|
||||||
ConsoleColors/colors/entry_subcommand_color = Color(0.584314, 0.901961, 0.796078, 1)
|
|
||||||
ConsoleColors/colors/entry_text_color = Color(0.796078, 0.8, 0.776471, 1)
|
|
||||||
ConsoleColors/colors/history_highlight_color = Color(0.317647, 0.364706, 0.439216, 1)
|
|
||||||
ConsoleColors/colors/output_command_color = Color(0.729412, 0.901961, 0.494118, 1)
|
|
||||||
ConsoleColors/colors/output_command_mention_color = Color(0.584314, 0.901961, 0.796078, 1)
|
|
||||||
ConsoleColors/colors/output_debug_color = Color(0.439216, 0.478431, 0.54902, 1)
|
|
||||||
ConsoleColors/colors/output_error_color = Color(1, 0.2, 0.2, 1)
|
|
||||||
ConsoleColors/colors/output_text_color = Color(0.796078, 0.8, 0.776471, 1)
|
|
||||||
ConsoleColors/colors/output_warning_color = Color(1, 0.654902, 0.34902, 1)
|
|
||||||
Panel/styles/panel = SubResource("StyleBoxFlat_yia2g")
|
|
||||||
PanelContainer/styles/panel = SubResource("StyleBoxFlat_yia2g")
|
|
||||||
RichTextLabel/fonts/bold_font = ExtResource("1_cry2i")
|
|
||||||
RichTextLabel/fonts/bold_italics_font = ExtResource("2_h3f73")
|
|
||||||
RichTextLabel/fonts/italics_font = ExtResource("3_2qq11")
|
|
||||||
RichTextLabel/fonts/mono_font = ExtResource("4_06w3f")
|
|
||||||
RichTextLabel/fonts/normal_font = ExtResource("5_p4ppy")
|
|
||||||
RichTextLabel/styles/focus = SubResource("StyleBoxEmpty_bt363")
|
|
||||||
RichTextLabel/styles/normal = SubResource("StyleBoxEmpty_txn37")
|
|
||||||
TextEdit/styles/focus = SubResource("StyleBoxFlat_ypc4n")
|
|
||||||
TextEdit/styles/normal = SubResource("StyleBoxFlat_ypc4n")
|
|
||||||
VScrollBar/styles/grabber = SubResource("StyleBoxTexture_xismo")
|
|
||||||
VScrollBar/styles/grabber_highlight = SubResource("StyleBoxTexture_oajxf")
|
|
||||||
VScrollBar/styles/grabber_pressed = SubResource("StyleBoxTexture_ev3il")
|
|
||||||
VScrollBar/styles/scroll = SubResource("StyleBoxTexture_lk88d")
|
|
||||||
VScrollBar/styles/scroll_focus = SubResource("StyleBoxTexture_4ui4a")
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
Copyright (c) 2023, GitHub https://github.com/githubnext/monaspace
|
|
||||||
with Reserved Font Name "Monaspace", including subfamilies: "Argon", "Neon", "Xenon", "Radon", and "Krypton"
|
|
||||||
|
|
||||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
||||||
This license is copied below, and is also available with a FAQ at:
|
|
||||||
http://scripts.sil.org/OFL
|
|
||||||
|
|
||||||
-----------------------------------------------------------
|
|
||||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
PREAMBLE
|
|
||||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
||||||
development of collaborative font projects, to support the font creation
|
|
||||||
efforts of academic and linguistic communities, and to provide a free and
|
|
||||||
open framework in which fonts may be shared and improved in partnership
|
|
||||||
with others.
|
|
||||||
|
|
||||||
The OFL allows the licensed fonts to be used, studied, modified and
|
|
||||||
redistributed freely as long as they are not sold by themselves. The
|
|
||||||
fonts, including any derivative works, can be bundled, embedded,
|
|
||||||
redistributed and/or sold with any software provided that any reserved
|
|
||||||
names are not used by derivative works. The fonts and derivatives,
|
|
||||||
however, cannot be released under any other type of license. The
|
|
||||||
requirement for fonts to remain under this license does not apply
|
|
||||||
to any document created using the fonts or their derivatives.
|
|
||||||
|
|
||||||
DEFINITIONS
|
|
||||||
"Font Software" refers to the set of files released by the Copyright
|
|
||||||
Holder(s) under this license and clearly marked as such. This may
|
|
||||||
include source files, build scripts and documentation.
|
|
||||||
|
|
||||||
"Reserved Font Name" refers to any names specified as such after the
|
|
||||||
copyright statement(s).
|
|
||||||
|
|
||||||
"Original Version" refers to the collection of Font Software components as
|
|
||||||
distributed by the Copyright Holder(s).
|
|
||||||
|
|
||||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
||||||
or substituting — in part or in whole — any of the components of the
|
|
||||||
Original Version, by changing formats or by porting the Font Software to a
|
|
||||||
new environment.
|
|
||||||
|
|
||||||
"Author" refers to any designer, engineer, programmer, technical
|
|
||||||
writer or other person who contributed to the Font Software.
|
|
||||||
|
|
||||||
PERMISSION & CONDITIONS
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
||||||
redistribute, and sell modified and unmodified copies of the Font
|
|
||||||
Software, subject to the following conditions:
|
|
||||||
|
|
||||||
1) Neither the Font Software nor any of its individual components,
|
|
||||||
in Original or Modified Versions, may be sold by itself.
|
|
||||||
|
|
||||||
2) Original or Modified Versions of the Font Software may be bundled,
|
|
||||||
redistributed and/or sold with any software, provided that each copy
|
|
||||||
contains the above copyright notice and this license. These can be
|
|
||||||
included either as stand-alone text files, human-readable headers or
|
|
||||||
in the appropriate machine-readable metadata fields within text or
|
|
||||||
binary files as long as those fields can be easily viewed by the user.
|
|
||||||
|
|
||||||
3) No Modified Version of the Font Software may use the Reserved Font
|
|
||||||
Name(s) unless explicit written permission is granted by the corresponding
|
|
||||||
Copyright Holder. This restriction only applies to the primary font name as
|
|
||||||
presented to the users.
|
|
||||||
|
|
||||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
||||||
Software shall not be used to promote, endorse or advertise any
|
|
||||||
Modified Version, except to acknowledge the contribution(s) of the
|
|
||||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
||||||
permission.
|
|
||||||
|
|
||||||
5) The Font Software, modified or unmodified, in part or in whole,
|
|
||||||
must be distributed entirely under this license, and must not be
|
|
||||||
distributed under any other license. The requirement for fonts to
|
|
||||||
remain under this license does not apply to any document created
|
|
||||||
using the Font Software.
|
|
||||||
|
|
||||||
TERMINATION
|
|
||||||
This license becomes null and void if any of the above conditions are
|
|
||||||
not met.
|
|
||||||
|
|
||||||
DISCLAIMER
|
|
||||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
||||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
||||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
||||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
||||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
||||||
Binary file not shown.
@ -1,36 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="font_data_dynamic"
|
|
||||||
type="FontFile"
|
|
||||||
uid="uid://dbbw833hg2v7o"
|
|
||||||
path="res://.godot/imported/monaspace_argon_bold.otf-5d916059e7810f56d30161557f70d71b.fontdata"
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/limbo_console/res/fonts/monaspace_argon_bold.otf"
|
|
||||||
dest_files=["res://.godot/imported/monaspace_argon_bold.otf-5d916059e7810f56d30161557f70d71b.fontdata"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
Rendering=null
|
|
||||||
antialiasing=1
|
|
||||||
generate_mipmaps=false
|
|
||||||
disable_embedded_bitmaps=true
|
|
||||||
multichannel_signed_distance_field=false
|
|
||||||
msdf_pixel_range=8
|
|
||||||
msdf_size=48
|
|
||||||
allow_system_fallback=true
|
|
||||||
force_autohinter=false
|
|
||||||
modulate_color_glyphs=false
|
|
||||||
hinting=1
|
|
||||||
subpixel_positioning=1
|
|
||||||
keep_rounding_remainders=true
|
|
||||||
oversampling=0.0
|
|
||||||
Fallbacks=null
|
|
||||||
fallbacks=[]
|
|
||||||
Compress=null
|
|
||||||
compress=true
|
|
||||||
preload=[]
|
|
||||||
language_support={}
|
|
||||||
script_support={}
|
|
||||||
opentype_features={}
|
|
||||||
Binary file not shown.
@ -1,36 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="font_data_dynamic"
|
|
||||||
type="FontFile"
|
|
||||||
uid="uid://dmeyp84repfbw"
|
|
||||||
path="res://.godot/imported/monaspace_argon_bold_italic.otf-cd05eebec36875096d59dc2e6dfb87db.fontdata"
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/limbo_console/res/fonts/monaspace_argon_bold_italic.otf"
|
|
||||||
dest_files=["res://.godot/imported/monaspace_argon_bold_italic.otf-cd05eebec36875096d59dc2e6dfb87db.fontdata"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
Rendering=null
|
|
||||||
antialiasing=1
|
|
||||||
generate_mipmaps=false
|
|
||||||
disable_embedded_bitmaps=true
|
|
||||||
multichannel_signed_distance_field=false
|
|
||||||
msdf_pixel_range=8
|
|
||||||
msdf_size=48
|
|
||||||
allow_system_fallback=true
|
|
||||||
force_autohinter=false
|
|
||||||
modulate_color_glyphs=false
|
|
||||||
hinting=1
|
|
||||||
subpixel_positioning=1
|
|
||||||
keep_rounding_remainders=true
|
|
||||||
oversampling=0.0
|
|
||||||
Fallbacks=null
|
|
||||||
fallbacks=[]
|
|
||||||
Compress=null
|
|
||||||
compress=true
|
|
||||||
preload=[]
|
|
||||||
language_support={}
|
|
||||||
script_support={}
|
|
||||||
opentype_features={}
|
|
||||||
Binary file not shown.
@ -1,36 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="font_data_dynamic"
|
|
||||||
type="FontFile"
|
|
||||||
uid="uid://dhm45nttm5i3s"
|
|
||||||
path="res://.godot/imported/monaspace_argon_italic.otf-69d64783adde526699a99a191cd14ed6.fontdata"
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/limbo_console/res/fonts/monaspace_argon_italic.otf"
|
|
||||||
dest_files=["res://.godot/imported/monaspace_argon_italic.otf-69d64783adde526699a99a191cd14ed6.fontdata"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
Rendering=null
|
|
||||||
antialiasing=1
|
|
||||||
generate_mipmaps=false
|
|
||||||
disable_embedded_bitmaps=true
|
|
||||||
multichannel_signed_distance_field=false
|
|
||||||
msdf_pixel_range=8
|
|
||||||
msdf_size=48
|
|
||||||
allow_system_fallback=true
|
|
||||||
force_autohinter=false
|
|
||||||
modulate_color_glyphs=false
|
|
||||||
hinting=1
|
|
||||||
subpixel_positioning=1
|
|
||||||
keep_rounding_remainders=true
|
|
||||||
oversampling=0.0
|
|
||||||
Fallbacks=null
|
|
||||||
fallbacks=[]
|
|
||||||
Compress=null
|
|
||||||
compress=true
|
|
||||||
preload=[]
|
|
||||||
language_support={}
|
|
||||||
script_support={}
|
|
||||||
opentype_features={}
|
|
||||||
Binary file not shown.
@ -1,36 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="font_data_dynamic"
|
|
||||||
type="FontFile"
|
|
||||||
uid="uid://ds7dvyquauqub"
|
|
||||||
path="res://.godot/imported/monaspace_argon_medium.otf-2c090420a59a2bfcd1b6b28a7c3469ad.fontdata"
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/limbo_console/res/fonts/monaspace_argon_medium.otf"
|
|
||||||
dest_files=["res://.godot/imported/monaspace_argon_medium.otf-2c090420a59a2bfcd1b6b28a7c3469ad.fontdata"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
Rendering=null
|
|
||||||
antialiasing=1
|
|
||||||
generate_mipmaps=false
|
|
||||||
disable_embedded_bitmaps=true
|
|
||||||
multichannel_signed_distance_field=false
|
|
||||||
msdf_pixel_range=8
|
|
||||||
msdf_size=48
|
|
||||||
allow_system_fallback=true
|
|
||||||
force_autohinter=false
|
|
||||||
modulate_color_glyphs=false
|
|
||||||
hinting=1
|
|
||||||
subpixel_positioning=1
|
|
||||||
keep_rounding_remainders=true
|
|
||||||
oversampling=0.0
|
|
||||||
Fallbacks=null
|
|
||||||
fallbacks=[]
|
|
||||||
Compress=null
|
|
||||||
compress=true
|
|
||||||
preload=[]
|
|
||||||
language_support={}
|
|
||||||
script_support={}
|
|
||||||
opentype_features={}
|
|
||||||
Binary file not shown.
@ -1,36 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="font_data_dynamic"
|
|
||||||
type="FontFile"
|
|
||||||
uid="uid://d4js20k8kslqt"
|
|
||||||
path="res://.godot/imported/monaspace_argon_regular.otf-7622e0becc7f9143f21c9951dd015f30.fontdata"
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/limbo_console/res/fonts/monaspace_argon_regular.otf"
|
|
||||||
dest_files=["res://.godot/imported/monaspace_argon_regular.otf-7622e0becc7f9143f21c9951dd015f30.fontdata"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
Rendering=null
|
|
||||||
antialiasing=1
|
|
||||||
generate_mipmaps=false
|
|
||||||
disable_embedded_bitmaps=true
|
|
||||||
multichannel_signed_distance_field=false
|
|
||||||
msdf_pixel_range=8
|
|
||||||
msdf_size=48
|
|
||||||
allow_system_fallback=true
|
|
||||||
force_autohinter=false
|
|
||||||
modulate_color_glyphs=false
|
|
||||||
hinting=1
|
|
||||||
subpixel_positioning=1
|
|
||||||
keep_rounding_remainders=true
|
|
||||||
oversampling=0.0
|
|
||||||
Fallbacks=null
|
|
||||||
fallbacks=[]
|
|
||||||
Compress=null
|
|
||||||
compress=true
|
|
||||||
preload=[]
|
|
||||||
language_support={}
|
|
||||||
script_support={}
|
|
||||||
opentype_features={}
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
extends Object
|
|
||||||
## Utility functions
|
|
||||||
|
|
||||||
|
|
||||||
static func bbcode_escape(p_text: String) -> String:
|
|
||||||
return p_text \
|
|
||||||
.replace("[", "~LB~") \
|
|
||||||
.replace("]", "~RB~") \
|
|
||||||
.replace("~LB~", "[lb]") \
|
|
||||||
.replace("~RB~", "[rb]")
|
|
||||||
|
|
||||||
|
|
||||||
static func bbcode_strip(p_text: String) -> String:
|
|
||||||
var stripped := ""
|
|
||||||
var in_brackets: bool = false
|
|
||||||
for c: String in p_text:
|
|
||||||
if c == '[':
|
|
||||||
in_brackets = true
|
|
||||||
elif c == ']':
|
|
||||||
in_brackets = false
|
|
||||||
elif not in_brackets:
|
|
||||||
stripped += c
|
|
||||||
return stripped
|
|
||||||
|
|
||||||
|
|
||||||
static func get_method_info(p_callable: Callable) -> Dictionary:
|
|
||||||
var method_info: Dictionary
|
|
||||||
var method_list: Array[Dictionary]
|
|
||||||
if p_callable.get_object() is GDScript:
|
|
||||||
method_list = p_callable.get_object().get_script_method_list()
|
|
||||||
else:
|
|
||||||
method_list = p_callable.get_object().get_method_list()
|
|
||||||
for m in method_list:
|
|
||||||
if m.name == p_callable.get_method():
|
|
||||||
method_info = m
|
|
||||||
break
|
|
||||||
if !method_info and p_callable.is_custom():
|
|
||||||
var args: Array
|
|
||||||
var default_args: Array
|
|
||||||
for i in p_callable.get_argument_count():
|
|
||||||
var argument: Dictionary
|
|
||||||
argument["name"] = "arg%d" % i
|
|
||||||
argument["type"] = TYPE_NIL
|
|
||||||
args.push_back(argument)
|
|
||||||
method_info["name"] = "<anonymous lambda>"
|
|
||||||
method_info["args"] = args
|
|
||||||
method_info["default_args"] = default_args
|
|
||||||
return method_info
|
|
||||||
|
|
||||||
|
|
||||||
## Finds the most similar string in an array.
|
|
||||||
static func fuzzy_match_string(p_string: String, p_max_edit_distance: int, p_array) -> String:
|
|
||||||
if typeof(p_array) < TYPE_ARRAY:
|
|
||||||
push_error("LimboConsole: Internal error: p_array is not an array")
|
|
||||||
return ""
|
|
||||||
if p_array.size() == 0:
|
|
||||||
return ""
|
|
||||||
var best_distance: int = 9223372036854775807
|
|
||||||
var best_match: String = ""
|
|
||||||
for i in p_array.size():
|
|
||||||
var elem := str(p_array[i])
|
|
||||||
var dist: float = _calculate_osa_distance(p_string, elem)
|
|
||||||
if dist < best_distance:
|
|
||||||
best_distance = dist
|
|
||||||
best_match = elem
|
|
||||||
return best_match if best_distance <= p_max_edit_distance else ""
|
|
||||||
|
|
||||||
|
|
||||||
## Calculates optimal string alignment distance [br]
|
|
||||||
## See: https://en.wikipedia.org/wiki/Levenshtein_distance
|
|
||||||
static func _calculate_osa_distance(s1: String, s2: String) -> int:
|
|
||||||
var s1_len: int = s1.length()
|
|
||||||
var s2_len: int = s2.length()
|
|
||||||
|
|
||||||
# Iterative approach with 3 matrix rows.
|
|
||||||
# Most of the work is done on row1 and row2 - row0 is only needed to calculate transposition cost.
|
|
||||||
var row0: PackedInt32Array # previous-previous
|
|
||||||
var row1: PackedInt32Array # previous
|
|
||||||
var row2: PackedInt32Array # current aka the one we need to calculate
|
|
||||||
row0.resize(s2_len + 1)
|
|
||||||
row1.resize(s2_len + 1)
|
|
||||||
row2.resize(s2_len + 1)
|
|
||||||
|
|
||||||
# edit distance is the number of characters to insert to get from empty string to s2
|
|
||||||
for i in range(s2_len + 1):
|
|
||||||
row1[i] = i
|
|
||||||
|
|
||||||
for i in range(s1_len):
|
|
||||||
# edit distance is the number of characters to delete from s1 to match empty s2
|
|
||||||
row2[0] = i + 1
|
|
||||||
|
|
||||||
for j in range(s2_len):
|
|
||||||
var deletion_cost: int = row1[j + 1] + 1
|
|
||||||
var insertion_cost: int = row2[j] + 1
|
|
||||||
var substitution_cost: int = row1[j] if s1[i] == s2[j] else row1[j] + 1
|
|
||||||
|
|
||||||
row2[j + 1] = min(deletion_cost, insertion_cost, substitution_cost)
|
|
||||||
|
|
||||||
if i > 1 and j > 1 and s1[i - 1] == s2[j] and s1[i - 1] == s2[j]:
|
|
||||||
var transposition_cost: int = row0[j - 1] + 1
|
|
||||||
row2[j + 1] = mini(transposition_cost, row2[j + 1])
|
|
||||||
|
|
||||||
# Swap rows.
|
|
||||||
var tmp: PackedInt32Array = row0
|
|
||||||
row0 = row1
|
|
||||||
row1 = row2
|
|
||||||
row2 = tmp
|
|
||||||
return row1[s2_len]
|
|
||||||
|
|
||||||
|
|
||||||
## Returns true, if a string is constructed of one or more space-separated valid
|
|
||||||
## command identifiers ("command" or "command sub1 sub2").
|
|
||||||
## A valid command identifier may contain only letters, digits, and underscores (_),
|
|
||||||
## and the first character may not be a digit.
|
|
||||||
static func is_valid_command_sequence(p_string: String) -> bool:
|
|
||||||
for part in p_string.split(' '):
|
|
||||||
if not part.is_valid_ascii_identifier():
|
|
||||||
return false
|
|
||||||
return true
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://cw6s1es6yjip5
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
[gd_scene format=3 uid="uid://cpfunq8fyixco"]
|
[gd_scene format=3 uid="uid://cpfunq8fyixco"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://b5b7ohheyhxr0" path="res://addons/sprint/sprint.gd" id="1_thwo5"]
|
[ext_resource type="Script" uid="uid://b5b7ohheyhxr0" path="res://addons/logger/logger.gd" id="1_thwo5"]
|
||||||
|
|
||||||
[node name="Logger" type="CanvasLayer" unique_id=665187677]
|
[node name="Logger" type="CanvasLayer" unique_id=665187677]
|
||||||
process_mode = 3
|
process_mode = 3
|
||||||
@ -3,7 +3,7 @@ extends EditorPlugin
|
|||||||
|
|
||||||
|
|
||||||
func _enable_plugin() -> void:
|
func _enable_plugin() -> void:
|
||||||
add_autoload_singleton("SPrint", "res://addons/sprint/sprint.tscn")
|
add_autoload_singleton("SPrint", "res://addons/logger/logger.tscn")
|
||||||
|
|
||||||
|
|
||||||
func _disable_plugin() -> void:
|
func _disable_plugin() -> void:
|
||||||
@ -1,10 +0,0 @@
|
|||||||
[gd_resource type="Texture2D" script_class="ControllerIconTexture" format=3 uid="uid://bi8jxa3gcbfdn"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://ch00l1e1rteyw" path="res://addons/controller_icons/objects/ControllerIconTexture.gd" id="1_lwov1"]
|
|
||||||
|
|
||||||
[resource]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = ""
|
|
||||||
script = ExtResource("1_lwov1")
|
|
||||||
path = "ui_accept"
|
|
||||||
metadata/_custom_type_script = "uid://ch00l1e1rteyw"
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
[gd_resource type="Texture2D" script_class="ControllerIconTexture" format=3 uid="uid://cjtlrpqb75elt"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://ch00l1e1rteyw" path="res://addons/controller_icons/objects/ControllerIconTexture.gd" id="1_11853"]
|
|
||||||
|
|
||||||
[resource]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = ""
|
|
||||||
script = ExtResource("1_11853")
|
|
||||||
path = "ui_cancel"
|
|
||||||
metadata/_custom_type_script = "uid://ch00l1e1rteyw"
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
shader_type spatial;
|
|
||||||
#include "uid://cy7b01or0ckgk" // Rhythm Helper
|
|
||||||
|
|
||||||
|
|
||||||
void vertex() {
|
|
||||||
// Called for every vertex the material is visible on.
|
|
||||||
}
|
|
||||||
|
|
||||||
void fragment() {
|
|
||||||
float phase = get_bar_phase();
|
|
||||||
ALPHA = (1.0 - phase) * (UV.y > phase ? 1.0 : 0.0) * (UV.x < 1.0 - get_song_time_ratio() ? 1.0 : 0.0);
|
|
||||||
ALBEDO.gb = vec2(distance(phase + 0.75, 0.75));
|
|
||||||
}
|
|
||||||
|
|
||||||
//void light() {
|
|
||||||
// // Called for every pixel for every light affecting the material.
|
|
||||||
// // Uncomment to replace the default light processing function with this one.
|
|
||||||
//}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://dlqjg6dby6pdy
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
shader_type spatial;
|
|
||||||
#include "uid://cy7b01or0ckgk" // Rhythm Helper
|
|
||||||
|
|
||||||
|
|
||||||
void vertex() {
|
|
||||||
// Called for every vertex the material is visible on.
|
|
||||||
}
|
|
||||||
|
|
||||||
void fragment() {
|
|
||||||
float phase = get_beat_phase();
|
|
||||||
ALPHA = (1.0 - phase) * (UV.y > phase ? 1.0 : 0.0) * (-UV.x < -get_song_time_ratio() ? 1.0 : 0.0);
|
|
||||||
ALBEDO.gb = vec2(distance(phase + 0.5, 0.5));
|
|
||||||
}
|
|
||||||
|
|
||||||
//void light() {
|
|
||||||
// // Called for every pixel for every light affecting the material.
|
|
||||||
// // Uncomment to replace the default light processing function with this one.
|
|
||||||
//}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://c7n5s8eodlca6
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
shader_type spatial;
|
|
||||||
render_mode unshaded, shadows_disabled, cull_disabled;
|
|
||||||
#include "uid://cy7b01or0ckgk" // Rhythm Helper
|
|
||||||
|
|
||||||
uniform vec3 color: source_color = vec3(1.0, 1.0, 1.0);
|
|
||||||
uniform float alpha_multiplier: hint_range(0.0, 1.0, 0.001) = 0.25;
|
|
||||||
uniform sampler2D albedo;
|
|
||||||
uniform float flash_exponent = 3.0;
|
|
||||||
|
|
||||||
void vertex() {
|
|
||||||
// Called for every vertex the material is visible on.
|
|
||||||
}
|
|
||||||
|
|
||||||
void fragment() {
|
|
||||||
float phase = get_beat_phase();
|
|
||||||
vec4 tex = texture(albedo, UV);
|
|
||||||
ALBEDO = mix(tex.rgb, color.rgb, 0.5);
|
|
||||||
ALPHA = tex.a * ease(1.0 - phase, flash_exponent) * alpha_multiplier;//mix(tex.a, color.a * (1.0 - phase), 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
//void light() {
|
|
||||||
// // Called for every pixel for every light affecting the material.
|
|
||||||
// // Uncomment to replace the default light processing function with this one.
|
|
||||||
//}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://c32arbbdbo7m8
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
shader_type spatial;
|
|
||||||
render_mode unshaded, shadows_disabled;
|
|
||||||
#include "uid://cy7b01or0ckgk" // Rhythm Helper
|
|
||||||
|
|
||||||
uniform sampler2D albedo;
|
|
||||||
uniform float flash_exponent = 3.0;
|
|
||||||
uniform float intensity_multiplier = 1.0;
|
|
||||||
|
|
||||||
void vertex() {
|
|
||||||
// Called for every vertex the material is visible on.
|
|
||||||
}
|
|
||||||
|
|
||||||
void fragment() {
|
|
||||||
float phase = get_beat_phase();
|
|
||||||
vec4 tex = texture(albedo, UV);
|
|
||||||
float intensity = ease((1.0 - phase), flash_exponent) * intensity_multiplier;
|
|
||||||
ALBEDO = tex.rgb * intensity;
|
|
||||||
}
|
|
||||||
|
|
||||||
//void light() {
|
|
||||||
// // Called for every pixel for every light affecting the material.
|
|
||||||
// // Uncomment to replace the default light processing function with this one.
|
|
||||||
//}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://b46o5a45g58xb
|
|
||||||
@ -1,106 +0,0 @@
|
|||||||
global uniform float beat;
|
|
||||||
global uniform float bar;
|
|
||||||
global uniform float song_time;
|
|
||||||
global uniform float total_song_time;
|
|
||||||
global uniform float user_offset_ms;
|
|
||||||
|
|
||||||
uniform float phase_offset = 0.0;
|
|
||||||
uniform float phase_multiplier = 1.0;
|
|
||||||
|
|
||||||
instance uniform float instance_phase_offset = 0.0;
|
|
||||||
instance uniform float instance_phase_multiplier = 1.0;
|
|
||||||
|
|
||||||
|
|
||||||
float get_beat_with_offset() {
|
|
||||||
return (beat * phase_multiplier * instance_phase_multiplier) + phase_offset + instance_phase_offset;// + (user_offset_ms / 1000.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float get_bar_with_offset() {
|
|
||||||
return (bar * phase_multiplier * instance_phase_multiplier) + phase_offset + instance_phase_offset;// + (user_offset_ms / 1000.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float get_beat_phase() {
|
|
||||||
float beat_offset = get_beat_with_offset();
|
|
||||||
return beat_offset - floor(beat_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float get_bar_phase() {
|
|
||||||
float bar_offset = get_bar_with_offset();
|
|
||||||
return bar_offset - floor(bar_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
float get_song_time_ratio() {
|
|
||||||
return song_time / max(total_song_time, 0.001);
|
|
||||||
}
|
|
||||||
|
|
||||||
float ease(float x, float c) {
|
|
||||||
// Godot's source code converted via ChatGPT to GLSL-style
|
|
||||||
//branchless:
|
|
||||||
//x = clamp(x, 0.0, 1.0);
|
|
||||||
//float pos = mix(
|
|
||||||
//1.0 - pow(1.0 - x, 1.0 / c),
|
|
||||||
//pow(x, c),
|
|
||||||
//step(1.0, c)
|
|
||||||
//);
|
|
||||||
//
|
|
||||||
//float k = -c;
|
|
||||||
//float x2 = x * 2.0;
|
|
||||||
//float inout_v = mix(
|
|
||||||
//pow(x2, k) * 0.5,
|
|
||||||
//(1.0 - pow(2.0 - x2, k)) * 0.5 + 0.5,
|
|
||||||
//step(0.5, x)
|
|
||||||
//);
|
|
||||||
//
|
|
||||||
//float isPos = step(0.0, c);
|
|
||||||
//float isNeg = step(c, 0.0);
|
|
||||||
//
|
|
||||||
//return pos * isPos + inout_v * isNeg;
|
|
||||||
|
|
||||||
// a few branches:
|
|
||||||
x = clamp(x, 0.0, 1.0);
|
|
||||||
|
|
||||||
float pos = mix(
|
|
||||||
1.0 - pow(1.0 - x, 1.0 / c), // 0 < c < 1
|
|
||||||
pow(x, c), // c >= 1
|
|
||||||
step(1.0, c)
|
|
||||||
);
|
|
||||||
|
|
||||||
float k = -c;
|
|
||||||
float x2 = x * 2.0;
|
|
||||||
float inout_v = mix(
|
|
||||||
pow(x2, k) * 0.5,
|
|
||||||
(1.0 - pow(2.0 - x2, k)) * 0.5 + 0.5,
|
|
||||||
step(0.5, x)
|
|
||||||
);
|
|
||||||
|
|
||||||
return (c > 0.0) ? pos :
|
|
||||||
(c < 0.0) ? inout_v :
|
|
||||||
0.0;
|
|
||||||
|
|
||||||
// From godot's source code:
|
|
||||||
//if (x < 0.0) {
|
|
||||||
//x = 0.0;
|
|
||||||
//} else if (x > 1.0) {
|
|
||||||
//x = 1.0;
|
|
||||||
//}
|
|
||||||
//if (c > 0.0) {
|
|
||||||
//if (c < 1.0) {
|
|
||||||
//return 1.0 - pow(1.0 - x, 1.0 / c);
|
|
||||||
//} else {
|
|
||||||
//return pow(x, c);
|
|
||||||
//}
|
|
||||||
//} else if (c < 0.0) {
|
|
||||||
////inout ease
|
|
||||||
//
|
|
||||||
//if (x < 0.5) {
|
|
||||||
//return pow(x * 2.0, -c) * 0.5;
|
|
||||||
//} else {
|
|
||||||
//return (1.0 - pow(1.0 - (x - 0.5) * 2.0, -c)) * 0.5 + 0.5;
|
|
||||||
//}
|
|
||||||
//} else {
|
|
||||||
//return 0.0; // no ease (raw)
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://cy7b01or0ckgk
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
[gd_resource type="AudioBusLayout" format=3 uid="uid://jka2h4facmsp"]
|
|
||||||
|
|
||||||
[resource]
|
|
||||||
bus/0/volume_db = -4.002995
|
|
||||||
bus/1/name = &"SFX"
|
|
||||||
bus/1/solo = false
|
|
||||||
bus/1/mute = false
|
|
||||||
bus/1/bypass_fx = false
|
|
||||||
bus/1/volume_db = 0.0
|
|
||||||
bus/1/send = &"Master"
|
|
||||||
bus/2/name = &"Music"
|
|
||||||
bus/2/solo = false
|
|
||||||
bus/2/mute = false
|
|
||||||
bus/2/bypass_fx = false
|
|
||||||
bus/2/volume_db = 0.0
|
|
||||||
bus/2/send = &"Master"
|
|
||||||
bus/3/name = &"Ambient"
|
|
||||||
bus/3/solo = false
|
|
||||||
bus/3/mute = false
|
|
||||||
bus/3/bypass_fx = false
|
|
||||||
bus/3/volume_db = 0.0
|
|
||||||
bus/3/send = &"Master"
|
|
||||||
@ -1,40 +1,27 @@
|
|||||||
KEYS,?plural,en,de,es,ja
|
KEYS,en,de,es,ja
|
||||||
|
|
||||||
# UI
|
# UI
|
||||||
CONTINUE,,Continue,Weiterspielen,Continue,Continue
|
CONTINUE,Continue,Weiterspielen,Continue,Continue
|
||||||
LOAD,,Load,Laden,Load,Load
|
LOAD,Load,Laden,Load,Load
|
||||||
NEW_GAME,,New Game,Neues Spiel,New Game,New Game
|
NEW_GAME,New Game,Neues Spiel,New Game,New Game
|
||||||
SETTINGS,,Settings,Einstellungen,Settings,Settings
|
SETTINGS,Settings,Einstellungen,Settings,Settings
|
||||||
QUIT,,Quit,Beenden,Quit,Quit
|
QUIT,Quit,Beenden,Quit,Quit
|
||||||
LOADING,,[loading]Loading...[/loading],[loading]Laden...[/loading],[loading]Loading...[/loading],[loading]Loading...[/loading]
|
LOADING,[loading]Loading...[/loading],[loading]Laden...[/loading],[loading]Loading...[/loading],[loading]Loading...[/loading]
|
||||||
BACK,,Back,Zurück,Back,Back
|
|
||||||
|
|
||||||
# Options/Settings
|
# Options/Settings
|
||||||
AUDIO,,Audio,Audio,Audio,Audio
|
AUDIO,Audio,Audio,Audio,Audio
|
||||||
MASTER,,Master,Master,Master,Master
|
Master,Master,Master,Master,Master
|
||||||
SFX,,SFX,SFX,SFX,SFX
|
SFX,SFX,SFX,SFX,SFX
|
||||||
MUSIC,,Music,Musik,Music,Music
|
MUSIC,Music,Musik,Music,Music
|
||||||
AMBIENT,,Ambience,Ambiente,Ambience,Ambience
|
AMBIENT,Ambient,Ambiente,Ambient,Ambient
|
||||||
GRAPHICS,,Graphics,Grafik,Graphics,Graphics
|
GRAPHICS,Graphics,Grafik,Graphics,Graphics
|
||||||
FULLSCREEN,,Fullscreen,Vollbild,Fullscreen,Fullscreen
|
FULLSCREEN,Fullscreen,Vollbild,Fullscreen,Fullscreen
|
||||||
MISC,,Misc,Misc,Misc,Misc
|
Misc,Misc,Misc,Misc,Misc
|
||||||
HEADBOBBING,,Headbobbing,Kopfwackeln,Headbobbing,Headbobbing
|
HEADBOBBING,Headbobbing,Kopfwackeln,Headbobbing,Headbobbing
|
||||||
HEADBOBBING_MULTIPLIER,,Headbobbing Multiplier,Wackel-Multiplikator,Headbobbing Multiplier,Headbobbing Multiplier
|
HEADBOBBING_MULTIPLIER,Headbobbing Multiplier,Wackel-Multiplikator,Headbobbing Multiplier, Headbobbing Multiplier
|
||||||
|
|
||||||
WINDOW_FULLSCREEN,,Fullscreen,Vollbild,Fullscreen,Fullscreen
|
|
||||||
WINDOW_EXCLUSIVE_FULLSCREEN,,Exclusive Fullscreen,Exklusiv-Vollbild,Exclusive Fullscreen,Exclusive Fullscreen
|
|
||||||
WINDOW_WINDOWED,,Windowed,Fenstermodus,Windowed,Windowed
|
|
||||||
|
|
||||||
SETTINGS_CONFIRMATION_TITLE,,Apply Changes?,Änderungen Anwenden?,Apply Changes?,Apply Changes?,Apply Changes?
|
|
||||||
SETTINGS_APPLY,,Apply,Anwenden,Apply,Apply,Apply
|
|
||||||
SETTINGS_REVERT,,Revert,Zurücksetzen,Revert,Revert,Revert
|
|
||||||
|
|
||||||
?pluralrule,,nplurals=2; plural=(n != 1);,nplurals=2; plural=(n != 1);,nplurals=2; plural=(n != 1);,nplurals=2; plural=(n != 1);
|
|
||||||
SETTINGS_REVERT_TIME_LEFT,.,Reverting in %s second...,Wird in %s sekunde zurückgesetzt...,Reverting in %s second...,Reverting in %s second...
|
|
||||||
,,Reverting in %s seconds...,Wird in %s sekunden zurückgesetzt...,Reverting in %s seconds...,Reverting in %s seconds...
|
|
||||||
|
|
||||||
# Death Types
|
# Death Types
|
||||||
DEATH_LABEL_SAW,,Killed by a saw,Von einer Säge getötet,Killed by a saw,Killed by a saw
|
DEATH_LABEL_SAW,Killed by a saw,Von einer Säge getötet,Killed by a saw,Killed by a saw
|
||||||
DEATH_MSG_SAW,,Better watch your step next time...,,,
|
DEATH_MSG_SAW,Better watch your step next time...,,,
|
||||||
DEATH_LABEL_DOOM_FALL,,Doomed to fall,Zum Fallen verdammt,,
|
DEATH_LABEL_DOOM_FALL,Doomed to fall,Zum Fallen verdammt,,
|
||||||
DEATH_MSG_DOOM_FALL,,"Yes, you can fall off the map now...","Ja, du kannst jetzt von der map fallen...",,
|
DEATH_MSG_DOOM_FALL,"Yes, you can fall off the map now...","Ja, du kannst jetzt von der map fallen...",,
|
||||||
|
|||||||
|
Can't render this file because it has a wrong number of fields in line 3.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -16,19 +16,12 @@ run/main_scene="uid://ga0d5817hbc8"
|
|||||||
config/features=PackedStringArray("4.6", "Forward Plus")
|
config/features=PackedStringArray("4.6", "Forward Plus")
|
||||||
config/icon="res://godot_icon.svg"
|
config/icon="res://godot_icon.svg"
|
||||||
|
|
||||||
[audio]
|
|
||||||
|
|
||||||
buses/default_bus_layout="uid://jka2h4facmsp"
|
|
||||||
|
|
||||||
[autoload]
|
[autoload]
|
||||||
|
|
||||||
SPrint="*uid://cpfunq8fyixco"
|
SPrint="*uid://cpfunq8fyixco"
|
||||||
VersionDisplay="*uid://bqxtpo2c64h22"
|
VersionDisplay="*uid://bqxtpo2c64h22"
|
||||||
InputManager="*uid://cocp0vmvgd4ln"
|
InputManager="*uid://cocp0vmvgd4ln"
|
||||||
ControllerIcons="*uid://bdosbfkp568je"
|
ControllerIcons="*uid://bdosbfkp568je"
|
||||||
ShaderGlobals="*uid://d2lr860r1ysrm"
|
|
||||||
LimboConsole="*uid://dyxornv8vwibg"
|
|
||||||
SettingsHandler="*uid://dj1t6rc6fpiti"
|
|
||||||
|
|
||||||
[debug]
|
[debug]
|
||||||
|
|
||||||
@ -41,7 +34,7 @@ settings/3d/volumetric_defaults/thickness=0.0
|
|||||||
|
|
||||||
[editor_plugins]
|
[editor_plugins]
|
||||||
|
|
||||||
enabled=PackedStringArray("res://addons/Todo_Manager/plugin.cfg", "res://addons/anim_player_refactor/plugin.cfg", "res://addons/controller_icons/plugin.cfg", "res://addons/lightmap_probe_grid/plugin.cfg", "res://addons/limbo_console/plugin.cfg", "res://addons/reflection_probe_preview/plugin.cfg", "res://addons/sprint/plugin.cfg", "res://addons/version_display/plugin.cfg")
|
enabled=PackedStringArray("res://addons/Todo_Manager/plugin.cfg", "res://addons/controller_icons/plugin.cfg", "res://addons/lightmap_probe_grid/plugin.cfg", "res://addons/logger/plugin.cfg", "res://addons/reflection_probe_preview/plugin.cfg", "res://addons/version_display/plugin.cfg")
|
||||||
|
|
||||||
[file_customization]
|
[file_customization]
|
||||||
|
|
||||||
@ -173,22 +166,6 @@ toggle_flashlight={
|
|||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":102,"location":0,"echo":false,"script":null)
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":102,"location":0,"echo":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
limbo_console_toggle={
|
|
||||||
"deadzone": 0.5,
|
|
||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":96,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
|
||||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194341,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
limbo_auto_complete_reverse={
|
|
||||||
"deadzone": 0.5,
|
|
||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194306,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
limbo_console_search_history={
|
|
||||||
"deadzone": 0.5,
|
|
||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":82,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
[internationalization]
|
[internationalization]
|
||||||
|
|
||||||
@ -210,26 +187,3 @@ locale/translations=PackedStringArray("res://localization/localization.en.transl
|
|||||||
rendering_device/driver.windows="d3d12"
|
rendering_device/driver.windows="d3d12"
|
||||||
anti_aliasing/quality/msaa_3d=3
|
anti_aliasing/quality/msaa_3d=3
|
||||||
occlusion_culling/use_occlusion_culling=true
|
occlusion_culling/use_occlusion_culling=true
|
||||||
|
|
||||||
[shader_globals]
|
|
||||||
|
|
||||||
song_time={
|
|
||||||
"type": "float",
|
|
||||||
"value": 0.0
|
|
||||||
}
|
|
||||||
total_song_time={
|
|
||||||
"type": "float",
|
|
||||||
"value": 0.0
|
|
||||||
}
|
|
||||||
beat={
|
|
||||||
"type": "float",
|
|
||||||
"value": 0.0
|
|
||||||
}
|
|
||||||
bar={
|
|
||||||
"type": "float",
|
|
||||||
"value": 0.0
|
|
||||||
}
|
|
||||||
user_offset_ms={
|
|
||||||
"type": "float",
|
|
||||||
"value": 0.0
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,101 +0,0 @@
|
|||||||
extends Node
|
|
||||||
|
|
||||||
signal settings_changed
|
|
||||||
|
|
||||||
const CONFIG_FILE_PATH: String = "user://settings.ini"
|
|
||||||
|
|
||||||
var settings: Dictionary[String, Dictionary] = {
|
|
||||||
"audio": {
|
|
||||||
"user_offset_ms": 0.0,
|
|
||||||
"Master": 0.85,
|
|
||||||
"Music": 1.0,
|
|
||||||
"SFX": 1.0,
|
|
||||||
"Ambient": 1.0,
|
|
||||||
},
|
|
||||||
"video": {
|
|
||||||
"window_mode": DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN,
|
|
||||||
},
|
|
||||||
"gameplay": {
|
|
||||||
"headbobbing": true,
|
|
||||||
"headbobbing_multiplier": 1.0,
|
|
||||||
},
|
|
||||||
"localization": {
|
|
||||||
"language": "en_US",
|
|
||||||
},
|
|
||||||
"controls": {
|
|
||||||
"controller_vibration_min_range": 0.0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var _previous_settings: Dictionary[String, Dictionary] = settings
|
|
||||||
|
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
|
||||||
func _ready() -> void:
|
|
||||||
load_from_file()
|
|
||||||
commit_settings()
|
|
||||||
apply_settings()
|
|
||||||
save_settings()
|
|
||||||
|
|
||||||
|
|
||||||
func load_from_file() -> void:
|
|
||||||
var config := ConfigFile.new()
|
|
||||||
if config.load(CONFIG_FILE_PATH) != OK:
|
|
||||||
return
|
|
||||||
|
|
||||||
for section: String in config.get_sections():
|
|
||||||
if not settings.has(section):
|
|
||||||
continue
|
|
||||||
|
|
||||||
for key: String in config.get_section_keys(section):
|
|
||||||
var default: Variant = get_setting(section, key)
|
|
||||||
set_setting(section, key, config.get_value(section, key, default), false)
|
|
||||||
|
|
||||||
settings_changed.emit()
|
|
||||||
|
|
||||||
|
|
||||||
func apply_settings() -> void:
|
|
||||||
# Audio
|
|
||||||
RhythmPlayer.user_offset_ms = get_setting("audio", "user_offset_ms", 0.0)
|
|
||||||
AudioServer.set_bus_volume_linear(AudioServer.get_bus_index(&"Master"), get_setting("audio", "Master", 0.85))
|
|
||||||
AudioServer.set_bus_volume_linear(AudioServer.get_bus_index(&"SFX"), get_setting("audio", "SFX", 1.0))
|
|
||||||
AudioServer.set_bus_volume_linear(AudioServer.get_bus_index(&"Music"), get_setting("audio", "Music", 1.0))
|
|
||||||
AudioServer.set_bus_volume_linear(AudioServer.get_bus_index(&"Ambient"), get_setting("audio", "Ambient", 1.0))
|
|
||||||
# Localization
|
|
||||||
TranslationServer.set_locale(get_setting("localization", "language", "en_US"))
|
|
||||||
# Video
|
|
||||||
if not Engine.is_embedded_in_editor():
|
|
||||||
DisplayServer.window_set_mode(get_setting("video", "window_mode", DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN))
|
|
||||||
|
|
||||||
|
|
||||||
func commit_settings() -> void:
|
|
||||||
_previous_settings = settings.duplicate(true)
|
|
||||||
|
|
||||||
|
|
||||||
func save_settings() -> void:
|
|
||||||
var config: ConfigFile = Utils.load_config(CONFIG_FILE_PATH)
|
|
||||||
|
|
||||||
for section: String in settings.keys():
|
|
||||||
for key: String in settings.get(section, {}).keys():
|
|
||||||
config.set_value(section, key, settings[section][key])
|
|
||||||
|
|
||||||
config.save(CONFIG_FILE_PATH)
|
|
||||||
|
|
||||||
|
|
||||||
func revert_settings() -> void:
|
|
||||||
settings = _previous_settings.duplicate(true)
|
|
||||||
apply_settings()
|
|
||||||
|
|
||||||
|
|
||||||
func set_setting(section: String, key: String, value: Variant, do_save: bool = true) -> void:
|
|
||||||
settings[section][key] = value
|
|
||||||
|
|
||||||
if do_save:
|
|
||||||
apply_settings()
|
|
||||||
commit_settings()
|
|
||||||
save_settings()
|
|
||||||
settings_changed.emit()
|
|
||||||
|
|
||||||
|
|
||||||
func get_setting(section: String, key: String, default: Variant = null) -> Variant:
|
|
||||||
return settings.get(section, {}).get(key, default)
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://dj1t6rc6fpiti
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
extends Node
|
|
||||||
|
|
||||||
|
|
||||||
var beat: float = 0.0: set = set_beat
|
|
||||||
var bar: float = 0.0: set = set_bar
|
|
||||||
var song_time: float = 0.0: set = set_song_time
|
|
||||||
var total_song_time: float = 0.0: set = set_total_song_time
|
|
||||||
var user_offset_ms: float = 0.0
|
|
||||||
|
|
||||||
|
|
||||||
func set_user_offset_ms(offset: float) -> void:
|
|
||||||
user_offset_ms = offset
|
|
||||||
RenderingServer.global_shader_parameter_set(&"user_offset_ms", beat)
|
|
||||||
|
|
||||||
|
|
||||||
func set_beat(_beat: float) -> void:
|
|
||||||
beat = _beat
|
|
||||||
RenderingServer.global_shader_parameter_set(&"beat", beat)
|
|
||||||
|
|
||||||
|
|
||||||
func set_bar(_bar: float) -> void:
|
|
||||||
bar = _bar
|
|
||||||
RenderingServer.global_shader_parameter_set(&"bar", bar)
|
|
||||||
|
|
||||||
|
|
||||||
func set_song_time(time: float) -> void:
|
|
||||||
song_time = time
|
|
||||||
RenderingServer.global_shader_parameter_set(&"song_time", time)
|
|
||||||
|
|
||||||
|
|
||||||
func set_total_song_time(time: float) -> void:
|
|
||||||
total_song_time = time
|
|
||||||
RenderingServer.global_shader_parameter_set(&"total_song_time", time)
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://d2lr860r1ysrm
|
|
||||||
@ -12,13 +12,14 @@ var time: float = 0.0
|
|||||||
var headbobbing_multiplier: float = 1.0 # User setting.
|
var headbobbing_multiplier: float = 1.0 # User setting.
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
#func _ready() -> void:
|
||||||
var _update_settings: Callable = func() -> void:
|
#var _update_settings: Callable = func() -> void:
|
||||||
effect_enabled = SettingsHandler.get_setting("gameplay", "headbobbing", true)
|
#effect_enabled = SettingsManager.get_setting(&"misc", &"headbobbing")
|
||||||
headbobbing_multiplier = SettingsHandler.get_setting("gameplay", "headbobbing_multiplier", 1.0)
|
#headbobbing_multiplier = SettingsManager.get_setting(&"misc", &"headbobbing_multiplier")
|
||||||
|
#
|
||||||
SettingsHandler.settings_changed.connect(_update_settings)
|
#SettingsManager.settings_changed.connect(_update_settings)
|
||||||
_update_settings.call()
|
#ProjectSettings.settings_changed.connect(_update_settings)
|
||||||
|
#_update_settings.call()
|
||||||
|
|
||||||
|
|
||||||
func _process_effect(delta: float) -> void:
|
func _process_effect(delta: float) -> void:
|
||||||
|
|||||||
@ -1,66 +0,0 @@
|
|||||||
class_name RhythmListener
|
|
||||||
extends Node
|
|
||||||
|
|
||||||
|
|
||||||
signal beat_tick(beat_index: int)
|
|
||||||
signal bar_tick(bar_index: int)
|
|
||||||
|
|
||||||
@export_range(0.0, 1.0, 0.001, "or_greater", "or_less") var phase_shift: float = 0.0
|
|
||||||
|
|
||||||
var beat: float = 0.0
|
|
||||||
var bar: float = 0.0
|
|
||||||
var _last_beat_time: float = 0.0
|
|
||||||
var _last_beat_index: int = -1
|
|
||||||
var _last_bar_index: int = -1
|
|
||||||
var _last_bar_time: float = 0.0
|
|
||||||
|
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
|
||||||
if RhythmPlayer.paused:
|
|
||||||
_last_beat_index = 0
|
|
||||||
_last_bar_index = 0
|
|
||||||
return
|
|
||||||
|
|
||||||
beat = RhythmPlayer.beat + phase_shift
|
|
||||||
bar = RhythmPlayer.bar + phase_shift
|
|
||||||
|
|
||||||
var beat_index: int = floori(beat)
|
|
||||||
var bar_index: int = floori(bar)
|
|
||||||
|
|
||||||
if _last_beat_index != beat_index:
|
|
||||||
on_beat_tick(beat_index)
|
|
||||||
_last_beat_index = beat_index
|
|
||||||
_last_beat_time = RhythmPlayer.song_time + phase_shift
|
|
||||||
|
|
||||||
if _last_bar_index != bar_index:
|
|
||||||
on_bar_tick(bar_index)
|
|
||||||
_last_bar_index = bar_index
|
|
||||||
_last_bar_time = RhythmPlayer.song_time + phase_shift
|
|
||||||
|
|
||||||
#SPrint.print_msgf("Time To Next Bar: %s" % get_time_to_next_bar())
|
|
||||||
#SPrint.print_msgf("Time To Next Beat: %s" % get_time_to_next_beat())
|
|
||||||
|
|
||||||
|
|
||||||
func get_time_to_next_beat() -> float:
|
|
||||||
var beat_duration: float = 60.0 / RhythmPlayer.bpm
|
|
||||||
return (_last_beat_time - (RhythmPlayer.song_time + phase_shift)) + beat_duration
|
|
||||||
|
|
||||||
|
|
||||||
func get_time_to_next_bar() -> float:
|
|
||||||
var bar_duration: float = 60.0 / (RhythmPlayer.bpm / RhythmPlayer.beats_per_bar)
|
|
||||||
return (_last_bar_time - (RhythmPlayer.song_time + phase_shift)) + bar_duration
|
|
||||||
|
|
||||||
|
|
||||||
#func get_time_to_beat(target_beat: float) -> float:
|
|
||||||
#var beat_duration: float = 60.0 / RhythmPlayer.bpm
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
func on_beat_tick(beat_index: int) -> void:
|
|
||||||
#SPrint.print_msg("Beat received %s" % beat_index, 0.75)
|
|
||||||
beat_tick.emit(beat_index)
|
|
||||||
|
|
||||||
|
|
||||||
func on_bar_tick(bar_index: int) -> void:
|
|
||||||
#SPrint.print_msg("Bar received %s" % bar_index, 1.0)
|
|
||||||
bar_tick.emit(bar_index)
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://co7j2qtqpud6b
|
|
||||||
@ -1,168 +0,0 @@
|
|||||||
class_name RhythmPlayer
|
|
||||||
extends Node
|
|
||||||
|
|
||||||
signal beat_tick(beat_index: int)
|
|
||||||
signal bar_tick(bar_index: int)
|
|
||||||
|
|
||||||
static var user_offset_ms: float = 0.0: set = set_user_offset_ms
|
|
||||||
|
|
||||||
# Since only one rhythm player should play at any time, these are all static.
|
|
||||||
static var current_song_info: SongInfo
|
|
||||||
static var paused: bool = true
|
|
||||||
static var bpm: float = 120.0
|
|
||||||
static var beats_per_bar: int = 4
|
|
||||||
|
|
||||||
static var song_time: float = 0.0
|
|
||||||
static var beat: float = 0.0
|
|
||||||
static var beat_phase: float = 0.0
|
|
||||||
static var bar: float = 0.0
|
|
||||||
static var bar_phase: float = 0.0
|
|
||||||
|
|
||||||
@export var song_info: SongInfo
|
|
||||||
@export var autoplay: bool = false
|
|
||||||
@export var playback_position: float = 0.0
|
|
||||||
|
|
||||||
@export_node_path("AudioStreamPlayer", "AudioStreamPlayer2D", "AudioStreamPlayer3D")
|
|
||||||
var audio_player: NodePath: set = set_audio_player
|
|
||||||
|
|
||||||
var _audio_player: Node
|
|
||||||
var _last_beat: int = -1
|
|
||||||
var _last_bar: int = -1
|
|
||||||
|
|
||||||
|
|
||||||
static func set_user_offset_ms(offset: float) -> void:
|
|
||||||
user_offset_ms = offset
|
|
||||||
ShaderGlobals.set_user_offset_ms(offset)
|
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
|
||||||
set_audio_player(audio_player)
|
|
||||||
|
|
||||||
if autoplay:
|
|
||||||
play(playback_position)
|
|
||||||
|
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
|
||||||
if not is_instance_valid(_audio_player):
|
|
||||||
return
|
|
||||||
|
|
||||||
paused = not _audio_player.playing
|
|
||||||
|
|
||||||
if not _audio_player.playing:
|
|
||||||
_last_beat = -1
|
|
||||||
_last_bar = -1
|
|
||||||
return
|
|
||||||
|
|
||||||
_update_playback()
|
|
||||||
|
|
||||||
|
|
||||||
func get_song_time() -> float:
|
|
||||||
if not is_instance_valid(_audio_player):
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
var time: float = _audio_player.get_playback_position()
|
|
||||||
time += AudioServer.get_time_since_last_mix()
|
|
||||||
time -= AudioServer.get_output_latency()
|
|
||||||
return time
|
|
||||||
|
|
||||||
|
|
||||||
func get_song_time_with_user_offset() -> float:
|
|
||||||
return get_song_time() + user_offset_ms / 1000.0
|
|
||||||
|
|
||||||
|
|
||||||
func get_beat() -> float:
|
|
||||||
return song_info.tempo_map.get_beat_at_time(get_song_time_with_user_offset() + song_info.phase_shift)
|
|
||||||
|
|
||||||
|
|
||||||
func get_bar_phase() -> float:
|
|
||||||
return bar - floorf(bar)
|
|
||||||
|
|
||||||
|
|
||||||
func get_bar() -> float:
|
|
||||||
return beat / beats_per_bar
|
|
||||||
|
|
||||||
|
|
||||||
## 0.0 -> beat start
|
|
||||||
## 0.5 -> halfway
|
|
||||||
## 0.99 -> almost next beat
|
|
||||||
func get_beat_phase() -> float:
|
|
||||||
return beat - floorf(beat)
|
|
||||||
|
|
||||||
|
|
||||||
func beat_to_signature() -> int:
|
|
||||||
return posmod(floori(beat), beats_per_bar) + 1
|
|
||||||
|
|
||||||
|
|
||||||
static func bar_to_signature(bar_index: int, _bar_signature: int) -> int:
|
|
||||||
return bar_index
|
|
||||||
#return posmod(bar_index, bar_signature) + 1
|
|
||||||
|
|
||||||
|
|
||||||
func set_audio_player(audio_player_path: NodePath) -> void:
|
|
||||||
audio_player = audio_player_path
|
|
||||||
_audio_player = get_node_or_null(audio_player)
|
|
||||||
|
|
||||||
if not is_instance_valid(_audio_player):
|
|
||||||
set_process(false)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (
|
|
||||||
not _audio_player.has_method(&"play")
|
|
||||||
or not _audio_player.has_method(&"get_playback_position")
|
|
||||||
or not &"playing" in _audio_player
|
|
||||||
):
|
|
||||||
_audio_player = null
|
|
||||||
push_error("audio_player is not a valid player.")
|
|
||||||
return
|
|
||||||
|
|
||||||
#set_process(_audio_player.playing)
|
|
||||||
set_process(true)
|
|
||||||
|
|
||||||
|
|
||||||
func play(position: float = 0.0) -> void:
|
|
||||||
current_song_info = song_info
|
|
||||||
_audio_player.stream = song_info.audio_stream
|
|
||||||
ShaderGlobals.set_total_song_time(song_info.audio_stream.get_length())
|
|
||||||
|
|
||||||
bpm = song_info.bpm
|
|
||||||
beats_per_bar = song_info.beats_per_bar
|
|
||||||
|
|
||||||
song_info.create_tempo_map()
|
|
||||||
_audio_player.play(position)
|
|
||||||
|
|
||||||
|
|
||||||
func stop() -> void:
|
|
||||||
_audio_player.stop()
|
|
||||||
|
|
||||||
|
|
||||||
func _update_playback() -> void:
|
|
||||||
beat = get_beat()
|
|
||||||
beat_phase = get_beat_phase()
|
|
||||||
|
|
||||||
bar = get_bar()
|
|
||||||
bar_phase = get_bar_phase()
|
|
||||||
|
|
||||||
song_time = get_song_time_with_user_offset() + song_info.phase_shift
|
|
||||||
|
|
||||||
ShaderGlobals.set_song_time(song_time)
|
|
||||||
|
|
||||||
var tempo_section: TempoMap.TempoSection = song_info.tempo_map.get_tempo_section_at_time(song_time)
|
|
||||||
bpm = tempo_section.bpm
|
|
||||||
beats_per_bar = tempo_section.beats_per_bar
|
|
||||||
|
|
||||||
ShaderGlobals.set_beat(beat)
|
|
||||||
ShaderGlobals.set_bar(bar)
|
|
||||||
|
|
||||||
var beat_index: int = floori(beat)
|
|
||||||
var bar_index: int = floori(bar)
|
|
||||||
SPrint.print_msgf("Beat: %s (%s) on beat: %s" % [beat_index, beat_to_signature(), beat_phase <= (1.0 / beats_per_bar)])
|
|
||||||
SPrint.print_msgf("Bar: %s (%s)" % [bar_to_signature(bar_index, beats_per_bar), str(bar_phase).pad_decimals(4)])
|
|
||||||
#SPrint.print_msgf("Bar: %s (%s) bar on beat: %s" % [bar_to_signature(bar_index, _bars), bar_index, bar_index % _bars == 0])
|
|
||||||
|
|
||||||
if _last_beat != beat_index:
|
|
||||||
beat_tick.emit(beat_index)
|
|
||||||
_last_beat = beat_index
|
|
||||||
|
|
||||||
if _last_bar != bar_index:
|
|
||||||
bar_tick.emit(bar_index)
|
|
||||||
_last_bar = bar_index
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://bdi06itcm6wfp
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
class_name RhythmPropertySetter
|
|
||||||
extends Node
|
|
||||||
|
|
||||||
|
|
||||||
@export var nodes: Array[Node] = []
|
|
||||||
@export var beat_nodes: Array[Node] = []
|
|
||||||
@export var bar_nodes: Array[Node] = []
|
|
||||||
@export var beat_property: StringName = &""
|
|
||||||
@export var bar_property: StringName = &""
|
|
||||||
|
|
||||||
@export var phase_shift: float = 0.0
|
|
||||||
@export var phase_multiplier: float = 1.0
|
|
||||||
|
|
||||||
@export_group("Beat Value", "beat_")
|
|
||||||
@export var beat_range_min: float = 0.0
|
|
||||||
@export var beat_range_max: float = 1.0
|
|
||||||
@export_exp_easing("attenuation") var beat_exponent: float = 1.0
|
|
||||||
|
|
||||||
@export_group("Bar Value", "bar_")
|
|
||||||
@export var bar_range_min: float = 0.0
|
|
||||||
@export var bar_range_max: float = 1.0
|
|
||||||
@export_exp_easing("attenuation") var bar_exponent: float = 1.0
|
|
||||||
|
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
|
||||||
if RhythmPlayer.paused:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not beat_property.is_empty():
|
|
||||||
_update_beat_property()
|
|
||||||
|
|
||||||
if not bar_property.is_empty():
|
|
||||||
_update_bar_property()
|
|
||||||
|
|
||||||
|
|
||||||
func _update_beat_property() -> void:
|
|
||||||
var beat: float = RhythmPlayer.beat * phase_multiplier + phase_shift
|
|
||||||
var beat_phase: float = beat - floorf(beat)
|
|
||||||
var beat_value: float = remap(beat_phase, 0.0, 1.0, beat_range_min, beat_range_max)
|
|
||||||
beat_value *= ease(1.0 - beat_phase, beat_exponent)
|
|
||||||
|
|
||||||
var _nodes: Array[Node] = beat_nodes.duplicate()
|
|
||||||
_nodes.append_array(nodes)
|
|
||||||
|
|
||||||
for node: Node in _nodes:
|
|
||||||
node.set_indexed(NodePath(beat_property), beat_value)
|
|
||||||
|
|
||||||
|
|
||||||
func _update_bar_property() -> void:
|
|
||||||
var bar: float = RhythmPlayer.bar * phase_multiplier + phase_shift
|
|
||||||
var bar_phase: float = bar - floorf(bar)
|
|
||||||
var bar_value: float = remap(bar_phase, 0.0, 1.0, bar_range_min, bar_range_max)
|
|
||||||
bar_value *= ease(1.0 - bar_phase, bar_exponent)
|
|
||||||
|
|
||||||
var _nodes: Array[Node] = bar_nodes.duplicate()
|
|
||||||
_nodes.append_array(nodes)
|
|
||||||
|
|
||||||
for node: Node in _nodes:
|
|
||||||
node.set_indexed(NodePath(bar_property), bar_value)
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://bvmfdeypbeqsn
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
class_name SongInfo
|
|
||||||
extends Resource
|
|
||||||
|
|
||||||
|
|
||||||
#@export_file("*.tres", "*.res", "*.ogg", "*.wav", "*.mp3")
|
|
||||||
#var audio_path: String
|
|
||||||
@export var audio_stream: AudioStream
|
|
||||||
@export var bpm: float = 120
|
|
||||||
@export_range(0, 8, 1, "or_greater") var beats_per_bar: int = 4
|
|
||||||
@export_range(0.0, 1.0, 0.001, "or_greater", "or_less") var phase_shift: float = 0.0
|
|
||||||
#@export_enum("1/2", "1/4", "1/8", "1/16", "1/32", "1/64") var time_signature: int = 1
|
|
||||||
|
|
||||||
@export_group("Tempo Sections")
|
|
||||||
@export var tempo_infos: Array[TempoSectionInfo] = []
|
|
||||||
@export var tempo_sections_in_seconds: Dictionary[float, float]
|
|
||||||
@export_subgroup("Tempo Sections Simple")
|
|
||||||
@export var tempo_section_round_to_bar: bool = true
|
|
||||||
@export var tempo_section_round_to_beat: bool = false
|
|
||||||
|
|
||||||
var tempo_map: TempoMap
|
|
||||||
|
|
||||||
|
|
||||||
func _init() -> void:
|
|
||||||
create_tempo_map()
|
|
||||||
|
|
||||||
|
|
||||||
func create_tempo_map() -> void:
|
|
||||||
if tempo_infos.is_empty():
|
|
||||||
tempo_map = TempoMap.create_from_time_dictionary(
|
|
||||||
tempo_sections_in_seconds,
|
|
||||||
bpm,
|
|
||||||
tempo_section_round_to_bar,
|
|
||||||
tempo_section_round_to_beat,
|
|
||||||
beats_per_bar
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
tempo_map = TempoMap.create_from_section_infos(tempo_infos, bpm, beats_per_bar)
|
|
||||||
|
|
||||||
|
|
||||||
#func get_time_signature() -> int:
|
|
||||||
#return int(pow(2, time_signature + 1))
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://c5mqtmsvgt4e8
|
|
||||||
@ -1,194 +0,0 @@
|
|||||||
class_name TempoMap
|
|
||||||
extends Resource
|
|
||||||
|
|
||||||
@export var time_sections: Dictionary[float, float] = {}
|
|
||||||
@export var section_infos: Array[TempoSectionInfo] = []
|
|
||||||
|
|
||||||
var sections: Array[TempoSection] = []
|
|
||||||
|
|
||||||
|
|
||||||
static func create_from_beat_dictionary(
|
|
||||||
dictionary: Dictionary[float, float],
|
|
||||||
fallback_bpm: float
|
|
||||||
) -> TempoMap:
|
|
||||||
var map := TempoMap.new()
|
|
||||||
|
|
||||||
if dictionary.is_empty():
|
|
||||||
map.add_section(0.0, fallback_bpm)
|
|
||||||
return map
|
|
||||||
|
|
||||||
var keys: Array[float] = dictionary.keys()
|
|
||||||
keys.sort()
|
|
||||||
|
|
||||||
for key: float in keys:
|
|
||||||
map.add_section(key, dictionary.get(key))
|
|
||||||
|
|
||||||
map.recalculate_times()
|
|
||||||
return map
|
|
||||||
|
|
||||||
|
|
||||||
static func create_from_time_dictionary(
|
|
||||||
dictionary: Dictionary[float, float],
|
|
||||||
fallback_bpm: float,
|
|
||||||
round_to_bars: bool = true,
|
|
||||||
round_to_whole_beats: bool = false,
|
|
||||||
beats_per_bar: int = 4
|
|
||||||
) -> TempoMap:
|
|
||||||
var map := TempoMap.new()
|
|
||||||
|
|
||||||
if dictionary.is_empty():
|
|
||||||
map.add_section(0.0, fallback_bpm, beats_per_bar)
|
|
||||||
return map
|
|
||||||
|
|
||||||
var times: Array[float] = dictionary.keys()
|
|
||||||
times.sort()
|
|
||||||
|
|
||||||
var current_beat: float = 0.0
|
|
||||||
var previous_time: float = 0.0
|
|
||||||
var previous_bpm: float = fallback_bpm
|
|
||||||
|
|
||||||
map.add_section(0.0, fallback_bpm)
|
|
||||||
|
|
||||||
for time: float in times:
|
|
||||||
var delta: float = time - previous_time
|
|
||||||
current_beat += delta * previous_bpm / 60.0
|
|
||||||
|
|
||||||
var bpm: float = dictionary[time]
|
|
||||||
|
|
||||||
if round_to_bars:
|
|
||||||
current_beat = roundf(current_beat / beats_per_bar) * beats_per_bar
|
|
||||||
if round_to_whole_beats:
|
|
||||||
current_beat = roundf(current_beat)
|
|
||||||
|
|
||||||
map.add_section(current_beat, bpm)
|
|
||||||
|
|
||||||
previous_time = time
|
|
||||||
previous_bpm = bpm
|
|
||||||
|
|
||||||
map.recalculate_times()
|
|
||||||
return map
|
|
||||||
|
|
||||||
|
|
||||||
static func create_from_section_infos(infos: Array[TempoSectionInfo], fallback_bpm: float, beats_per_bar_fallback: int) -> TempoMap:
|
|
||||||
var map := TempoMap.new()
|
|
||||||
|
|
||||||
if infos.is_empty():
|
|
||||||
map.add_section(0.0, fallback_bpm, beats_per_bar_fallback)
|
|
||||||
return map
|
|
||||||
|
|
||||||
var times: Dictionary[float, TempoSectionInfo] = {}
|
|
||||||
|
|
||||||
for info: TempoSectionInfo in infos:
|
|
||||||
times.set(info.time, info)
|
|
||||||
|
|
||||||
times.sort()
|
|
||||||
|
|
||||||
var current_beat: float = 0.0
|
|
||||||
var previous_time: float = 0.0
|
|
||||||
var previous_bpm: float = fallback_bpm
|
|
||||||
var previous_beats_per_bar: int = beats_per_bar_fallback
|
|
||||||
|
|
||||||
map.add_section(0.0, fallback_bpm)
|
|
||||||
|
|
||||||
for time: float in times.keys():
|
|
||||||
var delta: float = time - previous_time
|
|
||||||
current_beat += delta * previous_bpm / 60.0
|
|
||||||
|
|
||||||
var info: TempoSectionInfo = times[time]
|
|
||||||
var bpm: float = info.bpm
|
|
||||||
var beats_per_bar: int = info.beats_per_bar
|
|
||||||
|
|
||||||
if info.snap_to_previous_beat:
|
|
||||||
current_beat = roundf(current_beat)
|
|
||||||
if info.snap_to_previous_bar:
|
|
||||||
current_beat = roundf(current_beat / previous_beats_per_bar) * previous_beats_per_bar
|
|
||||||
|
|
||||||
map.add_section(current_beat, bpm, beats_per_bar)
|
|
||||||
|
|
||||||
previous_time = time
|
|
||||||
previous_bpm = bpm
|
|
||||||
previous_beats_per_bar = beats_per_bar
|
|
||||||
|
|
||||||
map.recalculate_times()
|
|
||||||
return map
|
|
||||||
|
|
||||||
|
|
||||||
func add_section(beat: float, bpm: float, beats_per_bar: int = 4) -> void:
|
|
||||||
if not sections.is_empty() and is_equal_approx(sections.back().beat, beat):
|
|
||||||
sections.back().bpm = bpm
|
|
||||||
sections.back().beats_per_bar = beats_per_bar
|
|
||||||
else:
|
|
||||||
sections.append(TempoSection.new(bpm, beat, beats_per_bar))
|
|
||||||
|
|
||||||
|
|
||||||
func recalculate_times() -> void:
|
|
||||||
if sections.is_empty():
|
|
||||||
return
|
|
||||||
|
|
||||||
sections.front().time = 0.0
|
|
||||||
print("[TempoMap] Calculating tempo changes:")
|
|
||||||
|
|
||||||
for index: int in range(1, sections.size()):
|
|
||||||
var previous: TempoSection = sections[index - 1]
|
|
||||||
var current: TempoSection = sections[index]
|
|
||||||
|
|
||||||
var beat_delta: float = current.beat - previous.beat
|
|
||||||
var seconds: float = beat_delta * 60.0 / previous.bpm
|
|
||||||
|
|
||||||
current.time = previous.time + seconds
|
|
||||||
print_rich("- [b]%s[/b] [i]bpm[/i] ([b]1/%s[/b]) at [b]%s[/b] [i]seconds[/i] (beat delta: [b]%s[/b], seconds delta: [b]%s[/b])." % [current.bpm, current.beats_per_bar, current.time, beat_delta, seconds])
|
|
||||||
|
|
||||||
|
|
||||||
func get_beat_at_time(time: float) -> float:
|
|
||||||
if sections.is_empty():
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
# O(log n) (Binary search)
|
|
||||||
var low: int = 0
|
|
||||||
var high: int = sections.size() - 1
|
|
||||||
|
|
||||||
while low <= high:
|
|
||||||
@warning_ignore("integer_division")
|
|
||||||
var mid: int = (low + high) / 2
|
|
||||||
|
|
||||||
if sections[mid].time <= time:
|
|
||||||
low = mid + 1
|
|
||||||
else:
|
|
||||||
high = mid - 1
|
|
||||||
|
|
||||||
var section: TempoSection = sections[maxi(high, 0)]
|
|
||||||
SPrint.print_msgf("Section: %s" % section.bpm)
|
|
||||||
return section.beat + (time - section.time) * section.bpm / 60.0
|
|
||||||
|
|
||||||
|
|
||||||
func get_tempo_section_at_time(time: float) -> TempoSection:
|
|
||||||
if sections.is_empty():
|
|
||||||
return null
|
|
||||||
|
|
||||||
# O(log n) (Binary search)
|
|
||||||
var low: int = 0
|
|
||||||
var high: int = sections.size() - 1
|
|
||||||
|
|
||||||
while low <= high:
|
|
||||||
@warning_ignore("integer_division")
|
|
||||||
var mid: int = (low + high) / 2
|
|
||||||
|
|
||||||
if sections[mid].time <= time:
|
|
||||||
low = mid + 1
|
|
||||||
else:
|
|
||||||
high = mid - 1
|
|
||||||
|
|
||||||
var section: TempoSection = sections[maxi(high, 0)]
|
|
||||||
return section
|
|
||||||
|
|
||||||
|
|
||||||
class TempoSection extends Resource:
|
|
||||||
var beat: float
|
|
||||||
var beats_per_bar: int
|
|
||||||
var bpm: float
|
|
||||||
var time: float
|
|
||||||
|
|
||||||
func _init(_bpm: float, _beat: float, _beats_per_bar: int = 4) -> void:
|
|
||||||
bpm = _bpm
|
|
||||||
beat = _beat
|
|
||||||
beats_per_bar = _beats_per_bar
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://b7vjbaiu1nst
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
class_name TempoSectionInfo
|
|
||||||
extends Resource
|
|
||||||
|
|
||||||
@export var time: float = 0.0
|
|
||||||
@export var bpm: float = 120.0
|
|
||||||
@export var beats_per_bar: int = 4
|
|
||||||
|
|
||||||
@export var snap_to_previous_beat: bool = true
|
|
||||||
@export var snap_to_previous_bar: bool = false
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://cx1nws4t8hs2u
|
|
||||||
@ -3,8 +3,6 @@ extends Object
|
|||||||
|
|
||||||
|
|
||||||
const VEC3_HOR := Vector3(1.0, 0.0, 1.0)
|
const VEC3_HOR := Vector3(1.0, 0.0, 1.0)
|
||||||
const VEC3_XY := Vector3(1.0, 1.0, 0.0)
|
|
||||||
const VEC3_YZ := Vector3(0.0, 1.0, 1.0)
|
|
||||||
|
|
||||||
|
|
||||||
static func free_node_safely(node: Node) -> void:
|
static func free_node_safely(node: Node) -> void:
|
||||||
@ -45,13 +43,13 @@ static func get_all_children(node: Node, internal: bool = false) -> Array[Node]:
|
|||||||
|
|
||||||
|
|
||||||
static func node_distance(
|
static func node_distance(
|
||||||
node_a: Node3D, node_b: Node3D, position_multiplier := Vector3.ONE
|
node_a: Node3D, node_b: Node3D, position_modifier := Vector3.ONE
|
||||||
) -> float:
|
) -> float:
|
||||||
if not is_instance_valid(node_a) or not is_instance_valid(node_b):
|
if not is_instance_valid(node_a) or not is_instance_valid(node_b):
|
||||||
return -1.0
|
return -1.0
|
||||||
|
|
||||||
return (node_a.global_position * position_multiplier).distance_to(
|
return (node_a.global_position * position_modifier).distance_to(
|
||||||
node_b.global_position * position_multiplier
|
node_b.global_position * position_modifier
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -134,15 +132,3 @@ static func uid_to_res_path(uid: Variant) -> Variant:
|
|||||||
return paths
|
return paths
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
|
||||||
static func create_box_mesh_from_aabb(aabb: AABB) -> BoxMesh:
|
|
||||||
var mesh := BoxMesh.new()
|
|
||||||
mesh.size = aabb.size
|
|
||||||
return mesh
|
|
||||||
|
|
||||||
|
|
||||||
static func load_config(config_path: String) -> ConfigFile:
|
|
||||||
var config := ConfigFile.new()
|
|
||||||
config.load(config_path)
|
|
||||||
return config
|
|
||||||
|
|||||||
@ -10,9 +10,6 @@ extends Node
|
|||||||
#static var total_weak_vibration: float = 0.0
|
#static var total_weak_vibration: float = 0.0
|
||||||
#static var total_strong_vibration: float = 0.0
|
#static var total_strong_vibration: float = 0.0
|
||||||
#static var _vibration_handled: bool = false
|
#static var _vibration_handled: bool = false
|
||||||
static var min_weak_magnitude_threshold: float = 0.0
|
|
||||||
static var min_strong_magnitude_threshold: float = 0.0
|
|
||||||
static var min_duration_threshold: float = 0.0
|
|
||||||
|
|
||||||
## If [code]false[/code], calling [method vibrate] will not start a controller vibration.
|
## If [code]false[/code], calling [method vibrate] will not start a controller vibration.
|
||||||
@export var enabled: bool = true:
|
@export var enabled: bool = true:
|
||||||
@ -34,6 +31,7 @@ static var min_duration_threshold: float = 0.0
|
|||||||
|
|
||||||
#region Editor tooling
|
#region Editor tooling
|
||||||
@warning_ignore_start("unused_private_class_variable")
|
@warning_ignore_start("unused_private_class_variable")
|
||||||
|
@export var _editor_weak_vibration: bool = false
|
||||||
@export_tool_button("Test Vibration", "InputEventJoypadMotion")
|
@export_tool_button("Test Vibration", "InputEventJoypadMotion")
|
||||||
var _editor_test_vibration: Callable = vibrate
|
var _editor_test_vibration: Callable = vibrate
|
||||||
@export_tool_button("Stop Vibration", "MissingNode")
|
@export_tool_button("Stop Vibration", "MissingNode")
|
||||||
@ -57,12 +55,25 @@ var _editor_stop_test_vibration: Callable = stop_vibration
|
|||||||
get = get_magnitude_multiplier,
|
get = get_magnitude_multiplier,
|
||||||
set = set_magnitude_multiplier
|
set = set_magnitude_multiplier
|
||||||
|
|
||||||
|
@export_group("Weak Controller Overrides", "weak_controller_")
|
||||||
|
@export var weak_controller_magnitude_multiplier: float = 2.0
|
||||||
|
@export var weak_controller_duration_multiplier: float = 1.0
|
||||||
|
|
||||||
var animated_weak_magnitude: float = 0.0:
|
var animated_weak_magnitude: float = 0.0:
|
||||||
set(value):
|
set(value):
|
||||||
animated_weak_magnitude = clampf(value, 0.0, 1.0)
|
animated_weak_magnitude = clampf(value, 0.0, 1.0)
|
||||||
var animated_strong_magnitude: float = 0.0:
|
var animated_strong_magnitude: float = 0.0:
|
||||||
set(value):
|
set(value):
|
||||||
animated_strong_magnitude = clampf(value, 0.0, 1.0)
|
animated_strong_magnitude = clampf(value, 0.0, 1.0)
|
||||||
|
var _ed_was_weak_vibration: bool = false
|
||||||
|
|
||||||
|
|
||||||
|
func _notification(what: int) -> void:
|
||||||
|
if what == NOTIFICATION_EDITOR_PRE_SAVE:
|
||||||
|
_ed_was_weak_vibration = _editor_weak_vibration
|
||||||
|
_editor_weak_vibration = false
|
||||||
|
elif what == NOTIFICATION_EDITOR_POST_SAVE:
|
||||||
|
_editor_weak_vibration = _ed_was_weak_vibration
|
||||||
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
@ -70,13 +81,9 @@ func _process(delta: float) -> void:
|
|||||||
can_vibrate()
|
can_vibrate()
|
||||||
and not is_zero_approx(animated_weak_magnitude + animated_strong_magnitude)
|
and not is_zero_approx(animated_weak_magnitude + animated_strong_magnitude)
|
||||||
):
|
):
|
||||||
var _weak_magnitude: float = animated_weak_magnitude
|
Input.start_joy_vibration.call_deferred(
|
||||||
var _strong_magnitude: float = animated_strong_magnitude
|
device, animated_weak_magnitude, animated_strong_magnitude, delta
|
||||||
|
)
|
||||||
_weak_magnitude = remap(_weak_magnitude, 0.0, 1.0, min_weak_magnitude_threshold * float(_weak_magnitude > 0.0), 1.0)
|
|
||||||
_strong_magnitude = remap(_strong_magnitude, 0.0, 1.0, min_strong_magnitude_threshold * float(_strong_magnitude > 0.0), 1.0)
|
|
||||||
|
|
||||||
Input.start_joy_vibration.call_deferred(device, _weak_magnitude, _strong_magnitude, delta)
|
|
||||||
animated_weak_magnitude = 0.0
|
animated_weak_magnitude = 0.0
|
||||||
animated_strong_magnitude = 0.0
|
animated_strong_magnitude = 0.0
|
||||||
|
|
||||||
@ -87,6 +94,13 @@ func vibrate() -> void:
|
|||||||
if not can_vibrate():
|
if not can_vibrate():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
var is_weak_controller: bool
|
||||||
|
|
||||||
|
#if Engine.is_editor_hint():
|
||||||
|
#is_weak_controller = _editor_weak_vibration
|
||||||
|
#else:
|
||||||
|
#is_weak_controller = SettingsManager.get_setting(&"controls", &"using_weak_controller")
|
||||||
|
|
||||||
if delay > 0.0:
|
if delay > 0.0:
|
||||||
await get_tree().create_timer(delay).timeout
|
await get_tree().create_timer(delay).timeout
|
||||||
|
|
||||||
@ -96,14 +110,16 @@ func vibrate() -> void:
|
|||||||
#SPrint.print_msg("Audio Delay: %s" % (last_mix_time + output_latency))
|
#SPrint.print_msg("Audio Delay: %s" % (last_mix_time + output_latency))
|
||||||
await get_tree().create_timer(last_mix_time + output_latency).timeout
|
await get_tree().create_timer(last_mix_time + output_latency).timeout
|
||||||
|
|
||||||
var _weak_magnitude: float = clampf(weak_magnitude * magnitude_multiplier, 0.0, 1.0)
|
var _multiplier: float = magnitude_multiplier * (
|
||||||
var _strong_magnitude: float = clampf(strong_magnitude * magnitude_multiplier, 0.0, 1.0)
|
weak_controller_magnitude_multiplier if is_weak_controller else 1.0
|
||||||
|
)
|
||||||
|
|
||||||
_weak_magnitude = remap(_weak_magnitude, 0.0, 1.0, min_weak_magnitude_threshold * float(_weak_magnitude > 0.0), 1.0)
|
Input.start_joy_vibration(
|
||||||
_strong_magnitude = remap(_strong_magnitude, 0.0, 1.0, min_strong_magnitude_threshold * float(_strong_magnitude > 0.0), 1.0)
|
device,
|
||||||
duration = maxf(duration, min_duration_threshold)
|
clampf(weak_magnitude * _multiplier, 0.0, 1.0),
|
||||||
|
clampf(strong_magnitude * _multiplier, 0.0, 1.0),
|
||||||
Input.start_joy_vibration(device, _weak_magnitude, _strong_magnitude, duration)
|
duration * (weak_controller_duration_multiplier if is_weak_controller else 1.0)
|
||||||
|
)
|
||||||
|
|
||||||
#started_vibration.emit()
|
#started_vibration.emit()
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
[gd_resource type="ShaderMaterial" format=3 uid="uid://dag4adrscn4b7"]
|
|
||||||
|
|
||||||
[ext_resource type="Shader" uid="uid://b3d0qp0pjfhmk" path="res://tools/debug_outline.gdshader" id="1_1ceck"]
|
|
||||||
|
|
||||||
[resource]
|
|
||||||
render_priority = 0
|
|
||||||
shader = ExtResource("1_1ceck")
|
|
||||||
shader_parameter/base_color = Color(0.3, 1, 0.3, 0.15)
|
|
||||||
shader_parameter/edge_color = Color(1, 1, 0.3, 0.8)
|
|
||||||
shader_parameter/edge_power = 3.0
|
|
||||||
shader_parameter/box_center = Vector3(0, 0, 0)
|
|
||||||
shader_parameter/box_extents = Vector3(0, 0, 0)
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
[gd_resource type="ShaderMaterial" format=3 uid="uid://c8jcuss805aie"]
|
|
||||||
|
|
||||||
[ext_resource type="Shader" uid="uid://b3d0qp0pjfhmk" path="res://tools/debug_outline.gdshader" id="1_ibo8j"]
|
|
||||||
|
|
||||||
[resource]
|
|
||||||
render_priority = 0
|
|
||||||
shader = ExtResource("1_ibo8j")
|
|
||||||
shader_parameter/base_color = Color(0.29803923, 1, 0.29803923, 0.019607844)
|
|
||||||
shader_parameter/edge_color = Color(1, 1, 0.49803922, 0.06666667)
|
|
||||||
shader_parameter/edge_power = 2.0
|
|
||||||
@ -34,7 +34,6 @@ signal loading_finished
|
|||||||
#for aabb: AABB in load_aabbs:
|
#for aabb: AABB in load_aabbs:
|
||||||
#add_gizmo.call_deferred(AABBGizmo.new(aabb, self))
|
#add_gizmo.call_deferred(AABBGizmo.new(aabb, self))
|
||||||
@export_tool_button("Auto Generate AABB", "CSGBox3D") var editor_auto_gen_aabb: Callable = _editor_auto_gen_aabb
|
@export_tool_button("Auto Generate AABB", "CSGBox3D") var editor_auto_gen_aabb: Callable = _editor_auto_gen_aabb
|
||||||
@export var editor_visualize_aabbs_with_box: bool = true: set = _editor_set_visualize_aabbs_with_box
|
|
||||||
|
|
||||||
var loaded_level: Node
|
var loaded_level: Node
|
||||||
var is_loading_level: bool = false
|
var is_loading_level: bool = false
|
||||||
@ -141,13 +140,6 @@ func precompute_aabb() -> void:
|
|||||||
for aabb: AABB in _precomputed_aabbs:
|
for aabb: AABB in _precomputed_aabbs:
|
||||||
_precomputed_aabb = _precomputed_aabb.merge(aabb)
|
_precomputed_aabb = _precomputed_aabb.merge(aabb)
|
||||||
|
|
||||||
if Engine.is_editor_hint() and editor_visualize_aabbs_with_box:
|
|
||||||
setup_debug_meshes()
|
|
||||||
|
|
||||||
|
|
||||||
func get_precomputed_aabbs() -> Array[AABB]:
|
|
||||||
return _precomputed_aabbs
|
|
||||||
|
|
||||||
|
|
||||||
func load_level() -> void:
|
func load_level() -> void:
|
||||||
_unload_time_left = unload_delay
|
_unload_time_left = unload_delay
|
||||||
@ -208,36 +200,6 @@ func is_position_in_bounds(point: Vector3) -> bool:
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
||||||
func setup_debug_meshes(debug_material: Material = load("uid://c8jcuss805aie")) -> void:
|
|
||||||
var children: Array[Node] = find_children("_DEBUG_VISIBILITY*", "MeshInstance3D", false, false)
|
|
||||||
|
|
||||||
if not children.is_empty():
|
|
||||||
for child: Node in children:
|
|
||||||
child.free()
|
|
||||||
|
|
||||||
var aabbs: Array[AABB] = get_precomputed_aabbs()
|
|
||||||
|
|
||||||
for aabb_index: int in range(aabbs.size()):
|
|
||||||
var aabb: AABB = aabbs[aabb_index]
|
|
||||||
var mesh: BoxMesh = Utils.create_box_mesh_from_aabb(aabb)
|
|
||||||
var mesh_instance := MeshInstance3D.new()
|
|
||||||
mesh_instance.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
|
||||||
mesh_instance.mesh = mesh
|
|
||||||
mesh_instance.material_override = debug_material
|
|
||||||
mesh_instance.name = "_DEBUG_VISIBILITY" + str(aabb_index)
|
|
||||||
add_child(mesh_instance)
|
|
||||||
mesh_instance.position = to_local(aabb.position + aabb.size / 2.0)
|
|
||||||
mesh_instance.set_instance_shader_parameter(&"box_extents", aabb.size * 0.5)
|
|
||||||
mesh_instance.set_instance_shader_parameter(&"box_center", mesh_instance.global_position)
|
|
||||||
|
|
||||||
|
|
||||||
func clear_debug_meshes() -> void:
|
|
||||||
var children: Array[Node] = find_children("_DEBUG_VISIBILITY*", "MeshInstance3D", false, false)
|
|
||||||
|
|
||||||
for child: Node in children:
|
|
||||||
child.free()
|
|
||||||
|
|
||||||
|
|
||||||
func _draw_debug() -> void:
|
func _draw_debug() -> void:
|
||||||
for aabb: AABB in _precomputed_aabbs:
|
for aabb: AABB in _precomputed_aabbs:
|
||||||
Game.debug_draw("aabb", [aabb, Color.LAWN_GREEN])
|
Game.debug_draw("aabb", [aabb, Color.LAWN_GREEN])
|
||||||
@ -281,11 +243,3 @@ func _editor_auto_gen_aabb() -> void:
|
|||||||
ud.add_do_property(self, &"load_aabbs", load_aabbs)
|
ud.add_do_property(self, &"load_aabbs", load_aabbs)
|
||||||
ud.add_undo_property(self, &"load_aabbs", previous_aabbs)
|
ud.add_undo_property(self, &"load_aabbs", previous_aabbs)
|
||||||
ud.commit_action()
|
ud.commit_action()
|
||||||
|
|
||||||
|
|
||||||
func _editor_set_visualize_aabbs_with_box(value: bool) -> void:
|
|
||||||
editor_visualize_aabbs_with_box = value
|
|
||||||
if value:
|
|
||||||
setup_debug_meshes()
|
|
||||||
else:
|
|
||||||
clear_debug_meshes()
|
|
||||||
|
|||||||
@ -5,12 +5,11 @@ signal loaded
|
|||||||
signal change_world_requested(new_world_path: String, save_previous: bool, load_from_save: bool)
|
signal change_world_requested(new_world_path: String, save_previous: bool, load_from_save: bool)
|
||||||
signal world_unload_requested(do_save: bool)
|
signal world_unload_requested(do_save: bool)
|
||||||
|
|
||||||
@export var level_streamers: Array[LevelStreamer] = []
|
@export var level_streamers: Array[LevelStreamer]
|
||||||
## Reference to the player.
|
## Reference to the player.
|
||||||
## Should probably have a [method apply_orientation] and [method get_orientation] function,
|
## Should probably have a [method apply_orientation] and [method get_orientation] function,
|
||||||
## since the return value get's put into [member WorldState.player_transform].
|
## since the return value get's put into [member WorldState.player_transform].
|
||||||
@export var player: Node3D
|
@export var player: Node3D
|
||||||
|
|
||||||
@export_group("Debug", "debug_")
|
@export_group("Debug", "debug_")
|
||||||
## Only applies when this world is the game's current scene.
|
## Only applies when this world is the game's current scene.
|
||||||
@export_custom(PROPERTY_HINT_GROUP_ENABLE, "debug_") var debug_enabled: bool = false
|
@export_custom(PROPERTY_HINT_GROUP_ENABLE, "debug_") var debug_enabled: bool = false
|
||||||
@ -24,10 +23,6 @@ signal world_unload_requested(do_save: bool)
|
|||||||
var world_state: WorldState
|
var world_state: WorldState
|
||||||
|
|
||||||
|
|
||||||
func _exit_tree() -> void:
|
|
||||||
LimboConsole.unregister_command("toggle_level_streamer_visibility")
|
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
if debug_enabled:
|
if debug_enabled:
|
||||||
if get_tree().current_scene == self:
|
if get_tree().current_scene == self:
|
||||||
@ -41,8 +36,6 @@ func _ready() -> void:
|
|||||||
initialize_level_streamers()
|
initialize_level_streamers()
|
||||||
initialize_world_proxies()
|
initialize_world_proxies()
|
||||||
|
|
||||||
register_commands()
|
|
||||||
|
|
||||||
loaded.emit()
|
loaded.emit()
|
||||||
|
|
||||||
|
|
||||||
@ -155,36 +148,6 @@ func get_instance_state(key: String, default: Variant) -> Variant:
|
|||||||
return world_state.instance_data.get(key, default)
|
return world_state.instance_data.get(key, default)
|
||||||
|
|
||||||
|
|
||||||
func register_commands() -> void:
|
|
||||||
var toggle_streamer_visibility: Callable = func() -> void:
|
|
||||||
var center_marker_mat := StandardMaterial3D.new()
|
|
||||||
center_marker_mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
|
||||||
center_marker_mat.no_depth_test = true
|
|
||||||
center_marker_mat.depth_draw_mode = BaseMaterial3D.DEPTH_DRAW_DISABLED
|
|
||||||
center_marker_mat.disable_ambient_light = true
|
|
||||||
center_marker_mat.disable_fog = true
|
|
||||||
center_marker_mat.disable_specular_occlusion = true
|
|
||||||
center_marker_mat.disable_receive_shadows = true
|
|
||||||
|
|
||||||
for streamer: LevelStreamer in level_streamers:
|
|
||||||
var children: Array[Node] = streamer.find_children("_DEBUG_VISIBILITY*", "MeshInstance3D", false, false)
|
|
||||||
|
|
||||||
if children.is_empty():
|
|
||||||
streamer.setup_debug_meshes(load("uid://dag4adrscn4b7"))
|
|
||||||
|
|
||||||
var center_marker := MeshInstance3D.new()
|
|
||||||
center_marker.mesh = SphereMesh.new()
|
|
||||||
center_marker.scale *= 0.2
|
|
||||||
center_marker.name = "_DEBUG_VISIBILITY_CMARKER" + streamer.level_id
|
|
||||||
center_marker.material_override = center_marker_mat
|
|
||||||
streamer.add_child(center_marker)
|
|
||||||
else:
|
|
||||||
for child: Node in children:
|
|
||||||
child.queue_free()
|
|
||||||
|
|
||||||
LimboConsole.register_command(toggle_streamer_visibility, "toggle_level_streamer_visibility")
|
|
||||||
|
|
||||||
|
|
||||||
func _load_levels_player_is_in() -> void:
|
func _load_levels_player_is_in() -> void:
|
||||||
if not is_instance_valid(player):
|
if not is_instance_valid(player):
|
||||||
return
|
return
|
||||||
|
|||||||
@ -34,7 +34,6 @@ static func debug_draw(draw_shape: String, args: Array) -> void:
|
|||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
if not Engine.is_editor_hint():
|
if not Engine.is_editor_hint():
|
||||||
load_main_menu()
|
load_main_menu()
|
||||||
register_commands()
|
|
||||||
|
|
||||||
|
|
||||||
func load_game(save_data: SaveData, save_slot: int) -> void:
|
func load_game(save_data: SaveData, save_slot: int) -> void:
|
||||||
@ -94,6 +93,7 @@ func unload_world(do_save: bool = true) -> void:
|
|||||||
save_world_state()
|
save_world_state()
|
||||||
|
|
||||||
world.process_mode = Node.PROCESS_MODE_DISABLED
|
world.process_mode = Node.PROCESS_MODE_DISABLED
|
||||||
|
await loading_screen.fade_in()
|
||||||
world.queue_free()
|
world.queue_free()
|
||||||
|
|
||||||
|
|
||||||
@ -142,27 +142,12 @@ func quit_game() -> void:
|
|||||||
get_tree().quit()
|
get_tree().quit()
|
||||||
|
|
||||||
|
|
||||||
func register_commands() -> void:
|
|
||||||
var load_save_func: Callable = func(save_index: int) -> void:
|
|
||||||
var path: String = MainMenu.get_save_path(save_index)
|
|
||||||
if ResourceLoader.exists(path):
|
|
||||||
load_game(load(path), save_index)
|
|
||||||
|
|
||||||
LimboConsole.register_command(load_save_func, "load_save", "Loads the save with the given save slot index.")
|
|
||||||
LimboConsole.register_command(save_game, "save_game", "Saves the game based on the current save slot.")
|
|
||||||
|
|
||||||
|
|
||||||
func _on_unload_world_request(do_save: bool) -> void:
|
func _on_unload_world_request(do_save: bool) -> void:
|
||||||
|
await unload_world(do_save)
|
||||||
|
|
||||||
if do_save:
|
if do_save:
|
||||||
save_game()
|
save_game()
|
||||||
|
|
||||||
if is_instance_valid(world):
|
|
||||||
world.process_mode = Node.PROCESS_MODE_DISABLED
|
|
||||||
|
|
||||||
await loading_screen.fade_in()
|
|
||||||
|
|
||||||
unload_world(do_save)
|
|
||||||
|
|
||||||
await load_main_menu()
|
await load_main_menu()
|
||||||
loading_screen.fade_out()
|
loading_screen.fade_out()
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,5 @@ metadata/_custom_type_script = "uid://cl1u038dbrou2"
|
|||||||
[node name="WorldContainer" type="Node" parent="." unique_id=1835125942]
|
[node name="WorldContainer" type="Node" parent="." unique_id=1835125942]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
||||||
[node name="CanvasLayer" type="CanvasLayer" parent="." unique_id=919699501]
|
[node name="LoadingScreen" parent="." unique_id=1509462056 instance=ExtResource("2_3msxb")]
|
||||||
|
|
||||||
[node name="LoadingScreen" parent="CanvasLayer" unique_id=1509462056 instance=ExtResource("2_3msxb")]
|
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|||||||
@ -31,8 +31,6 @@ var has_control: bool = true
|
|||||||
var can_run: bool = true
|
var can_run: bool = true
|
||||||
var is_crouching: bool = false
|
var is_crouching: bool = false
|
||||||
var crouch_speed_affection: float = 0.0
|
var crouch_speed_affection: float = 0.0
|
||||||
var _noclipping: bool = false
|
|
||||||
var _flying: bool = false
|
|
||||||
|
|
||||||
@onready var head: PlayerHead = %Head
|
@onready var head: PlayerHead = %Head
|
||||||
@onready var collision_shape: CollisionShape3D = %CylinderCollider
|
@onready var collision_shape: CollisionShape3D = %CylinderCollider
|
||||||
@ -45,19 +43,12 @@ func _init() -> void:
|
|||||||
player = self
|
player = self
|
||||||
|
|
||||||
|
|
||||||
func _exit_tree() -> void:
|
|
||||||
LimboConsole.unregister_command("player_fly")
|
|
||||||
LimboConsole.unregister_command("player_noclip")
|
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
head.rotation.y = global_rotation.y
|
head.rotation.y = global_rotation.y
|
||||||
global_rotation.y = 0.0
|
global_rotation.y = 0.0
|
||||||
|
|
||||||
flashlight.manager = flashlight_manager
|
flashlight.manager = flashlight_manager
|
||||||
|
|
||||||
_register_commands()
|
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
#var lagspike: bool = randf() < 0.005
|
#var lagspike: bool = randf() < 0.005
|
||||||
@ -179,66 +170,3 @@ func handle_crouching(delta: float) -> void:
|
|||||||
"Crouching Speed Affection: %s\nIs Crouching: %s" % [crouch_speed_affection, is_crouching],
|
"Crouching Speed Affection: %s\nIs Crouching: %s" % [crouch_speed_affection, is_crouching],
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func _register_commands() -> void:
|
|
||||||
var noclip_func: Callable = func() -> void:
|
|
||||||
const _NOCLIP_SPEED: float = 15.0
|
|
||||||
_flying = not _flying
|
|
||||||
|
|
||||||
if _flying and _noclipping:
|
|
||||||
_flying = false
|
|
||||||
|
|
||||||
while _flying and is_inside_tree():
|
|
||||||
if InputManager.is_window_focused():
|
|
||||||
set_physics_process(false)
|
|
||||||
var input_dir: Vector3 = get_input_direction()
|
|
||||||
movement_direction = transform.basis * Vector3(input_dir.x, 1.0, input_dir.z)
|
|
||||||
movement_direction = (movement_direction * Utils.VEC3_HOR).normalized()
|
|
||||||
movement_direction = movement_direction.rotated(Vector3.UP, head.global_rotation.y)
|
|
||||||
|
|
||||||
if Input.is_action_pressed(ACTION_JUMP):
|
|
||||||
movement_direction.y = 1.0 * _NOCLIP_SPEED
|
|
||||||
elif Input.is_action_pressed(ACTION_CROUCH):
|
|
||||||
movement_direction.y = -1.0 * _NOCLIP_SPEED
|
|
||||||
|
|
||||||
speed = _NOCLIP_SPEED
|
|
||||||
move(movement_direction, get_physics_process_delta_time())
|
|
||||||
velocity.y = movement_direction.y
|
|
||||||
global_position += velocity * get_physics_process_delta_time()
|
|
||||||
|
|
||||||
await get_tree().process_frame
|
|
||||||
|
|
||||||
set_physics_process(true)
|
|
||||||
|
|
||||||
var fly_func: Callable = func() -> void:
|
|
||||||
const _FLY_SPEED: float = 15.0
|
|
||||||
_flying = not _flying
|
|
||||||
|
|
||||||
if _noclipping and _flying:
|
|
||||||
_noclipping = false
|
|
||||||
|
|
||||||
while _flying and is_inside_tree():
|
|
||||||
if InputManager.is_window_focused():
|
|
||||||
set_physics_process(false)
|
|
||||||
var input_dir: Vector3 = get_input_direction()
|
|
||||||
movement_direction = transform.basis * Vector3(input_dir.x, 1.0, input_dir.z)
|
|
||||||
movement_direction = (movement_direction * Utils.VEC3_HOR).normalized()
|
|
||||||
movement_direction = movement_direction.rotated(Vector3.UP, head.global_rotation.y)
|
|
||||||
|
|
||||||
if Input.is_action_pressed(ACTION_JUMP):
|
|
||||||
movement_direction.y = 1.0 * _FLY_SPEED
|
|
||||||
elif Input.is_action_pressed(ACTION_CROUCH):
|
|
||||||
movement_direction.y = -1.0 * _FLY_SPEED
|
|
||||||
|
|
||||||
speed = _FLY_SPEED
|
|
||||||
move(movement_direction, get_physics_process_delta_time())
|
|
||||||
velocity.y = movement_direction.y
|
|
||||||
move_and_slide()
|
|
||||||
|
|
||||||
await get_tree().process_frame
|
|
||||||
|
|
||||||
set_physics_process(true)
|
|
||||||
|
|
||||||
LimboConsole.register_command(noclip_func, "player_noclip")
|
|
||||||
LimboConsole.register_command(fly_func, "player_fly")
|
|
||||||
|
|||||||
@ -1,65 +0,0 @@
|
|||||||
@tool
|
|
||||||
class_name Stagelight
|
|
||||||
extends Node3D
|
|
||||||
|
|
||||||
@export var light_color := Color.WHITE: set = set_light_color
|
|
||||||
@export var lightbeam_material: ShaderMaterial: set = set_lightbeam_material
|
|
||||||
@export var light_surface_material: ShaderMaterial: set = set_light_surface_material
|
|
||||||
@export var phase_multiplier: float = 0.5: set = set_phase_multiplier
|
|
||||||
@export var phase_offset: float = 0.0: set = set_phase_offset
|
|
||||||
|
|
||||||
@onready var spot_light: SpotLight3D = %SpotLight
|
|
||||||
@onready var rhythm_property_setter: RhythmPropertySetter = %RhythmPropertySetter
|
|
||||||
@onready var lightbeam: MeshInstance3D = %Lightbeam
|
|
||||||
@onready var cylinder: MeshInstance3D = $StudioSpotLight/Cylinder
|
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
|
||||||
set_light_color(light_color)
|
|
||||||
set_lightbeam_material(lightbeam_material)
|
|
||||||
set_light_surface_material(light_surface_material)
|
|
||||||
|
|
||||||
|
|
||||||
func set_light_color(color: Color) -> void:
|
|
||||||
light_color = color
|
|
||||||
|
|
||||||
if not is_node_ready():
|
|
||||||
await ready
|
|
||||||
|
|
||||||
spot_light.light_color = light_color
|
|
||||||
|
|
||||||
|
|
||||||
func set_lightbeam_material(shader_material: ShaderMaterial) -> void:
|
|
||||||
lightbeam_material = shader_material
|
|
||||||
|
|
||||||
if not is_node_ready():
|
|
||||||
await ready
|
|
||||||
|
|
||||||
lightbeam.material_override = lightbeam_material
|
|
||||||
|
|
||||||
|
|
||||||
func set_light_surface_material(shader_material: ShaderMaterial) -> void:
|
|
||||||
light_surface_material = shader_material
|
|
||||||
|
|
||||||
if not is_node_ready():
|
|
||||||
await ready
|
|
||||||
|
|
||||||
cylinder.set_surface_override_material(1, light_surface_material)
|
|
||||||
|
|
||||||
|
|
||||||
func set_phase_multiplier(multiplier: float) -> void:
|
|
||||||
phase_multiplier = multiplier
|
|
||||||
|
|
||||||
if not is_node_ready():
|
|
||||||
await ready
|
|
||||||
|
|
||||||
rhythm_property_setter.phase_multiplier = phase_multiplier
|
|
||||||
|
|
||||||
|
|
||||||
func set_phase_offset(offset: float) -> void:
|
|
||||||
phase_offset = offset
|
|
||||||
|
|
||||||
if not is_node_ready():
|
|
||||||
await ready
|
|
||||||
|
|
||||||
rhythm_property_setter.phase_shift = phase_offset
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://cmh1y78o47f06
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
[gd_scene format=3 uid="uid://bc84qk6pfgctg"]
|
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://pvll6bliout1" path="res://assets/models/electrics/spot_light/studio_spot_light.glb" id="1_3cilv"]
|
|
||||||
[ext_resource type="Script" uid="uid://cmh1y78o47f06" path="res://src/gameplay/objects/stagelight/stagelight.gd" id="1_wce40"]
|
|
||||||
[ext_resource type="Shader" uid="uid://b46o5a45g58xb" path="res://assets/shaders/rhythm/beating_surface.gdshader" id="2_wce40"]
|
|
||||||
[ext_resource type="PackedScene" uid="uid://bwejp67rw5erj" path="res://assets/models/electrics/spot_light/studio_spot_light_framing_01.glb" id="3_otype"]
|
|
||||||
[ext_resource type="Shader" uid="uid://c32arbbdbo7m8" path="res://assets/shaders/rhythm/beating_lightbeam.gdshader" id="4_d6ag8"]
|
|
||||||
[ext_resource type="Script" uid="uid://bvmfdeypbeqsn" path="res://src/core/rhythm/rhythm_property_setter.gd" id="5_uce1x"]
|
|
||||||
|
|
||||||
[sub_resource type="Gradient" id="Gradient_wj31t"]
|
|
||||||
interpolation_mode = 2
|
|
||||||
offsets = PackedFloat32Array(0, 0.38108107, 0.5054054, 0.7162162)
|
|
||||||
colors = PackedColorArray(1, 1, 1, 1, 1, 1, 1, 0.44602686, 1, 1, 1, 0.08750595, 1, 1, 1, 0)
|
|
||||||
|
|
||||||
[sub_resource type="GradientTexture2D" id="GradientTexture2D_du0pf"]
|
|
||||||
gradient = SubResource("Gradient_wj31t")
|
|
||||||
width = 1
|
|
||||||
fill_to = Vector2(0, 0.48931623)
|
|
||||||
|
|
||||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_xn83m"]
|
|
||||||
render_priority = 0
|
|
||||||
shader = ExtResource("4_d6ag8")
|
|
||||||
shader_parameter/phase_offset = 0.0
|
|
||||||
shader_parameter/phase_multiplier = 0.5
|
|
||||||
shader_parameter/color = Color(1, 1, 1, 1)
|
|
||||||
shader_parameter/alpha_multiplier = 0.25
|
|
||||||
shader_parameter/albedo = SubResource("GradientTexture2D_du0pf")
|
|
||||||
shader_parameter/flash_exponent = 4.0
|
|
||||||
|
|
||||||
[sub_resource type="Gradient" id="Gradient_vtuqv"]
|
|
||||||
interpolation_mode = 1
|
|
||||||
offsets = PackedFloat32Array(1)
|
|
||||||
colors = PackedColorArray(1, 1, 1, 1)
|
|
||||||
|
|
||||||
[sub_resource type="GradientTexture2D" id="GradientTexture2D_glp4g"]
|
|
||||||
gradient = SubResource("Gradient_vtuqv")
|
|
||||||
width = 1
|
|
||||||
height = 1
|
|
||||||
|
|
||||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_nqu5b"]
|
|
||||||
render_priority = 0
|
|
||||||
shader = ExtResource("2_wce40")
|
|
||||||
shader_parameter/phase_offset = 0.0
|
|
||||||
shader_parameter/phase_multiplier = 0.5
|
|
||||||
shader_parameter/albedo = SubResource("GradientTexture2D_glp4g")
|
|
||||||
shader_parameter/flash_exponent = 4.0
|
|
||||||
shader_parameter/intensity_multiplier = 8.0
|
|
||||||
|
|
||||||
[sub_resource type="CylinderMesh" id="CylinderMesh_787dp"]
|
|
||||||
top_radius = 0.275
|
|
||||||
bottom_radius = 2.0
|
|
||||||
height = 12.0
|
|
||||||
cap_top = false
|
|
||||||
cap_bottom = false
|
|
||||||
|
|
||||||
[node name="Stagelight" type="Node3D" unique_id=729680764]
|
|
||||||
script = ExtResource("1_wce40")
|
|
||||||
lightbeam_material = SubResource("ShaderMaterial_xn83m")
|
|
||||||
light_surface_material = SubResource("ShaderMaterial_nqu5b")
|
|
||||||
|
|
||||||
[node name="StudioSpotLight" parent="." unique_id=1407009402 instance=ExtResource("1_3cilv")]
|
|
||||||
|
|
||||||
[node name="Cylinder" parent="StudioSpotLight" index="0" unique_id=1873656815]
|
|
||||||
surface_material_override/1 = SubResource("ShaderMaterial_nqu5b")
|
|
||||||
|
|
||||||
[node name="StudioSpotLightFraming01" parent="." unique_id=166030914 instance=ExtResource("3_otype")]
|
|
||||||
|
|
||||||
[node name="LightbeamRoot" type="Node3D" parent="." unique_id=1922157293]
|
|
||||||
transform = Transform3D(-4.371139e-08, -1, 0, 1, -4.371139e-08, 0, 0, 0, 1, 0.2, 8.742278e-09, 0)
|
|
||||||
|
|
||||||
[node name="Lightbeam" type="MeshInstance3D" parent="LightbeamRoot" unique_id=1840014799]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
transform = Transform3D(0.5, 0, 0, 0, 0.49999994, 0, 0, 0, 0.5, 0, -2.9999995, 0)
|
|
||||||
material_override = SubResource("ShaderMaterial_xn83m")
|
|
||||||
cast_shadow = 0
|
|
||||||
instance_shader_parameters/instance_phase_offset = 0.0
|
|
||||||
mesh = SubResource("CylinderMesh_787dp")
|
|
||||||
|
|
||||||
[node name="SpotLight" type="SpotLight3D" parent="LightbeamRoot" unique_id=1841045966]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 0.8, 0)
|
|
||||||
spot_range = 50.0
|
|
||||||
spot_attenuation = 1.38
|
|
||||||
spot_angle = 8.75
|
|
||||||
spot_angle_attenuation = 0.23325838
|
|
||||||
|
|
||||||
[node name="RhythmPropertySetter" type="Node" parent="LightbeamRoot" unique_id=1355223878 node_paths=PackedStringArray("nodes")]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
script = ExtResource("5_uce1x")
|
|
||||||
nodes = [NodePath("../SpotLight")]
|
|
||||||
beat_property = &"light_energy"
|
|
||||||
phase_multiplier = 0.5
|
|
||||||
beat_range_min = 10.0
|
|
||||||
beat_range_max = 0.0
|
|
||||||
beat_exponent = 4.0
|
|
||||||
metadata/_custom_type_script = "uid://bvmfdeypbeqsn"
|
|
||||||
|
|
||||||
[editable path="StudioSpotLight"]
|
|
||||||
@ -31,14 +31,6 @@ func _enter_tree() -> void:
|
|||||||
_world_proxy = WorldProxy.new()
|
_world_proxy = WorldProxy.new()
|
||||||
add_child(_world_proxy)
|
add_child(_world_proxy)
|
||||||
|
|
||||||
LimboConsole.register_command(refill_power, "flashlight_refill", "Fill up the flashlight's power by this amount.")
|
|
||||||
LimboConsole.register_command(drain_power, "flashlight_drain", "Drain the flashlight's power by this amount.")
|
|
||||||
|
|
||||||
|
|
||||||
func _exit_tree() -> void:
|
|
||||||
LimboConsole.unregister_command(refill_power)
|
|
||||||
LimboConsole.unregister_command(drain_power)
|
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
world = await _world_proxy.wait_for_world()
|
world = await _world_proxy.wait_for_world()
|
||||||
|
|||||||
@ -23,7 +23,7 @@ func _ready() -> void:
|
|||||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
|
||||||
|
|
||||||
func fade_in(duration: float = fade_in_duration) -> void:
|
func fade_in() -> void:
|
||||||
color_rect.show()
|
color_rect.show()
|
||||||
set_process(true)
|
set_process(true)
|
||||||
fading = true
|
fading = true
|
||||||
@ -33,13 +33,13 @@ func fade_in(duration: float = fade_in_duration) -> void:
|
|||||||
_tween.kill()
|
_tween.kill()
|
||||||
|
|
||||||
_tween = create_tween()
|
_tween = create_tween()
|
||||||
_tween.tween_property(color_rect, ^"color:a", 1.0, duration)
|
_tween.tween_property(color_rect, ^"color:a", 1.0, fade_in_duration)
|
||||||
await _tween.finished
|
await _tween.finished
|
||||||
|
|
||||||
set_loading_hints_visible(true)
|
set_loading_hints_visible(true)
|
||||||
|
|
||||||
|
|
||||||
func fade_out(duration: float = fade_out_duration) -> void:
|
func fade_out() -> void:
|
||||||
set_loading_hints_visible(false)
|
set_loading_hints_visible(false)
|
||||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
fading = false
|
fading = false
|
||||||
@ -48,7 +48,7 @@ func fade_out(duration: float = fade_out_duration) -> void:
|
|||||||
_tween.kill()
|
_tween.kill()
|
||||||
|
|
||||||
_tween = create_tween()
|
_tween = create_tween()
|
||||||
_tween.tween_property(color_rect, ^"color:a", 0.0, duration)
|
_tween.tween_property(color_rect, ^"color:a", 0.0, fade_out_duration)
|
||||||
await _tween.finished
|
await _tween.finished
|
||||||
|
|
||||||
color_rect.hide()
|
color_rect.hide()
|
||||||
|
|||||||
@ -11,12 +11,10 @@ const SAVE_DATA_PATH: String = SAVES_DIR + "%s/save_data.tres"
|
|||||||
@onready var continue_button: Button = %ContinueButton
|
@onready var continue_button: Button = %ContinueButton
|
||||||
@onready var load_button: Button = %LoadButton
|
@onready var load_button: Button = %LoadButton
|
||||||
@onready var new_game_button: Button = %NewGameButton
|
@onready var new_game_button: Button = %NewGameButton
|
||||||
@onready var options_button: Button = %OptionsButton
|
|
||||||
@onready var quit_button: Button = %QuitButton
|
@onready var quit_button: Button = %QuitButton
|
||||||
|
|
||||||
@onready var saves_scroll_container: ScrollContainer = %SavesScrollContainer
|
@onready var saves_scroll_container: ScrollContainer = %SavesScrollContainer
|
||||||
@onready var saves_container: VBoxContainer = %SavesContainer
|
@onready var saves_container: VBoxContainer = %SavesContainer
|
||||||
@onready var settings_menu: SettingsMenu = %SettingsMenu
|
|
||||||
|
|
||||||
|
|
||||||
static func get_save_slots() -> PackedInt32Array:
|
static func get_save_slots() -> PackedInt32Array:
|
||||||
@ -54,17 +52,15 @@ func _ready() -> void:
|
|||||||
continue_button.pressed.connect(_on_continue_pressed)
|
continue_button.pressed.connect(_on_continue_pressed)
|
||||||
load_button.pressed.connect(_on_load_pressed)
|
load_button.pressed.connect(_on_load_pressed)
|
||||||
new_game_button.pressed.connect(_on_new_game_pressed)
|
new_game_button.pressed.connect(_on_new_game_pressed)
|
||||||
options_button.pressed.connect(_on_options_pressed)
|
|
||||||
quit_button.pressed.connect(_on_quit_pressed)
|
quit_button.pressed.connect(_on_quit_pressed)
|
||||||
|
|
||||||
settings_menu.close_request.connect(_on_settings_close_request)
|
|
||||||
|
|
||||||
continue_button.visible = has_save()
|
continue_button.visible = has_save()
|
||||||
|
|
||||||
|
|
||||||
func has_save() -> bool:
|
func has_save() -> bool:
|
||||||
var config: ConfigFile = Utils.load_config(USER_SETTINGS_PATH)
|
var config := ConfigFile.new()
|
||||||
return config.has_section_key("save", "last_slot")
|
config.load(USER_SETTINGS_PATH)
|
||||||
|
return config.has_section_key("game", "last_slot")
|
||||||
|
|
||||||
|
|
||||||
func populate_save_entries() -> void:
|
func populate_save_entries() -> void:
|
||||||
@ -78,8 +74,9 @@ func populate_save_entries() -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _on_continue_pressed() -> void:
|
func _on_continue_pressed() -> void:
|
||||||
var config: ConfigFile = Utils.load_config(USER_SETTINGS_PATH)
|
var config := ConfigFile.new()
|
||||||
var slot: int = config.get_value("save", "last_slot", 0)
|
config.load(USER_SETTINGS_PATH)
|
||||||
|
var slot: int = config.get_value("game", "last_slot", 0)
|
||||||
var path: String = get_save_path(slot)
|
var path: String = get_save_path(slot)
|
||||||
|
|
||||||
if not FileAccess.file_exists(path):
|
if not FileAccess.file_exists(path):
|
||||||
@ -89,7 +86,7 @@ func _on_continue_pressed() -> void:
|
|||||||
_on_new_game_pressed()
|
_on_new_game_pressed()
|
||||||
return
|
return
|
||||||
|
|
||||||
config.set_value("save", "last_slot", slot)
|
config.set_value("game", "last_slot", slot)
|
||||||
config.save(USER_SETTINGS_PATH)
|
config.save(USER_SETTINGS_PATH)
|
||||||
|
|
||||||
var save_data: SaveData = load(path)
|
var save_data: SaveData = load(path)
|
||||||
@ -106,22 +103,20 @@ func _on_new_game_pressed() -> void:
|
|||||||
var slot_index: int = get_unique_save_slot_index()
|
var slot_index: int = get_unique_save_slot_index()
|
||||||
load_game_request.emit(save_data, slot_index)
|
load_game_request.emit(save_data, slot_index)
|
||||||
|
|
||||||
var config: ConfigFile = Utils.load_config(USER_SETTINGS_PATH)
|
var config := ConfigFile.new()
|
||||||
config.set_value("save", "last_slot", slot_index)
|
config.load(USER_SETTINGS_PATH)
|
||||||
|
config.set_value("game", "last_slot", slot_index)
|
||||||
config.save(USER_SETTINGS_PATH)
|
config.save(USER_SETTINGS_PATH)
|
||||||
|
|
||||||
|
|
||||||
func _on_options_pressed() -> void:
|
|
||||||
settings_menu.show()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_quit_pressed() -> void:
|
func _on_quit_pressed() -> void:
|
||||||
quit_request.emit()
|
quit_request.emit()
|
||||||
|
|
||||||
|
|
||||||
func _on_save_entry_pressed(save_slot: int) -> void:
|
func _on_save_entry_pressed(save_slot: int) -> void:
|
||||||
var config: ConfigFile = Utils.load_config(USER_SETTINGS_PATH)
|
var config := ConfigFile.new()
|
||||||
config.set_value("save", "last_slot", save_slot)
|
config.load(USER_SETTINGS_PATH)
|
||||||
|
config.set_value("game", "last_slot", save_slot)
|
||||||
config.save(USER_SETTINGS_PATH)
|
config.save(USER_SETTINGS_PATH)
|
||||||
|
|
||||||
var save_path: String = get_save_path(save_slot)
|
var save_path: String = get_save_path(save_slot)
|
||||||
@ -132,7 +127,3 @@ func _on_save_entry_pressed(save_slot: int) -> void:
|
|||||||
|
|
||||||
var save_data: SaveData = load(save_path)
|
var save_data: SaveData = load(save_path)
|
||||||
load_game_request.emit(save_data, save_slot)
|
load_game_request.emit(save_data, save_slot)
|
||||||
|
|
||||||
|
|
||||||
func _on_settings_close_request() -> void:
|
|
||||||
settings_menu.hide()
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
[ext_resource type="Script" uid="uid://ckaouf136x7rh" path="res://src/ui/main_menu/main_menu.gd" id="1_2hyyg"]
|
[ext_resource type="Script" uid="uid://ckaouf136x7rh" path="res://src/ui/main_menu/main_menu.gd" id="1_2hyyg"]
|
||||||
[ext_resource type="Material" uid="uid://c38215ysnknyk" path="res://assets/dev/dark/dark_01.tres" id="2_4ux21"]
|
[ext_resource type="Material" uid="uid://c38215ysnknyk" path="res://assets/dev/dark/dark_01.tres" id="2_4ux21"]
|
||||||
[ext_resource type="PackedScene" uid="uid://cxm2uc8fw031f" path="res://src/ui/settings_menu/settings_menu.tscn" id="2_osdni"]
|
|
||||||
|
|
||||||
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_4ux21"]
|
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_4ux21"]
|
||||||
sky_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1)
|
sky_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1)
|
||||||
@ -68,11 +67,6 @@ unique_name_in_owner = true
|
|||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "NEW_GAME"
|
text = "NEW_GAME"
|
||||||
|
|
||||||
[node name="OptionsButton" type="Button" parent="MainButtons" unique_id=666018363]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
text = "SETTINGS"
|
|
||||||
|
|
||||||
[node name="QuitButton" type="Button" parent="MainButtons" unique_id=851953121]
|
[node name="QuitButton" type="Button" parent="MainButtons" unique_id=851953121]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
@ -117,22 +111,6 @@ text = "Save 1"
|
|||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Save 1"
|
text = "Save 1"
|
||||||
|
|
||||||
[node name="SettingsMenu" parent="." unique_id=8078639 instance=ExtResource("2_osdni")]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
visible = false
|
|
||||||
layout_mode = 1
|
|
||||||
|
|
||||||
[node name="ColorRect" type="ColorRect" parent="SettingsMenu" unique_id=1002221363]
|
|
||||||
show_behind_parent = true
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
mouse_filter = 2
|
|
||||||
color = Color(0, 0, 0, 0.6901961)
|
|
||||||
|
|
||||||
[node name="Background" type="Node3D" parent="." unique_id=877986421]
|
[node name="Background" type="Node3D" parent="." unique_id=877986421]
|
||||||
|
|
||||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="Background" unique_id=3797920]
|
[node name="WorldEnvironment" type="WorldEnvironment" parent="Background" unique_id=3797920]
|
||||||
|
|||||||
@ -5,26 +5,17 @@ extends Control
|
|||||||
@export var world: World
|
@export var world: World
|
||||||
|
|
||||||
@onready var continue_button: Button = %ContinueButton
|
@onready var continue_button: Button = %ContinueButton
|
||||||
@onready var settings_button: Button = %SettingsButton
|
|
||||||
@onready var main_menu_button: Button = %MainMenuButton
|
@onready var main_menu_button: Button = %MainMenuButton
|
||||||
|
|
||||||
@onready var settings_menu: SettingsMenu = %SettingsMenu
|
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
continue_button.pressed.connect(set_paused.bind(false))
|
continue_button.pressed.connect(set_paused.bind(false))
|
||||||
settings_button.pressed.connect(_on_settings_button_pressed)
|
|
||||||
main_menu_button.pressed.connect(_on_main_menu_button_pressed)
|
main_menu_button.pressed.connect(_on_main_menu_button_pressed)
|
||||||
|
|
||||||
settings_menu.close_request.connect(_on_settings_menu_close_requested)
|
|
||||||
|
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
if event.is_action_pressed(&"pause"):
|
if event.is_action_pressed(&"pause"):
|
||||||
if settings_menu.is_visible_in_tree():
|
toggle_pause()
|
||||||
settings_menu.close()
|
|
||||||
else:
|
|
||||||
toggle_pause()
|
|
||||||
|
|
||||||
|
|
||||||
func set_paused(value: bool) -> void:
|
func set_paused(value: bool) -> void:
|
||||||
@ -40,13 +31,5 @@ func toggle_pause() -> void:
|
|||||||
set_paused(not is_paused())
|
set_paused(not is_paused())
|
||||||
|
|
||||||
|
|
||||||
func _on_settings_button_pressed() -> void:
|
|
||||||
settings_menu.show()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_settings_menu_close_requested() -> void:
|
|
||||||
settings_menu.hide()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_main_menu_button_pressed() -> void:
|
func _on_main_menu_button_pressed() -> void:
|
||||||
world.request_world_unload()
|
world.request_world_unload()
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
[gd_scene format=3 uid="uid://yuthv3c7rx8"]
|
[gd_scene format=3 uid="uid://yuthv3c7rx8"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://brd2041bv6vdk" path="res://src/ui/pause_menu/pause_menu.gd" id="1_fsbbv"]
|
[ext_resource type="Script" uid="uid://brd2041bv6vdk" path="res://src/ui/pause_menu/pause_menu.gd" id="1_fsbbv"]
|
||||||
[ext_resource type="PackedScene" uid="uid://cxm2uc8fw031f" path="res://src/ui/settings_menu/settings_menu.tscn" id="2_u4p1g"]
|
|
||||||
|
|
||||||
[node name="PauseMenu" type="Control" unique_id=90120455]
|
[node name="PauseMenu" type="Control" unique_id=90120455]
|
||||||
process_mode = 3
|
process_mode = 3
|
||||||
@ -14,15 +13,6 @@ grow_vertical = 2
|
|||||||
script = ExtResource("1_fsbbv")
|
script = ExtResource("1_fsbbv")
|
||||||
metadata/_custom_type_script = "uid://brd2041bv6vdk"
|
metadata/_custom_type_script = "uid://brd2041bv6vdk"
|
||||||
|
|
||||||
[node name="ColorRect" type="ColorRect" parent="." unique_id=1353751197]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
color = Color(0, 0, 0, 0.6901961)
|
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=905531263]
|
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=905531263]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 8
|
anchors_preset = 8
|
||||||
@ -42,28 +32,7 @@ unique_name_in_owner = true
|
|||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Continue"
|
text = "Continue"
|
||||||
|
|
||||||
[node name="SettingsButton" type="Button" parent="VBoxContainer" unique_id=1154942305]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Options"
|
|
||||||
|
|
||||||
[node name="MainMenuButton" type="Button" parent="VBoxContainer" unique_id=2079918606]
|
[node name="MainMenuButton" type="Button" parent="VBoxContainer" unique_id=2079918606]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Main Menu"
|
text = "Main Menu"
|
||||||
|
|
||||||
[node name="SettingsMenu" parent="." unique_id=8078639 instance=ExtResource("2_u4p1g")]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
visible = false
|
|
||||||
layout_mode = 1
|
|
||||||
|
|
||||||
[node name="ColorRect" type="ColorRect" parent="SettingsMenu" unique_id=660173424]
|
|
||||||
show_behind_parent = true
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
mouse_filter = 2
|
|
||||||
color = Color(0, 0, 0, 0.6901961)
|
|
||||||
|
|||||||
@ -1,190 +0,0 @@
|
|||||||
class_name SettingsMenu
|
|
||||||
extends Control
|
|
||||||
|
|
||||||
signal close_request
|
|
||||||
|
|
||||||
var _previously_focused_control: Control
|
|
||||||
|
|
||||||
@onready var back_button: Button = %BackButton
|
|
||||||
# Confirmation
|
|
||||||
@onready var revert_timer: Timer = %RevertTimer
|
|
||||||
@onready var commit_settings: Button = %CommitSettings
|
|
||||||
@onready var revert_settings: Button = %RevertSettings
|
|
||||||
@onready var confirmation_dialog: Control = %ConfirmationDialog
|
|
||||||
@onready var time_left_label: Label = %TimeLeftLabel
|
|
||||||
# Gameplay
|
|
||||||
@onready var headbobbing: CheckButton = %Headbobbing
|
|
||||||
@onready var headbobbing_slider: HSlider = %HeadbobbingSlider
|
|
||||||
# Localization
|
|
||||||
@onready var language: OptionButton = %Language
|
|
||||||
# Video Settings
|
|
||||||
@onready var window_mode: OptionButton = %WindowMode
|
|
||||||
# Audio Settings
|
|
||||||
@onready var rhythm_delay: Button = %RhythmDelay
|
|
||||||
@onready var master_slider: HSlider = %MasterSlider
|
|
||||||
@onready var sfx_slider: HSlider = %SFXSlider
|
|
||||||
@onready var music_slider: HSlider = %MusicSlider
|
|
||||||
@onready var ambience_slider: HSlider = %AmbienceSlider
|
|
||||||
|
|
||||||
@onready var rhythm_setup_menu: RhythmSetupMenu = $RhythmSetupMenu
|
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
|
||||||
update_controls_from_settings()
|
|
||||||
|
|
||||||
back_button.pressed.connect(close_request.emit)
|
|
||||||
# Confirmation
|
|
||||||
revert_timer.timeout.connect(_on_revert_timer_timeout)
|
|
||||||
commit_settings.pressed.connect(_on_commit_settings_pressed)
|
|
||||||
revert_settings.pressed.connect(_on_revert_settings_pressed)
|
|
||||||
# Gameplay
|
|
||||||
headbobbing.toggled.connect(_on_headbobbing_toggled)
|
|
||||||
headbobbing_slider.value_changed.connect(_on_headbobbing_intensity_value_changed)
|
|
||||||
# Localization
|
|
||||||
language.item_selected.connect(_on_language_selected)
|
|
||||||
# Video settings
|
|
||||||
window_mode.item_selected.connect(_on_window_mode_item_selected)
|
|
||||||
# Audio Settings
|
|
||||||
rhythm_delay.pressed.connect(_on_configure_rhythm_delay_pressed)
|
|
||||||
rhythm_setup_menu.confirmed.connect(_on_configure_rhythm_confirmed)
|
|
||||||
master_slider.value_changed.connect(_on_volume_slider_changed.bind(&"Master"))
|
|
||||||
sfx_slider.value_changed.connect(_on_volume_slider_changed.bind(&"SFX"))
|
|
||||||
music_slider.value_changed.connect(_on_volume_slider_changed.bind(&"Music"))
|
|
||||||
ambience_slider.value_changed.connect(_on_volume_slider_changed.bind(&"Ambient"))
|
|
||||||
|
|
||||||
visibility_changed.connect(func() -> void: if is_visible_in_tree(): update_controls_from_settings())
|
|
||||||
|
|
||||||
set_process(false)
|
|
||||||
|
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
|
||||||
if not revert_timer.is_stopped():
|
|
||||||
var second: int = ceili(revert_timer.time_left)
|
|
||||||
time_left_label.text = tr_n("SETTINGS_REVERT_TIME_LEFT", "", second) % second
|
|
||||||
|
|
||||||
|
|
||||||
func close() -> void:
|
|
||||||
if rhythm_setup_menu.is_visible_in_tree():
|
|
||||||
rhythm_setup_menu.close()
|
|
||||||
else:
|
|
||||||
close_request.emit()
|
|
||||||
|
|
||||||
|
|
||||||
func update_controls_from_settings() -> void:
|
|
||||||
# Gameplay
|
|
||||||
headbobbing.set_pressed_no_signal(SettingsHandler.get_setting("gameplay", "headbobbing", true))
|
|
||||||
headbobbing_slider.set_value_no_signal(SettingsHandler.get_setting("gameplay", "headbobbing_multiplier", 1.0))
|
|
||||||
|
|
||||||
# Localization
|
|
||||||
match SettingsHandler.get_setting("localization", "language", "en_US"):
|
|
||||||
"en_US":
|
|
||||||
language.select(0)
|
|
||||||
"de_DE":
|
|
||||||
language.select(1)
|
|
||||||
"es_ES":
|
|
||||||
language.select(2)
|
|
||||||
"ja_JA":
|
|
||||||
language.select(3)
|
|
||||||
|
|
||||||
# Video
|
|
||||||
match SettingsHandler.get_setting("video", "window_mode", 3):
|
|
||||||
3:
|
|
||||||
window_mode.select(0)
|
|
||||||
4:
|
|
||||||
window_mode.select(1)
|
|
||||||
0:
|
|
||||||
window_mode.select(2)
|
|
||||||
|
|
||||||
# Audio
|
|
||||||
master_slider.set_value_no_signal(SettingsHandler.get_setting("audio", "Master", 0.85))
|
|
||||||
sfx_slider.set_value_no_signal(SettingsHandler.get_setting("audio", "SFX", 1.0))
|
|
||||||
music_slider.set_value_no_signal(SettingsHandler.get_setting("audio", "Music", 1.0))
|
|
||||||
ambience_slider.set_value_no_signal(SettingsHandler.get_setting("audio", "Ambient", 1.0))
|
|
||||||
|
|
||||||
|
|
||||||
func ask_for_confirmation() -> void:
|
|
||||||
_previously_focused_control = get_viewport().gui_get_focus_owner()
|
|
||||||
confirmation_dialog.show()
|
|
||||||
revert_settings.grab_focus()
|
|
||||||
revert_timer.start()
|
|
||||||
set_process(true)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_commit_settings_pressed() -> void:
|
|
||||||
SettingsHandler.commit_settings()
|
|
||||||
SettingsHandler.save_settings()
|
|
||||||
revert_timer.stop()
|
|
||||||
confirmation_dialog.hide()
|
|
||||||
set_process(false)
|
|
||||||
|
|
||||||
if is_instance_valid(_previously_focused_control):
|
|
||||||
_previously_focused_control.grab_focus()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_revert_settings_pressed() -> void:
|
|
||||||
revert_timer.stop()
|
|
||||||
_on_revert_timer_timeout()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_revert_timer_timeout() -> void:
|
|
||||||
SettingsHandler.revert_settings()
|
|
||||||
confirmation_dialog.hide()
|
|
||||||
update_controls_from_settings()
|
|
||||||
set_process(false)
|
|
||||||
|
|
||||||
if is_instance_valid(_previously_focused_control):
|
|
||||||
_previously_focused_control.grab_focus()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_headbobbing_toggled(toggled_on: bool) -> void:
|
|
||||||
SettingsHandler.set_setting("gameplay", "headbobbing", toggled_on)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_headbobbing_intensity_value_changed(value: float) -> void:
|
|
||||||
SettingsHandler.set_setting("gameplay", "headbobbing_multiplier", value)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_language_selected(index: int) -> void:
|
|
||||||
var language_string: String = "en_US"
|
|
||||||
match index:
|
|
||||||
0:
|
|
||||||
language_string = "en_US"
|
|
||||||
1:
|
|
||||||
language_string = "de_DE"
|
|
||||||
2:
|
|
||||||
language_string = "es_ES"
|
|
||||||
3:
|
|
||||||
language_string = "ja_JA"
|
|
||||||
|
|
||||||
SettingsHandler.set_setting("localization", "language", language_string, false)
|
|
||||||
SettingsHandler.apply_settings()
|
|
||||||
ask_for_confirmation()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_window_mode_item_selected(index: int) -> void:
|
|
||||||
var mode: DisplayServer.WindowMode
|
|
||||||
match index:
|
|
||||||
0:
|
|
||||||
mode = DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN
|
|
||||||
1:
|
|
||||||
mode = DisplayServer.WindowMode.WINDOW_MODE_EXCLUSIVE_FULLSCREEN
|
|
||||||
2:
|
|
||||||
mode = DisplayServer.WindowMode.WINDOW_MODE_WINDOWED
|
|
||||||
_:
|
|
||||||
mode = DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN
|
|
||||||
|
|
||||||
SettingsHandler.set_setting("video", "window_mode", mode, false)
|
|
||||||
SettingsHandler.apply_settings()
|
|
||||||
ask_for_confirmation()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_configure_rhythm_delay_pressed() -> void:
|
|
||||||
rhythm_setup_menu.show()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_configure_rhythm_confirmed() -> void:
|
|
||||||
rhythm_setup_menu.hide()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_volume_slider_changed(value: float, bus: StringName) -> void:
|
|
||||||
SettingsHandler.set_setting("audio", bus, value)
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://cll4odq5p7iuv
|
|
||||||
@ -1,414 +0,0 @@
|
|||||||
[gd_scene format=3 uid="uid://cxm2uc8fw031f"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://cll4odq5p7iuv" path="res://src/ui/settings_menu/settings_menu.gd" id="1_3iv5y"]
|
|
||||||
[ext_resource type="Script" uid="uid://bfpr421kg4s" path="res://src/ui/settings_menu/slider_label.gd" id="2_axtgf"]
|
|
||||||
[ext_resource type="PackedScene" uid="uid://b41bjano4bw6s" path="res://src/ui/setup/rhythm/rhythm_setup_menu.tscn" id="3_kktd5"]
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_axtgf"]
|
|
||||||
content_margin_left = 0.0
|
|
||||||
content_margin_top = 0.0
|
|
||||||
content_margin_right = 0.0
|
|
||||||
content_margin_bottom = 0.0
|
|
||||||
bg_color = Color(0.1, 0.1, 0.1, 0.6)
|
|
||||||
border_width_left = 4
|
|
||||||
border_width_top = 4
|
|
||||||
border_width_right = 4
|
|
||||||
border_width_bottom = 4
|
|
||||||
corner_radius_top_left = 3
|
|
||||||
corner_radius_top_right = 3
|
|
||||||
corner_radius_bottom_right = 3
|
|
||||||
corner_radius_bottom_left = 3
|
|
||||||
corner_detail = 5
|
|
||||||
|
|
||||||
[node name="SettingsMenu" type="Control" unique_id=8078639]
|
|
||||||
layout_mode = 3
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
script = ExtResource("1_3iv5y")
|
|
||||||
metadata/_custom_type_script = "uid://cll4odq5p7iuv"
|
|
||||||
|
|
||||||
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=747218901]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = -1
|
|
||||||
anchor_left = 0.5
|
|
||||||
anchor_top = 0.05
|
|
||||||
anchor_right = 0.5
|
|
||||||
anchor_bottom = 0.95
|
|
||||||
offset_left = -200.0
|
|
||||||
offset_right = 200.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer" unique_id=481159029]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/VBoxContainer" unique_id=1719442009]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_vertical = 3
|
|
||||||
follow_focus = true
|
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/VBoxContainer/ScrollContainer" unique_id=1482670452]
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
size_flags_vertical = 3
|
|
||||||
theme_override_constants/margin_left = 12
|
|
||||||
theme_override_constants/margin_top = 12
|
|
||||||
theme_override_constants/margin_right = 12
|
|
||||||
theme_override_constants/margin_bottom = 12
|
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer" unique_id=1149761963]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="GameplaySettings" type="Control" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=2072631132]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="GameplayLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1726505815]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_font_sizes/font_size = 32
|
|
||||||
text = "Gameplay"
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="HSeparator" type="HSeparator" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=136767090]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="Headbobbing" type="CheckButton" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1121847591]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
button_pressed = true
|
|
||||||
text = "Headbobbing"
|
|
||||||
|
|
||||||
[node name="HeadbobbingLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1435927865]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Headbobbing-Intensity"
|
|
||||||
|
|
||||||
[node name="HeadbobbingMultiplier" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=883105611]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="HeadbobbingSlider" type="HSlider" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HeadbobbingMultiplier" unique_id=1792577413]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
max_value = 1.0
|
|
||||||
step = 0.01
|
|
||||||
value = 1.0
|
|
||||||
tick_count = 11
|
|
||||||
ticks_on_borders = true
|
|
||||||
|
|
||||||
[node name="SliderLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HeadbobbingMultiplier" unique_id=790300279 node_paths=PackedStringArray("slider")]
|
|
||||||
custom_minimum_size = Vector2(32, 0)
|
|
||||||
layout_mode = 2
|
|
||||||
text = "1.00"
|
|
||||||
horizontal_alignment = 2
|
|
||||||
script = ExtResource("2_axtgf")
|
|
||||||
slider = NodePath("../HeadbobbingSlider")
|
|
||||||
|
|
||||||
[node name="LocalizationSettings" type="Control" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1387881590]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="LocalizationLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1225964665]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_font_sizes/font_size = 32
|
|
||||||
text = "Localization"
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="HSeparator2" type="HSeparator" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=282328287]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="Language" type="OptionButton" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=210620308]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
flat = true
|
|
||||||
selected = 0
|
|
||||||
item_count = 4
|
|
||||||
popup/item_0/text = "English"
|
|
||||||
popup/item_0/id = 0
|
|
||||||
popup/item_1/text = "German"
|
|
||||||
popup/item_1/id = 1
|
|
||||||
popup/item_2/text = "Spanish"
|
|
||||||
popup/item_2/id = 2
|
|
||||||
popup/item_3/text = "Japanese"
|
|
||||||
popup/item_3/id = 3
|
|
||||||
|
|
||||||
[node name="VideoSettings" type="Control" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=421589929]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="VideoLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=80166823]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_font_sizes/font_size = 32
|
|
||||||
text = "Video"
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="HSeparator3" type="HSeparator" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=188845275]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="WindowMode" type="OptionButton" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=512024521]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
selected = 0
|
|
||||||
item_count = 3
|
|
||||||
popup/item_0/text = "WINDOW_FULLSCREEN"
|
|
||||||
popup/item_0/id = 1
|
|
||||||
popup/item_1/text = "WINDOW_EXCLUSIVE_FULLSCREEN"
|
|
||||||
popup/item_1/id = 2
|
|
||||||
popup/item_2/text = "WINDOW_WINDOWED"
|
|
||||||
popup/item_2/id = 0
|
|
||||||
|
|
||||||
[node name="AudioSettings" type="Control" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1454411793]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="AudioLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1475246011]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_font_sizes/font_size = 32
|
|
||||||
text = "Audio"
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="HSeparator4" type="HSeparator" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1708002596]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="RhythmDelay" type="Button" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1954746002]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Configure Rhythm Delay"
|
|
||||||
|
|
||||||
[node name="AudioSliders" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1541515152]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="MasterLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=1666127429]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "MASTER"
|
|
||||||
|
|
||||||
[node name="MasterContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=63007160]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="MasterSlider" type="HSlider" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/MasterContainer" unique_id=43103749]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
max_value = 1.0
|
|
||||||
step = 0.01
|
|
||||||
value = 0.85
|
|
||||||
tick_count = 11
|
|
||||||
ticks_on_borders = true
|
|
||||||
|
|
||||||
[node name="SliderLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/MasterContainer" unique_id=1539756970 node_paths=PackedStringArray("slider")]
|
|
||||||
custom_minimum_size = Vector2(32, 0)
|
|
||||||
layout_mode = 2
|
|
||||||
text = "85"
|
|
||||||
horizontal_alignment = 2
|
|
||||||
script = ExtResource("2_axtgf")
|
|
||||||
slider = NodePath("../MasterSlider")
|
|
||||||
pad_decimals = 0
|
|
||||||
do_remap = true
|
|
||||||
remap_output_max = 100.0
|
|
||||||
|
|
||||||
[node name="SFXLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=96391289]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "SFX"
|
|
||||||
|
|
||||||
[node name="SFXContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=1321350084]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="SFXSlider" type="HSlider" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/SFXContainer" unique_id=954640158]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
max_value = 1.0
|
|
||||||
step = 0.01
|
|
||||||
value = 1.0
|
|
||||||
tick_count = 11
|
|
||||||
ticks_on_borders = true
|
|
||||||
|
|
||||||
[node name="SliderLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/SFXContainer" unique_id=1852166469 node_paths=PackedStringArray("slider")]
|
|
||||||
custom_minimum_size = Vector2(32, 0)
|
|
||||||
layout_mode = 2
|
|
||||||
text = "100"
|
|
||||||
horizontal_alignment = 2
|
|
||||||
script = ExtResource("2_axtgf")
|
|
||||||
slider = NodePath("../SFXSlider")
|
|
||||||
pad_decimals = 0
|
|
||||||
do_remap = true
|
|
||||||
remap_output_max = 100.0
|
|
||||||
|
|
||||||
[node name="MusicLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=1699726056]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "MUSIC"
|
|
||||||
|
|
||||||
[node name="MusicContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=1571401865]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="MusicSlider" type="HSlider" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/MusicContainer" unique_id=1554055762]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
max_value = 1.0
|
|
||||||
step = 0.01
|
|
||||||
value = 1.0
|
|
||||||
tick_count = 11
|
|
||||||
ticks_on_borders = true
|
|
||||||
|
|
||||||
[node name="SliderLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/MusicContainer" unique_id=722315379 node_paths=PackedStringArray("slider")]
|
|
||||||
custom_minimum_size = Vector2(32, 0)
|
|
||||||
layout_mode = 2
|
|
||||||
text = "100"
|
|
||||||
horizontal_alignment = 2
|
|
||||||
script = ExtResource("2_axtgf")
|
|
||||||
slider = NodePath("../MusicSlider")
|
|
||||||
pad_decimals = 0
|
|
||||||
do_remap = true
|
|
||||||
remap_output_max = 100.0
|
|
||||||
|
|
||||||
[node name="AmbienceLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=679820709]
|
|
||||||
layout_mode = 2
|
|
||||||
text = "AMBIENT"
|
|
||||||
|
|
||||||
[node name="AmbienceContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=852171003]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="AmbienceSlider" type="HSlider" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/AmbienceContainer" unique_id=1743356533]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
max_value = 1.0
|
|
||||||
step = 0.01
|
|
||||||
value = 1.0
|
|
||||||
tick_count = 11
|
|
||||||
ticks_on_borders = true
|
|
||||||
|
|
||||||
[node name="SliderLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/AmbienceContainer" unique_id=1842144008 node_paths=PackedStringArray("slider")]
|
|
||||||
custom_minimum_size = Vector2(32, 0)
|
|
||||||
layout_mode = 2
|
|
||||||
text = "100"
|
|
||||||
horizontal_alignment = 2
|
|
||||||
script = ExtResource("2_axtgf")
|
|
||||||
slider = NodePath("../AmbienceSlider")
|
|
||||||
pad_decimals = 0
|
|
||||||
do_remap = true
|
|
||||||
remap_output_max = 100.0
|
|
||||||
|
|
||||||
[node name="ControlsSettings" type="Control" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=738451135]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="ControlsLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=997989979]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_font_sizes/font_size = 32
|
|
||||||
text = "Controls"
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="HSeparator5" type="HSeparator" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1211938154]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="VibrationConfig" type="Button" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=253824648]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
text = "Configure Vibrations"
|
|
||||||
|
|
||||||
[node name="BackButton" type="Button" parent="PanelContainer/VBoxContainer" unique_id=1438707398]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
text = "BACK"
|
|
||||||
|
|
||||||
[node name="ConfirmationDialog" type="Control" parent="." unique_id=1084371767]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
visible = false
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
|
|
||||||
[node name="RevertTimer" type="Timer" parent="ConfirmationDialog" unique_id=494220922]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
wait_time = 5.0
|
|
||||||
one_shot = true
|
|
||||||
|
|
||||||
[node name="ColorRect" type="ColorRect" parent="ConfirmationDialog" unique_id=87130800]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
color = Color(0, 0, 0, 0.7254902)
|
|
||||||
|
|
||||||
[node name="ConfirmationPanel" type="PanelContainer" parent="ConfirmationDialog" unique_id=1058124629]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 8
|
|
||||||
anchor_left = 0.5
|
|
||||||
anchor_top = 0.5
|
|
||||||
anchor_right = 0.5
|
|
||||||
anchor_bottom = 0.5
|
|
||||||
offset_left = -132.5
|
|
||||||
offset_top = -85.5
|
|
||||||
offset_right = 132.5
|
|
||||||
offset_bottom = 85.5
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_axtgf")
|
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="ConfirmationDialog/ConfirmationPanel" unique_id=1476949599]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_constants/margin_left = 12
|
|
||||||
theme_override_constants/margin_top = 12
|
|
||||||
theme_override_constants/margin_right = 12
|
|
||||||
theme_override_constants/margin_bottom = 12
|
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer" unique_id=1736269226]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_constants/separation = 8
|
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer" unique_id=458855790]
|
|
||||||
layout_mode = 2
|
|
||||||
theme_override_font_sizes/font_size = 32
|
|
||||||
text = "SETTINGS_CONFIRMATION_TITLE"
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="HSeparator" type="HSeparator" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer" unique_id=1567845501]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="TimeLeftLabel" type="Label" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer" unique_id=130409981]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
text = "SETTINGS_REVERT_TIME_LEFT"
|
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="HSeparator2" type="HSeparator" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer" unique_id=11885534]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="Control" type="Control" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer" unique_id=1620955474]
|
|
||||||
layout_mode = 2
|
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer" unique_id=1766652365]
|
|
||||||
layout_mode = 2
|
|
||||||
alignment = 1
|
|
||||||
|
|
||||||
[node name="CommitSettings" type="Button" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer/HBoxContainer" unique_id=1797911803]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
text = "SETTINGS_APPLY"
|
|
||||||
|
|
||||||
[node name="RevertSettings" type="Button" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer/HBoxContainer" unique_id=1089460477]
|
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
|
||||||
size_flags_horizontal = 3
|
|
||||||
text = "SETTINGS_REVERT"
|
|
||||||
|
|
||||||
[node name="RhythmSetupMenu" parent="." unique_id=552044082 instance=ExtResource("3_kktd5")]
|
|
||||||
visible = false
|
|
||||||
layout_mode = 1
|
|
||||||
mouse_filter = 0
|
|
||||||
|
|
||||||
[node name="ColorRect" type="ColorRect" parent="RhythmSetupMenu" unique_id=539350747]
|
|
||||||
modulate = Color(0, 0, 0, 1)
|
|
||||||
show_behind_parent = true
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = 15
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
mouse_filter = 2
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user